1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package net.sumaris.rdf.util;
24
25 import org.apache.jena.ontology.OntClass;
26 import org.apache.jena.ontology.OntModel;
27 import org.apache.jena.ontology.OntProperty;
28 import org.apache.jena.rdf.model.*;
29 import org.apache.jena.vocabulary.OWL;
30 import org.apache.jena.vocabulary.RDF;
31 import org.apache.jena.vocabulary.RDFS;
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.lang.reflect.ParameterizedType;
41 import java.lang.reflect.Type;
42 import java.util.*;
43 import java.util.stream.Stream;
44 import static net.sumaris.rdf.util.OwlUtils.*;
45
46 public class Bean2Owl {
47
48
49
50
51 private static Logger LOG = LoggerFactory.getLogger(Bean2Owl.class);
52
53 private String modelPrefix;
54
55 public Bean2Owl(String modelPrefix) {
56 this.modelPrefix = modelPrefix;
57 }
58
59 protected String getModelPrefix() {
60 return modelPrefix;
61 }
62
63
64
65
66
67
68
69
70 public OntClass classToOwl(OntModel ontology, Class clazz, Map<OntClass, List<OntClass>> mutuallyDisjoint, boolean addInterface, boolean addMethods) {
71
72 Resource schema = ontology.listSubjectsWithProperty(RDF.type, OWL.Ontology).nextResource();
73
74 try {
75
76
77
78
79
80 OntClass aClass = ontology.createClass(classToURI(schema, clazz));
81 aClass.setIsDefinedBy(schema);
82
83
84
85 String label = clazz.getName().substring(clazz.getName().lastIndexOf(".") + 1);
86
87 aClass.addLabel(label, "en");
88
89 String entityName = clazz.getName();
90 if (!"".equals(entityName))
91 aClass.addComment(entityName, "en");
92
93
94 if (mutuallyDisjoint != null && addInterface) {
95 Stream.of(clazz.getGenericInterfaces())
96 .filter(interfaze -> !ParameterizedType.class.equals(interfaze))
97 .forEach(interfaze -> {
98
99 OntClass r = typeToUri(schema, interfaze);
100 if (!mutuallyDisjoint.containsKey(r)) {
101 mutuallyDisjoint.put(r, new ArrayList<>());
102 }
103 mutuallyDisjoint.get(r).add(aClass);
104
105 aClass.addSuperClass(r);
106 });
107 }
108
109
110 Stream.of(clazz.getGenericSuperclass())
111 .filter(c -> c != null && !Object.class.equals(c))
112 .forEach(i -> {
113 OntClass ont = typeToUri(schema, i);
114 aClass.addSuperClass(ont);
115 }
116 );
117
118
119 if (addMethods) {
120 Stream.of(clazz.getMethods())
121 .filter(m -> !isSetter(m))
122 .filter(m -> !isGetter(m))
123 .filter(m -> Stream.of("getBytes", "hashCode", "getClass", "toString", "equals", "wait", "notify", "notifyAll").noneMatch(s -> s.equals(m.getName())))
124
125 .forEach(met -> {
126
127 String name = classToURI(schema, clazz) + "#" + met.getName();
128 OntProperty function = ontology.createObjectProperty(name, true);
129 function.addDomain(aClass.asResource());
130 if (isJavaType(met.getReturnType())) {
131 function.addRange(getStdType(met.getReturnType()));
132 } else {
133 OntClass o = typeToUri(schema, met.getReturnType());
134 if (o.getURI().endsWith("void"))
135 function.addRange(RDFS.Literal);
136 else
137 function.addRange(o);
138 }
139 function.setIsDefinedBy(schema);
140 function.addLabel(met.getName(), "en");
141 });
142 }
143
144
145 Stream.of(clazz.getMethods())
146 .filter(OwlUtils::isGetter)
147 .map(OwlUtils::getFieldOfGetteR)
148 .forEach(field -> {
149
150 String fieldName = classToURI(schema, clazz) + "#" + field.getName();
151
152 if (isJavaType(field)) {
153 Resource type = getStdType(field);
154
155 OntProperty stdType = ontology.createDatatypeProperty(fieldName, true);
156
157 stdType.setDomain(aClass.asResource());
158 stdType.setRange(type);
159 stdType.setIsDefinedBy(schema);
160
161
162 stdType.addLabel(field.getName(), "en");
163
164
165 } else if (field.getDeclaringClass().isArray()) {
166
167 } else if (isListType(field.getGenericType())) {
168 Type contained = getListType(field.getGenericType());
169 OntProperty list = null;
170 Resource resou = null;
171
172
173 if (isJavaType(contained)) {
174 list = ontology.createDatatypeProperty(fieldName, true);
175 resou = getStdType(contained);
176 } else {
177 list = ontology.createObjectProperty(fieldName, false);
178 resou = typeToUri(schema, contained);
179 }
180
181 list.addRange(resou);
182 list.addDomain(aClass.asResource());
183 list.setIsDefinedBy(schema);
184 list.addLabel("list" + field.getName(), "en");
185
186 createZeroToMany(ontology, aClass, list, resou);
187
188 } else {
189
190
191 OntProperty bean = ontology.createObjectProperty(fieldName, true);
192 bean.addDomain(aClass.asResource());
193 bean.addRange(typeToUri(schema, field.getType()));
194 bean.setIsDefinedBy(schema);
195 bean.addLabel(field.getName(), "en");
196
197
198 }
199 });
200 return aClass;
201 } catch (Exception e) {
202 LOG.error(e.getMessage(), e);
203 }
204 return null;
205 }
206
207 protected OntClass typeToUri(Resource schema, Type t) {
208
209 OntModel model = ((OntModel) schema.getModel());
210
211 String uri = schema + t.getTypeName();
212 if (t instanceof ParameterizedType) {
213 uri = uri.substring(0, uri.indexOf("<"));
214 }
215
216 uri = uri.substring(uri.lastIndexOf(".") + 1);
217
218 OntClass ont = model.getOntClass(uri);
219
220 if (ont == null) {
221
222 String name = t.getTypeName();
223 name = name.substring(name.lastIndexOf(".") + 1);
224
225 ont = model.createClass(schema + name);
226 }
227
228 ont.setIsDefinedBy(schema);
229 ont.addComment(t.getTypeName(), "en");
230
231
232 return ont;
233
234 }
235
236 protected OntClass interfaceToOwl(OntModel model, Type type) {
237
238 String name = type.getTypeName();
239 name = name.substring(name.lastIndexOf(".") + 1);
240
241 return model.createClass(getModelPrefix() + name);
242 }
243
244 public Resource bean2Owl(OntModel model, Object obj, int depth,
245 List<Method> includes,
246 List<Method> excludes) {
247 Resource schema = model.listSubjectsWithProperty(RDF.type, OWL.Ontology).nextResource();
248
249 if (obj == null) {
250 LOG.error("bean2Owl received a null object as parameter");
251 return null;
252 }
253 String classURI = classToURI(schema, obj.getClass());
254 OntClass ontClazz = model.getOntClass(classURI);
255 if (ontClazz == null) {
256 LOG.warn("ontClazz " + ontClazz + ", not found in model, mak" +
257 "ing one at " + classURI);
258 ontClazz = classToOwl(model, obj.getClass(), null, true, true);
259 }
260
261
262 String individualURI;
263 try {
264 Method m = findGetterAnnotatedID(obj.getClass());
265 individualURI = classURI + "#" + m.invoke(obj);
266
267 } catch (Exception e) {
268 individualURI = "";
269 LOG.error(e.getClass().getName() + " bean2Owl " + classURI + " - ");
270 }
271 Resource individual = ontClazz.createIndividual(individualURI);
272 if (depth < 0) {
273 LOG.error("Max depth reached " + depth);
274 return individual;
275 } else {
276
277
278 }
279
280
281
282 Stream.of(obj.getClass().getMethods())
283 .filter(OwlUtils::isGetter)
284 .filter(met -> excludes.stream().noneMatch(x -> x.equals(met)))
285 .filter(met -> {
286
287
288
289
290 return (!isManyToOne(met) || includes.contains(met));
291 })
292 .forEach(met -> {
293
294
295 try {
296 Object invoked = met.invoke(obj);
297 if (invoked == null) {
298
299 return;
300 }
301 Property pred = model.createProperty(classURI, "#" + met.getName().replace("get", ""));
302
303 if (isId(met)) {
304 individual.addProperty(pred, invoked + "");
305 } else if ("getClass".equals(met.getName())) {
306 individual.addProperty(RDF.type, pred);
307 } else if (invoked.getClass().getCanonicalName().contains("$")) {
308
309 } else if (!isJavaType(met)) {
310
311
312 Resource recurse = bean2Owl(model, invoked, (depth - 1), includes, excludes);
313 if (recurse != null)
314 individual.addProperty(pred, recurse);
315 } else if (met.getGenericReturnType() instanceof ParameterizedType) {
316
317 Resource anonId = model.createResource(new AnonId("params" + new Random().nextInt(1000000)));
318
319
320 Optional<Resource> listNode = fillDataList(model, met.getGenericReturnType(), invoked, pred, anonId, depth - 1,
321 includes,
322 excludes);
323 if (listNode.isPresent()) {
324 LOG.info(" --and res : " + listNode.get().getURI());
325 individual.addProperty(pred, listNode.get());
326 }
327
328 } else {
329 if (met.getName().toLowerCase().contains("date")) {
330
331 individual.addProperty(pred, SIMPLE_DATE_FORMAT.format((Date) invoked));
332
333 } else {
334 individual.addProperty(pred, invoked + "");
335 }
336 }
337
338 } catch (Exception e) {
339 LOG.error(e.getMessage(), e);
340 }
341
342 });
343
344 return individual;
345 }
346
347
348 protected Method findGetterAnnotatedID(Class clazz) {
349 for (Field f : clazz.getDeclaredFields())
350 for (Annotation an : f.getDeclaredAnnotations())
351 if (an instanceof Id)
352 return getterOfField(clazz, f.getName());
353
354 return null;
355 }
356
357 protected Optional<Resource> fillDataList(OntModel model, Type type, Object listObject, Property prop, Resource fieldId, int depth,
358 List<Method> includes,
359 List<Method> excludes) {
360
361 if (isListType(type)) {
362
363
364
365 List<RDFNode> nodes = new ArrayList<>();
366 List<? extends Object> asList = castListSafe((List<? extends Object>) listObject, Object.class);
367
368 if (asList.isEmpty()) {
369 LOG.warn(" - empty list, ignoring ");
370 return Optional.empty();
371 }
372 for (Object x : asList) {
373 Resource listItem = bean2Owl(model, x, (depth - 1), includes, excludes);
374 nodes.add(listItem);
375
376 }
377
378 RDFList list = model.createList(nodes.toArray(new RDFNode[nodes.size()]));
379
380 LOG.info(" - rdflist " + list.size() + " : " + list);
381
382 fieldId.addProperty(prop, list);
383
384 return Optional.of(list);
385
386 }
387
388
389 return Optional.empty();
390 }
391
392
393
394
395
396
397
398
399 protected <T> List<? super T> castListSafe(List<?> data, Class<T> listType) {
400 List<T> retval = null;
401
402 if (data != null && !data.isEmpty() && listType.isInstance(data.iterator().next().getClass())) {
403 LOG.info(" - castListSafe passed check ");
404
405 @SuppressWarnings("unchecked")
406 List<T> foo = (List<T>) data;
407 return foo;
408 }
409
410 LOG.info(" - castListSafe failed check forcing it though");
411 return (List<T>) data;
412 }
413
414
415 }