0001: /*
0002: * (c) Copyright 2000, 2001, 2002, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
0003: * All rights reserved.
0004: * [See end of file]
0005: * $Id: Unparser.java,v 1.45 2008/02/11 11:10:13 jeremy_carroll Exp $
0006: */
0007:
0008: package com.hp.hpl.jena.xmloutput.impl;
0009:
0010: /*
0011: * @author Jeremy Carroll
0012: *
0013: * Want todo List - easy efficiency gains in listSubjects() and
0014: * modelListSubjects() by removing those subjects that we have already
0015: * considered.
0016: * - Set Default language during first pass.
0017: *
0018: *
0019: * Notes on ID and BagID: Our preferences are follows: for a Stating with an
0020: * explicit local ID we avoid explicitly constructing the reification, and try
0021: * and use rule 6.12 with an idAttr. If the Stating is anonymous or non-local
0022: * then we construct the reification explicitly.
0023: *
0024: *
0025: * Notes: The following rules are not supported by the current Jena RDF parser:
0026: * 6.8
0027: *
0028: *
0029: * [6.1] RDF ::= ['<rdf:RDF>'] obj* ['</rdf:RDF>'] [6.2] obj ::= description |
0030: * container [6.3] description ::= '<rdf:Description' idAboutAttr? bagIdAttr?
0031: * propAttr* '/>' | '<rdf:Description' idAboutAttr? bagIdAttr? propAttr* '>'
0032: * propertyElt* '</rdf:Description>' | typedNode [6.4] container ::= sequence |
0033: * bag | alternative [6.5] idAboutAttr ::= idAttr | aboutAttr | aboutEachAttr
0034: * [6.6] idAttr ::= ' ID="' IDsymbol '"' [6.7] aboutAttr ::= ' about="'
0035: * URI-reference '"' [6.8] aboutEachAttr ::= ' aboutEach="' URI-reference '"' | '
0036: * aboutEachPrefix="' string '"' [6.9] bagIdAttr ::= ' bagID="' IDsymbol '"'
0037: * [6.10] propAttr ::= typeAttr | propName '="' string '"' (with embedded quotes
0038: * escaped) [6.11] typeAttr ::= ' type="' URI-reference '"' [6.12] propertyElt
0039: * ::= '<' propName idAttr? '>' value '</' propName '>' | '<' propName
0040: * idAttr? parseLiteral '>' literal '</' propName '>' | '<' propName idAttr?
0041: * parseResource '>' propertyElt* '</' propName '>' | '<' propName idRefAttr?
0042: * bagIdAttr? propAttr* '/>'
0043: *
0044: * [daml.1 - 6.12 cont.] | '<' propName idAttr? parseDamlCollection '>' obj* '</'
0045: * propName '>' [daml.2] parseDamlCollection ::= ' parseType="daml:collection"'
0046: *
0047: * [6.13] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr* '/>' | '<'
0048: * typeName idAboutAttr? bagIdAttr? propAttr* '>' propertyElt* '</' typeName
0049: * '>' [6.14] propName ::= Qname [6.15] typeName ::= Qname [6.16] idRefAttr ::=
0050: * idAttr | resourceAttr [6.17] value ::= obj | string [6.18] resourceAttr ::= '
0051: * resource="' URI-reference '"' [6.19] Qname ::= [ NSprefix ':' ] name [6.20]
0052: * URI-reference ::= string, interpreted per [URI] [6.21] IDsymbol ::= (any
0053: * legal XML name symbol) [6.22] name ::= (any legal XML name symbol) [6.23]
0054: * NSprefix ::= (any legal XML namespace prefix) [6.24] string ::= (any XML
0055: * text, with "<", ">", and "&" escaped) [6.25] sequence ::= '<rdf:Seq'
0056: * idAttr? '>' member* '</rdf:Seq>' | '<rdf:Seq' idAttr? memberAttr* '/>'
0057: * [6.26] bag ::= '<rdf:Bag' idAttr? '>' member* '</rdf:Bag>' | '<rdf:Bag'
0058: * idAttr? memberAttr* '/>' [6.27] alternative ::= '<rdf:Alt' idAttr? '>'
0059: * member+ '</rdf:Alt>' | '<rdf:Alt' idAttr? memberAttr? '/>' [6.28] member
0060: * ::= referencedItem | inlineItem [6.29] referencedItem ::= '<rdf:li'
0061: * resourceAttr '/>' [6.30] inlineItem ::= '<rdf:li' '>' value </rdf:li>' | '<rdf:li'
0062: * parseLiteral '>' literal </rdf:li>' | '<rdf:li' parseResource '>'
0063: * propertyElt* </rdf:li>' [6.31] memberAttr ::= ' rdf:_n="' string '"' (where n
0064: * is an integer) [6.32] parseLiteral ::= ' parseType="Literal"' [6.33]
0065: * parseResource ::= ' parseType="Resource"' [6.34] literal ::= (any well-formed
0066: * XML)
0067: *
0068: */
0069: import java.io.PrintWriter;
0070: import java.util.*;
0071:
0072: import org.apache.commons.logging.*;
0073: import org.apache.xerces.util.XMLChar;
0074:
0075: import com.hp.hpl.jena.iri.IRI;
0076: import com.hp.hpl.jena.rdf.model.*;
0077: import com.hp.hpl.jena.rdf.model.impl.*;
0078: import com.hp.hpl.jena.shared.*;
0079: import com.hp.hpl.jena.util.iterator.*;
0080: import com.hp.hpl.jena.vocabulary.*;
0081:
0082: /**
0083: * An Unparser will output a model in the abbreviated syntax. *
0084: *
0085: * @version Release='$Name: $' Revision='$Revision: 1.45 $' Date='$Date:
0086: * 2005/07/13 15:33:51 $'
0087: *
0088: */
0089: class Unparser {
0090: static private Property LI = new PropertyImpl(RDF.getURI(), "li");
0091:
0092: static private Property DESCRIPTION = new PropertyImpl(
0093: RDF.getURI(), "Description");
0094:
0095: static protected Log logger = LogFactory.getLog(Unparser.class);
0096:
0097: /**
0098: * Creates an Unparser for the specified model. The localName is the URI
0099: * (typical URL) intended for the output file. No trailing "#" should be
0100: * used. This will control the use of <I>ID</I> or <I>about</I> or
0101: * <I>resource</I> on various rules.
0102: *
0103: * @param localName
0104: * The intended URI of the output file. No trailing "#".
0105: * @param m
0106: * The model.
0107: * @param w
0108: * The output.
0109: */
0110: Unparser(Abbreviated parent, String localName, Model m,
0111: PrintWriter w) {
0112: setLocalName(localName);
0113: prettyWriter = parent;
0114: out = w;
0115: model = m;
0116: addTypeNameSpaces();
0117: objectTable = new HashMap();
0118: StmtIterator ss = m.listStatements();
0119: try {
0120: while (ss.hasNext()) {
0121: Statement s = ss.nextStatement();
0122: RDFNode rn = s.getObject();
0123: if (rn instanceof Resource) {
0124: increaseObjectCount((Resource) rn);
0125: }
0126: }
0127: } finally {
0128: ss.close();
0129: }
0130: try {
0131: res2statement = new HashMap();
0132: statement2res = new HashMap();
0133: ClosableIterator reified = new MapFilterIterator(
0134: new MapFilter() {
0135: public Object accept(Object o) {
0136: Resource r = (Resource) o;
0137: return (r.hasProperty(RDF.subject)
0138: && r.hasProperty(RDF.object) && r
0139: .hasProperty(RDF.predicate)) ? r
0140: : null;
0141:
0142: }
0143: }, model.listResourcesWithProperty(RDF.type,
0144: RDF.Statement));
0145: while (reified.hasNext()) {
0146: Resource r = (Resource) reified.next();
0147: try {
0148: /**
0149: * This block of code assumes that really we are dealing
0150: * with a reification. We may, on the contrary, be dealing
0151: * with a random collection of triples that do not make
0152: * sense.
0153: */
0154: Statement subj = r.getRequiredProperty(RDF.subject);
0155: Statement pred = r
0156: .getRequiredProperty(RDF.predicate);
0157: Statement obj = r.getRequiredProperty(RDF.object);
0158: RDFNode nobj = obj.getObject();
0159: Resource rsubj = (Resource) subj.getObject();
0160: Resource rpred = (Resource) pred.getObject();
0161:
0162: Property ppred = model.createProperty(rpred
0163: .getURI());
0164:
0165: Statement statement = model.createStatement(rsubj,
0166: ppred, nobj);
0167: res2statement.put(r, statement);
0168: statement2res.put(statement, r);
0169: } catch (Exception ignored) {
0170: }
0171: }
0172: } finally {
0173: ss.close();
0174: }
0175: }
0176:
0177: /**
0178: * Note: must work with uri being null.
0179: */
0180: private void setLocalName(String uri) {
0181: if (uri == null || uri.equals(""))
0182: localName = "";
0183: else
0184: // try
0185: {
0186: IRI u = BaseXMLWriter.factory.create(uri);
0187: u = u.create("");
0188: localName = u.toString();
0189: }
0190: // catch (MalformedURIException e) {
0191: // throw new BadURIException(uri, e);
0192: // }
0193: }
0194:
0195: /**
0196: * Should be called exactly once for each Unparser. Calling it a second time
0197: * will have undesired results.
0198: */
0199: void write() {
0200: prettyWriter.workOutNamespaces();
0201: wRDF();
0202: /*
0203: * System.out.print("Coverage = "); for (int i=0;i<codeCoverage.length;i++)
0204: * System.out.print(" c[" + i + "] = " + codeCoverage[i]+ ";");
0205: * System.out.println();
0206: */
0207: }
0208:
0209: /**
0210: * Set a list of types of objects that will be expanded at the top-level of
0211: * the file.
0212: *
0213: * @param types
0214: * An array of rdf:Class'es.
0215: *
0216: */
0217: void setTopLevelTypes(Resource types[]) {
0218: pleasingTypes = types;
0219: pleasingTypeSet = new HashSet(Arrays.asList(types));
0220: }
0221:
0222: private String xmlBase;
0223:
0224: void setXMLBase(String b) {
0225: xmlBase = b;
0226: }
0227:
0228: /*
0229: * THE MORE INTERESTING MEMBER VARIABLES. Note there are others scattered
0230: * throughout the file, but those are only used by one or two methods.
0231: */
0232:
0233: final private static String rdfns = RDF.type.getNameSpace();
0234:
0235: final private static Integer one = new Integer(1);
0236:
0237: private String localName;
0238:
0239: private Map objectTable; // This is a map from Resource to Integer
0240:
0241: // which indicates how many times each resource
0242: // occurs as an object of a triple.
0243: private Model model;
0244:
0245: private PrintWriter out;
0246:
0247: private Set doing = new HashSet(); // Some of the resources that
0248:
0249: // are currently being written.
0250: private Set doneSet = new HashSet(); // The triples that have been
0251: // output.
0252:
0253: private Set haveReified = new HashSet(); // Those local resources that
0254: // are
0255:
0256: // the id's of a reification, used to ensure that anonymous
0257: // resources are made non-anonymous when reified in certain ways.
0258:
0259: private Resource pleasingTypes[] = null;
0260:
0261: private Set pleasingTypeSet = new HashSet();
0262:
0263: final private Abbreviated prettyWriter;
0264:
0265: private boolean avoidExplicitReification = true;
0266:
0267: // We set this to false as we start giving up on elegance.
0268:
0269: // Reification stuff.
0270:
0271: Map res2statement;
0272:
0273: Map statement2res;
0274:
0275: /*
0276: * The top-down recursive descent unparser. The methods starting in w all
0277: * refer to one of the rules of the grammar, which they implement. boolean
0278: * valued rules first check whether they are applicable and return false if
0279: * not. Otherwise they create appropriate output (using recursive descent)
0280: * and return true. Note all necessary checks are made before any output or
0281: * any recursive descent. The void w- methods just implement the rule, which
0282: * typically does not involve any choice.
0283: */
0284: /*
0285: * [6.1] RDF ::= ['<rdf:RDF>'] obj* ['</rdf:RDF>']
0286: */
0287: private void wRDF() {
0288: tab();
0289: print("<");
0290: print(prettyWriter.rdfEl("RDF"));
0291: indentPlus();
0292: printNameSpaceDefn();
0293: if (xmlBase != null) {
0294: setLocalName(xmlBase);
0295: tab();
0296: print("xml:base=" + quote(xmlBase));
0297: }
0298: print(">");
0299: wObjStar();
0300: indentMinus();
0301: tab();
0302: print("</");
0303: print(prettyWriter.rdfEl("RDF"));
0304: print(">");
0305: tab();
0306: }
0307:
0308: /**
0309: * All subjects get listed, for top level use only.
0310: */
0311: private void wObjStar() {
0312: Iterator rs = listSubjects();
0313: while (rs.hasNext()) {
0314: Resource r = (Resource) rs.next();
0315: increaseObjectCount(r);
0316: // This forces us to not be anonymous unless
0317: // we are never an object. See isGenuineAnon().
0318: wObj(r, true);
0319: }
0320: closeAllResIterators();
0321: }
0322:
0323: /*
0324: * [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>' | '<'
0325: * propName idAttr? parseLiteral '>' literal '</' propName '>' | '<'
0326: * propName idAttr? parseResource '>' propertyElt* '</' propName '>' | '<'
0327: * propName idRefAttr? bagIdAttr? propAttr* '/>' [daml.1 - 6.12 cont.] | '<'
0328: * propName idAttr? parseDamlCollection '>' obj* '</' propName '>' [daml.2]
0329: * parseDamlCollection ::= ' parseType="daml:collection"'
0330: *
0331: * For daml collections we prefer the special syntax otherwise: We prefer
0332: * choice 4 where possible, except in the case where the statement is
0333: * reified and the object is not anonymous in which case we use one of the
0334: * others (e.g. choice 1). For embedded XML choice 2 is obligatory. For
0335: * untyped, anonymous resource valued items choice 3 is used. Choice 1 is
0336: * the fall back.
0337: */
0338: private boolean wPropertyElt(WType wt, Property prop, Statement s,
0339: RDFNode val) {
0340: return wPropertyEltCompact(wt, prop, s, val)
0341: || // choice 4
0342: wPropertyEltDamlCollection(wt, prop, s, val)
0343: || // choice daml.1
0344: wPropertyEltLiteral(wt, prop, s, val)
0345: || // choice 2
0346: wPropertyEltResource(wt, prop, s, val)
0347: || // choice 3
0348: wPropertyEltDatatype(wt, prop, s, val)
0349: || wPropertyEltValue(wt, prop, s, val);
0350: // choice 1.
0351: }
0352:
0353: /*
0354: * [6.12.4] propertyElt ::= '<' propName idRefAttr? bagIdAttr? propAttr*
0355: * '/>'
0356: */
0357: private boolean wPropertyEltCompact(WType wt, Property prop,
0358: Statement s, RDFNode val) {
0359: // Conditions
0360: if (!(val instanceof Resource))
0361: return false;
0362: Resource r = (Resource) val;
0363: if (!(allPropsAreAttr(r) || doing.contains(r)))
0364: return false;
0365: // '<' propName '/>' is 6.12.1 rather than 6.12.4
0366: // and it becomes an empty string value.
0367: // Whether this is a mistake or not is debatable.
0368: // We avoid the construction.
0369: if ((!hasProperties(r)) && isGenuineAnon(r))
0370: return false;
0371: // Write out
0372: done(s);
0373: tab();
0374: print("<");
0375: wt.wTypeStart(prop);
0376: indentPlus();
0377: wIdRefAttrOpt(s, r);
0378: if (!doing.contains(r)) {
0379: wPropAttrAll(r);
0380: } else if (isGenuineAnon(r)) {
0381: // ???
0382: error("Genuine anon resource in cycle?");
0383: }
0384: indentMinus();
0385: print("/>");
0386: return true;
0387: }
0388:
0389: /*
0390: * [6.12.2] propertyElt ::= '<' propName idAttr? parseLiteral '>' literal '</'
0391: * propName '>'
0392: */
0393: private boolean wPropertyEltLiteral(WType wt, Property prop,
0394: Statement s, RDFNode r) {
0395: if (prettyWriter.sParseTypeLiteralPropertyElt)
0396: return false;
0397: if (!((r instanceof Literal) && ((Literal) r).isWellFormedXML())) {
0398: return false;
0399: }
0400: // print out.
0401: done(s);
0402: tab();
0403: print("<");
0404: wt.wTypeStart(prop);
0405: wIdAttrReified(s);
0406: maybeNewline();
0407: wParseLiteral();
0408: maybeNewline();
0409: print(">");
0410: print(((Literal) r).getLexicalForm());
0411: print("</");
0412: wt.wTypeEnd(prop);
0413: print(">");
0414: return true;
0415: }
0416:
0417: private boolean wPropertyEltDatatype(WType wt, Property prop,
0418: Statement s, RDFNode r) {
0419: if (!((r instanceof Literal) && ((Literal) r).getDatatypeURI() != null)) {
0420: return false;
0421: }
0422: // print out.
0423: done(s);
0424: tab();
0425: print("<");
0426: wt.wTypeStart(prop);
0427: wIdAttrReified(s);
0428: maybeNewline();
0429: wDatatype(((Literal) r).getDatatypeURI());
0430: maybeNewline();
0431: print(">");
0432: print(Util.substituteEntitiesInElementContent(((Literal) r)
0433: .getLexicalForm()));
0434: print("</");
0435: wt.wTypeEnd(prop);
0436: print(">");
0437: return true;
0438: }
0439:
0440: /*
0441: * [6.12.3] propertyElt ::= '<' propName idAttr? parseResource '>'
0442: * propertyElt* '</' propName '>'
0443: */
0444: private boolean wPropertyEltResource(WType wt, Property prop,
0445: Statement s, RDFNode r) {
0446: if (prettyWriter.sParseTypeResourcePropertyElt)
0447: return false;
0448: if (r instanceof Literal)
0449: return false;
0450: Resource res = (Resource) r;
0451: if (!isGenuineAnon(res))
0452: return false;
0453: if (getType(res) != null)
0454: return false; // preferred typed node construction.
0455: // print out.
0456: done(s);
0457: tab();
0458: print("<");
0459: wt.wTypeStart(prop);
0460: indentPlus();
0461: wIdAttrReified(s);
0462: wParseResource();
0463: print(">");
0464: wPropertyEltStar(res);
0465: indentMinus();
0466: tab();
0467: print("</");
0468: wt.wTypeEnd(prop);
0469: print(">");
0470: return true;
0471: }
0472:
0473: /*
0474: * [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
0475: */
0476: private boolean wPropertyEltValue(WType wt, Property prop,
0477: Statement s, RDFNode r) {
0478: return wPropertyEltValueString(wt, prop, s, r)
0479: || wPropertyEltValueObj(wt, prop, s, r);
0480: }
0481:
0482: /*
0483: * [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
0484: */
0485: private boolean wPropertyEltValueString(WType wt, Property prop,
0486: Statement s, RDFNode r) {
0487: if (r instanceof Literal) {
0488: done(s);
0489: Literal lt = (Literal) r;
0490: String lang = lt.getLanguage();
0491: tab();
0492: print("<");
0493: wt.wTypeStart(prop);
0494: wIdAttrReified(s);
0495: maybeNewline();
0496: if (lang != null && lang.length() > 0)
0497: print(" xml:lang=" + q(lang));
0498: maybeNewline();
0499: print(">");
0500: wValueString(lt);
0501: print("</");
0502: wt.wTypeEnd(prop);
0503: print(">");
0504: return true;
0505: }
0506: return false;
0507:
0508: }
0509:
0510: /*
0511: * [6.17.2] value ::= string
0512: */
0513: private void wValueString(Literal lt) {
0514: String val = lt.getString();
0515: print(Util.substituteEntitiesInElementContent(val));
0516: }
0517:
0518: /*
0519: * [6.12] propertyElt ::= '<' propName idAttr? '>' value '</' propName '>'
0520: * [6.17.1] value ::= obj
0521: */
0522: private boolean wPropertyEltValueObj(WType wt, Property prop,
0523: Statement s, RDFNode r) {
0524: if (r instanceof Resource && !prettyWriter.sResourcePropertyElt) {
0525: Resource res = (Resource) r;
0526: done(s);
0527: tab();
0528: print("<");
0529: wt.wTypeStart(prop);
0530: wIdAttrReified(s);
0531: print(">");
0532: tab();
0533: indentPlus();
0534: wObj(res, false);
0535: indentMinus();
0536: tab();
0537: print("</");
0538: wt.wTypeEnd(prop);
0539: print(">");
0540: return true;
0541: }
0542:
0543: return false;
0544:
0545: }
0546:
0547: /*
0548: * [daml.1 - 6.12 cont.] | '<' propName idAttr? parseDamlCollection '>'
0549: * obj* '</' propName '>'
0550: */
0551: private boolean wPropertyEltDamlCollection(WType wt, Property prop,
0552: Statement s, RDFNode r) {
0553: boolean daml = true;
0554: Statement list[][] = getDamlList(r);
0555: if (list == null) {
0556: daml = false;
0557: list = getRDFList(r);
0558: }
0559: if (list == null)
0560: return false;
0561: // print out.
0562: done(s);
0563: // record all done's first - they may impact the
0564: // way we print the values.
0565: for (int i = 0; i < list.length; i++) {
0566: done(list[i][0]);
0567: done(list[i][1]);
0568: if (daml)
0569: done(list[i][2]);
0570: }
0571: tab();
0572: print("<");
0573: wt.wTypeStart(prop);
0574: indentPlus();
0575: wIdAttrReified(s);
0576: if (daml)
0577: wParseDamlCollection();
0578: else
0579: wParseCollection();
0580:
0581: print(">");
0582: for (int i = 0; i < list.length; i++) {
0583: wObj((Resource) list[i][0].getObject(), false);
0584: }
0585: indentMinus();
0586: tab();
0587: print("</");
0588: wt.wTypeEnd(prop);
0589: print(">");
0590: return true;
0591: }
0592:
0593: // propAttr* with no left over statements.
0594: private void wPropAttrAll(Resource r) {
0595: wPropAttrSome(r);
0596: if (hasProperties(r))
0597: error("Bad call to wPropAttrAll");
0598: }
0599:
0600: // propAttr* possibly with left over statements.
0601: private void wPropAttrSome(Resource r) {
0602: ClosableIterator ss = listProperties(r);
0603: try {
0604: Set seen = new HashSet();
0605: while (ss.hasNext()) {
0606: Statement s = (Statement) ss.next();
0607: if (canBeAttribute(s, seen)) {
0608: done(s);
0609: wPropAttr(s.getPredicate(), s.getObject());
0610: }
0611: }
0612: } finally {
0613: ss.close();
0614: }
0615: }
0616:
0617: /*
0618: * [6.2] obj ::= description | container [6.3] description ::= '<rdf:Description'
0619: * idAboutAttr? bagIdAttr? propAttr* '/>' | '<rdf:Description' idAboutAttr?
0620: * bagIdAttr? propAttr* '>' propertyElt* '</rdf:Description>' | typedNode
0621: * [6.4] container ::= sequence | bag | alternative We use: [6.2a] obj ::=
0622: * description | container | typedNode [6.3a] description ::= '<rdf:Description'
0623: * idAboutAttr? bagIdAttr? propAttr* '/>' | '<rdf:Description' idAboutAttr?
0624: * bagIdAttr? propAttr* '>' propertyElt* '</rdf:Description>'
0625: *
0626: * This method has got somewhat messy. If we are not at the topLevel we may
0627: * choose to not expand a node but just use a typedNode ::= '<' typeName
0628: * idAboutAttr '/>' rule. This rules also applies to Bags that we feel
0629: * unconfortable with, such as a Bag arising from a BagId rule that we don't
0630: * handle properly.
0631: *
0632: *
0633: */
0634: private boolean wObj(Resource r, boolean topLevel) {
0635: try {
0636: doing.add(r);
0637: Statement typeStatement = getType(r);
0638: if (typeStatement != null) {
0639: Resource t = typeStatement.getResource();
0640: if (!topLevel) {
0641: if (pleasingTypeSet.contains(t)
0642: && (!isGenuineAnon(r))) {
0643: return wTypedNodeNoProperties(r);
0644: }
0645: }
0646: return wTypedNode(r) || wDescription(r);
0647: }
0648: return wDescription(r);
0649: } finally {
0650: doing.remove(r);
0651: }
0652: }
0653:
0654: abstract private class WType {
0655: abstract void wTypeStart(Resource uri);
0656:
0657: abstract void wTypeEnd(Resource uri);
0658: }
0659:
0660: static private int RDF_HASH = RDF.getURI().length();
0661:
0662: private WType wdesc = new WType() {
0663: void wTypeStart(Resource u) {
0664: print(prettyWriter.rdfEl(u.getURI().substring(RDF_HASH)));
0665: }
0666:
0667: void wTypeEnd(Resource u) {
0668: print(prettyWriter.rdfEl(u.getURI().substring(RDF_HASH)));
0669: }
0670: };
0671:
0672: private WType wtype = new WType() {
0673: void wTypeStart(Resource u) {
0674: print(prettyWriter.startElementTag(u.getURI()));
0675: }
0676:
0677: void wTypeEnd(Resource u) {
0678: print(prettyWriter.endElementTag(u.getURI()));
0679: }
0680: };
0681:
0682: /*
0683: * [6.3a] description ::= '<rdf:Description' idAboutAttr? bagIdAttr?
0684: * propAttr* '/>' | '<rdf:Description' idAboutAttr? bagIdAttr? propAttr*
0685: * '>' propertyElt* '</rdf:Description>'
0686: */
0687: private boolean wDescription(Resource r) {
0688: return wTypedNodeOrDescription(wdesc, DESCRIPTION, r);
0689: }
0690:
0691: /*
0692: * [6.13] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr* '/>' | '<'
0693: * typeName idAboutAttr? bagIdAttr? propAttr* '>' propertyElt* '</'
0694: * typeName '>'
0695: */
0696: private boolean wTypedNode(Resource r) {
0697: Statement st = getType(r);
0698: if (st == null)
0699: return false;
0700: Resource type = st.getResource();
0701: done(st);
0702: return wTypedNodeOrDescription(wtype, type, r);
0703: }
0704:
0705: private boolean wTypedNodeOrDescription(WType wt, Resource ty,
0706: Resource r) {
0707: // preparation - look for the li's.
0708: Vector found = new Vector();
0709: ClosableIterator ss = listProperties(r);
0710: try {
0711: int greatest = 0;
0712: if (!prettyWriter.sListExpand)
0713: while (ss.hasNext()) {
0714: Statement s = (Statement) ss.next();
0715: int ix = s.getPredicate().getOrdinal();
0716: if (ix != 0) {
0717: if (ix > greatest) {
0718: found.setSize(ix);
0719: greatest = ix;
0720: }
0721: found.set(ix - 1, s);
0722: }
0723: }
0724: } finally {
0725: ss.close();
0726: }
0727: int last = found.indexOf(null);
0728: List li = last == -1 ? found : found.subList(0, last);
0729:
0730: return wTypedNodeOrDescriptionCompact(wt, ty, r, li)
0731: || wTypedNodeOrDescriptionLong(wt, ty, r, li);
0732: }
0733:
0734: /*
0735: * [6.13.1] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr*
0736: * '/>'
0737: */
0738: private boolean wTypedNodeOrDescriptionCompact(WType wt,
0739: Resource ty, Resource r, List li) {
0740: // Conditions
0741: if ((!li.isEmpty()) || !allPropsAreAttr(r))
0742: return false;
0743: // Write out
0744: tab();
0745: print("<");
0746: wt.wTypeStart(ty);
0747: indentPlus();
0748: wIdAboutAttrOpt(r);
0749: wPropAttrAll(r);
0750: print("/>");
0751: indentMinus();
0752: return true;
0753: }
0754:
0755: /*
0756: * [6.13.1] typedNode ::= '<' typeName idAboutAttr '/>'
0757: */
0758: private boolean wTypedNodeNoProperties(Resource r) {
0759: // Conditions
0760: if (isGenuineAnon(r))
0761: return false;
0762: Statement st = getType(r);
0763: if (st == null)
0764: return false;
0765: Resource type = st.getResource();
0766: done(st);
0767: // Write out
0768: tab();
0769: print("<");
0770: wtype.wTypeStart(type);
0771: indentPlus();
0772: // if (hasProperties(r))
0773: // wAboutAttr(r);
0774: // else
0775: wIdAboutAttrOpt(r);
0776: print("/>");
0777: indentMinus();
0778: return true;
0779: }
0780:
0781: /*
0782: * [6.13.2] typedNode ::= '<' typeName idAboutAttr? bagIdAttr? propAttr*
0783: * '>' propertyElt* '</' typeName '>'
0784: */
0785: private boolean wTypedNodeOrDescriptionLong(WType wt, Resource ty,
0786: Resource r, List li) {
0787: Iterator it = li.iterator();
0788: while (it.hasNext()) {
0789: done((Statement) it.next());
0790: }
0791:
0792: tab();
0793: print("<");
0794: wt.wTypeStart(ty);
0795: indentPlus();
0796: wIdAboutAttrOpt(r);
0797: wPropAttrSome(r);
0798: print(">");
0799: wLiEltStar(li.iterator());
0800: wPropertyEltStar(r);
0801: indentMinus();
0802: tab();
0803: print("</");
0804: wt.wTypeEnd(ty);
0805: print(">");
0806: return true;
0807: }
0808:
0809: private void wPropertyEltStar(Resource r) {
0810: ClosableIterator ss = this .listProperties(r);
0811: try {
0812: while (ss.hasNext()) {
0813: Statement s = (Statement) ss.next();
0814: wPropertyElt(wtype, s.getPredicate(), s, s.getObject());
0815: }
0816: } finally {
0817: ss.close();
0818: }
0819: }
0820:
0821: private void wLiEltStar(Iterator ss) {
0822: while (ss.hasNext()) {
0823: Statement s = (Statement) ss.next();
0824: wPropertyElt(wdesc, LI, s, s.getObject());
0825: }
0826: }
0827:
0828: /*
0829: * [6.5] idAboutAttr ::= idAttr | aboutAttr | aboutEachAttr we use [6.5a]
0830: * idAboutAttr ::= idAttr | aboutAttr
0831: */
0832: private Set idDone = new HashSet();
0833:
0834: private boolean wIdAboutAttrOpt(Resource r) {
0835: return wIdAttrOpt(r) || wNodeIDAttr(r) || wAboutAttr(r);
0836: }
0837:
0838: /**
0839: * Returns false if the resource is not genuinely anonymous and cannot be
0840: * referred to using an ID. [6.6] idAttr ::= ' ID="' IDsymbol '"'
0841: */
0842: private boolean wIdAttrOpt(Resource r) {
0843:
0844: if (isGenuineAnon(r))
0845: return true; // We have output resource (with nothing).
0846: if (prettyWriter.sIdAttr)
0847: return false;
0848: if (r.isAnon())
0849: return false;
0850: if (isLocalReference(r)) {
0851: // Try and use the reification rules if they apply.
0852: // Issue: aren't we just about to list those statements explicitly.
0853: if (wantReification(r))
0854: return false;
0855: // Can be an ID if not already output.
0856: if (idDone.contains(r)) {
0857: return false; // We have already output this one.
0858: }
0859:
0860: idDone.add(r);
0861: print(" ");
0862: printRdfAt("ID");
0863: print("=");
0864: print(quote(getLocalName(r)));
0865: return true;
0866:
0867: }
0868: return false;
0869:
0870: }
0871:
0872: /*
0873: * [6.7] aboutAttr ::= ' about="' URI-reference '"'
0874: */
0875: private boolean wAboutAttr(Resource r) {
0876: print(" ");
0877: printRdfAt("about");
0878: print("=");
0879: wURIreference(r);
0880: return true;
0881: }
0882:
0883: private void wURIreference(String s) {
0884: print(quote(prettyWriter.relativize(s)));
0885: }
0886:
0887: private void wURIreference(Resource r) {
0888: wURIreference(r.getURI());
0889: }
0890:
0891: /*
0892: * [6.16] idRefAttr ::= idAttr | resourceAttr
0893: */
0894: private void wIdRefAttrOpt(Statement s, Resource r) {
0895: wIdAttrReified(s);
0896: if (!isGenuineAnon(r)) {
0897: wResourceNodeIDAttr(r);
0898: }
0899: }
0900:
0901: /*
0902: * [6.6] idAttr ::= ' ID="' IDsymbol '"'
0903: */
0904: private void wIdAttrReified(Statement s) {
0905: if (wantReification(s)) {
0906: /*
0907: * if ( prettyWriter.sReification ) System.err.println("???"); else
0908: * System.err.println("!!!");
0909: */
0910: Statement reify[] = reification(s);
0911: Resource res = (Resource) statement2res.get(s);
0912: idDone.add(res);
0913: int i;
0914: for (i = 0; i < reify.length; i++)
0915: done(reify[i]);
0916: print(" ");
0917: printRdfAt("ID");
0918: print("=");
0919: print(quote(getLocalName(res)));
0920: haveReified.add(res);
0921: }
0922: }
0923:
0924: /*
0925: * [6.18] resourceAttr ::= ' resource="' URI-reference '"'
0926: */
0927: private boolean wResourceNodeIDAttr(Resource r) {
0928: return wNodeIDAttr(r) || wResourceAttr(r);
0929: }
0930:
0931: /*
0932: * nodeIDAttr ::= ' rdf:nodeID="' URI-reference '"'
0933: */
0934: private boolean wNodeIDAttr(Resource r) {
0935: if (!r.isAnon())
0936: return false;
0937: print(" ");
0938: printRdfAt("nodeID");
0939: print("=");
0940: print(q(prettyWriter.anonId(r)));
0941:
0942: return true;
0943: }
0944:
0945: /*
0946: * [6.18] resourceAttr ::= ' resource="' URI-reference '"'
0947: */
0948: private boolean wResourceAttr(Resource r) {
0949: if (r.isAnon())
0950: return false;
0951: print(" ");
0952: printRdfAt("resource");
0953: print("=");
0954: wURIreference(r);
0955: return true;
0956: }
0957:
0958: int codeCoverage[] = new int[8];
0959:
0960: /*
0961: * [6.19] Qname ::= [ NSprefix ':' ] name
0962: *
0963: * private void wQnameStart(String ns, String local) {
0964: * print(prettyWriter.startElementTag(ns, local)); }
0965: *
0966: * private void wQnameEnd(String ns, String local) {
0967: * print(prettyWriter.endElementTag(ns, local)); }
0968: */
0969: private void wQNameAttr(Property p) {
0970: print(prettyWriter.attributeTag(p.getURI()));
0971: }
0972:
0973: private void printRdfAt(String s) {
0974: print(prettyWriter.rdfAt(s));
0975: }
0976:
0977: /*
0978: * [6.10] propAttr ::= typeAttr | propName '="' string '"' (with embedded
0979: * quotes escaped) [6.11] typeAttr ::= ' type="' URI-reference '"'
0980: */
0981: private void wPropAttr(Property p, RDFNode n) {
0982: tab();
0983: if (p.equals(RDF.type))
0984: wTypeAttr((Resource) n);
0985: else
0986: wPropAttrString(p, (Literal) n);
0987: }
0988:
0989: private void wTypeAttr(Resource r) {
0990: print(" ");
0991: printRdfAt("type");
0992: print("=");
0993: wURIreference(r);
0994: // print(quote(r.getURI()));
0995: }
0996:
0997: private void wPropAttrString(Property p, Literal l) {
0998: print(" ");
0999: wQNameAttr(p);
1000: print("=" + quote(l.getString()));
1001: }
1002:
1003: /*
1004: * [daml.2] parseDamlCollection ::= ' parseType="daml:collection"'
1005: */
1006: private void wParseDamlCollection() {
1007: print(" ");
1008: printRdfAt("parseType");
1009: print("=" + q("daml:collection"));
1010: }
1011:
1012: /*
1013: * [List.2] parseCollection ::= ' parseType="Collection"'
1014: */
1015: private void wParseCollection() {
1016: print(" ");
1017: printRdfAt("parseType");
1018: print("=" + q("Collection"));
1019: }
1020:
1021: /*
1022: * [6.32] parseLiteral ::= ' parseType="Literal"'
1023: */
1024: private void wParseLiteral() {
1025: print(" ");
1026: printRdfAt("parseType");
1027: print("=" + q("Literal"));
1028: }
1029:
1030: private void wDatatype(String dtURI) {
1031: print(" ");
1032: printRdfAt("datatype");
1033: print("=");
1034: maybeNewline();
1035: wURIreference(dtURI);
1036: }
1037:
1038: /*
1039: * [6.33] parseResource ::= ' parseType="Resource"'
1040: */
1041: private void wParseResource() {
1042: print(" ");
1043: printRdfAt("parseType");
1044: print("=" + q("Resource"));
1045: }
1046:
1047: private void printNameSpaceDefn() {
1048: print(prettyWriter.xmlnsDecl());
1049: }
1050:
1051: /***************************************************************************
1052: * Utility routines ...
1053: *
1054: **************************************************************************/
1055:
1056: /***************************************************************************
1057: * Output and indentation.
1058: **************************************************************************/
1059: private int indentLevel = 0;
1060:
1061: private int currentColumn = 0;
1062:
1063: static private String filler(int lgth) {
1064: char rslt[] = new char[lgth];
1065: Arrays.fill(rslt, ' ');
1066: return new String(rslt);
1067: }
1068:
1069: private void tab() {
1070: int desiredColumn = prettyWriter.tabSize * indentLevel;
1071: if (desiredColumn > prettyWriter.width) {
1072: desiredColumn = 4 + (desiredColumn - 4)
1073: % prettyWriter.width;
1074: }
1075: if ((desiredColumn == 0 && currentColumn == 0)
1076: || desiredColumn > currentColumn) {
1077: String spaces = filler(desiredColumn - currentColumn);
1078: out.print(spaces);
1079: } else {
1080: out.println();
1081: out.print(filler(desiredColumn));
1082: }
1083: currentColumn = desiredColumn;
1084: }
1085:
1086: private void maybeNewline() {
1087: if (currentColumn > prettyWriter.width) {
1088: tab();
1089: }
1090: }
1091:
1092: /**
1093: * Quote str with either ' or " quotes to be in attribute position in XML.
1094: * The real rules are found at http://www.w3.org/TR/REC-xml#AVNormalize
1095: */
1096: private String quote(String str) {
1097: return prettyWriter.substitutedAttribute(str);
1098: }
1099:
1100: private String q(String str) {
1101: return prettyWriter.attributeQuoted(str);
1102: }
1103:
1104: /**
1105: * Indentation screws up if there is a tab character in s. We do not check
1106: * this.
1107: */
1108: private void print(String s) {
1109: out.print(s);
1110: int ix = s.lastIndexOf('\n');
1111: if (ix == -1)
1112: currentColumn += s.length();
1113: else
1114: currentColumn = s.length() - ix - 1;
1115: }
1116:
1117: private void indentPlus() {
1118: indentLevel++;
1119: }
1120:
1121: private void indentMinus() {
1122: indentLevel--;
1123: }
1124:
1125: /*
1126: * Unexpected error.
1127: */
1128: private void error(String msg) {
1129: JenaException e = new BrokenException(
1130: "Internal error in Unparser: " + msg);
1131: this .prettyWriter.fatalError(e);
1132: throw e; // Just in case.
1133: }
1134:
1135: /**
1136: * Name space stuff.
1137: */
1138: private void addTypeNameSpaces() {
1139: NodeIterator nn = model.listObjectsOfProperty(RDF.type);
1140: try {
1141: while (nn.hasNext()) {
1142: RDFNode obj = nn.nextNode();
1143: int split = isOKType(obj);
1144: if (split != -1)
1145: prettyWriter.addNameSpace(((Resource) obj).getURI()
1146: .substring(0, split));
1147: }
1148: } finally {
1149: nn.close();
1150: }
1151: }
1152:
1153: private String getNameSpace(Resource r) {
1154: if (r.isAnon()) {
1155: logger
1156: .error("Internal error - Unparser.getNameSpace; giving up");
1157: throw new BrokenException(
1158: "Internal error: getNameSpace(bNode)");
1159: }
1160: String uri = r.getURI();
1161: int split = Util.splitNamespace(uri);
1162: return uri.substring(0, split);
1163:
1164: }
1165:
1166: /**
1167: * Local and/or anonymous resources.
1168: */
1169: private boolean isGenuineAnon(Resource r) {
1170: if (!r.isAnon())
1171: return false;
1172: Integer v = (Integer) objectTable.get(r);
1173: return v == null
1174: || ((!prettyWriter.sResourcePropertyElt)
1175: && v.intValue() <= 1 && (!haveReified
1176: .contains(r)));
1177: }
1178:
1179: private boolean isLocalReference(Resource r) {
1180: return (!r.isAnon()) && getNameSpace(r).equals(localName + "#")
1181: && XMLChar.isValidNCName(getLocalName(r));
1182: }
1183:
1184: /*
1185: * Utility for turning an integer into an alphabetic string.
1186: *
1187: * private static String getSuffix(int suffixId) { if (suffixId == 0) return
1188: * ""; else { suffixId--; int more = (suffixId / 26);
1189: *
1190: * return getSuffix(more) + new Character((char) ('a' + suffixId % 26)); } }
1191: */
1192:
1193: private String getLocalName(Resource r) {
1194: if (r.isAnon()) {
1195: logger
1196: .error("Internal error - giving up - Unparser.getLocalName");
1197: throw new BrokenException(
1198: "Internal error: getLocalName(bNode)");
1199: }
1200: String uri = r.getURI();
1201: int split = Util.splitNamespace(uri);
1202: return uri.substring(split);
1203:
1204: }
1205:
1206: /**
1207: * objectTable initialization.
1208: */
1209:
1210: private void increaseObjectCount(Resource r) {
1211: // if (!r.isAnon())
1212: // return;
1213: Integer cnt = (Integer) objectTable.get(r);
1214: if (cnt == null) {
1215: cnt = one;
1216: } else {
1217: cnt = new Integer(cnt.intValue() + 1);
1218: }
1219: objectTable.put(r, cnt);
1220: }
1221:
1222: /***************************************************************************
1223: * Reification support.
1224: **************************************************************************/
1225: /*
1226: * Is the use of ID in rule [6.12] to create a reification helpful or not?
1227: */
1228: private boolean wantReification(Statement s) {
1229: return wantReification(s, (Resource) statement2res.get(s));
1230: }
1231:
1232: private boolean wantReification(Resource res) {
1233: return wantReification((Statement) res2statement.get(res), res);
1234: }
1235:
1236: private boolean wantReification(Statement s, Resource ref) {
1237: if (s == null || ref == null || ref.isAnon()
1238: || prettyWriter.sReification)
1239: return false;
1240: if (!(isLocalReference(ref)))
1241: return false;
1242: Statement reify[] = reification(s);
1243: int i;
1244: for (i = 0; i < reify.length; i++)
1245: if (doneSet.contains(reify[i])
1246: || (!model.contains(reify[i])))
1247: return false; // Some of reification already done.
1248: return true; // Reification rule helps.
1249: }
1250:
1251: private Statement[] reification(Statement s) {
1252: Model m = s.getModel();
1253: Resource r = (Resource) statement2res.get(s);
1254: return new Statement[] {
1255: m.createStatement(r, RDF.type, RDF.Statement),
1256: m.createStatement(r, RDF.subject, s.getSubject()),
1257: m.createStatement(r, RDF.predicate, s.getPredicate()),
1258: m.createStatement(r, RDF.object, s.getObject()) };
1259: }
1260:
1261: private boolean hasProperties(Resource r) {
1262: ExtendedIterator ss = listProperties(r);
1263: if (avoidExplicitReification
1264: && // ( r instanceof Statement ) &&
1265: (!r.isAnon()) && isLocalReference(r)
1266: && res2statement.containsKey(r)) {
1267: ss = new MapFilterIterator(new MapFilter() {
1268: public Object accept(Object o) {
1269: Statement s = (Statement) o;
1270: Property p = s.getPredicate();
1271: return ((!p.getNameSpace().equals(rdfns)) || !((RDF.type
1272: .equals(p) && s.getObject().equals(
1273: RDF.Statement))
1274: || RDF.object.equals(p)
1275: || RDF.predicate.equals(p) || RDF.subject
1276: .equals(p))) ? o : null;
1277: }
1278: }, ss);
1279: }
1280: try {
1281: return ss.hasNext();
1282: } finally {
1283: ss.close();
1284: }
1285: }
1286:
1287: private ExtendedIterator listProperties(Resource r) {
1288: return new MapFilterIterator(new MapFilter() {
1289: public Object accept(Object o) {
1290: return doneSet.contains(o) ? null : o;
1291: }
1292: }, r.listProperties());
1293: }
1294:
1295: // Good type statement, or simple string valued statement with no langID
1296: // See http://www.w3.org/TR/REC-xml#AVNormalize
1297: private boolean canBeAttribute(Statement s, Set seen) {
1298: Property p = s.getPredicate();
1299: // Check seen first.
1300: if (prettyWriter.sPropertyAttr || seen.contains(p)) // We can't use the
1301: // same attribute
1302: // twice in one rule.
1303: return false;
1304: seen.add(p);
1305:
1306: if (p.equals(RDF.type)) {
1307: // If we have a model in which a type is given
1308: // as a string, then we avoid the attribute rule 6.10 which is
1309: // ambiguous with 6.11.
1310: RDFNode n = s.getObject();
1311: // return (n instanceof Resource) && !((Resource) n).isAnon();
1312: return n.isURIResource();
1313: }
1314:
1315: if (s.getObject() instanceof Literal) {
1316: Literal l = s.getLiteral();
1317: if (l.getDatatypeURI() != null)
1318: return false;
1319:
1320: if (l.getLanguage().equals("")) {
1321: // j.cook.up bug fix
1322: if (prettyWriter.isDefaultNamespace(getNameSpace(p)))
1323: return false;
1324:
1325: String str = l.getString();
1326: if (str.length() < 40) {
1327: char buf[] = str.toCharArray();
1328: for (int i = 0; i < buf.length; i++) {
1329: // See http://www.w3.org/TR/REC-xml#AVNormalize
1330: if (buf[i] <= ' ' || buf[i] == 0xFFFF
1331: || buf[i] == 0xFFFE)
1332: return false;
1333: }
1334: return !wantReification(s);
1335: }
1336: }
1337: }
1338: return false;
1339: }
1340:
1341: private boolean allPropsAreAttr(Resource r) {
1342: ClosableIterator ss = listProperties(r);
1343: Set seen = new HashSet();
1344: try {
1345: while (ss.hasNext()) {
1346: Statement s = (Statement) ss.next();
1347: if (!canBeAttribute(s, seen))
1348: return false;
1349: }
1350: } finally {
1351: ss.close();
1352: }
1353: return true;
1354: }
1355:
1356: private void done(Statement s) {
1357: doneSet.add(s);
1358: // return false;
1359: }
1360:
1361: /**
1362: * If r represent a daml:collection return a 2D array of its statements. For
1363: * each member there are three statements the first gives the DAML.first
1364: * statement, the second the DAML.rest statement and the third the RDF.type
1365: * statement.
1366: *
1367: * @return null on failure or the elements of the collection.
1368: *
1369: */
1370: private Statement[][] getDamlList(RDFNode r) {
1371: return prettyWriter.sDamlCollection ? null : getList(r,
1372: DAML_OIL.List, DAML_OIL.first, DAML_OIL.rest,
1373: DAML_OIL.nil);
1374: }
1375:
1376: private Statement[][] getRDFList(RDFNode r) {
1377: return prettyWriter.sParseTypeCollectionPropertyElt ? null
1378: : getList(r, null, RDF.first, RDF.rest, RDF.nil);
1379: }
1380:
1381: private Statement[][] getList(RDFNode r, Resource list,
1382: Property first, Property rest, Resource nil) {
1383:
1384: Vector rslt = new Vector();
1385: Set seen = new HashSet();
1386: RDFNode next = r;
1387: // We walk down the list and check each member.
1388: try {
1389:
1390: while (!next.equals(nil)) {
1391: Statement elt[] = new Statement[list == null ? 2 : 3];
1392: if (next instanceof Literal)
1393: return null;
1394: Resource res = (Resource) next;
1395: // We cannot label the nodes in the daml:collection
1396: // construction.
1397: if (!isGenuineAnon(res))
1398: return null;
1399: // The occurs check - cyclic loop rather than a list.
1400: if (seen.contains(next))
1401: return null;
1402: seen.add(next);
1403:
1404: // We must have exactly three properties.
1405: StmtIterator ss = res.listProperties();
1406: try {
1407: while (ss.hasNext()) {
1408: Statement s = ss.nextStatement();
1409: Property p = s.getPredicate();
1410: int ix;
1411: RDFNode obj = s.getObject();
1412: if (doneSet.contains(s))
1413: return null;
1414: if (!(obj instanceof Resource)) {
1415: return null;
1416: }
1417: if (p.equals(RDF.type)) {
1418: ix = 2;
1419: if (!obj.equals(list))
1420: return null;
1421: } else if (p.equals(first)) {
1422: ix = 0;
1423: } else if (p.equals(rest)) {
1424: ix = 1;
1425: next = obj;
1426: } else {
1427: return null;
1428: }
1429: if (elt[ix] != null)
1430: return null;
1431: elt[ix] = s;
1432: }
1433: } finally {
1434: ss.close();
1435: }
1436: for (int i = 0; i < elt.length; i++)
1437: if (elt[i] == null)
1438: // didn't have the three required elements.
1439: return null;
1440: rslt.add(elt);
1441: }
1442: if (rslt.size() == 0)
1443: return null;
1444: } finally {
1445:
1446: }
1447: Statement array[][] = new Statement[rslt.size()][];
1448: rslt.copyInto(array);
1449: return array;
1450:
1451: }
1452:
1453: /**
1454: * @return A statement that is suitable for a typed node construction or
1455: * null.
1456: */
1457: private Statement getType(Resource r) {
1458: Statement rslt;
1459: try {
1460: if (r instanceof Statement) {
1461: rslt = ((Statement) r).getStatementProperty(RDF.type);
1462: if (rslt == null
1463: || (!rslt.getObject().equals(RDF.Statement)))
1464: error("Statement type problem");
1465: } else {
1466: rslt = r.getRequiredProperty(RDF.type);
1467: }
1468: } catch (PropertyNotFoundException rdfe) {
1469: if (r instanceof Statement)
1470: error("Statement type problem");
1471: rslt = null;
1472: }
1473: if (rslt == null || isOKType(rslt.getObject()) == -1)
1474: return null;
1475:
1476: return rslt;
1477: }
1478:
1479: /**
1480: * @param n
1481: * The value of some rdf:type (precondition).
1482: * @return The split point or -1.
1483: */
1484:
1485: private int isOKType(RDFNode n) {
1486:
1487: if (!(n instanceof Resource))
1488: return -1;
1489: if (((Resource) n).isAnon())
1490: return -1;
1491: // Only allow resources with namespace and fragment ID
1492: String uri = ((Resource) n).getURI();
1493:
1494: int split = Util.splitNamespace(uri);
1495: if (split == 0 || split == uri.length())
1496: return -1;
1497:
1498: return split;
1499: }
1500:
1501: /**
1502: * The order of outputting the resources. This all supports wObjStar.
1503: */
1504: private Set infinite;
1505:
1506: private void findInfiniteCycles() {
1507: // find all statements that haven't been done.
1508: StmtIterator ss = model.listStatements();
1509: Relation relation = new Relation();
1510: try {
1511: while (ss.hasNext()) {
1512: Statement s = ss.nextStatement();
1513: if (!doneSet.contains(s)) {
1514: RDFNode rn = s.getObject();
1515: if (rn instanceof Resource) {
1516: relation.set(s.getSubject(), rn);
1517: }
1518: }
1519: }
1520: } finally {
1521: ss.close();
1522: }
1523: relation.transitiveClosure();
1524: infinite = relation.getDiagonal();
1525: }
1526:
1527: /**
1528: * This class is an iterator over the set infinite, but we wait until it is
1529: * used before instantiating the underlying iterator.
1530: */
1531: private Iterator allInfiniteLeft() {
1532: return new LateBindingIterator() {
1533: public Iterator create() {
1534: return infinite.iterator();
1535: }
1536: };
1537: }
1538:
1539: private Iterator pleasingTypeIterator() {
1540: if (pleasingTypes == null)
1541: return new NullIterator();
1542: Map buckets = new HashMap();
1543: Set bucketArray[] = new Set[pleasingTypes.length];
1544: // Set up buckets and bucketArray. Each is a collection
1545: // of the same buckets, one ordered, the other hashed.
1546: for (int i = 0; i < pleasingTypes.length; i++) {
1547: bucketArray[i] = new HashSet();
1548: buckets.put(pleasingTypes[i], bucketArray[i]);
1549: }
1550:
1551: ResIterator rs = model.listSubjects();
1552: try {
1553: while (rs.hasNext()) {
1554: Resource r = rs.nextResource();
1555: Statement s = getType(r);
1556: if (s != null) {
1557: Set bucket = (Set) buckets.get(s.getObject());
1558: if (bucket != null) {
1559: if (isGenuineAnon(r)) {
1560: Integer v = (Integer) objectTable.get(r);
1561: if (v != null && v.intValue() == 1)
1562: continue;
1563: }
1564: bucket.add(r);
1565: }
1566: }
1567: }
1568: } finally {
1569: rs.close();
1570: }
1571:
1572: // Now all the pleasing resources are in the buckets.
1573: // Add all their iterators togethor:
1574:
1575: return new IteratorIterator(new Map1Iterator(new Map1() {
1576: public Object map1(Object bkt) {
1577: return ((Set) bkt).iterator();
1578: }
1579: }, new ArrayIterator(bucketArray)));
1580:
1581: }
1582:
1583: /**
1584: * listSubjects - generates a list of subjects for the wObjStar rule. We
1585: * wish to order these elegantly. The current implementation goes for:
1586: * <ul>
1587: * <li> The current file - mainly intended for good DAML.
1588: * <li> Subjects that are not objects of anything, excluding reifications
1589: * <li> At these stage we evaluate a dependency graph of the remaining
1590: * resources.
1591: * <li>non-anonymous resources that are the object of more than one rule
1592: * that are in infinite cycles.
1593: * <li> any non genuinely anonymous resources that are in infinite cycles
1594: * <li>any other resource in an infinite cyle
1595: * <li>any other resource.
1596: * <li>reifications
1597: * </ul>
1598: *
1599: *
1600: * At the end, we need to close any underlying ResIterators from the model,
1601: * however to avoid complications in much of this code we use general
1602: * java.util.Iterator-s. We hence use a wrapper around a ResIterator to
1603: * allow us to manage the closing issue.
1604: */
1605: private Iterator listSubjects() {
1606: // The current file - mainly intended for good DAML.
1607: Iterator currentFile =
1608: // new ArrayIterator(
1609: // new Resource[] { model.createResource(this.localName)});
1610: new SingletonIterator(model.createResource(this .localName));
1611: // The pleasing types
1612: Iterator pleasing = pleasingTypeIterator();
1613:
1614: Iterator fakeStopPleasing = new NullIterator() {
1615: public boolean hasNext() {
1616: pleasingTypeSet = new HashSet();
1617: return false;
1618: }
1619: };
1620:
1621: // Subjects that are not objects of anything.
1622: Iterator nonObjects = new FilterIterator(new Filter() {
1623: public boolean accept(Object o) {
1624: return (!objectTable.containsKey(o))
1625: && (!wantReification((Resource) o));
1626: }
1627: }, modelListSubjects());
1628: // At these stage we evaluate a dependency graph of the remaining
1629: // resources.
1630: // This is stuck in the master iterator so that it's hasNext is called
1631: // at an appropriate time (after the earlier stages, before the later
1632: // stages).
1633: // We use this to trigger the dependency graph evalaution.
1634: Iterator fakeLazyEvaluator = new NullIterator() {
1635: public boolean hasNext() {
1636: // Evalaute dependency graph.
1637: findInfiniteCycles();
1638: return false;
1639: }
1640: };
1641: // non-anonymous resources that are the object of more than one
1642: // triple that are in infinite cycles.
1643: Iterator firstChoiceCyclic = new FilterIterator(new Filter() {
1644: public boolean accept(Object o) {
1645: Resource r = (Resource) o;
1646: codeCoverage[4]++;
1647: if (r.isAnon())
1648: return false;
1649: Integer cnt = (Integer) objectTable.get(r);
1650: if (cnt == null || cnt.intValue() <= 1)
1651: return false;
1652: return true;
1653: }
1654: }, this .allInfiniteLeft());
1655: // any non genuinely anonymous resources that are in infinite cycles
1656: Iterator nonAnonInfinite = new FilterIterator(new Filter() {
1657: public boolean accept(Object o) {
1658: codeCoverage[5]++;
1659: Resource r = (Resource) o;
1660: return !isGenuineAnon(r);
1661: }
1662: }, allInfiniteLeft());
1663: // any other resource in an infinite cyle
1664: Iterator inf = allInfiniteLeft();
1665: Iterator anotherFake = new NullIterator() {
1666: public boolean hasNext() {
1667: avoidExplicitReification = false;
1668: return false;
1669: }
1670: };
1671: Iterator reifications = new FilterIterator(new Filter() {
1672: public boolean accept(Object o) {
1673: codeCoverage[6]++;
1674: return res2statement.containsKey(o);
1675: }
1676: }, allInfiniteLeft());
1677: // any other resource.
1678: Iterator backStop = modelListSubjects();
1679:
1680: Iterator all[] = new Iterator[] { currentFile, pleasing,
1681: fakeStopPleasing, nonObjects, fakeLazyEvaluator,
1682: firstChoiceCyclic, nonAnonInfinite, inf, anotherFake,
1683: reifications, new NullIterator() {
1684: public boolean hasNext() {
1685: if (modelListSubjects().hasNext())
1686: codeCoverage[7]++;
1687: return false;
1688: }
1689: }, backStop };
1690: Iterator allAsOne = new IteratorIterator(new ArrayIterator(all));
1691:
1692: // Filter for those that still have something to list.
1693: return new FilterIterator(new Filter() {
1694: public boolean accept(Object o) {
1695: return hasProperties((Resource) o);
1696: }
1697: }, allAsOne);
1698: }
1699:
1700: private Set openResIterators = new HashSet();
1701:
1702: private synchronized void closeAllResIterators() {
1703: Iterator members = openResIterators.iterator();
1704: while (members.hasNext()) {
1705: ((ResIterator) members.next()).close();
1706: }
1707: openResIterators = new HashSet();
1708: }
1709:
1710: private Iterator modelListSubjects() {
1711: ResIterator resIt = model.listSubjects();
1712: openResIterators.add(resIt);
1713: return resIt;
1714:
1715: }
1716:
1717: }
1718:
1719: /*
1720: * (c) Copyright 2000, 2001, 2002, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard
1721: * Development Company, LP All rights reserved.
1722: *
1723: * Redistribution and use in source and binary forms, with or without
1724: * modification, are permitted provided that the following conditions are met:
1725: *
1726: * 1. Redistributions of source code must retain the above copyright notice,
1727: * this list of conditions and the following disclaimer.
1728: *
1729: * 2. Redistributions in binary form must reproduce the above copyright notice,
1730: * this list of conditions and the following disclaimer in the documentation
1731: * and/or other materials provided with the distribution.
1732: *
1733: * 3. The name of the author may not be used to endorse or promote products
1734: * derived from this software without specific prior written permission.
1735: *
1736: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
1737: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1738: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
1739: * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1740: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1741: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
1742: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
1743: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
1744: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1745: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1746: */
|