001: /*
002: (c) Copyright 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: All rights reserved - see end of file.
004: $Id: ModelExpansion.java,v 1.13 2008/01/02 12:05:52 andy_seaborne Exp $
005: */
006:
007: package com.hp.hpl.jena.assembler;
008:
009: import java.util.*;
010:
011: import com.hp.hpl.jena.rdf.model.*;
012: import com.hp.hpl.jena.util.IteratorCollection;
013: import com.hp.hpl.jena.vocabulary.*;
014:
015: /**
016: The ModelExpansion code expands a model <code>M</code> against a
017: schema <code>S</code>, returning a new model which contains
018:
019: <ul>
020: <li>the statements of M
021: <li>any statements (A rdfs:subClassOf B) from S where neither A nor B
022: is a bnode.
023: <li>statements (A rdf:type T) if M contains (A P any) and
024: S contains (P rdfs:domain T).
025: <li>statements (A rdf:type T) if M contains (any P A) and
026: S contains (P rdfs:range T).
027: <li>statements (A rdf:type T) if (A rdf:type U) and (U rdfs:subClassOf T).
028: </ul>
029:
030: This is sufficient to allow the subjects in <code>M</code> which have
031: properties from <code>S</code> to have enough type information for
032: AssemblerGroup dispatch.
033:
034: @author kers
035: */
036: public class ModelExpansion {
037: /**
038: Answer a new model which is the aggregation of
039: <ul>
040: <li>the statements of <code>model</code>
041: <li>the non-bnode subclass statements of <code>schema</code>
042: <li>the subclass closure of those statements
043: <li>the rdf:type statements implied by the rdfs:domain statements
044: of <code>schema</code> and the <code>model</code>
045: statements using that statements property
046: <li>similarly for rdfs:range
047: <li>the rdf:type statements implied by the subclass closure
048: </ul>
049: */
050: public static Model withSchema(Model model, Model schema) {
051: Model result = ModelFactory.createDefaultModel().add(model);
052: addSubclassesFrom(result, schema);
053: addSubClassClosure(result);
054: addDomainTypes(result, schema);
055: addRangeTypes(result, schema);
056: addIntersections(result, schema);
057: addSupertypes(result);
058: return result;
059: }
060:
061: private static final Property ANY = null;
062:
063: protected static void addSubclassesFrom(Model result, Model schema) {
064: for (StmtIterator it = schema.listStatements(ANY,
065: RDFS.subClassOf, ANY); it.hasNext();) {
066: Statement s = it.nextStatement();
067: if (s.getSubject().isURIResource()
068: && s.getObject().isURIResource())
069: result.add(s);
070: }
071: }
072:
073: /**
074: Do (limited) subclass closure on <code>m</code>.
075: <p>
076: Those classes in <code>m</code> that appear in <code>subClassOf</code>
077: statements are given as explicit superclasses all their indirect superclasses.
078: */
079: public static void addSubClassClosure(Model m) {
080: Set roots = selectRootClasses(m, findClassesBySubClassOf(m));
081: for (Iterator it = roots.iterator(); it.hasNext();)
082: addSuperClasses(m, (Resource) it.next());
083: }
084:
085: /**
086: To each subclass X of <code>type</code> add as superclass all the
087: classes between X and <code>type</code>.
088: */
089: private static void addSuperClasses(Model m, Resource type) {
090: addSuperClasses(m, new LinkedSeq(type));
091: }
092:
093: /**
094: To each subclass X of <code>parents.item</code> add as superclass
095: all the classes between X and that item and all the items in the
096: rest of <code>parents</code>.
097: */
098: private static void addSuperClasses(Model m, LinkedSeq parents) {
099: Model toAdd = ModelFactory.createDefaultModel();
100: addSuperClasses(m, parents, toAdd);
101: m.add(toAdd);
102: }
103:
104: /**
105: Add to <code>toAdd</code> all the superclass statements needed
106: to note that any indirect subclass of <code>X = parents.item</code> has
107: as superclass all the classes between it and X and all the remaining
108: elements of <code>parents</code>.
109: */
110: private static void addSuperClasses(Model m, LinkedSeq parents,
111: Model toAdd) {
112: Resource type = parents.item;
113: for (StmtIterator it = m.listStatements(null, RDFS.subClassOf,
114: type); it.hasNext();) {
115: Resource t = it.nextStatement().getSubject();
116: for (LinkedSeq scan = parents.rest; scan != null; scan = scan.rest)
117: toAdd.add(t, RDFS.subClassOf, scan.item);
118: addSuperClasses(m, parents.push(t), toAdd);
119: }
120: }
121:
122: /**
123: Answer the subset of <code>classes</code> which have no
124: superclass in <code>m</code>.
125: */
126: private static Set selectRootClasses(Model m, Set classes) {
127: Set roots = new HashSet();
128: for (Iterator it = classes.iterator(); it.hasNext();) {
129: Resource type = (Resource) it.next();
130: if (!m.contains(type, RDFS.subClassOf, (RDFNode) null))
131: roots.add(type);
132: }
133: return roots;
134: }
135:
136: /**
137: Answer the set of all classes which appear in <code>m</code> as the
138: subject or object of a <code>rdfs:subClassOf</code> statement.
139: */
140: private static Set findClassesBySubClassOf(Model m) {
141: Set classes = new HashSet();
142: StmtIterator it = m.listStatements(null, RDFS.subClassOf,
143: (RDFNode) null);
144: while (it.hasNext())
145: addClasses(classes, it.nextStatement());
146: return classes;
147: }
148:
149: /**
150: Add to <code>classes</code> the subject and object of the statement
151: <code>xSubClassOfY</code>.
152: */
153: private static void addClasses(Set classes, Statement xSubClassOfY) {
154: classes.add(xSubClassOfY.getSubject());
155: classes.add(xSubClassOfY.getObject());
156: }
157:
158: protected static void addDomainTypes(Model result, Model schema) {
159: for (StmtIterator it = schema.listStatements(ANY, RDFS.domain,
160: ANY); it.hasNext();) {
161: Statement s = it.nextStatement();
162: Property property = (Property) s.getSubject().as(
163: Property.class);
164: RDFNode type = s.getObject();
165: for (StmtIterator x = result.listStatements(ANY, property,
166: ANY); x.hasNext();) {
167: Statement t = x.nextStatement();
168: result.add(t.getSubject(), RDF.type, type);
169: }
170: }
171: }
172:
173: protected static void addRangeTypes(Model result, Model schema) {
174: Model toAdd = ModelFactory.createDefaultModel();
175: for (StmtIterator it = schema.listStatements(ANY, RDFS.range,
176: ANY); it.hasNext();) {
177: Statement s = it.nextStatement();
178: RDFNode type = s.getObject();
179: Property property = (Property) s.getSubject().as(
180: Property.class);
181: for (StmtIterator x = result.listStatements(ANY, property,
182: ANY); x.hasNext();) {
183: RDFNode ob = x.nextStatement().getObject();
184: if (ob.isResource())
185: toAdd.add((Resource) ob, RDF.type, type);
186: }
187: }
188: result.add(toAdd);
189: }
190:
191: protected static void addSupertypes(Model result) {
192: Model temp = ModelFactory.createDefaultModel();
193: for (StmtIterator it = result
194: .listStatements(ANY, RDF.type, ANY); it.hasNext();) {
195: Statement s = it.nextStatement();
196: Resource c = AssemblerHelp.getResource(s);
197: for (StmtIterator subclasses = result.listStatements(c,
198: RDFS.subClassOf, ANY); subclasses.hasNext();) {
199: RDFNode type = subclasses.nextStatement().getObject();
200: // System.err.println( ">> adding super type: subject " + s.getSubject() + ", type " + type );
201: temp.add(s.getSubject(), RDF.type, type);
202: }
203: }
204: result.add(temp);
205: }
206:
207: private static void addIntersections(Model result, Model schema) {
208: StmtIterator it = schema.listStatements(ANY,
209: OWL.intersectionOf, ANY);
210: while (it.hasNext())
211: addIntersections(result, schema, it.nextStatement());
212: }
213:
214: private static void addIntersections(Model result, Model schema,
215: Statement s) {
216: Resource type = s.getSubject();
217: List types = asJavaList(AssemblerHelp.getResource(s));
218: Set candidates = subjectSet(result, ANY, RDF.type,
219: (Resource) types.get(0));
220: for (int i = 1; i < types.size(); i += 1)
221: removeElementsWithoutType(candidates, (Resource) types
222: .get(i));
223: addTypeToAll(type, candidates);
224: }
225:
226: private static void addTypeToAll(Resource type, Set candidates) {
227: List types = equivalentTypes(type);
228: for (Iterator it = candidates.iterator(); it.hasNext();) {
229: Resource resource = ((Resource) it.next());
230: for (int i = 0; i < types.size(); i += 1)
231: resource.addProperty(RDF.type, (Resource) types.get(i));
232: }
233: }
234:
235: private static List equivalentTypes(Resource type) {
236: List types = new ArrayList();
237: types.add(type);
238: for (StmtIterator it = type.getModel().listStatements(ANY,
239: OWL.equivalentClass, type); it.hasNext();)
240: types.add(it.nextStatement().getSubject());
241: return types;
242: }
243:
244: private static void removeElementsWithoutType(Set candidates,
245: Resource type) {
246: for (Iterator it = candidates.iterator(); it.hasNext();) {
247: Resource candidate = (Resource) it.next();
248: if (!candidate.hasProperty(RDF.type, type))
249: it.remove();
250: }
251: }
252:
253: private static Set subjectSet(Model result, Resource S, Property P,
254: RDFNode O) {
255: return IteratorCollection.iteratorToSet(result.listStatements(
256: S, P, O).mapWith(Statement.Util.getSubject));
257: }
258:
259: private static List asJavaList(Resource resource) {
260: return ((RDFList) resource.as(RDFList.class)).asJavaList();
261: }
262:
263: /**
264: A Lisp-style linked list. Used because we want non-updating cons
265: operations.
266: */
267: protected static class LinkedSeq {
268: final Resource item;
269: final LinkedSeq rest;
270:
271: LinkedSeq(Resource item) {
272: this (item, null);
273: }
274:
275: LinkedSeq(Resource item, LinkedSeq rest) {
276: this .item = item;
277: this .rest = rest;
278: }
279:
280: LinkedSeq push(Resource item) {
281: return new LinkedSeq(item, this );
282: }
283:
284: public String toString() {
285: StringBuffer result = new StringBuffer("[");
286: LinkedSeq scan = this ;
287: while (scan != null) {
288: result.append(scan.item);
289: scan = scan.rest;
290: result.append(" ");
291: }
292: return result.append("]").toString();
293: }
294: }
295: }
296:
297: /*
298: * (c) Copyright 2006, 2007, 2008 Hewlett-Packard Development Company, LP
299: * All rights reserved.
300: *
301: * Redistribution and use in source and binary forms, with or without
302: * modification, are permitted provided that the following conditions
303: * are met:
304: * 1. Redistributions of source code must retain the above copyright
305: * notice, this list of conditions and the following disclaimer.
306: * 2. Redistributions in binary form must reproduce the above copyright
307: * notice, this list of conditions and the following disclaimer in the
308: * documentation and/or other materials provided with the distribution.
309: * 3. The name of the author may not be used to endorse or promote products
310: * derived from this software without specific prior written permission.
311: *
312: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
313: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
314: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
315: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
316: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
317: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
318: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
319: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
320: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
321: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
322: */
|