001: package org.ontoware.rdfreactor.generator.java;
002:
003: import java.util.ArrayList;
004: import java.util.HashMap;
005: import java.util.HashSet;
006: import java.util.List;
007: import java.util.Map;
008: import java.util.Set;
009:
010: import org.apache.commons.logging.Log;
011: import org.apache.commons.logging.LogFactory;
012: import org.ontoware.rdf2go.model.node.Resource;
013: import org.ontoware.rdf2go.model.node.URI;
014:
015: /**
016: * A <b>JModel</b> is the result of the translation of a given RDFS/OWL
017: * ontology, and is used as an internal representation of the ontology. This
018: * model of the ontology can then be used to generate the source code through a
019: * Apache Velocity template.
020: *
021: * Every <b>JModel</b> consists of one or more <b>JPackage</b> instances, a
022: * root JClass and a Mapping of Java Objects to JClass Objects representing them
023: * in the JModel.
024: *
025: * A JModel, which is represented through the classes JModel, JPackage, JClass
026: * and JProperty is much more expressive then the object oriented Model
027: * internaly used by Java. A JModel allows class cycles, multiple inheritance,
028: * properties without a type. Inverse Properties can be explicitly stated for a
029: * property.
030: *
031: * This expressiveness is the result of the need to transform the RDF Model into
032: * the the Object Oriented Model of Java. Therefore a JModel must be able to
033: * express both the Java and the RDF Model.
034: *
035: * The Transformation from an ontology into a JModel is initiated in
036: * CodeGenerator.generate(...), the Apache Velocity template engine is invoked
037: * in SourceCodeWriter.write(...)
038: *
039: * In the future EMF/Ecore of the Eclipse Project might be used instead of
040: * JModel, because Ecore is an UML dialect and has a far superior tool support
041: * over JModel.
042: *
043: * @author $Author: xamde $
044: * @version $Id: JModel.java,v 1.9 2006/09/11 10:07:57 xamde Exp $
045: *
046: * TODO enable anonymous objects as class and property identifiers for OWL support
047: *
048: */
049:
050: // TODO consider switching to EMF/Ecore from Eclipse
051: public class JModel {
052:
053: private static Log log = LogFactory.getLog(JModel.class);
054:
055: /** the List of JPackages in this JModel */
056: private List<JPackage> packages;
057:
058: /**
059: * a Map of Java Objects to the JClass representing them in the JModel TODO:
060: * make private
061: */
062: // FIXME -> private
063: public Map<Resource, JClass> classMap;
064:
065: public Set<URI> knownProperties;
066:
067: /** the root JClass of this JModel */
068: private JClass root;
069:
070: /**
071: * the only constructor:
072: *
073: * @param root
074: * is the root JClass of the new JModel
075: * @param writetostore -
076: * if genertaed classes should write to the rdf model
077: */
078: public JModel(JClass root) {
079: this .packages = new ArrayList<JPackage>();
080: this .classMap = new HashMap<Resource, JClass>();
081: this .knownProperties = new HashSet<URI>();
082: this .root = root;
083: }
084:
085: /**
086: * apply consistency checks to this JModel and all instances of JPackage,
087: * JClass and JProperty in it.
088: *
089: * @return true if the JModel and all its parts are consistent
090: */
091: public boolean isConsistent() {
092: boolean result = true;
093: for (JPackage jp : packages) {
094: if (!jp.isConsistent())
095: log.warn(jp + " is not consistent");
096: result &= jp.isConsistent();
097: }
098: return result;
099: }
100:
101: public String toString() {
102: StringBuffer buf = new StringBuffer();
103: buf.append("\nJModel\n");
104: for (JPackage jp : packages) {
105: buf.append(jp.toString());
106: }
107: return buf.toString();
108: }
109:
110: /** generate a verbose report of this JModel and its parts */
111: public String toReport() {
112: StringBuffer buf = new StringBuffer();
113: for (JPackage jp : packages) {
114: buf.append(jp.toReport());
115: }
116: return buf.toString();
117: }
118:
119: /**
120: * remove self-references and circles. Reducing to a maximally nested
121: * inheritance tree.
122: *
123: * circles always are of length two, due to RDFS and OWL sematics
124: */
125: public void flattenInheritanceHierarchy(JPackage jp) {
126: for (JClass jc : jp.getClasses()) {
127: // FIXME for (JClass jc : mapping.values()) {
128: switch (jc.getSuperclasses().size()) {
129: case 0:
130: log
131: .debug("Class '"
132: + jc
133: + "' has no superclass --> set superclass to 'Thing'");
134: jc.setJavaSuperclass(root);
135: break;
136: case 1: {
137: JClass super class = jc.getSuperclasses().get(0);
138: assert super class != null;
139: if (super class.equals(jc)) {
140: log
141: .debug("Class '"
142: + jc
143: + "' has exactly one superclass: itself --> set superclass to 'Thing'");
144: jc.setJavaSuperclass(root);
145: } else {
146: log.debug("Class '" + jc
147: + "' has exactly one superclass '"
148: + super class + "'");
149: // use the one we have
150: jc.setJavaSuperclass(super class);
151: }
152: }
153: break;
154: default: {
155: log.debug("Class '" + jc + "' has "
156: + jc.getSuperclasses().size()
157: + " superclasses. Needs pruning.... ");
158:
159: // prune hierarchy.
160:
161: // Alg:
162: // try to flatten superclasses to a strict hierarchy
163: // find most specific class among superclasses
164: // and assert that one is a subclass of all other classes
165: // so there must be one superclass, that has most other
166: // superclasses
167:
168: // FIXME detec circles
169:
170: // find the superclass that has itself the largest number of
171: // superclasses (not depth)
172: // Note: this can handle cycles (uses no recursion)
173: int maxSuperclassCount = 0;
174: JClass mostSpecificSuperClass = root;
175: for (JClass super class : jc.getSuperclasses()) {
176: log.debug("trying " + super class + " ("
177: + super class.getSuperclasses().size()
178: + " superclasses)");
179: if ((!super class.equals(jc))
180: && super class.getSuperclasses().size() > maxSuperclassCount
181: && !super class.getSuperclasses().contains(
182: jc)) {
183: maxSuperclassCount = super class
184: .getSuperclasses().size();
185: mostSpecificSuperClass = super class;
186: log.debug(super class + " has "
187: + maxSuperclassCount + " superclasses");
188: }
189: }
190: // have we found a valid superclass?
191: if (!mostSpecificSuperClass.equals(jc)) {
192: log.debug("most specific superclass of "
193: + jc.getName() + " is "
194: + mostSpecificSuperClass);
195: jc.setJavaSuperclass(mostSpecificSuperClass);
196: } else {
197: log.debug("most specific superclass of "
198: + jc.getName() + " is root");
199: jc.setJavaSuperclass(root);
200: }
201: }
202: break;
203: }
204: assert jc.getSuperclass() != null;
205: }
206:
207: }
208:
209: public void addPackage(JPackage jp) {
210: this .packages.add(jp);
211: }
212:
213: public List<JPackage> getPackages() {
214: return this .packages;
215: }
216:
217: /**
218: * generates artificial inverse properties, named propertyName + "_Inverse"
219: * TODO read inverse props from OWL, this generates ALL inverse
220: */
221: public void addInverseProperties() {
222: // add inverse properties
223: for (JPackage jp : getPackages()) {
224: for (JClass jc : jp.getClasses()) {
225: for (JProperty jprop : jc.getProperties()) {
226: JProperty inverse;
227: if (!jprop.hasInverse()) {
228: // TODO: the _inverse seems to be missing, right ?
229: inverse = new JProperty(jc, jprop.getName(),
230: jprop.getMappedTo());
231: jprop.setInverse(inverse);
232: inverse.setInverse(jprop);
233: }
234: inverse = jprop.getInverse();
235:
236: for (JClass type : jprop.getTypes()) {
237: // add only if not already present
238: if (!type.getInverseProperties().contains(
239: inverse))
240: type.getInverseProperties().add(inverse);
241: // always:
242: inverse.addType(jc);
243: }
244: }
245: }
246: }
247: }
248:
249: /**
250: * @return the root JClass of the JModel
251: */
252: public JClass getRoot() {
253: return this .root;
254: }
255:
256: /**
257: * Add a mapping to the JModel
258: *
259: * @param id
260: * is a Java Object
261: * @param jc
262: * is the JClass in this JModel to which the Java Object should
263: * be mapped
264: */
265: public void addMapping(Resource id, JClass jc) {
266: this .classMap.put(id, jc);
267: }
268:
269: /**
270: * @param resource
271: * is a Java Object
272: * @return the JClass to which the Java Object is mapped in this JModel
273: */
274: public JClass getMapping(Resource resource) {
275: JClass result = this .classMap.get(resource);
276: assert result != null : "no class mapped to resource "
277: + resource + " type of resource = "
278: + resource.getClass();
279: return result;
280: }
281:
282: /**
283: * @param id
284: * is a Java Object
285: * @return true if the given Java Object has a mapping to a JClass in this
286: * JModel
287: */
288: public boolean hasMapping(Object id) {
289: return classMap.containsKey(id);
290: }
291:
292: /**
293: * Set the root of this JModel
294: *
295: * @param root
296: * is the new root JClass
297: */
298: public void setRoot(JClass root) {
299: this .root = root;
300: }
301:
302: // TODO: implement equals() ?
303: }
|