001: package org.ontoware.rdfreactor.generator;
002:
003: import java.util.HashSet;
004: import java.util.List;
005: import java.util.Set;
006:
007: import org.apache.commons.logging.Log;
008: import org.apache.commons.logging.LogFactory;
009: import org.ontoware.aifbcommons.collection.ClosableIterator;
010: import org.ontoware.rdf2go.RDF2Go;
011: import org.ontoware.rdf2go.Reasoning;
012: import org.ontoware.rdf2go.model.Model;
013: import org.ontoware.rdf2go.model.Statement;
014: import org.ontoware.rdf2go.model.node.BlankNode;
015: import org.ontoware.rdf2go.model.node.URI;
016: import org.ontoware.rdf2go.model.node.Variable;
017: import org.ontoware.rdf2go.vocabulary.OWL;
018: import org.ontoware.rdf2go.vocabulary.RDF;
019: import org.ontoware.rdf2go.vocabulary.RDFS;
020: import org.ontoware.rdfreactor.generator.java.JClass;
021: import org.ontoware.rdfreactor.generator.java.JModel;
022: import org.ontoware.rdfreactor.generator.java.JPackage;
023: import org.ontoware.rdfreactor.generator.java.JProperty;
024: import org.ontoware.rdfreactor.schema.bootstrap.Class;
025: import org.ontoware.rdfreactor.schema.bootstrap.DeprecatedProperty;
026: import org.ontoware.rdfreactor.schema.bootstrap.Property;
027: import org.ontoware.rdfreactor.schema.bootstrap.Resource;
028: import org.ontoware.rdfreactor.schema.bootstrap.Restriction;
029: import org.ontoware.rdfreactor.schema.bootstrap.TypeUtils;
030:
031: /**
032: * Creates an internal JModel from an ontology model.
033: *
034: * @author voelkel
035: */
036: public class ModelGenerator {
037:
038: private static Log log = LogFactory.getLog(ModelGenerator.class);
039:
040: public static JModel createFromRDFS_Schema(
041: Model modelWithSchemaData, String packagename,
042: boolean skipbuiltins) throws Exception {
043:
044: log.info("Input model has " + modelWithSchemaData.size()
045: + " triples");
046:
047: // enable RDFS inferencing
048: Model m = RDF2Go.getModelFactory().createModel(Reasoning.rdfs);
049: m.open();
050: m.addAll(modelWithSchemaData.iterator());
051:
052: // prepare JModel
053: log.debug("add build-ins");
054: JModel jm = Semantics.getbuiltIns_RDFS();
055: JPackage jp = new JPackage(packagename);
056: jm.getPackages().add(jp);
057:
058: // set local ontology root
059: JClass localRoot = new JClass(jp, "Thing", RDFS.Class);
060: localRoot
061: .setComment("This class acts as a catch-all for all properties, for which no domain has specified.");
062: localRoot.addSuperclass(jm.getRoot());
063: jm.setRoot(localRoot);
064:
065: // process
066: log.debug("de-anonymizing (replacing bnodes with random uris");
067: Utils.deanonymize(m);
068:
069: // FIXME
070: ClosableIterator<org.ontoware.rdf2go.model.node.Resource> iit = Class
071: .getAllInstancesAsRdf2GoResources(m);
072: while (iit.hasNext()) {
073: System.out.println(iit.next());
074: }
075:
076: // analysis (triggers also inferencing)
077: List<? extends Class> rdfclasses = Class.getAllInstance_as(m)
078: .asList();
079: log.info("Got " + rdfclasses.size() + " rdfs:Classes");
080: for (Class c : rdfclasses) {
081: log.debug("Found class: " + c.getResource());
082: }
083: Property[] rdfproperties = Property.getAllInstance_as(m)
084: .asArray();
085: for (Property p : rdfproperties) {
086: log.debug("Found property: " + p.getResource());
087: }
088: log.info("Got " + rdfproperties.length + " rdfs:Properties");
089: log.debug("Found " + m.size()
090: + " statements in schema after inferencing.");
091:
092: // get all classes and assign to package
093: Set<String> usedClassnames = new HashSet<String>();
094: usedClassnames.add(jm.getRoot().getName());
095: Set<Class> rdfsClasses = new HashSet<Class>();
096:
097: for (Class rc : Class.getAllInstance_as(m).asList()) {
098:
099: if (skipbuiltins && jm.hasMapping(rc.getResource())) {
100: log.debug("CLASS " + rc
101: + " is known -> skipping generation");
102: } else if (!(rc.getResource() instanceof URI)) {
103: log
104: .warn("A Class with a blank node ID makes not much sense -> ignored");
105: } else {
106: rdfsClasses.add(rc);
107: // TODO better classname guessing
108: String classname = JavaNamingUtils.toBeanName(rc,
109: usedClassnames);
110: assert classname != null;
111: usedClassnames.add(classname);
112:
113: log.debug("CLASS " + classname + " generated for "
114: + rc.getResource() + " ...");
115: assert rc.getResource() instanceof URI : "A Class with a blank node ID makes not much sense";
116: JClass jc = new JClass(jp, classname, (URI) rc
117: .getResource());
118: jc.setComment(Utils.toJavaComment(rc
119: .getAllComment_asList())); // might
120: // be
121: // null, ok.
122: jm.addMapping(rc.getResource(), jc);
123: }
124: }
125:
126: log.debug(">>>> Inheritance");
127: // get all classes and link superclasses
128: for (org.ontoware.rdfreactor.schema.bootstrap.Class rc : rdfsClasses) {
129: log.debug("rdfs:Class " + rc.getResource());
130: JClass jc = jm.getMapping(rc.getResource());
131: for (org.ontoware.rdfreactor.schema.bootstrap.Class super class : rc
132: .getAllSubClassOf_asList())
133: jc.addSuperclass(jm
134: .getMapping(super class.getResource()));
135: }
136:
137: jm.flattenInheritanceHierarchy(jp);
138:
139: log.info("-------------- PROPERTIES ...");
140:
141: for (Property rp : Property.getAllInstance_as(m).asList()) {
142: log.info("PROPERTY " + rp.getResource());
143:
144: if (skipbuiltins
145: && jm.knownProperties.contains(rp.getResource()
146: .asURI())) {
147: // do nothing
148: log.debug("Skipping built-in property "
149: + rp.getResource().asURI().toSPARQL());
150: } else if (DeprecatedProperty.hasInstance(rp.getModel(), rp
151: .getResource().asURI())) {
152: log.info("Skipping deprecated property " + rp
153: + "(as indicated by owl:DeprecatedProperty)");
154: } else {
155: // inspect domains
156: List<Class> domains = rp.getAllDomain_asList();
157: // TODO: ignore if already in higher level
158: if (domains == null || domains.size() == 0) {
159: log.warn("PROPERTY " + rp.getResource()
160: + " has no domain, using root");
161: handleProperty(m, jm, jm.getRoot(), rp);
162: } else {
163: for (Resource domain : domains) {
164: log.info("PROPERTY " + rp.getResource()
165: + " has domain " + domain);
166: JClass domainClass = jm.getMapping(domain
167: .getResource());
168: assert domainClass != null : "found no JClass for "
169: + rp.getAllDomain_asList().get(0)
170: .getResource();
171:
172: // domainclass might be a built-in, redirect to root
173: if (Semantics.getbuiltIns_RDFS().classMap
174: .containsValue(domainClass)) {
175: log
176: .info("domain "
177: + domainClass
178: + " is a built-in, hence we attach the property to the root ("
179: + jm.getRoot() + ")");
180: domainClass = jm.getRoot();
181: }
182:
183: handleProperty(m, jm, domainClass, rp);
184: }
185: }
186: }
187:
188: }
189: return jm;
190: }
191:
192: /**
193: * TODO: this is experimental
194: *
195: * @param schemaDataModel
196: * com.hp.hpl.jena.rdf.model.Model
197: * @param packagename
198: * @return
199: * @throws Exception
200: */
201: public static JModel createFromRDFS_AND_OWL(Model schemaDataModel,
202: String packagename, boolean skipbuiltins) throws Exception {
203: log.info("Initialising JModel");
204: JModel jm = Semantics.getbuiltIns_RDFS();
205:
206: log.info("Loading schema triples");
207: Model m = RDF2Go.getModelFactory().createModel(
208: Reasoning.rdfsAndOwl);
209: m.open();
210: m.addAll(schemaDataModel.iterator());
211:
212: // com.hp.hpl.jena.rdf.model.Model jenaModel = ModelFactory
213: // .createRDFSModel(schemaDataModel);
214: // Model m = new ModelImplJena24(null, jenaModel);
215: // m.open();
216:
217: log
218: .info("Skolemisation (replacing all blank nodes with random URIs)");
219: Utils.deanonymize(m);
220:
221: log.info("Add mapping from OWL to RDF");
222: // add mapping from OWL to RDF
223: m.addStatement(OWL.Class, RDFS.subClassOf, RDFS.Class);
224: m.addStatement(OWL.AnnotationProperty, RDFS.subClassOf,
225: RDF.Property);
226: m.addStatement(OWL.DatatypeProperty, RDFS.subClassOf,
227: RDF.Property);
228: m.addStatement(OWL.FunctionalProperty, RDFS.subClassOf,
229: RDF.Property);
230: m.addStatement(OWL.InverseFunctionalProperty, RDFS.subClassOf,
231: RDF.Property);
232: m.addStatement(OWL.ObjectProperty, RDFS.subClassOf,
233: RDF.Property);
234: m.addStatement(OWL.OntologyProperty, RDFS.subClassOf,
235: RDF.Property);
236:
237: log.debug("MODEL after inferencing, found " + m.size()
238: + " statements");
239: JPackage jp = new JPackage(packagename);
240: jm.getPackages().add(jp);
241:
242: log
243: .info("Creating a class called 'Thing' for all properties with no given domain");
244: JClass localClass = new JClass(jp, "Thing", RDFS.Class);
245: localClass.addSuperclass(jm.getRoot());
246: jm.setRoot(localClass);
247:
248: // get all classes and assign to package
249: Set<String> usedClassnames = new HashSet<String>();
250: usedClassnames.add(jm.getRoot().getName());
251: Set<Class> rdfsClasses = new HashSet<Class>();
252:
253: for (Class rc : Class.getAllInstance_as(m).asList()) {
254:
255: if (skipbuiltins && jm.hasMapping(rc.getResource())) {
256: log.debug("CLASS " + rc
257: + " is known -> skipping generation");
258: } else {
259: rdfsClasses.add(rc);
260: // TODO better class-name guessing
261: String classname = JavaNamingUtils.toBeanName(rc,
262: usedClassnames);
263: assert classname != null;
264: usedClassnames.add(classname);
265:
266: log.debug("CLASS " + classname + " generated for "
267: + rc.getResource().toSPARQL() + " ...");
268: JClass jc = new JClass(jp, classname, (URI) rc
269: .getResource());
270: jc.setComment(rc.getAllComment_asList().get(0)); // might be
271: // null, ok.
272: jm.addMapping(rc.getResource(), jc);
273: }
274: }
275:
276: log.info(">>>> Inheritance");
277: // get all classes and link super-classes
278: for (org.ontoware.rdfreactor.schema.bootstrap.Class rc : rdfsClasses) {
279: log.debug("rdfs:Class " + rc.getResource());
280: JClass jc = jm.getMapping(rc.getResource());
281: for (org.ontoware.rdfreactor.schema.bootstrap.Class super class : rc
282: .getAllSubClassOf_asList())
283: jc.addSuperclass(jm
284: .getMapping(super class.getResource()));
285: }
286:
287: log.info(">>>> Flatten inheritance hierarchy");
288: jm.flattenInheritanceHierarchy(jp);
289:
290: // get all properties
291: log.info("-------------- PROPERTIES ...");
292: for (Property rp : Property.getAllInstance_as(m).asList()) {
293: log.debug("PROPERTY " + rp.getResource());
294: List<Class> domains = rp.getAllDomain_asList();
295: // no domain = no generated property
296: if (domains == null || domains.size() == 0) {
297: // log.warn("PROPERTY " + rp.getID() + " has no domain, so we
298: // ignore it");
299: log.debug("PROPERTY " + rp.getResource()
300: + " has no domain, using root");
301: handleProperty(m, jm, jm.getRoot(), rp);
302: } else {
303: for (Resource domain : domains) {
304: JClass domainClass = jm.getMapping(domain
305: .getResource());
306: assert domainClass != null : "found no JClass for "
307: + rp.getAllDomain_asList().get(0)
308: .getResource();
309: handleProperty(m, jm, domainClass, rp);
310: }
311: }
312: }
313: return jm;
314: }
315:
316: public static JModel createFromOWL(Model schemaDataModel,
317: String packagename, boolean skipbuiltins) throws Exception {
318: // DIGReasonerFactory drf = (DIGReasonerFactory) ReasonerRegistry
319: // .theRegistry().getFactory(DIGReasonerFactory.URI);
320: // DIGReasoner r = (DIGReasoner) drf.createWithOWLAxioms(null);
321: // OntModel base = ModelFactory.createOntologyModel(
322: // OntModelSpec.OWL_DL_MEM, null);
323: // // ... build or load the model contents ...
324: // base.add(schemaDataModel);
325: //
326: // OntModelSpec spec = new OntModelSpec(OntModelSpec.OWL_DL_MEM);
327: // spec.setReasoner(r);
328: //
329: // OntModel combined = ModelFactory.createOntologyModel(spec, base);
330: //
331: // Model m = new ModelImplJena22(combined);
332:
333: Model m = RDF2Go.getModelFactory().createModel(Reasoning.owl);
334: m.open();
335: m.addAll(schemaDataModel.iterator());
336:
337: // Reasoner reasoner = ReasonerRegistry.getOWLMicroReasoner();//
338: // miniReasoner();
339: // reasoner = reasoner.bindSchema(schemaDataModel);
340: // InfModel jenaModel = ModelFactory.createInfModel(reasoner,
341: // schemaDataModel);
342: // Model m = new ModelImplJena24(null, jenaModel);
343:
344: log.debug("de-anonymizing");
345: Utils.deanonymize(m);
346:
347: log.debug("after inferencing, found " + m.size()
348: + " statements");
349:
350: // // DEBUG
351: // File debugOut = new File(outDir, packagename + "/schema.nt");
352: // debugOut.mkdir();
353: // jenaModel.write(new FileWriter(debugOut), "N-TRIPLES");
354:
355: JPackage jp = new JPackage(packagename);
356:
357: // bootstrapping?
358:
359: JModel jm = Semantics.getbuiltIns_OWL();
360: jm.addPackage(jp);
361:
362: /**
363: * local ontology root
364: */
365: JClass localRoot = new JClass(jp, "Thing", OWL.Class);
366: localRoot
367: .setComment("This class acts as a catch-all for all properties, for which no domain has specified.");
368: localRoot.addSuperclass(jm.getRoot());
369: jm.setRoot(localRoot);
370:
371: // get all classes and assign to package
372: Set<org.ontoware.rdfreactor.schema.bootstrap.OwlClass> owlClasses = new HashSet<org.ontoware.rdfreactor.schema.bootstrap.OwlClass>();
373: Set<String> usedClassnames = new HashSet<String>();
374:
375: for (org.ontoware.rdfreactor.schema.bootstrap.OwlClass oc : org.ontoware.rdfreactor.schema.bootstrap.OwlClass
376: .getAllInstance_as(m).asList()) {
377: log.debug("Found owl:Class " + oc.getResource() + " (have "
378: + owlClasses.size() + " already)");
379:
380: org.ontoware.rdf2go.model.node.Resource classURI = oc
381: .getResource();
382:
383: // check if restriction or real class
384: if (m.contains(classURI, RDF.type, OWL.Restriction)) {
385: log.debug("skipping restriction " + classURI);
386: } else if (skipbuiltins && jm.hasMapping(classURI)) {
387: log.debug("skipping known class " + classURI);
388: // TODO add all XSD classes to default JModels and remove this
389: // check
390: } else if (classURI.toString().startsWith(Semantics.NS_XSD)) {
391: log.debug("skipping XML Schema class " + classURI);
392: // TODO: what is the purpose of this?
393: } else if (oc.getResource() instanceof BlankNode) {
394: log.debug("skipping blank class " + classURI);
395: } else {
396: log.debug("owl:Class : " + classURI);
397: owlClasses.add(oc);
398: // TODO better classname guessing
399: String classname = JavaNamingUtils.toBeanName(oc,
400: usedClassnames);
401: assert classname != null;
402: usedClassnames.add(classname);
403: log.debug("generating class " + classname + " for "
404: + classURI + " ...");
405: JClass jc = new JClass(jp, classname, (URI) oc
406: .getResource());
407: jc.setComment(oc.getAllComment_asList().get(0));
408: jm.addMapping((URI) oc.getResource(), jc);
409: }
410: }
411: log.debug("dealing with " + owlClasses.size()
412: + " 'real' classes");
413:
414: log.debug(">>>> Inheritance");
415: // get all classes and link superclasses
416: for (org.ontoware.rdfreactor.schema.bootstrap.OwlClass oc : owlClasses) {
417: log.debug("owl:Class " + oc.getResource());
418: JClass jc = jm.getMapping(oc.getResource());
419: for (org.ontoware.rdfreactor.schema.bootstrap.OwlClass super class : TypeUtils
420: .getAllRealSuperclasses(oc, owlClasses))
421: jc.addSuperclass(jm
422: .getMapping(super class.getResource()));
423: }
424: jm.flattenInheritanceHierarchy(jp);
425:
426: // get all properties
427: log.info(">>> Processing properties ...");
428:
429: // this uniqueness constraint can be weakened,
430: // property names need only to be unique within a class,
431: // but this might be more consistent anyways
432: Set<String> usedPropertynames = new HashSet<String>();
433:
434: for (Property rp : Property.getAllInstance_as(m).asList()) {
435: log.debug("> Processing property " + rp.getResource());
436: // name it
437: String propertyName = JavaNamingUtils.toBeanName(rp,
438: usedPropertynames);
439: usedPropertynames.add(propertyName);
440: assert propertyName != null;
441:
442: List<Class> domains = rp.getAllDomain_asList();
443: // no domain = no generated property
444: if (domains == null || domains.size() == 0) {
445: log.warn("Property " + rp.getResource()
446: + " has no domain, so we ignore it");
447: } else {
448: for (Class domain : domains) {
449: if (!owlClasses.contains(domain)) {
450: // log.debug("ignored");
451: } else {
452: JClass domainClass = jm.getMapping(domain
453: .getResource());
454: assert domainClass != null : "found no JClass for "
455: + rp.getAllDomain_asList().get(0)
456: .getResource();
457:
458: JProperty jprop = new JProperty(domainClass,
459: propertyName, (URI) rp.getResource());
460: // wire
461: log.debug("Adding property '" + jprop.getName()
462: + "' to '" + domainClass.getName()
463: + "'");
464: jprop.getJClass().getProperties().add(jprop);
465: jprop.setComment(rp.getAllComment_asList().get(
466: 0));
467:
468: for (Class range : rp.getAllRange_asList()) {
469: if (owlClasses
470: .contains(range
471: .castTo(org.ontoware.rdfreactor.schema.owl.OwlClass.class)))
472: jprop.addType(jm.getMapping(range
473: .getResource()));
474: }
475: jprop.fixRanges(jm);
476:
477: // figure out cardinality
478:
479: ClosableIterator<Statement> it = m
480: .findStatements(Variable.ANY,
481: OWL.onProperty, rp
482: .getResource());
483: while (it.hasNext()) {
484: Statement stmt = (Statement) it.next();
485: org.ontoware.rdf2go.model.node.Resource restrictionResource = stmt
486: .getSubject();
487: Restriction restriction = Restriction
488: .getInstance(m, restrictionResource);
489:
490: int min = restriction
491: .getAllMinCardinality_asList().get(
492: 0);
493: log.debug("Found minrestriction on " + rp
494: + " minCard = " + min);
495: if (min != -1)
496: jprop.setMinCardinality(min);
497: int max = restriction
498: .getAllMaxCardinality_asList().get(
499: 0);
500: log.debug("Found maxrestriction on " + rp
501: + " maxCard = " + max);
502: if (max != -1)
503: jprop.setMaxCardinality(max);
504: }
505: it.close();
506: }
507: }
508: }
509: }
510:
511: // // prune
512: // log.debug(">>>>>> Pruning");
513: // for (JClass jc : jp.getClasses()) {
514: // // FIXME: this is too simple: if no properties: remove
515: // if (jc.getProperties().size() == 0) {
516: // log.debug(jc.getName() + " has no properties, removing");
517: // jp.getClasses().remove(jc);
518: // }
519: // }
520: return jm;
521: }
522:
523: /**
524: * Handle all aspects of integrating a property into the JModel: construct
525: * the JProperty, add all its ranges to the JProperty and set min and max
526: * cardinality.
527: *
528: * @param m -
529: * the underlying RDF2Go model
530: * @param jm -
531: * the target JModel
532: * @param domainClass -
533: * the JClass domain of the property
534: * @param rp -
535: * the Property instance representing the Property in the RDF2Go
536: * model
537: */
538: private static void handleProperty(Model m, JModel jm,
539: JClass domainClass, Property rp) {
540:
541: // obtain a nice Java-conform name which has not yet been used
542: String propertyName = JavaNamingUtils.toBeanName(rp,
543: domainClass.getUsedPropertyNames());
544: assert propertyName != null;
545: JProperty jprop = new JProperty(domainClass, propertyName,
546: (URI) rp.getResource());
547: // carry over the comment from RDF to Java, might be null
548: jprop
549: .setComment(Utils.toJavaComment(rp
550: .getAllComment_asList()));
551: log.debug("PROPERTY Adding '" + jprop.getName() + "' to '"
552: + domainClass.getName() + "'");
553: jprop.getJClass().getProperties().add(jprop);
554:
555: // process range information
556: log.debug("PROPERTY checking ranges...");
557: for (Class range : rp.getAllRange_asList()) {
558: log.debug("range is " + range);
559: jprop.addType(jm.getMapping(range.getResource()));
560: }
561: if (rp.getAllRange_asList().size() == 0) {
562: // if no range is given, set to ontology root class (rdfs:Class or
563: // owl:Class)
564: jprop.addType(jm.getRoot());
565: }
566:
567: // process cardinality constraints (convert this property to an OWL
568: // restriciton)
569: assert rp != null;
570: Restriction restriction = (Restriction) rp
571: .castTo(Restriction.class);
572: assert restriction != null;
573:
574: Integer card = restriction.getCardinality();
575: Integer minCard = restriction.getMinCardinality();
576: Integer maxCard = restriction.getMaxCardinality();
577: int min = -1;
578: int max = -1;
579:
580: if (minCard != null) {
581: min = minCard;
582: log.debug("Found minrestriction on " + rp + " minCard = "
583: + min);
584: } else if (card != null) {
585: log.debug("Found card.restriction on " + rp + " card = "
586: + min);
587: min = card;
588: }
589: jprop.setMinCardinality(min);
590:
591: if (maxCard != null) {
592: max = maxCard;
593: log.debug("Found maxrestriction on " + rp + " maxCard = "
594: + max);
595: } else if (card != null) {
596: log.debug("Found card.restriction on " + rp + " card = "
597: + min);
598: max = card;
599: }
600: jprop.setMaxCardinality(max);
601:
602: if (min != -1 || max != -1) {
603: domainClass.cardinalityexception = true;
604: log.debug("added card.exception in class "
605: + domainClass.getName());
606: }
607:
608: }
609: }
|