1 package eu.ehri.project.exporters.cvoc;
2
3 import com.google.common.collect.ImmutableMap;
4 import com.google.common.collect.Lists;
5 import com.google.common.collect.Sets;
6 import com.tinkerpop.blueprints.Direction;
7 import com.tinkerpop.frames.Adjacency;
8 import com.tinkerpop.frames.Property;
9 import eu.ehri.project.models.EntityClass;
10 import eu.ehri.project.models.annotations.InverseOf;
11 import eu.ehri.project.models.annotations.Mandatory;
12 import eu.ehri.project.models.utils.ClassUtils;
13 import org.apache.jena.datatypes.xsd.XSDDatatype;
14 import org.apache.jena.rdf.model.Model;
15 import org.apache.jena.rdf.model.ModelFactory;
16 import org.apache.jena.rdf.model.RDFNode;
17 import org.apache.jena.rdf.model.Resource;
18 import org.apache.jena.vocabulary.OWL;
19 import org.apache.jena.vocabulary.RDF;
20 import org.apache.jena.vocabulary.RDFS;
21 import org.apache.jena.vocabulary.XSD;
22
23 import java.io.OutputStream;
24 import java.io.UnsupportedEncodingException;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.ParameterizedType;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31
32
33
34
35 public class SchemaExporter {
36
37 private final String rdfFormat;
38
39 public SchemaExporter(String rdfFormat) {
40 this.rdfFormat = rdfFormat;
41 }
42
43 public final static String DEFAULT_BASE_URI = "http://data.ehri-project.eu/ontology/";
44 public final static Map<String, String> NAMESPACES = ImmutableMap.<String, String>builder()
45 .put("owl", OWL.getURI())
46 .put("xsd", XSD.getURI())
47 .put("rdfs", RDFS.getURI())
48 .put("ehri", DEFAULT_BASE_URI)
49 .build();
50
51 public void dumpSchema(OutputStream outputStream, String baseUri) throws UnsupportedEncodingException {
52
53 Model model = ModelFactory.createDefaultModel();
54 model.setNsPrefixes(NAMESPACES);
55
56 Set<Class<?>> baseClasses = Sets.newHashSet();
57
58 for (EntityClass entityClass : EntityClass.values()) {
59 Collections.addAll(baseClasses, entityClass.getJavaClass().getInterfaces());
60 }
61
62 for (Class<?> sup : baseClasses) {
63 dumpEntityClass(model, sup);
64 }
65
66 for (EntityClass entityClass : EntityClass.values()) {
67 Class<?> cls = entityClass.getJavaClass();
68 Resource shape = dumpEntityClass(model, cls);
69
70 for (Class<?> sup : cls.getInterfaces()) {
71 model.add(shape, RDFS.subClassOf,
72 model.createResource(DEFAULT_BASE_URI + sup.getSimpleName()));
73 }
74 }
75
76 model.getWriter(rdfFormat).write(model, outputStream, baseUri);
77 }
78
79
80 private XSDDatatype typeOf(Class<?> cls) {
81 if (Number.class.isAssignableFrom(cls)) {
82 return XSDDatatype.XSDinteger;
83 } else if (Boolean.class.isAssignableFrom(cls)) {
84 return XSDDatatype.XSDboolean;
85 } else {
86 return XSDDatatype.XSDstring;
87 }
88 }
89
90 private Resource dumpEntityClass(Model model, Class<?> cls) {
91 String name = cls.getSimpleName();
92
93
94 Resource rdfClass = model.createResource(DEFAULT_BASE_URI + name);
95 model.add(rdfClass, RDF.type, OWL.Class);
96 model.add(rdfClass, RDFS.label, name);
97
98 for (Method method : cls.getDeclaredMethods()) {
99 Property property = method.getAnnotation(Property.class);
100 Class<?> returnType = method.getReturnType();
101
102 if (property != null) {
103 String propertyName = property.value();
104 boolean mandatory = method.getAnnotation(Mandatory.class) != null;
105 addDatatypeProperty(model, rdfClass, propertyName, returnType, mandatory);
106 } else {
107 Adjacency adjacency = method.getAnnotation(Adjacency.class);
108 InverseOf inverseOf = method.getAnnotation(InverseOf.class);
109 if (adjacency != null
110 && method.getName().startsWith(ClassUtils.FETCH_METHOD_PREFIX)
111 && (adjacency.direction().equals(Direction.OUT) || inverseOf != null)) {
112 boolean mandatory = method.getAnnotation(Mandatory.class) != null;
113 String label = inverseOf != null ? inverseOf.value() : adjacency.label();
114 addObjectProperty(model, rdfClass, method, returnType, label, mandatory);
115 }
116 }
117 }
118
119 return rdfClass;
120 }
121
122 private void addObjectProperty(Model model, Resource rdfClass, Method method,
123 Class<?> returnType, String name, boolean mandatory) {
124 boolean collection = Iterable.class.isAssignableFrom(returnType);
125 if (collection) {
126
127 ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
128 returnType = (Class<?>) type.getActualTypeArguments()[0];
129 mandatory = false;
130 }
131
132 Resource propResource = model.createResource(DEFAULT_BASE_URI + name);
133 model.add(propResource, RDF.type, OWL.ObjectProperty);
134 model.add(propResource, RDFS.domain, rdfClass);
135 model.add(propResource, RDFS.range,
136 model.createResource(DEFAULT_BASE_URI + returnType.getSimpleName()));
137
138 Resource restriction = model.createResource();
139 model.add(restriction, RDF.type, OWL.Restriction);
140 model.add(restriction, OWL.onProperty, propResource);
141 model.add(restriction, OWL.allValuesFrom,
142 model.createResource(DEFAULT_BASE_URI + returnType.getSimpleName()));
143 if (mandatory) {
144 model.add(restriction, OWL.cardinality,
145 model.createTypedLiteral(1, XSDDatatype.XSDnonNegativeInteger));
146 }
147 model.add(rdfClass, RDFS.subClassOf, restriction);
148 }
149
150 private void addDatatypeProperty(Model model, Resource rdfClass,
151 String propertyName, Class<?> type, boolean mandatory) {
152 Resource propResource = model.createResource(DEFAULT_BASE_URI + propertyName);
153 model.add(propResource, RDF.type, OWL.DatatypeProperty);
154
155 model.add(propResource, RDFS.domain, rdfClass);
156 model.add(propResource, RDFS.range,
157 model.createResource(typeOf(type).getURI()));
158
159 Resource restriction = model.createResource();
160 model.add(restriction, RDF.type, OWL.Restriction);
161 model.add(restriction, OWL.onProperty, propResource);
162 model.add(restriction, mandatory ? OWL.cardinality : OWL.maxCardinality,
163 model.createTypedLiteral(1, XSDDatatype.XSDnonNegativeInteger));
164 model.add(rdfClass, RDFS.subClassOf, restriction);
165
166 if (type.isEnum()) {
167 List<RDFNode> inValues = Lists.newArrayList();
168 for (Object constValue : type.getEnumConstants()) {
169 inValues.add(model.createLiteral(constValue.toString()));
170 }
171 model.add(propResource, OWL.oneOf, model.createList(inValues.iterator()));
172 }
173 }
174 }