View Javadoc
1   /*
2    * #%L
3    * SUMARiS
4    * %%
5    * Copyright (C) 2019 SUMARiS Consortium
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as
9    * published by the Free Software Foundation, either version 3 of the
10   * License, or (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/gpl-3.0.html>.
20   * #L%
21   */
22  
23  package net.sumaris.rdf.util;
24  
25  import net.sumaris.core.model.referential.Status;
26  import net.sumaris.core.model.referential.taxon.TaxonName;
27  import net.sumaris.core.model.referential.taxon.TaxonomicLevel;
28  import org.apache.jena.ontology.OntClass;
29  import org.apache.jena.ontology.OntResource;
30  import org.apache.jena.rdf.model.RDFNode;
31  import org.apache.jena.rdf.model.Resource;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import javax.persistence.EntityManager;
36  import javax.persistence.Id;
37  import java.lang.annotation.Annotation;
38  import java.lang.reflect.Field;
39  import java.lang.reflect.Method;
40  import java.util.*;
41  import static net.sumaris.rdf.util.OwlUtils.*;
42  
43  public abstract class Owl2Bean {
44      /**
45       * Logger.
46       */
47      private static Logger log = LoggerFactory.getLogger(Owl2Bean.class);
48  
49      private EntityManager entityManager;
50  
51      private String modelPrefix;
52  
53      public Owl2Bean(EntityManager entityManager, String modelPrefix) {
54          this.entityManager = entityManager;
55          this.modelPrefix = modelPrefix;
56      }
57  
58      protected String getModelPrefix() {
59          return modelPrefix;
60      }
61  
62      protected EntityManager getEntityManager() {
63          return entityManager;
64      }
65  
66      public Optional<Class> ontToJavaClass(OntClass ontClass, RdfImportContext context) {
67          String uri = ontClass.getURI();
68          if (uri != null) {
69              if (uri.contains("#")) {
70                  uri = uri.substring(0, uri.indexOf("#"));
71                  log.warn(" tail '#'  " + uri);
72              }
73              if (uri.contains("<")) {
74                  uri = uri.substring(0, uri.indexOf("<"));
75                  log.warn(" tail <parametrized> " + uri);
76              }
77          }
78  
79          if (uri == null) {
80              log.error(" uri null for OntClass " + ontClass);
81              return Optional.empty();
82          }
83  
84  
85          String cName = uri.substring(uri.lastIndexOf("/") + 1);
86          Class clazz = context.URI_2_CLASS.get(cName);
87  
88          if (clazz == null) {
89              log.warn(" clazz not mapped for class " + cName);
90              return Optional.empty();
91          }
92  
93          if (clazz.isInterface()) {
94              log.warn(" corresponding Type is interface, skip instances " + clazz);
95              return Optional.empty();
96          }
97          return Optional.of(clazz);
98      }
99  
100     protected boolean classEquals(Class c, Class<?> d) {
101         return Objects.equals(d.getTypeName(), c.getTypeName());
102     }
103 
104     protected abstract List getCacheTL();
105     protected abstract List getCacheStatus();
106 
107     protected Object getTranslatedReference(RDFNode val, Class<?> setterParam, Object obj, RdfImportContext context) {
108 
109         String identifier = val.toString();
110         String ontClass = null;
111         if (identifier.contains("#")) {
112             ontClass = identifier.substring(0, identifier.indexOf("#"));
113             identifier = identifier.substring(identifier.indexOf("#") + 1);
114         }
115         if (setterParam == TaxonomicLevel.class) {
116 
117             for (Object ctl : getCacheTL()) {
118                 String lab = ((TaxonomicLevel) ctl).getLabel();
119                 if (identifier.endsWith(lab)) {
120                     return ctl;
121                 }
122             }
123 
124 
125             // if none were cached, create a new TaxonomicLevel
126             TaxonomicLevelxonomicLevel./../../net/sumaris/core/model/referential/taxon/TaxonomicLevel.html#TaxonomicLevel">TaxonomicLevel tl = (TaxonomicLevelxonomicLevel) context.URI_2_OBJ_REF.getOrDefault(val.toString(), new TaxonomicLevel());
127             tl.setLabel(identifier);
128             tl.setCreationDate(new Date());
129             tl.setName("");
130             tl.setRankOrder(1);
131             tl.setStatus((Status) getCacheStatus().get(0));
132 
133             getEntityManager().persist(tl);
134             log.warn("getEntityManager().persist(  TaxonomicLevel ) " + tl);
135 
136             //B2O_ARBITRARY_MAPPER.get(ontClass).apply( val.as(OntResource.class));
137 
138             return context.URI_2_OBJ_REF.putIfAbsent(val.toString(), tl);
139         }
140 
141 
142         // protected case, try to fetch reference (@Id) as Integer or String
143         log.warn("getTranslatedReference " + identifier + " - " + val + " - " + obj);
144         Object ref;
145         try {
146             Integer asInt = Integer.parseInt(identifier);
147             ref = getEntityManager().getReference(setterParam, asInt);
148         } catch (NumberFormatException e) {
149             ref = getEntityManager().getReference(setterParam, identifier);
150         }
151         return ref;
152     }
153 
154     protected String attributeOf(String pred) {
155         String fName = pred.substring(pred.indexOf("#") + 1);
156         fName = fName.substring(0, 1).toLowerCase() + fName.substring(1);
157         return fName;
158     }
159 
160     protected void fillObjectWithStdAttribute(Method setter, Object obj, RDFNode val) {
161         String value = val.isURIResource() ? val.toString().substring(val.toString().lastIndexOf("#") + 1) : val.toString();
162         Class<?> setterParam = setter.getParameterTypes()[0];
163         try {
164             if (classEquals(setterParam, String.class)) {
165                 setter.invoke(obj, val.asLiteral().getString());
166             }
167             if (classEquals(setterParam, Long.class) || classEquals(setterParam, long.class)) {
168                 setter.invoke(obj, val.asLiteral().getLong());
169             } else if (classEquals(setterParam, Integer.class) || classEquals(setterParam, int.class)) {
170                 setter.invoke(obj, Integer.parseInt(value));
171             } else if (classEquals(setterParam, Date.class)) {
172                 setter.invoke(obj, OwlUtils.SIMPLE_DATE_FORMAT.parse(val.asLiteral().getString()));
173             } else if (classEquals(setterParam, Boolean.class) || classEquals(setterParam, boolean.class)) {
174                 setter.invoke(obj, val.asLiteral().getBoolean());
175             }
176         } catch (Exception e) {
177             log.warn("fillObjectWithStdAttribute could not reconstruct attribute "
178                     + setter.getDeclaringClass().getSimpleName() + "." + setter.getName() + "(" + setterParam.getSimpleName() + ") for val " + val, e);
179         }
180     }
181 
182     public Optional<Object> owl2Bean(Resource ont, OntResource ontResource, Class clazz, RdfImportContext context) {
183         log.info("processing ont Instance " + ontResource + " - " +
184                 ontResource
185                         .asIndividual()
186                         .listProperties().toList().size());
187 
188         try {
189             Object obj = clazz.newInstance();
190 
191             ontResource
192                     .asIndividual()
193                     .listProperties()
194                     .toList()
195                     .forEach(stmt -> {
196                         String pred = stmt.getPredicate().getURI();
197                         RDFNode val = stmt.getObject();
198                         if ((pred.startsWith(getModelPrefix()) || pred.startsWith(OwlUtils.ADAGIO_PREFIX)) && pred.contains("#")) {
199                             String fName = attributeOf(pred);
200                             try {
201 
202                                 Optional<Method> setter;
203                                 if ("setId".equals(fName)) {
204                                     setter = findSetterAnnotatedID(ont, clazz, context);
205                                 } else {
206                                     setter = setterOfField(ont, clazz, fName, context);
207                                 }
208 
209                                 if (setter.isPresent()) {
210                                     Class<?> setterParam = setter.get().getParameterTypes()[0];
211 
212                                     if (log.isTraceEnabled()) log.trace("Trying to insert  " + fName + " => " + val + " using method ??");
213 
214                                     if (isJavaType(setterParam)) {
215                                         fillObjectWithStdAttribute(setter.get(), obj, val);
216                                     } else {
217                                         //FIXME if entity  is different we shouldn't use the invoked method
218                                         setter.get().invoke(obj, getTranslatedReference(val, setterParam, obj, context));
219                                     }
220                                 }
221 
222                             } catch (Exception e) {
223                                 log.error(String.format("%s on field %s => %s using class %s using method %s %s",
224                                         e.getClass().getSimpleName(), fName, val, clazz, setterOfField(ont, clazz, fName, context), e.getMessage()), e);
225                             }
226 
227                             //values.put(fName, safeCastRDFNode(val, fName, clazz));
228                         }
229 
230                     });
231             if (obj instanceof TaxonName) {
232                 TaxonName"../../../../net/sumaris/core/model/referential/taxon/TaxonName.html#TaxonName">TaxonName tn = (TaxonName) obj;
233                // tn.setName("tn");
234                 tn.setReferenceTaxon(null);
235                 getEntityManager().merge(tn);
236             }
237             //getEntityManager().merge(obj);
238             log.info("  - created object " + ontResource + " - " + " of class " + ontResource.getClass() + "  - ");
239             return Optional.of(obj);
240         } catch (Exception e) {
241             log.error(" processing individual " + ontResource + " - " + clazz, e);
242         }
243         return Optional.empty();
244     }
245 
246 
247     protected Optional<Method> findSetterAnnotatedID(Resource ont, Class clazz, RdfImportContext context) {
248         for (Field f : clazz.getDeclaredFields())
249             for (Annotation an : f.getDeclaredAnnotations())
250                 if (an instanceof Id) {
251                     return setterOfField(ont, clazz, f.getName(), context);
252 
253                 }
254         return Optional.empty();
255     }
256 
257 }