package com.hp.hpl.jena.rdf.arp.states; import java.util.ArrayList; import org.xml.sax.Attributes; import org.xml.sax.SAXParseException; import com.hp.hpl.jena.rdf.arp.AResource; import com.hp.hpl.jena.rdf.arp.QuotedStatementHandler; import com.hp.hpl.jena.rdf.arp.StatementHandler; import com.hp.hpl.jena.rdf.arp.impl.ANode; import com.hp.hpl.jena.rdf.arp.impl.ARPResource; import com.hp.hpl.jena.rdf.arp.impl.AResourceInternal; import com.hp.hpl.jena.rdf.arp.impl.AbsXMLContext; import com.hp.hpl.jena.rdf.arp.impl.AttributeLexer; import com.hp.hpl.jena.rdf.arp.impl.ElementLexer; import com.hp.hpl.jena.rdf.arp.impl.TaintImpl; import com.hp.hpl.jena.rdf.arp.impl.URIReference; /** * Modified parser Frame to support RDF quoting. */ public class WantPropertyElement extends Frame implements WantsObjectFrameI, HasSubjectFrameI { int liCounter = 1; ANode predicate; ANode object; ANode reify; boolean objectIsBlank = false; public WantPropertyElement(HasSubjectFrameI s, AbsXMLContext x) { super(s, x); } // These three are used as bitfields static final private int TYPEDLITERAL = 1; static final private int EMPTYWITHOBJ = 2; static final private int PARSETYPE = 4; public FrameI startElement(String uri, String localName, String rawName, Attributes atts) throws SAXParseException { clearObject(); if (nonWhiteMsgGiven) taint.isTainted(); nonWhiteMsgGiven = false; if (uri==null || uri.equals("")) { warning(WARN_UNQUALIFIED_ELEMENT,"Unqualified property elements are not allowed. Treated as a relative URI."); } ElementLexer el = new ElementLexer(taint, this, uri, localName, rawName, E_LI, CoreAndOldTerms | E_DESCRIPTION, false); // if (el.badMatch) // warning(ERR_SYNTAX_ERROR,"bad use of " + rawName); predicate = el.goodMatch ? (AResourceInternal) rdf_n(liCounter++) : URIReference.fromQName(this, uri, localName); if (taint.isTainted()) predicate.taint(); taint = new TaintImpl(); AttributeLexer ap = new AttributeLexer(this, // xml: A_XMLLANG | A_XMLBASE | A_XML_OTHER // legal rdf: | A_DATATYPE | A_ID | A_NODEID | A_PARSETYPE | A_RESOURCE | A_TYPE, // bad rdf: A_BADATTRS); int cnt = ap.processSpecials(taint, atts); // These three states are intended as mutually // incompatible, but all three can occur // together. Any two of the three, or all // three is a syntax errror. // Having none of these is legal. final int nextStateCode = (ap.datatype == null ? 0 : TYPEDLITERAL) | (ap.parseType == null ? 0 : PARSETYPE) | (mustBeEmpty(ap, atts, cnt) ? EMPTYWITHOBJ : 0); if (this.badStateCode(nextStateCode)) { warning(errorNumber(nextStateCode), descriptionOfCases(ap, nextStateCode, propertyAttributeDescription(atts, ap, cnt))); } AbsXMLContext x = ap.xml(xml); reify = ap.id == null ? null : URIReference.fromID(this, x, ap.id); if (taint.isTainted()) predicate.taint(); if (mustBeEmpty(ap, atts, cnt)) { if (ap.nodeID != null) { object = new ARPResource(arp, ap.nodeID); checkXMLName(object, ap.nodeID); objectIsBlank = true; } if (ap.resource != null) { if (object != null) { if (!badStateCode(nextStateCode)) // otherwise warning already given warning(ERR_SYNTAX_ERROR, "On a property element, only one of the attributes rdf:nodeID or rdf:resource is permitted."); } else object = URIReference.resolve(this, x, ap.resource); } if (object == null) { object = new ARPResource(arp); objectIsBlank = true; } if (taint.isTainted()) object.taint(); processPropertyAttributes(ap, atts, x); } FrameI nextFrame = nextFrame(atts, ap, cnt, nextStateCode, x); if (object != null) { if (taint.isTainted()) object.taint(); theObject(object); } if (taint.isTainted()) predicate.taint(); if(quoter != null) quoter.startQuote((AResource)object); return nextFrame; } private boolean mustBeEmpty(AttributeLexer ap, Attributes atts, int cnt) { return cnt < atts.getLength() || ap.type != null || ap.nodeID != null || ap.resource != null; } private FrameI nextFrame(Attributes atts, AttributeLexer ap, int cnt, int nextStateCode, AbsXMLContext x) throws SAXParseException { switch (nextStateCode) { case 0: return new WantLiteralValueOrDescription(this, x); case PARSETYPE | TYPEDLITERAL: case PARSETYPE | TYPEDLITERAL | EMPTYWITHOBJ: case PARSETYPE | EMPTYWITHOBJ: case PARSETYPE: return withParsetype(ap.parseType, x); case TYPEDLITERAL | EMPTYWITHOBJ: case TYPEDLITERAL: return new WantTypedLiteral(this, ap.datatype, x); case EMPTYWITHOBJ: return new WantEmpty(this, x); } throw new IllegalStateException("impossible"); } private FrameI withParsetype(String pt, AbsXMLContext x) throws SAXParseException { if (pt.equals("Collection")) { return new RDFCollection(this, x); } if (pt.equals("daml:collection") && !arp.isError(WARN_IN_STRICT_MODE)) { warning(IGN_DAML_COLLECTION, "'daml:collection' is not really a legal value for rdf:parseType"); return new DAMLCollection(this, x); } if (pt.equals("Resource")) { if (object == null) { // in some error cases the object has already been set. object = new ARPResource(arp); objectIsBlank = true; } return new WantPropertyElement(this, x); } if( pt.equals("Statements")) { StatementHandler handler = arp.getHandlers().getStatementHandler(); if( handler instanceof QuotedStatementHandler) { quoter = (QuotedStatementHandler) handler; object = new ARPResource(arp); objectIsBlank = true; return new WantQuotedDescription(this, x); } } if (!pt.equals("Literal")) { warning(WARN_UNKNOWN_PARSETYPE, "Unknown rdf:parseType: '" + pt + "' (treated as 'Literal'."); } return new OuterXMLLiteral(this, x, pt); } @Override String suggestParsetypeLiteral() { return (getParent() instanceof WantTopLevelDescription) ? "" : super .suggestParsetypeLiteral(); } public void aPredAndObj(ANode p, ANode o) { triple(object, p, o); } public void makeSubjectReificationWith(ANode r) { triple(r, RDF_SUBJECT, object); } public void theObject(ANode o) { HasSubjectFrameI p = (HasSubjectFrameI) getParent(); p.aPredAndObj(predicate, o); if (reify != null) { triple(reify, RDF_TYPE, RDF_STATEMENT); triple(reify, RDF_OBJECT, o); triple(reify, RDF_PREDICATE, predicate); p.makeSubjectReificationWith(reify); } } @Override public void endElement() { clearObject(); } @Override public void abort() { clearObject(); } private void clearObject() { if(quoter != null) { quoter.endQuote(); quoter = null; } if (objectIsBlank) arp.endLocalScope(object); objectIsBlank = false; object = null; } static private URIReference _rdf_n[] = new URIReference[0]; private QuotedStatementHandler quoter; static private URIReference rdf_n(int i) { if (i >= _rdf_n.length) { int newLength = (i + 10) * 3 / 2; URIReference new_rdf_n[] = new URIReference[newLength]; System.arraycopy(_rdf_n, 0, new_rdf_n, 0, _rdf_n.length); for (int j = _rdf_n.length; j < newLength; j++) { new_rdf_n[j] = URIReference.createNoChecks(rdfns + "_" + j); } _rdf_n = new_rdf_n; } return _rdf_n[i]; } /*************************************************************************** * * ERROR HANDLING CODE * **************************************************************************/ // Error detection private boolean badStateCode(int nextStateCode) { switch (nextStateCode) { case PARSETYPE | TYPEDLITERAL: case PARSETYPE | TYPEDLITERAL | EMPTYWITHOBJ: case PARSETYPE | EMPTYWITHOBJ: case TYPEDLITERAL | EMPTYWITHOBJ: return true; case 0: case PARSETYPE: case TYPEDLITERAL: case EMPTYWITHOBJ: return false; } throw new IllegalStateException("impossible"); } // Error classification private int errorNumber(int nextStateCode) { // TODO: not for 2.3. refine this error code. return ERR_SYNTAX_ERROR; } /*************************************************************************** * * ERROR MESSAGES * **************************************************************************/ private String descriptionOfCases(AttributeLexer ap, int nextStateCode, String propAttrs) { return ((propAttrs == null && ap.type == null) || (ap.nodeID == null && ap.resource == null && ap.type == null) || (ap.nodeID == null && ap.resource == null && propAttrs == null)) ? pairwiseIncompatibleErrorMessage( nextStateCode, ap, propAttrs) : complicatedErrorMessage(nextStateCode, ap, propAttrs); } private String pairwiseIncompatibleErrorMessage(int nextStateCode, AttributeLexer ap, String propAttrs) { ArrayList cases = new ArrayList(); if ((nextStateCode & PARSETYPE) != 0) cases.add("rdf:parseType"); if ((nextStateCode & TYPEDLITERAL) != 0) cases.add("rdf:datatype"); if (ap.nodeID != null) cases.add("rdf:nodeID"); if (ap.resource != null) cases.add("rdf:resource"); if (ap.type != null) cases.add("rdf:type"); if (cases.size() == 1) { if (propAttrs == null) throw new IllegalStateException("Shouldn't happen."); return "The attribute " + cases.get(0) + " is not permitted with " + propAttrs + " on a property element."; } String rslt = "On a property element, only one of the "; if (propAttrs == null) rslt += "attributes "; for (int i = 0; i < cases.size(); i++) { rslt += cases.get(i); switch (cases.size() - i) { case 1: break; case 2: rslt += " or "; break; default: rslt += ", "; break; } } if (propAttrs != null) { rslt += " attributes or " + propAttrs; } rslt += " is permitted."; return rslt; } private String complicatedErrorMessage(int nextStateCode, AttributeLexer ap, String propAttrs) { String subjectIs; if (ap.nodeID == null && ap.resource == null && (ap.type == null || propAttrs == null)) throw new IllegalStateException("precondition failed."); switch (nextStateCode & (TYPEDLITERAL | PARSETYPE)) { case TYPEDLITERAL | PARSETYPE: subjectIs = "the mutually incompatible attributes rdf:datatype and rdf:parseType are"; break; case TYPEDLITERAL: subjectIs = "the attribute rdf:datatype is"; break; case PARSETYPE: subjectIs = "the attribute rdf:parseType is"; break; default: throw new IllegalStateException("precondition failed"); } String nodeIDResource = null; if (ap.nodeID != null && ap.resource != null) { nodeIDResource = "the mutually incompatible attributes rdf:nodeID and rdf:resource"; } else if (ap.nodeID != null) { nodeIDResource = "the attribute rdf:nodeID"; } else if (ap.resource != null) { nodeIDResource = "the attribute rdf:resource"; } int otherAttCount = nodeIDResource == null ? 0 : 1; String otherAtts; if (ap.type != null) otherAttCount++; if (propAttrs != null) otherAttCount++; if (otherAttCount < 2) throw new IllegalStateException("logic error"); otherAtts = otherAttCount == 2 ? "both " : "each of "; if (ap.type != null && propAttrs != null) { if (nodeIDResource == null) otherAtts += "the attribute rdf:type and the " + propAttrs; else otherAtts += "the attribute rdf:type, the " + propAttrs; } else if (ap.type != null) { otherAtts += "the attribute rdf:type"; } else { otherAtts = "the " + propAttrs; } if (nodeIDResource != null) otherAtts += " and "+nodeIDResource; return "On a property element, " + subjectIs + " incompatible with " + otherAtts +"."; } private String propertyAttributeDescription(Attributes atts, AttributeLexer ap, int cnt) { String propAttrs = ""; int propAttrCount = atts.getLength() - cnt; int found = 0; if (propAttrCount == 0) return null; switch (propAttrCount) { case 0: break; case 1: case 2: case 3: for (int i = 0; i < atts.getLength(); i++) if (!ap.done(i)) { propAttrs += atts.getQName(i); found++; switch (propAttrCount - found) { case 0: break; case 1: propAttrs += " and "; break; default: propAttrs += ", "; } } break; default: if (propAttrCount < 0) throw new IllegalStateException("Shouldn't happen."); for (int i = 0; i < atts.getLength(); i++) if (!ap.done(i)) { found++; switch (found) { case 1: propAttrs += atts.getQName(i) + ", "; break; case 2: propAttrs += atts.getQName(i) + ", ..."; break; default: // ignore } } } return "property attributes (" + propAttrs + ")"; } } /* * (c) Copyright 2005, 2006, 2007 Hewlett-Packard Development Company, LP All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. 2. Redistributions in * binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. 3. The name of the author may not * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */