001: /*
002: * (c) Copyright 2000, 2001, 2002, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: * All rights reserved.
004: * [See end of file]
005: * $Id: BaseXMLWriter.java,v 1.67 2008/01/02 12:09:33 andy_seaborne Exp $
006: */
007:
008: package com.hp.hpl.jena.xmloutput.impl;
009:
010: import java.io.*;
011: import java.util.*;
012: import java.util.regex.Pattern;
013:
014: import org.apache.commons.logging.*;
015: import org.apache.xerces.util.XMLChar;
016:
017: import com.hp.hpl.jena.JenaRuntime;
018: import com.hp.hpl.jena.iri.*;
019: import com.hp.hpl.jena.rdf.model.*;
020: import com.hp.hpl.jena.rdf.model.impl.RDFDefaultErrorHandler;
021: import com.hp.hpl.jena.rdf.model.impl.ResourceImpl;
022: import com.hp.hpl.jena.rdf.model.impl.Util;
023: import com.hp.hpl.jena.shared.*;
024: import com.hp.hpl.jena.util.CharEncoding;
025: import com.hp.hpl.jena.util.FileUtils;
026: import com.hp.hpl.jena.vocabulary.*;
027: import com.hp.hpl.jena.xmloutput.RDFXMLWriterI;
028:
029: /**
030: * This is not part of the public API.
031: * Base class for XML serializers.
032: * All methods with side-effects should be synchronized in this class and its
033: * subclasses. (i. e. XMLWriters assume that the world is not changing around
034: * them while they are writing).
035: *
036: * Functionality:
037: *
038: * <ul>
039: * <li>setProperty etc
040: * <li>namespace prefixes
041: * <li>xmlbase
042: * <li>relative URIs
043: * <li>encoding issues
044: * <li>anonymous node presentationall
045: * <li>errorHandler
046: * </ul>
047: *
048: * @author jjcnee
049: * @version Release='$Name: $' Revision='$Revision: 1.67 $' Date='$Date: 2008/01/02 12:09:33 $'
050: */
051: abstract public class BaseXMLWriter implements RDFXMLWriterI {
052:
053: private static final String newline = JenaRuntime
054: .getSystemProperty("line.separator");
055:
056: public BaseXMLWriter() {
057: setupMaps();
058: }
059:
060: private static Log xlogger = LogFactory.getLog(BaseXMLWriter.class);
061:
062: protected static SimpleLogger logger = new SimpleLogger() {
063: public void warn(String s) {
064: xlogger.warn(s);
065: }
066:
067: public void warn(String s, Exception e) {
068: xlogger.warn(s, e);
069: }
070: };
071:
072: public static SimpleLogger setLogger(SimpleLogger lg) {
073: SimpleLogger old = logger;
074: logger = lg;
075: return old;
076: }
077:
078: abstract protected void unblockAll();
079:
080: abstract protected void blockRule(Resource r);
081:
082: abstract protected void writeBody(Model mdl, PrintWriter pw,
083: String baseUri, boolean inclXMLBase);
084:
085: static private Set badRDF = new HashSet();
086:
087: /**
088: Counter used for allocating Jena transient namespace declarations.
089: */
090: private int jenaPrefixCount;
091:
092: static String RDFNS = RDF.getURI();
093:
094: static private Pattern jenaNamespace;
095:
096: static {
097: jenaNamespace = Pattern.compile("j\\.([1-9][0-9]*|cook\\.up)");
098:
099: badRDF.add("RDF");
100: badRDF.add("Description");
101: badRDF.add("li");
102: badRDF.add("about");
103: badRDF.add("aboutEach");
104: badRDF.add("aboutEachPrefix");
105: badRDF.add("ID");
106: badRDF.add("nodeID");
107: badRDF.add("parseType");
108: badRDF.add("datatype");
109: badRDF.add("bagID");
110: badRDF.add("resource");
111: }
112:
113: String xmlBase = null;
114:
115: private IRI baseURI;
116:
117: boolean longId = false;
118:
119: private boolean demandGoodURIs = true;
120:
121: int tabSize = 2;
122:
123: int width = 60;
124:
125: HashMap anonMap = new HashMap();
126:
127: int anonCount = 0;
128:
129: static private RDFDefaultErrorHandler defaultErrorHandler = new RDFDefaultErrorHandler();
130:
131: RDFErrorHandler errorHandler = defaultErrorHandler;
132:
133: Boolean showXmlDeclaration = null;
134:
135: protected Boolean showDoctypeDeclaration = Boolean.FALSE;
136:
137: /*
138: * There are two sorts of id's for anonymous resources. Short id's are the
139: * default, but require a mapping table. The mapping table means that
140: * serializing a large model could run out of memory. Long id's require no
141: * mapping table, but are less readable.
142: */
143:
144: String anonId(Resource r) {
145: return longId ? longAnonId(r) : shortAnonId(r);
146: }
147:
148: /*
149: * A shortAnonId is computed by maintaining a mapping table from the internal
150: * id's of anon resources. The short id is the index into the table of the
151: * internal id.
152: */
153: private String shortAnonId(Resource r) {
154: String result = (String) anonMap.get(r.getId());
155: if (result == null) {
156: result = "A" + Integer.toString(anonCount++);
157: anonMap.put(r.getId(), result);
158: }
159: return result;
160: }
161:
162: /*
163: * A longAnonId is the internal id of the anon resource expressed as a
164: * character string.
165: *
166: * This code makes no assumptions about the characters used in the
167: * implementation of an anon id. It checks if they are valid namechar
168: * characters and escapes the id if not.
169: */
170:
171: private String longAnonId(Resource r) {
172: String rid = r.getId().toString();
173: return XMLChar.isValidNCName(rid) ? rid : escapedId(rid);
174: }
175:
176: /**
177: true means all namespaces defined in the model prefixes will be noted in xmlns
178: declarations; false means only "required" ones will be noted. Hook for configuration.
179: */
180: private boolean writingAllModelPrefixNamespaces = true;
181:
182: private Relation nameSpaces = new Relation();
183:
184: private Map ns;
185:
186: private PrefixMapping modelPrefixMapping;
187:
188: private Set namespacesNeeded;
189:
190: void addNameSpace(String uri) {
191: namespacesNeeded.add(uri);
192: }
193:
194: boolean isDefaultNamespace(String uri) {
195: return "".equals(ns.get(uri));
196: }
197:
198: private void addNameSpaces(Model model) {
199: NsIterator nsIter = model.listNameSpaces();
200: while (nsIter.hasNext())
201: this .addNameSpace(nsIter.nextNs());
202: }
203:
204: private void primeNamespace(Model model) {
205: Map m = model.getNsPrefixMap();
206: Iterator it = m.entrySet().iterator();
207: while (it.hasNext()) {
208: Map.Entry e = (Map.Entry) it.next();
209: // String key = (String) e.getKey();
210: String value = (String) e.getValue();
211: String already = this .getPrefixFor(value);
212: if (already == null) {
213: this .setNsPrefix(model.getNsURIPrefix(value), value);
214: if (writingAllModelPrefixNamespaces)
215: this .addNameSpace(value);
216: }
217: }
218: }
219:
220: void setupMaps() {
221: nameSpaces.set11(RDF.getURI(), "rdf");
222: nameSpaces.set11(RDFS.getURI(), "rdfs");
223: nameSpaces.set11(DC.getURI(), "dc");
224: nameSpaces.set11(RSS.getURI(), "rss");
225: nameSpaces.set11("http://www.daml.org/2001/03/daml+oil.daml#",
226: "daml");
227: nameSpaces.set11(VCARD.getURI(), "vcard");
228: nameSpaces.set11("http://www.w3.org/2002/07/owl#", "owl");
229: }
230:
231: void workOutNamespaces() {
232: if (ns == null) {
233: ns = new HashMap();
234: Set prefixesUsed = new HashSet();
235: setFromWriterSystemProperties(ns, prefixesUsed);
236: setFromGivenNamespaces(ns, prefixesUsed);
237: }
238: }
239:
240: private void setFromWriterSystemProperties(Map ns, Set prefixesUsed) {
241: Iterator it = namespacesNeeded.iterator();
242: while (it.hasNext()) {
243: String uri = (String) it.next();
244: String val = JenaRuntime
245: .getSystemProperty(RDFWriter.NSPREFIXPROPBASE + uri);
246: if (val != null && checkLegalPrefix(val)
247: && !prefixesUsed.contains(val)) {
248: ns.put(uri, val);
249: prefixesUsed.add(val);
250: }
251: }
252: }
253:
254: private void setFromGivenNamespaces(Map ns, Set prefixesUsed) {
255: Iterator it = namespacesNeeded.iterator();
256: while (it.hasNext()) {
257: String uri = (String) it.next();
258: if (ns.containsKey(uri))
259: continue;
260: String val = null;
261: Set s = nameSpaces.forward(uri);
262: if (s != null) {
263: Iterator it2 = s.iterator();
264: if (it2.hasNext())
265: val = (String) it2.next();
266: if (prefixesUsed.contains(val))
267: val = null;
268: }
269: if (val == null) {
270: // just in case the prefix has already been used, look for a free one.
271: // (the usual source of such prefixes is reading in a model we wrote out earlier)
272: do {
273: val = "j." + (jenaPrefixCount++);
274: } while (prefixesUsed.contains(val));
275: }
276: ns.put(uri, val);
277: prefixesUsed.add(val);
278: }
279: }
280:
281: final synchronized public void setNsPrefix(String prefix, String ns) {
282: if (checkLegalPrefix(prefix)) {
283: nameSpaces.set11(ns, prefix);
284: }
285: }
286:
287: final public String getPrefixFor(String uri) {
288: Set s = nameSpaces.backward(uri);
289: if (s != null && s.size() == 1)
290: return (String) s.iterator().next();
291: return null;
292: }
293:
294: String xmlnsDecl() {
295: workOutNamespaces();
296: StringBuffer result = new StringBuffer();
297: Iterator it = ns.entrySet().iterator();
298: while (it.hasNext()) {
299: Map.Entry ent = (Map.Entry) it.next();
300: String prefix = (String) ent.getValue();
301: String uri = (String) ent.getKey();
302: result.append(newline).append(" xmlns");
303: if (prefix.length() > 0)
304: result.append(':').append(prefix);
305: result.append('=').append(
306: substitutedAttribute(checkURI(uri)));
307: }
308: return result.toString();
309: }
310:
311: static final private int FAST = 1;
312: static final private int START = 2;
313: static final private int END = 3;
314: static final private int ATTR = 4;
315: static final private int FASTATTR = 5;
316:
317: String rdfEl(String local) {
318: return tag(RDFNS, local, FAST, true);
319: }
320:
321: String startElementTag(String uri, String local) {
322: return tag(uri, local, START, false);
323: }
324:
325: protected String startElementTag(String uriref) {
326: return splitTag(uriref, START);
327: }
328:
329: String attributeTag(String uriref) {
330: return splitTag(uriref, ATTR);
331: }
332:
333: String attributeTag(String uri, String local) {
334: return tag(uri, local, ATTR, false);
335: }
336:
337: String rdfAt(String local) {
338: return tag(RDFNS, local, FASTATTR, true);
339: }
340:
341: String endElementTag(String uri, String local) {
342: return tag(uri, local, END, false);
343: }
344:
345: protected String endElementTag(String uriref) {
346: return splitTag(uriref, END);
347: }
348:
349: String splitTag(String uriref, int type) {
350: int split = Util.splitNamespace(uriref);
351: if (split == uriref.length())
352: throw new InvalidPropertyURIException(uriref);
353: return tag(uriref.substring(0, split), uriref.substring(split),
354: type, true);
355: }
356:
357: static public boolean dbg = false;
358:
359: String tag(String namespace, String local, int type,
360: boolean localIsQname) {
361: if (dbg)
362: System.err.println(namespace + " - " + local);
363: String prefix = (String) ns.get(namespace);
364: if (type != FAST && type != FASTATTR) {
365: if ((!localIsQname) && !XMLChar.isValidNCName(local))
366: return splitTag(namespace + local, type);
367: if (namespace.equals(RDFNS)) {
368: // Description, ID, nodeID, about, aboutEach, aboutEachPrefix, li
369: // bagID parseType resource datatype RDF
370: if (badRDF.contains(local)) {
371: logger.warn("The URI rdf:" + local
372: + " cannot be serialized in RDF/XML.");
373: throw new InvalidPropertyURIException("rdf:"
374: + local);
375: }
376: }
377: }
378: boolean cookUp = false;
379: if (prefix == null) {
380: checkURI(namespace);
381: logger.warn("Internal error: unexpected QName URI: <"
382: + namespace + ">. Fixing up with j.cook.up code.",
383: new BrokenException("unexpected QName URI "
384: + namespace));
385: cookUp = true;
386: } else if (prefix.length() == 0) {
387: if (type == ATTR || type == FASTATTR)
388: cookUp = true;
389: else
390: return local;
391: }
392: if (cookUp)
393: return cookUpAttribution(type, namespace, local);
394: return prefix + ":" + local;
395: }
396:
397: private String cookUpAttribution(int type, String namespace,
398: String local) {
399: String prefix = "j.cook.up";
400: switch (type) {
401: case FASTATTR:
402: case ATTR:
403: return "xmlns:" + prefix + "="
404: + substitutedAttribute(namespace) + " " + prefix
405: + ":" + local;
406: case START:
407: return prefix + ":" + local + " xmlns:" + prefix + "="
408: + substitutedAttribute(namespace);
409: default:
410: case END:
411: return prefix + ":" + local;
412: case FAST:
413: // logger.fatal("Unreachable code - reached.");
414: throw new BrokenException("cookup reached final FAST");
415: }
416: }
417:
418: /** Write out an XML serialization of a model.
419: * @param model the model to be serialized
420: * @param out the OutputStream to receive the serialization
421: * @param base The URL at which the file will be placed.
422: */
423: final public void write(Model model, OutputStream out, String base) {
424: write(model, FileUtils.asUTF8(out), base);
425: }
426:
427: /** Serialize Model <code>model</code> to Writer <code>out</out>.
428: * @param out The Writer to which the serialization should be sent.
429: * @param baseModel The model to be written.
430: * @param base the base URI for relative URI calculations. <code>
431: * null</code> means use only absolute URI's.
432: */
433: synchronized public void write(Model baseModel, Writer out,
434: String base) {
435: Model model = ModelFactory.withHiddenStatements(baseModel);
436: setupNamespaces(baseModel, model);
437: PrintWriter pw = out instanceof PrintWriter ? (PrintWriter) out
438: : new PrintWriter(out);
439: if (!Boolean.FALSE.equals(showXmlDeclaration))
440: writeXMLDeclaration(out, pw);
441: writeXMLBody(model, pw, base);
442: pw.flush();
443: }
444:
445: /**
446: @param baseModel
447: @param model
448: */
449: private void setupNamespaces(Model baseModel, Model model) {
450: this .namespacesNeeded = new HashSet();
451: this .ns = null;
452: this .modelPrefixMapping = baseModel;
453: primeNamespace(baseModel);
454: addNameSpace(RDF.getURI());
455: addNameSpaces(model);
456: jenaPrefixCount = 0;
457: }
458:
459: static IRIFactory factory = IRIFactory.jenaImplementation();
460:
461: private void writeXMLBody(Model model, PrintWriter pw, String base) {
462: if (showDoctypeDeclaration.booleanValue())
463: generateDoctypeDeclaration(model, pw);
464: // try {
465: // TODO errors?
466: if (xmlBase == null) {
467: baseURI = (base == null || base.length() == 0) ? null
468: : factory.create(base);
469: writeBody(model, pw, base, false);
470: } else {
471: baseURI = xmlBase.length() == 0 ? null : factory
472: .create(xmlBase);
473: writeBody(model, pw, xmlBase, true);
474: }
475: // } catch (MalformedURIException e) {
476: // throw new BadURIException( e.getMessage(), e);
477: // }
478: }
479:
480: protected static final Pattern predefinedEntityNames = Pattern
481: .compile("amp|lt|gt|apos|quot");
482:
483: public boolean isPredefinedEntityName(String name) {
484: return predefinedEntityNames.matcher(name).matches();
485: }
486:
487: private String attributeQuoteChar = "\"";
488:
489: protected String attributeQuoted(String s) {
490: return attributeQuoteChar + s + attributeQuoteChar;
491: }
492:
493: protected String substitutedAttribute(String s) {
494: String substituted = Util.substituteStandardEntities(s);
495: if (!showDoctypeDeclaration.booleanValue())
496: return attributeQuoted(substituted);
497: else {
498: int split = Util.splitNamespace(substituted);
499: String namespace = substituted.substring(0, split);
500: String prefix = modelPrefixMapping
501: .getNsURIPrefix(namespace);
502: return prefix == null || isPredefinedEntityName(prefix) ? attributeQuoted(substituted)
503: : attributeQuoted("&" + prefix + ";"
504: + substituted.substring(split));
505: }
506: }
507:
508: private void generateDoctypeDeclaration(Model model, PrintWriter pw) {
509: String rdfns = RDF.getURI();
510: String rdfRDF = model.qnameFor(rdfns + "RDF");
511: if (rdfRDF == null) {
512: model.setNsPrefix("rdf", rdfns);
513: rdfRDF = "rdf:RDF";
514: }
515: Map prefixes = model.getNsPrefixMap();
516: pw.print("<!DOCTYPE " + rdfRDF + " [");
517: for (Iterator it = prefixes.keySet().iterator(); it.hasNext();) {
518: String prefix = (String) it.next();
519: if (!isPredefinedEntityName(prefix))
520: pw
521: .print(newline
522: + " <!ENTITY "
523: + prefix
524: + " '"
525: + Util
526: .substituteEntitiesInEntityValue((String) prefixes
527: .get(prefix)) + "'>");
528: }
529: pw.print("]>" + newline);
530: }
531:
532: private void writeXMLDeclaration(Writer out, PrintWriter pw) {
533: String decl = null;
534: if (out instanceof OutputStreamWriter) {
535: String javaEnc = ((OutputStreamWriter) out).getEncoding();
536: // System.err.println(javaEnc);
537: if (!(javaEnc.equals("UTF8") || javaEnc.equals("UTF-16"))) {
538: CharEncoding encodingInfo = CharEncoding
539: .create(javaEnc);
540:
541: String ianaEnc = encodingInfo.name();
542: decl = "<?xml version=" + attributeQuoted("1.0")
543: + " encoding=" + attributeQuoted(ianaEnc)
544: + "?>";
545: if (!encodingInfo.isIANA())
546: logger
547: .warn(encodingInfo.warningMessage()
548: + "\n"
549: + " It is better to use a FileOutputStream, in place of a FileWriter.");
550:
551: }
552: }
553: if (decl == null && showXmlDeclaration != null)
554: decl = "<?xml version=" + attributeQuoted("1.0") + "?>";
555: if (decl != null) {
556: pw.println(decl);
557: }
558: }
559:
560: /** Set an error handler.
561: * @param errHandler The new error handler to be used, or null for the default handler.
562: * @return the old error handler
563: */
564: synchronized public RDFErrorHandler setErrorHandler(
565: RDFErrorHandler errHandler) {
566: // null means no user defined error handler.
567: // We implement this using defaultErrorHandler,
568: // but hide this fact from the user.
569: RDFErrorHandler rslt = errorHandler;
570: if (rslt == defaultErrorHandler)
571: rslt = null;
572: errorHandler = errHandler == null ? defaultErrorHandler
573: : errHandler;
574: return rslt;
575: }
576:
577: static private final char ESCAPE = 'X';
578:
579: static private String escapedId(String id) {
580: StringBuffer result = new StringBuffer();
581: for (int i = 0; i < id.length(); i++) {
582: char ch = id.charAt(i);
583: if (ch != ESCAPE
584: && (i == 0 ? XMLChar.isNCNameStart(ch) : XMLChar
585: .isNCName(ch))) {
586: result.append(ch);
587: } else {
588: escape(result, ch);
589: }
590: }
591: return result.toString();
592: }
593:
594: static final char[] hexchar = "0123456789abcdef".toCharArray();
595:
596: static private void escape(StringBuffer sb, char ch) {
597: sb.append(ESCAPE);
598: int charcode = ch;
599: do {
600: sb.append(hexchar[charcode & 15]);
601: charcode = charcode >> 4;
602: } while (charcode != 0);
603: sb.append(ESCAPE);
604: }
605:
606: /**
607: Set the writer property propName to the value obtained from propValue. Return an
608: Object representation of the original value.
609:
610: @see com.hp.hpl.jena.rdf.model.RDFWriter#setProperty(java.lang.String, java.lang.Object)
611: */
612: final synchronized public Object setProperty(String propName,
613: Object propValue) {
614: if (propName.equalsIgnoreCase("showXmlDeclaration")) {
615: return setShowXmlDeclaration(propValue);
616: } else if (propName.equalsIgnoreCase("showDoctypeDeclaration")) {
617: return setShowDoctypeDeclaration(propValue);
618: } else if (propName.equalsIgnoreCase("minimalPrefixes")) {
619: try {
620: return new Boolean(!writingAllModelPrefixNamespaces);
621: } finally {
622: writingAllModelPrefixNamespaces = !getBoolean(propValue);
623: }
624: } else if (propName.equalsIgnoreCase("xmlbase")) {
625: String result = xmlBase;
626: xmlBase = (String) propValue;
627: return result;
628: } else if (propName.equalsIgnoreCase("tab")) {
629: return setTab(propValue);
630: } else if (propName.equalsIgnoreCase("width")) {
631: return setWidth(propValue);
632: } else if (propName.equalsIgnoreCase("longid")) {
633: Boolean result = new Boolean(longId);
634: longId = getBoolean(propValue);
635: return result;
636: } else if (propName.equalsIgnoreCase("attributeQuoteChar")) {
637: return setAttributeQuoteChar(propValue);
638: } else if (propName.equalsIgnoreCase("allowBadURIs")) {
639: Boolean result = new Boolean(!demandGoodURIs);
640: demandGoodURIs = !getBoolean(propValue);
641: return result;
642: } else if (propName.equalsIgnoreCase("prettyTypes")) {
643: return setTypes((Resource[]) propValue);
644: } else if (propName.equalsIgnoreCase("relativeURIs")) {
645: int old = relativeFlags;
646: relativeFlags = str2flags((String) propValue);
647: return flags2str(old);
648: } else if (propName.equalsIgnoreCase("blockRules")) {
649: return setBlockRules(propValue);
650: } else {
651: logger.warn("Unsupported property: " + propName);
652: return null;
653: }
654: }
655:
656: private String setAttributeQuoteChar(Object propValue) {
657: String oldValue = attributeQuoteChar;
658: if ("\"".equals(propValue) || "'".equals(propValue))
659: attributeQuoteChar = (String) propValue;
660: else
661: logger
662: .warn("attributeQutpeChar must be either \"\\\"\" or \', not \""
663: + propValue + "\"");
664: return oldValue;
665: }
666:
667: private Integer setWidth(Object propValue) {
668: Integer oldValue = new Integer(width);
669: if (propValue instanceof Integer) {
670: width = ((Integer) propValue).intValue();
671: } else {
672: try {
673: width = Integer.parseInt((String) propValue);
674: } catch (Exception e) {
675: logger.warn("Bad value for width: '" + propValue
676: + "' [" + e.getMessage() + "]");
677: }
678: }
679: return oldValue;
680: }
681:
682: private Integer setTab(Object propValue) {
683: Integer result = new Integer(tabSize);
684: if (propValue instanceof Integer) {
685: tabSize = ((Integer) propValue).intValue();
686: } else {
687: try {
688: tabSize = Integer.parseInt((String) propValue);
689: } catch (Exception e) {
690: logger.warn("Bad value for tab: '" + propValue + "' ["
691: + e.getMessage() + "]");
692: }
693: }
694: return result;
695: }
696:
697: private String setShowDoctypeDeclaration(Object propValue) {
698: String oldValue = showDoctypeDeclaration.toString();
699: showDoctypeDeclaration = getBooleanValue(propValue,
700: Boolean.FALSE);
701: return oldValue;
702: }
703:
704: private String setShowXmlDeclaration(Object propValue) {
705: String oldValue = showXmlDeclaration == null ? null
706: : showXmlDeclaration.toString();
707: showXmlDeclaration = getBooleanValue(propValue, null);
708: return oldValue;
709: }
710:
711: /**
712: Answer the boolean value corresponding to o, which must either be a Boolean,
713: or a String parsable as a Boolean.
714: */
715: static private boolean getBoolean(Object o) {
716: return getBooleanValue(o, Boolean.FALSE).booleanValue();
717: }
718:
719: private static Boolean getBooleanValue(Object propValue,
720: Boolean theDefault) {
721: if (propValue == null)
722: return theDefault;
723: else if (propValue instanceof Boolean)
724: return (Boolean) propValue;
725: else if (propValue instanceof String)
726: return stringToBoolean((String) propValue, theDefault);
727: else
728: throw new JenaException("cannot treat as boolean: "
729: + propValue);
730: }
731:
732: private static Boolean stringToBoolean(String b, Boolean theDefault) {
733: if (b.equals("default"))
734: return theDefault;
735: if (b.equalsIgnoreCase("true"))
736: return Boolean.TRUE;
737: if (b.equalsIgnoreCase("false"))
738: return Boolean.FALSE;
739: throw new BadBooleanException(b);
740: }
741:
742: Resource[] setTypes(Resource x[]) {
743: logger
744: .warn("prettyTypes is not a property on the Basic RDF/XML writer.");
745: return null;
746: }
747:
748: private Resource blockedRules[] = new Resource[] { RDFSyntax.propertyAttr };
749:
750: Resource[] setBlockRules(Object o) {
751: Resource rslt[] = blockedRules;
752: unblockAll();
753: if (o instanceof Resource[]) {
754: blockedRules = (Resource[]) o;
755: } else {
756: StringTokenizer tkn = new StringTokenizer((String) o, ", ");
757: Vector v = new Vector();
758: while (tkn.hasMoreElements()) {
759: String frag = tkn.nextToken();
760: // System.err.println("Blocking " + frag);
761: if (frag.equals("daml:collection"))
762: v.add(DAML_OIL.collection);
763: else
764: v.add(new ResourceImpl(RDFSyntax.getURI() + frag));
765: }
766:
767: blockedRules = new Resource[v.size()];
768: v.copyInto(blockedRules);
769: }
770: for (int i = 0; i < blockedRules.length; i++)
771: blockRule(blockedRules[i]);
772: return rslt;
773: }
774:
775: /*
776: private boolean sameDocument = true;
777: private boolean network = false;
778: private boolean absolute = true;
779: private boolean relative = true;
780: private boolean parent = true;
781: private boolean grandparent = false;
782: */
783: private int relativeFlags = IRI.SAMEDOCUMENT | IRI.ABSOLUTE
784: | IRI.CHILD | IRI.PARENT;
785:
786: /**
787: Answer the form of the URI after relativisation according to the relativeFlags set
788: by properties. If the flags are 0 or the base URI is null, answer the original URI.
789: Throw an exception if the URI is "bad" and we demandGoodURIs.
790: */
791: protected String relativize(String uri) {
792: return relativeFlags != 0 && baseURI != null ? relativize(
793: baseURI, uri) : checkURI(uri);
794: }
795:
796: /**
797: Answer the relative form of the URI against the base, according to the relativeFlags.
798: */
799: private String relativize(IRI base, String uri) {
800: // TODO errors?
801: return base.relativize(uri, relativeFlags).toString();
802: }
803:
804: /**
805: Answer the argument URI, but if we demandGoodURIs and it isn't good, throw
806: a JenaException that encapsulates a MalformedURIException. There doesn't
807: appear to be a convenient URI.checkGood() kind of method, alas.
808: */
809: private String checkURI(String uri) {
810: if (demandGoodURIs) {
811: IRI iri = factory.create(uri);
812:
813: if (iri.hasViolation(false))
814: throw new BadURIException(
815: "Only well-formed absolute URIrefs can be included in RDF/XML output: "
816: + ((Violation) iri.violations(false)
817: .next()).getShortMessage());
818: }
819:
820: return uri;
821: }
822:
823: /**
824: Answer true iff prefix is a "legal" prefix to use, ie, is empty [for the default namespace]
825: or an NCName that does not start with "xml" and does not match the reserved-to-Jena
826: pattern.
827: */
828: private boolean checkLegalPrefix(String prefix) {
829: if (prefix.equals(""))
830: return true;
831: if (prefix.toLowerCase().startsWith("xml"))
832: logger.warn("Namespace prefix '" + prefix
833: + "' is reserved by XML.");
834: else if (!XMLChar.isValidNCName(prefix))
835: logger.warn("'" + prefix
836: + "' is not a legal namespace prefix.");
837: else if (jenaNamespace.matcher(prefix).matches())
838: logger.warn("Namespace prefix '" + prefix
839: + "' is reserved by Jena.");
840: else
841: return true;
842: return false;
843: }
844:
845: static private String flags2str(int f) {
846: StringBuffer oldValue = new StringBuffer(64);
847: if ((f & IRI.SAMEDOCUMENT) != 0)
848: oldValue.append("same-document, ");
849: if ((f & IRI.NETWORK) != 0)
850: oldValue.append("network, ");
851: if ((f & IRI.ABSOLUTE) != 0)
852: oldValue.append("absolute, ");
853: if ((f & IRI.CHILD) != 0)
854: oldValue.append("relative, ");
855: if ((f & IRI.PARENT) != 0)
856: oldValue.append("parent, ");
857: if ((f & IRI.GRANDPARENT) != 0)
858: oldValue.append("grandparent, ");
859: if (oldValue.length() > 0)
860: oldValue.setLength(oldValue.length() - 2);
861: return oldValue.toString();
862: }
863:
864: public static int str2flags(String pv) {
865: StringTokenizer tkn = new StringTokenizer(pv, ", ");
866: int rslt = 0;
867: while (tkn.hasMoreElements()) {
868: String flag = tkn.nextToken();
869: if (flag.equals("same-document"))
870: rslt |= IRI.SAMEDOCUMENT;
871: else if (flag.equals("network"))
872: rslt |= IRI.NETWORK;
873: else if (flag.equals("absolute"))
874: rslt |= IRI.ABSOLUTE;
875: else if (flag.equals("relative"))
876: rslt |= IRI.CHILD;
877: else if (flag.equals("parent"))
878: rslt |= IRI.PARENT;
879: else if (flag.equals("grandparent"))
880: rslt |= IRI.GRANDPARENT;
881: else
882:
883: logger
884: .warn("Incorrect property value for relativeURIs: "
885: + flag);
886: }
887: return rslt;
888: }
889:
890: }
891:
892: /*
893: (c) Copyright 2000, 2001, 2002, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
894: All rights reserved.
895:
896: Redistribution and use in source and binary forms, with or without
897: modification, are permitted provided that the following conditions
898: are met:
899:
900: 1. Redistributions of source code must retain the above copyright
901: notice, this list of conditions and the following disclaimer.
902:
903: 2. Redistributions in binary form must reproduce the above copyright
904: notice, this list of conditions and the following disclaimer in the
905: documentation and/or other materials provided with the distribution.
906:
907: 3. The name of the author may not be used to endorse or promote products
908: derived from this software without specific prior written permission.
909:
910: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
911: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
912: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
913: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
914: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
915: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
916: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
917: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
918: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
919: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
920: */
|