001: /*
002: * (c) Copyright 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: * [See end of file]
004: */
005:
006: package com.hp.hpl.jena.rdf.arp.states;
007:
008: import java.util.ArrayList;
009:
010: import org.xml.sax.Attributes;
011: import org.xml.sax.SAXParseException;
012:
013: import com.hp.hpl.jena.rdf.arp.impl.ANode;
014: import com.hp.hpl.jena.rdf.arp.impl.ARPResource;
015: import com.hp.hpl.jena.rdf.arp.impl.AResourceInternal;
016: import com.hp.hpl.jena.rdf.arp.impl.AbsXMLContext;
017: import com.hp.hpl.jena.rdf.arp.impl.AttributeLexer;
018: import com.hp.hpl.jena.rdf.arp.impl.ElementLexer;
019: import com.hp.hpl.jena.rdf.arp.impl.TaintImpl;
020: import com.hp.hpl.jena.rdf.arp.impl.URIReference;
021:
022: public class WantPropertyElement extends Frame implements
023: WantsObjectFrameI, HasSubjectFrameI {
024: int liCounter = 1;
025:
026: ANode predicate;
027:
028: ANode object;
029:
030: ANode reify;
031:
032: boolean objectIsBlank = false;
033:
034: public WantPropertyElement(HasSubjectFrameI s, AbsXMLContext x) {
035: super (s, x);
036: }
037:
038: // These three are used as bitfields
039: static final private int TYPEDLITERAL = 1;
040:
041: static final private int EMPTYWITHOBJ = 2;
042:
043: static final private int PARSETYPE = 4;
044:
045: public FrameI startElement(String uri, String localName,
046: String rawName, Attributes atts) throws SAXParseException {
047: clearObject();
048: if (nonWhiteMsgGiven)
049: taint.isTainted();
050: nonWhiteMsgGiven = false;
051: if (uri == null || uri.equals("")) {
052: warning(WARN_UNQUALIFIED_ELEMENT,
053: "Unqualified property elements are not allowed. Treated as a relative URI.");
054: }
055: ElementLexer el = new ElementLexer(taint, this , uri, localName,
056: rawName, E_LI, CoreAndOldTerms | E_DESCRIPTION, false);
057: // if (el.badMatch)
058: // warning(ERR_SYNTAX_ERROR,"bad use of " + rawName);
059: predicate = el.goodMatch ? (AResourceInternal) rdf_n(liCounter++)
060: : URIReference.fromQName(this , uri, localName);
061: if (taint.isTainted())
062: predicate.taint();
063: taint = new TaintImpl();
064:
065: AttributeLexer ap = new AttributeLexer(this ,
066: // xml:
067: A_XMLLANG | A_XMLBASE | A_XML_OTHER
068: // legal rdf:
069: | A_DATATYPE | A_ID | A_NODEID | A_PARSETYPE
070: | A_RESOURCE | A_TYPE,
071: // bad rdf:
072: A_BADATTRS);
073: int cnt = ap.processSpecials(taint, atts);
074:
075: // These three states are intended as mutually
076: // incompatible, but all three can occur
077: // together. Any two of the three, or all
078: // three is a syntax errror.
079: // Having none of these is legal.
080: final int nextStateCode = (ap.datatype == null ? 0
081: : TYPEDLITERAL)
082: | (ap.parseType == null ? 0 : PARSETYPE)
083: | (mustBeEmpty(ap, atts, cnt) ? EMPTYWITHOBJ : 0);
084:
085: if (this .badStateCode(nextStateCode)) {
086: warning(errorNumber(nextStateCode), descriptionOfCases(ap,
087: nextStateCode, propertyAttributeDescription(atts,
088: ap, cnt)));
089: }
090:
091: AbsXMLContext x = ap.xml(xml);
092:
093: reify = ap.id == null ? null : URIReference.fromID(this , x,
094: ap.id);
095: if (taint.isTainted())
096: predicate.taint();
097:
098: if (mustBeEmpty(ap, atts, cnt)) {
099: if (ap.nodeID != null) {
100:
101: object = new ARPResource(arp, ap.nodeID);
102: checkXMLName(object, ap.nodeID);
103: objectIsBlank = true;
104: }
105: if (ap.resource != null) {
106: if (object != null) {
107: if (!badStateCode(nextStateCode))
108: // otherwise warning already given
109: warning(
110: ERR_SYNTAX_ERROR,
111: "On a property element, only one of the attributes rdf:nodeID or rdf:resource is permitted.");
112: } else
113: object = URIReference.resolve(this , x, ap.resource);
114: }
115: if (object == null) {
116: object = new ARPResource(arp);
117: objectIsBlank = true;
118: }
119: if (taint.isTainted())
120: object.taint();
121: processPropertyAttributes(ap, atts, x);
122: }
123:
124: FrameI nextFrame = nextFrame(atts, ap, cnt, nextStateCode, x);
125: if (object != null) {
126: if (taint.isTainted())
127: object.taint();
128: theObject(object);
129: }
130: if (taint.isTainted())
131: predicate.taint();
132: return nextFrame;
133:
134: }
135:
136: private boolean mustBeEmpty(AttributeLexer ap, Attributes atts,
137: int cnt) {
138: return cnt < atts.getLength() || ap.type != null
139: || ap.nodeID != null || ap.resource != null;
140: }
141:
142: private FrameI nextFrame(Attributes atts, AttributeLexer ap,
143: int cnt, int nextStateCode, AbsXMLContext x)
144: throws SAXParseException {
145: switch (nextStateCode) {
146: case 0:
147: return new WantLiteralValueOrDescription(this , x);
148: case PARSETYPE | TYPEDLITERAL:
149: case PARSETYPE | TYPEDLITERAL | EMPTYWITHOBJ:
150: case PARSETYPE | EMPTYWITHOBJ:
151: case PARSETYPE:
152: return withParsetype(ap.parseType, x);
153: case TYPEDLITERAL | EMPTYWITHOBJ:
154: case TYPEDLITERAL:
155: return new WantTypedLiteral(this , ap.datatype, x);
156: case EMPTYWITHOBJ:
157: return new WantEmpty(this , x);
158: }
159: throw new IllegalStateException("impossible");
160: }
161:
162: private FrameI withParsetype(String pt, AbsXMLContext x)
163: throws SAXParseException {
164: if (pt.equals("Collection")) {
165: return new RDFCollection(this , x);
166: }
167: if (pt.equals("daml:collection")
168: && !arp.isError(WARN_IN_STRICT_MODE)) {
169: warning(IGN_DAML_COLLECTION,
170: "'daml:collection' is not really a legal value for rdf:parseType");
171: return new DAMLCollection(this , x);
172: }
173: if (pt.equals("Resource")) {
174: if (object == null) {
175: // in some error cases the object has already been set.
176: object = new ARPResource(arp);
177: objectIsBlank = true;
178: }
179: return new WantPropertyElement(this , x);
180: }
181: if (!pt.equals("Literal")) {
182: warning(WARN_UNKNOWN_PARSETYPE, "Unknown rdf:parseType: '"
183: + pt + "' (treated as 'Literal'.");
184: }
185: return new OuterXMLLiteral(this , x, pt);
186: }
187:
188: String suggestParsetypeLiteral() {
189: return (getParent() instanceof WantTopLevelDescription) ? ""
190: : super .suggestParsetypeLiteral();
191: }
192:
193: public void aPredAndObj(ANode p, ANode o) {
194: triple(object, p, o);
195: }
196:
197: public void makeSubjectReificationWith(ANode r) {
198: triple(r, RDF_SUBJECT, object);
199: }
200:
201: public void theObject(ANode o) {
202: HasSubjectFrameI p = (HasSubjectFrameI) getParent();
203: p.aPredAndObj(predicate, o);
204: if (reify != null) {
205: triple(reify, RDF_TYPE, RDF_STATEMENT);
206: triple(reify, RDF_OBJECT, o);
207: triple(reify, RDF_PREDICATE, predicate);
208: p.makeSubjectReificationWith(reify);
209: }
210: }
211:
212: public void endElement() {
213: clearObject();
214: }
215:
216: public void abort() {
217: clearObject();
218: }
219:
220: private void clearObject() {
221: if (objectIsBlank)
222: arp.endLocalScope(object);
223: objectIsBlank = false;
224: object = null;
225: }
226:
227: static private URIReference _rdf_n[] = new URIReference[0];
228:
229: static private URIReference rdf_n(int i) {
230: if (i >= _rdf_n.length) {
231: int newLength = (i + 10) * 3 / 2;
232: URIReference new_rdf_n[] = new URIReference[newLength];
233: System.arraycopy(_rdf_n, 0, new_rdf_n, 0, _rdf_n.length);
234: for (int j = _rdf_n.length; j < newLength; j++) {
235: new_rdf_n[j] = URIReference.createNoChecks(rdfns + "_"
236: + j);
237: }
238: _rdf_n = new_rdf_n;
239: }
240: return _rdf_n[i];
241: }
242:
243: /***************************************************************************
244: *
245: * ERROR HANDLING CODE
246: *
247: **************************************************************************/
248:
249: // Error detection
250: private boolean badStateCode(int nextStateCode) {
251: switch (nextStateCode) {
252: case PARSETYPE | TYPEDLITERAL:
253: case PARSETYPE | TYPEDLITERAL | EMPTYWITHOBJ:
254: case PARSETYPE | EMPTYWITHOBJ:
255: case TYPEDLITERAL | EMPTYWITHOBJ:
256: return true;
257: case 0:
258: case PARSETYPE:
259: case TYPEDLITERAL:
260: case EMPTYWITHOBJ:
261: return false;
262: }
263: throw new IllegalStateException("impossible");
264: }
265:
266: // Error classification
267:
268: private int errorNumber(int nextStateCode) {
269: // TODO: not for 2.3. refine this error code.
270: return ERR_SYNTAX_ERROR;
271: }
272:
273: /***************************************************************************
274: *
275: * ERROR MESSAGES
276: *
277: **************************************************************************/
278: private String descriptionOfCases(AttributeLexer ap,
279: int nextStateCode, String propAttrs) {
280: return ((propAttrs == null && ap.type == null)
281: || (ap.nodeID == null && ap.resource == null && ap.type == null) || (ap.nodeID == null
282: && ap.resource == null && propAttrs == null)) ? pairwiseIncompatibleErrorMessage(
283: nextStateCode, ap, propAttrs)
284: : complicatedErrorMessage(nextStateCode, ap, propAttrs);
285: }
286:
287: private String pairwiseIncompatibleErrorMessage(int nextStateCode,
288: AttributeLexer ap, String propAttrs) {
289: ArrayList cases = new ArrayList();
290: if ((nextStateCode & PARSETYPE) != 0)
291: cases.add("rdf:parseType");
292: if ((nextStateCode & TYPEDLITERAL) != 0)
293: cases.add("rdf:datatype");
294: if (ap.nodeID != null)
295: cases.add("rdf:nodeID");
296: if (ap.resource != null)
297: cases.add("rdf:resource");
298: if (ap.type != null)
299: cases.add("rdf:type");
300:
301: if (cases.size() == 1) {
302: if (propAttrs == null)
303: throw new IllegalStateException("Shouldn't happen.");
304: return "The attribute " + cases.get(0)
305: + " is not permitted with " + propAttrs
306: + " on a property element.";
307: }
308: String rslt = "On a property element, only one of the ";
309: if (propAttrs == null)
310: rslt += "attributes ";
311: for (int i = 0; i < cases.size(); i++) {
312: rslt += cases.get(i);
313: switch (cases.size() - i) {
314: case 1:
315: break;
316: case 2:
317: rslt += " or ";
318: break;
319: default:
320: rslt += ", ";
321: break;
322: }
323: }
324: if (propAttrs != null) {
325: rslt += " attributes or " + propAttrs;
326: }
327: rslt += " is permitted.";
328: return rslt;
329: }
330:
331: private String complicatedErrorMessage(int nextStateCode,
332: AttributeLexer ap, String propAttrs) {
333: String subjectIs;
334:
335: if (ap.nodeID == null && ap.resource == null
336: && (ap.type == null || propAttrs == null))
337: throw new IllegalStateException("precondition failed.");
338:
339: switch (nextStateCode & (TYPEDLITERAL | PARSETYPE)) {
340: case TYPEDLITERAL | PARSETYPE:
341: subjectIs = "the mutually incompatible attributes rdf:datatype and rdf:parseType are";
342: break;
343: case TYPEDLITERAL:
344: subjectIs = "the attribute rdf:datatype is";
345: break;
346: case PARSETYPE:
347: subjectIs = "the attribute rdf:parseType is";
348: break;
349: default:
350: throw new IllegalStateException("precondition failed");
351: }
352:
353: String nodeIDResource = null;
354: if (ap.nodeID != null && ap.resource != null) {
355: nodeIDResource = "the mutually incompatible attributes rdf:nodeID and rdf:resource";
356: } else if (ap.nodeID != null) {
357: nodeIDResource = "the attribute rdf:nodeID";
358: } else if (ap.resource != null) {
359: nodeIDResource = "the attribute rdf:resource";
360: }
361:
362: int otherAttCount = nodeIDResource == null ? 0 : 1;
363: String otherAtts;
364: if (ap.type != null)
365: otherAttCount++;
366: if (propAttrs != null)
367: otherAttCount++;
368: if (otherAttCount < 2)
369: throw new IllegalStateException("logic error");
370: otherAtts = otherAttCount == 2 ? "both " : "each of ";
371:
372: if (ap.type != null && propAttrs != null) {
373: if (nodeIDResource == null)
374: otherAtts += "the attribute rdf:type and the "
375: + propAttrs;
376: else
377: otherAtts += "the attribute rdf:type, the " + propAttrs;
378: } else if (ap.type != null) {
379: otherAtts += "the attribute rdf:type";
380: } else {
381: otherAtts = "the " + propAttrs;
382: }
383:
384: if (nodeIDResource != null)
385: otherAtts += " and " + nodeIDResource;
386:
387: return "On a property element, " + subjectIs
388: + " incompatible with " + otherAtts + ".";
389: }
390:
391: private String propertyAttributeDescription(Attributes atts,
392: AttributeLexer ap, int cnt) {
393: String propAttrs = "";
394: int propAttrCount = atts.getLength() - cnt;
395: int found = 0;
396: if (propAttrCount == 0)
397: return null;
398: switch (propAttrCount) {
399: case 0:
400: break;
401: case 1:
402: case 2:
403: case 3:
404: for (int i = 0; i < atts.getLength(); i++)
405: if (!ap.done(i)) {
406: propAttrs += atts.getQName(i);
407: found++;
408: switch (propAttrCount - found) {
409: case 0:
410: break;
411: case 1:
412: propAttrs += " and ";
413: break;
414: default:
415: propAttrs += ", ";
416: }
417: }
418: break;
419: default:
420: if (propAttrCount < 0)
421: throw new IllegalStateException("Shouldn't happen.");
422: for (int i = 0; i < atts.getLength(); i++)
423: if (!ap.done(i)) {
424: found++;
425: switch (found) {
426: case 1:
427: propAttrs += atts.getQName(i) + ", ";
428: break;
429: case 2:
430: propAttrs += atts.getQName(i) + ", ...";
431: break;
432: default:
433: // ignore
434: }
435: }
436: }
437: return "property attributes (" + propAttrs + ")";
438: }
439:
440: }
441:
442: /*
443: * (c) Copyright 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP All rights
444: * reserved.
445: *
446: * Redistribution and use in source and binary forms, with or without
447: * modification, are permitted provided that the following conditions are met:
448: * 1. Redistributions of source code must retain the above copyright notice,
449: * this list of conditions and the following disclaimer. 2. Redistributions in
450: * binary form must reproduce the above copyright notice, this list of
451: * conditions and the following disclaimer in the documentation and/or other
452: * materials provided with the distribution. 3. The name of the author may not
453: * be used to endorse or promote products derived from this software without
454: * specific prior written permission.
455: *
456: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
457: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
458: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
459: * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
460: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
461: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
462: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
463: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
464: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
465: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
466: */
|