blob: 14ccc64738958b16ff6430cf302b8300dfff4a62 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.rdfxml.xmloutput.impl;
import java.io.OutputStream ;
import java.io.OutputStreamWriter ;
import java.io.PrintWriter ;
import java.io.Writer ;
import java.util.* ;
import java.util.Map.Entry ;
import java.util.regex.Pattern ;
import org.apache.jena.JenaRuntime ;
import org.apache.jena.ext.xerces.util.XMLChar;
import org.apache.jena.iri.IRI ;
import org.apache.jena.iri.IRIFactory ;
import org.apache.jena.rdf.model.* ;
import org.apache.jena.rdf.model.impl.RDFDefaultErrorHandler ;
import org.apache.jena.rdf.model.impl.ResourceImpl ;
import org.apache.jena.rdf.model.impl.Util ;
import org.apache.jena.rdfxml.xmloutput.RDFXMLWriterI ;
import org.apache.jena.shared.* ;
import org.apache.jena.util.CharEncoding ;
import org.apache.jena.util.FileUtils ;
import org.apache.jena.vocabulary.* ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
/**
* This is not part of the public API.
* Base class for XML serializers.
* All methods with side-effects should be synchronized in this class and its
* subclasses. (i. e. XMLWriters assume that the world is not changing around
* them while they are writing).
*
* Functionality:
*
* <ul>
* <li>setProperty etc
* <li>namespace prefixes
* <li>xmlbase
* <li>relative URIs
* <li>encoding issues
* <li>anonymous node presentational
* <li>errorHandler
* </ul>
*/
abstract public class BaseXMLWriter implements RDFXMLWriterI {
private static final String newline =
JenaRuntime.getSystemProperty( "line.separator" );
static private final String DEFAULT_NS_ENTITY_NAME = "this";
static private final String DEFAULT_NS_ENTITY_NAME_ALT = "here";
private String defaultNSEntityName = "UNSET" ;
public BaseXMLWriter() {
setupMaps();
}
private static Logger xlogger = LoggerFactory.getLogger( BaseXMLWriter.class );
protected static SimpleLogger logger = new SimpleLogger() {
@Override
public void warn(String s) {
xlogger.warn(s);
}
@Override
public void warn(String s, Exception e) {
xlogger.warn(s,e);
}
};
public static SimpleLogger setLogger(SimpleLogger lg) {
SimpleLogger old = logger;
logger= lg;
return old;
}
abstract protected void unblockAll();
abstract protected void blockRule(Resource r);
abstract protected void writeBody
( Model mdl, PrintWriter pw, String baseUri, boolean inclXMLBase );
static private Set<String> badRDF = new HashSet<>();
/**
Counter used for allocating Jena transient namespace declarations.
*/
private int jenaPrefixCount;
static String RDFNS = RDF.getURI();
static private Pattern jenaNamespace;
static {
jenaNamespace =
Pattern.compile("j\\.([1-9][0-9]*|cook\\.up)");
badRDF.add("RDF");
badRDF.add("Description");
badRDF.add("li");
badRDF.add("about");
badRDF.add("aboutEach");
badRDF.add("aboutEachPrefix");
badRDF.add("ID");
badRDF.add("nodeID");
badRDF.add("parseType");
badRDF.add("datatype");
badRDF.add("bagID");
badRDF.add("resource");
}
String xmlBase = null;
private IRI baseURI;
boolean longId = false;
private boolean demandGoodURIs = true;
int tabSize = 2;
int width = 60;
HashMap<AnonId, String> anonMap = new HashMap<>();
int anonCount = 0;
static private RDFDefaultErrorHandler defaultErrorHandler =
new RDFDefaultErrorHandler();
RDFErrorHandler errorHandler = defaultErrorHandler;
Boolean showXmlDeclaration = null;
protected Boolean showDoctypeDeclaration = Boolean.FALSE;
/*
* There are two sorts of id's for anonymous resources. Short id's are the
* default, but require a mapping table. The mapping table means that
* serializing a large model could run out of memory. Long id's require no
* mapping table, but are less readable.
*/
String anonId(Resource r) {
return longId ? longAnonId( r ) : shortAnonId( r );
}
/*
* A shortAnonId is computed by maintaining a mapping table from the internal
* id's of anon resources. The short id is the index into the table of the
* internal id.
*/
private String shortAnonId(Resource r) {
String result = anonMap.get(r.getId());
if (result == null) {
result = "A" + Integer.toString(anonCount++);
anonMap.put(r.getId(), result);
}
return result;
}
/*
* A longAnonId is the internal id of the anon resource expressed as a
* character string.
*
* This code makes no assumptions about the characters used in the
* implementation of an anon id. It checks if they are valid namechar
* characters and escapes the id if not.
*/
private String longAnonId(Resource r) {
String rid = r.getId().toString();
return XMLChar.isValidNCName( rid ) ? rid : escapedId( rid );
}
/**
true means all namespaces defined in the model prefixes will be noted in xmlns
declarations; false means only "required" ones will be noted. Hook for configuration.
*/
private boolean writingAllModelPrefixNamespaces = true;
private Relation<String> nameSpaces = new Relation<>();
private Map<String, String> ns;
private PrefixMapping modelPrefixMapping;
private Set<String> namespacesNeeded;
void addNameSpace(String uri) {
namespacesNeeded.add(uri);
}
boolean isDefaultNamespace( String uri ) {
return "".equals( ns.get( uri ) );
}
private void addNameSpaces( Model model ) {
NsIterator nsIter = model.listNameSpaces();
while (nsIter.hasNext()) this.addNameSpace( nsIter.nextNs() );
}
private void primeNamespace( Model model )
{
Map<String, String> m = model.getNsPrefixMap();
for ( Entry<String, String> e : m.entrySet() )
{
String value = e.getValue();
String already = this.getPrefixFor( value );
if ( already == null )
{
this.setNsPrefix( model.getNsURIPrefix( value ), value );
if ( writingAllModelPrefixNamespaces )
{
this.addNameSpace( value );
}
}
}
if ( usesPrefix(model, "") )
{
// Doing "" prefix. Ensure it is a non-clashing, non-empty entity name.
String entityForEmptyPrefix = DEFAULT_NS_ENTITY_NAME ;
if ( usesPrefix(model, entityForEmptyPrefix) )
entityForEmptyPrefix = DEFAULT_NS_ENTITY_NAME_ALT ;
int i = 0 ;
while ( usesPrefix(model,entityForEmptyPrefix) )
{
entityForEmptyPrefix = DEFAULT_NS_ENTITY_NAME_ALT+"."+i ;
i++ ;
}
defaultNSEntityName = entityForEmptyPrefix ;
}
}
void setupMaps() {
nameSpaces.set11(RDF.getURI(), "rdf");
nameSpaces.set11(RDFS.getURI(), "rdfs");
nameSpaces.set11(DC.getURI(), "dc");
nameSpaces.set11(RSS.getURI(), "rss");
nameSpaces.set11("http://www.daml.org/2001/03/daml+oil.daml#", "daml");
nameSpaces.set11(VCARD.getURI(), "vcard");
nameSpaces.set11("http://www.w3.org/2002/07/owl#", "owl");
}
void workOutNamespaces() {
if (ns == null) {
ns = new HashMap<>();
Set<String> prefixesUsed = new HashSet<>();
setFromWriterSystemProperties( ns, prefixesUsed );
setFromGivenNamespaces( ns, prefixesUsed );
}
}
private void setFromWriterSystemProperties( Map<String, String> ns, Set<String> prefixesUsed ) {
for ( String uri : namespacesNeeded )
{
String val = JenaRuntime.getSystemProperty( RDFWriter.NSPREFIXPROPBASE + uri );
if ( val != null && checkLegalPrefix( val ) && !prefixesUsed.contains( val ) )
{
ns.put( uri, val );
prefixesUsed.add( val );
}
}
}
private void setFromGivenNamespaces( Map<String, String> ns, Set<String> prefixesUsed ) {
for ( String uri : namespacesNeeded )
{
if ( ns.containsKey( uri ) )
{
continue;
}
String val = null;
Set<String> s = nameSpaces.forward( uri );
if ( s != null )
{
Iterator<String> it2 = s.iterator();
if ( it2.hasNext() )
{
val = it2.next();
}
if ( prefixesUsed.contains( val ) )
{
val = null;
}
}
if ( val == null )
{
// just in case the prefix has already been used, look for a free one.
// (the usual source of such prefixes is reading in a model we wrote out earlier)
do
{
val = "j." + ( jenaPrefixCount++ );
}
while ( prefixesUsed.contains( val ) );
}
ns.put( uri, val );
prefixesUsed.add( val );
}
}
final synchronized public void setNsPrefix(String prefix, String ns) {
if (checkLegalPrefix(prefix)) {
nameSpaces.set11(ns, prefix);
}
}
final public String getPrefixFor( String uri )
{
// xml and xmlns namespaces are pre-bound
if ("http://www.w3.org/XML/1998/namespace".equals(uri)) return "xml";
if ("http://www.w3.org/2000/xmlns/".equals(uri)) return "xmlns";
Set<String> s = nameSpaces.backward( uri );
if (s != null && s.size() == 1) return s.iterator().next();
return null;
}
String xmlnsDecl() {
workOutNamespaces();
StringBuilder result = new StringBuilder();
Iterator<Entry<String, String>> it = ns.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> ent = it.next();
String prefix = ent.getValue();
String uri = ent.getKey();
result.append( newline ).append( " xmlns" );
if (prefix.length() > 0) result.append( ':' ).append( prefix );
result.append( '=' ).append( substitutedAttribute( checkURI( uri ) ) );
}
return result.toString();
}
static final private int FAST = 1;
static final private int START = 2;
static final private int END = 3;
static final private int ATTR = 4;
static final private int FASTATTR = 5;
String rdfEl(String local) {
return tag(RDFNS, local, FAST, true);
}
String startElementTag(String uri, String local) {
return tag(uri, local, START, false);
}
protected String startElementTag(String uriref) {
return splitTag(uriref, START);
}
String attributeTag(String uriref) {
return splitTag(uriref, ATTR);
}
String attributeTag(String uri, String local) {
return tag(uri, local, ATTR, false);
}
String rdfAt(String local) {
return tag(RDFNS, local, FASTATTR, true);
}
String endElementTag(String uri, String local) {
return tag(uri, local, END, false);
}
protected String endElementTag(String uriref) {
return splitTag(uriref, END);
}
String splitTag(String uriref, int type) {
int split = Util.splitNamespaceXML( uriref );
if (split == uriref.length()) throw new InvalidPropertyURIException( uriref );
return tag( uriref.substring( 0, split ), uriref.substring( split ), type, true );
}
static public boolean dbg = false;
String tag( String namespace, String local, int type, boolean localIsQname) {
if (dbg)
System.err.println(namespace + " - " + local);
String prefix = ns.get( namespace );
if (type != FAST && type != FASTATTR) {
if ((!localIsQname) && !XMLChar.isValidNCName(local))
return splitTag(namespace + local, type);
if (namespace.equals(RDFNS)) {
// Description, ID, nodeID, about, aboutEach, aboutEachPrefix, li
// bagID parseType resource datatype RDF
if (badRDF.contains(local)) {
logger.warn( "The URI rdf:" + local + " cannot be serialized in RDF/XML." );
throw new InvalidPropertyURIException( "rdf:" + local );
}
}
}
boolean cookUp = false;
if (prefix == null) {
checkURI( namespace );
logger.warn(
"Internal error: unexpected QName URI: <"
+ namespace
+ ">. Fixing up with j.cook.up code.",
new BrokenException( "unexpected QName URI " + namespace ));
cookUp = true;
} else if (prefix.length() == 0) {
if (type == ATTR || type == FASTATTR)
cookUp = true;
else
return local;
}
if (cookUp) return cookUpAttribution( type, namespace, local );
return prefix + ":" + local;
}
private String cookUpAttribution( int type, String namespace, String local )
{
String prefix = "j.cook.up";
switch (type) {
case FASTATTR :
case ATTR :
return "xmlns:" + prefix + "=" + substitutedAttribute( namespace ) + " " + prefix + ":" + local;
case START :
return prefix + ":" + local + " xmlns:" + prefix+ "=" + substitutedAttribute( namespace );
default:
case END :
return prefix + ":" + local;
case FAST :
// logger.error("Unreachable code - reached.");
throw new BrokenException( "cookup reached final FAST" );
}
}
/** Write out an XML serialization of a model.
* @param model the model to be serialized
* @param out the OutputStream to receive the serialization
* @param base The URL at which the file will be placed.
*/
@Override
final public void write(Model model, OutputStream out, String base)
{ write( model, FileUtils.asUTF8(out), base ); }
/** Serialize Model <code>model</code> to Writer <code>out</out>.
* @param model The model to be written.
* @param out The Writer to which the serialization should be sent.
* @param base the base URI for relative URI calculations. <code>null</code> means use only absolute URI's.
*/
@Override
synchronized public void write(Model model, Writer out, String base)
{
setupNamespaces( model );
PrintWriter pw = out instanceof PrintWriter ? (PrintWriter) out : new PrintWriter( out );
if (!Boolean.FALSE.equals(showXmlDeclaration)) writeXMLDeclaration( out, pw );
writeXMLBody( model, pw, base );
pw.flush();
}
/**
@param baseModel
@param model
*/
private void setupNamespaces( Model model )
{
this.namespacesNeeded = new HashSet<>();
this.ns = null;
this.modelPrefixMapping = model;
primeNamespace( model );
addNameSpace( RDF.getURI() );
addNameSpaces(model);
jenaPrefixCount = 0;
}
@SuppressWarnings("deprecation")
// Testing.
// Agrees with ARPOptions.defaultIriFactory.
static IRIFactory factory = IRIFactory.jenaImplementation();
private void writeXMLBody( Model model, PrintWriter pw, String base ) {
if (showDoctypeDeclaration.booleanValue()) generateDoctypeDeclaration( model, pw );
// try {
// errors?
if (xmlBase == null) {
baseURI = (base == null || base.length() == 0) ? null : factory.create(base);
writeBody(model, pw, base, false);
} else {
baseURI = xmlBase.length() == 0 ? null : factory.create(xmlBase);
writeBody(model, pw, xmlBase, true);
}
// } catch (MalformedURIException e) {
// throw new BadURIException( e.getMessage(), e);
// }
}
protected static final Pattern predefinedEntityNames = Pattern.compile( "amp|lt|gt|apos|quot" );
public boolean isPredefinedEntityName( String name )
{ return predefinedEntityNames.matcher( name ).matches(); }
private String attributeQuoteChar ="\"";
protected String attributeQuoted( String s )
{ return attributeQuoteChar + s + attributeQuoteChar; }
protected String substitutedAttribute( String s )
{
String substituted = Util.substituteStandardEntities( s );
if (!showDoctypeDeclaration.booleanValue())
return attributeQuoted( substituted );
else
{
int split = Util.splitNamespaceXML( substituted );
String namespace = substituted.substring( 0, split );
String prefix = modelPrefixMapping.getNsURIPrefix( namespace );
return prefix == null || isPredefinedEntityName( prefix )
? attributeQuoted( substituted )
: attributeQuoted( "&" + strForPrefix(prefix) + ";" + substituted.substring( split ) )
;
}
}
private void generateDoctypeDeclaration( Model model, PrintWriter pw )
{
String rdfns = RDF.getURI();
String rdfRDF = model.qnameFor( rdfns + "RDF" );
if ( rdfRDF == null ) {
model.setNsPrefix("rdf",rdfns);
rdfRDF = "rdf:RDF";
}
Map<String, String> prefixes = model.getNsPrefixMap();
pw.print( "<!DOCTYPE " + rdfRDF +" [" );
for ( String prefix : prefixes.keySet() )
{
if ( isPredefinedEntityName( prefix ) )
{
continue;
}
pw.print(
newline + " <!ENTITY " + strForPrefix( prefix ) + " '" + Util.substituteEntitiesInEntityValue(
prefixes.get( prefix ) ) + "'>" );
}
pw.print( "]>" + newline );
}
private String strForPrefix(String prefix)
{
if ( prefix.length() == 0 )
return defaultNSEntityName ;
return prefix ;
}
private static boolean usesPrefix(Model model, String prefix)
{
return model.getNsPrefixURI(prefix) != null ;
}
private void writeXMLDeclaration(Writer out, PrintWriter pw) {
String decl = null;
if (out instanceof OutputStreamWriter) {
String javaEnc = ((OutputStreamWriter) out).getEncoding();
// System.err.println(javaEnc);
if (!(javaEnc.equals("UTF8") || javaEnc.equals("UTF-16"))) {
CharEncoding encodingInfo = CharEncoding.create(javaEnc);
String ianaEnc = encodingInfo.name();
decl = "<?xml version="+attributeQuoted("1.0")+" encoding=" + attributeQuoted(ianaEnc) + "?>";
if (!encodingInfo.isIANA())
logger.warn(encodingInfo.warningMessage()+"\n"+
" It is better to use a FileOutputStream, in place of a FileWriter.");
}
}
if (decl == null && showXmlDeclaration != null)
decl = "<?xml version="+attributeQuoted("1.0")+"?>";
if (decl != null) {
pw.println(decl);
}
}
/** Set an error handler.
* @param errHandler The new error handler to be used, or null for the default handler.
* @return the old error handler
*/
@Override
synchronized public RDFErrorHandler setErrorHandler(RDFErrorHandler errHandler) {
// null means no user defined error handler.
// We implement this using defaultErrorHandler,
// but hide this fact from the user.
RDFErrorHandler rslt = errorHandler;
if (rslt == defaultErrorHandler) rslt = null;
errorHandler = errHandler == null ? defaultErrorHandler : errHandler;
return rslt;
}
static private final char ESCAPE = 'X';
static private String escapedId(String id) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < id.length(); i++) {
char ch = id.charAt(i);
if (ch != ESCAPE
&& (i == 0 ? XMLChar.isNCNameStart(ch) : XMLChar.isNCName(ch))) {
result.append( ch );
} else {
escape( result, ch );
}
}
return result.toString();
}
static final char [] hexchar = "0123456789abcdef".toCharArray();
static private void escape( StringBuffer sb, char ch) {
sb.append( ESCAPE );
int charcode = ch;
do {
sb.append( hexchar[charcode & 15] );
charcode = charcode >> 4;
} while (charcode != 0);
sb.append( ESCAPE );
}
/**
Set the writer property propName to the value obtained from propValue. Return an
Object representation of the original value.
@see org.apache.jena.rdf.model.RDFWriter#setProperty(java.lang.String, java.lang.Object)
*/
@Override
final synchronized public Object setProperty( String propName, Object propValue ) {
if (propName.equalsIgnoreCase("showXmlDeclaration")) {
return setShowXmlDeclaration(propValue);
} else if (propName.equalsIgnoreCase( "showDoctypeDeclaration" )) {
return setShowDoctypeDeclaration( propValue );
} else if (propName.equalsIgnoreCase( "minimalPrefixes" )) {
try { return new Boolean( !writingAllModelPrefixNamespaces ); }
finally { writingAllModelPrefixNamespaces = !getBoolean( propValue ); }
} else if (propName.equalsIgnoreCase("xmlbase")) {
String result = xmlBase;
xmlBase = (String) propValue;
return result;
} else if (propName.equalsIgnoreCase("tab")) {
return setTab( propValue );
} else if (propName.equalsIgnoreCase("width")) {
return setWidth(propValue);
} else if (propName.equalsIgnoreCase("longid")) {
Boolean result = new Boolean(longId);
longId = getBoolean(propValue);
return result;
} else if (propName.equalsIgnoreCase("attributeQuoteChar")) {
return setAttributeQuoteChar(propValue);
} else if (propName.equalsIgnoreCase( "allowBadURIs" )) {
Boolean result = new Boolean( !demandGoodURIs );
demandGoodURIs = !getBoolean(propValue);
return result;
} else if (propName.equalsIgnoreCase("prettyTypes")) {
return setTypes((Resource[]) propValue);
} else if (propName.equalsIgnoreCase("relativeURIs")) {
int old = relativeFlags;
relativeFlags = str2flags((String) propValue);
return flags2str(old);
} else if (propName.equalsIgnoreCase("blockRules")) {
return setBlockRules(propValue);
} else {
logger.warn("Unsupported property: " + propName);
return null;
}
}
private String setAttributeQuoteChar(Object propValue) {
String oldValue = attributeQuoteChar;
if ( "\"".equals(propValue) || "'".equals(propValue) )
attributeQuoteChar = (String)propValue;
else
logger.warn("attributeQutpeChar must be either \"\\\"\" or \', not \""+propValue+"\"" );
return oldValue;
}
private Integer setWidth(Object propValue) {
Integer oldValue = new Integer(width);
if (propValue instanceof Integer) {
width = ((Integer) propValue).intValue();
} else {
try {
width = Integer.parseInt((String) propValue);
} catch (Exception e) {
logger.warn( "Bad value for width: '" + propValue + "' [" + e.getMessage() + "]" );
}
}
return oldValue;
}
private Integer setTab(Object propValue) {
Integer result = new Integer(tabSize);
if (propValue instanceof Integer) {
tabSize = ((Integer) propValue).intValue();
} else {
try {
tabSize = Integer.parseInt((String) propValue);
} catch (Exception e) {
logger.warn( "Bad value for tab: '" + propValue + "' [" + e.getMessage() + "]" );
}
}
return result;
}
private String setShowDoctypeDeclaration( Object propValue )
{
String oldValue = showDoctypeDeclaration.toString();
showDoctypeDeclaration = getBooleanValue( propValue, Boolean.FALSE );
return oldValue;
}
private String setShowXmlDeclaration( Object propValue )
{
String oldValue = showXmlDeclaration == null ? null : showXmlDeclaration.toString();
showXmlDeclaration = getBooleanValue( propValue, null );
return oldValue;
}
/**
Answer the boolean value corresponding to o, which must either be a Boolean,
or a String parsable as a Boolean.
*/
static private boolean getBoolean( Object o )
{ return getBooleanValue( o, Boolean.FALSE ).booleanValue(); }
private static Boolean getBooleanValue( Object propValue, Boolean theDefault )
{
if (propValue == null)
return theDefault;
else if (propValue instanceof Boolean)
return (Boolean) propValue;
else if (propValue instanceof String)
return stringToBoolean( (String) propValue, theDefault );
else
throw new JenaException( "cannot treat as boolean: " + propValue );
}
private static Boolean stringToBoolean( String b, Boolean theDefault )
{
if (b.equals( "default" )) return theDefault;
if (b.equalsIgnoreCase( "true" )) return Boolean.TRUE;
if (b.equalsIgnoreCase( "false" )) return Boolean.FALSE;
throw new BadBooleanException( b );
}
Resource[] setTypes( Resource x[] ) {
logger.warn( "prettyTypes is not a property on the Basic RDF/XML writer." );
return null;
}
private Resource blockedRules[] = new Resource[]{RDFSyntax.propertyAttr};
Resource[] setBlockRules(Object o) {
Resource rslt[] = blockedRules;
unblockAll();
if (o instanceof Resource[]) {
blockedRules = (Resource[]) o;
} else {
StringTokenizer tkn = new StringTokenizer((String) o, ", ");
Vector<Resource> v = new Vector<>();
while (tkn.hasMoreElements()) {
String frag = tkn.nextToken();
// System.err.println("Blocking " + frag);
v.add(new ResourceImpl(RDFSyntax.getURI() + frag));
}
blockedRules = new Resource[v.size()];
v.copyInto(blockedRules);
}
for ( Resource blockedRule : blockedRules )
{
blockRule( blockedRule );
}
return rslt;
}
/*
private boolean sameDocument = true;
private boolean network = false;
private boolean absolute = true;
private boolean relative = true;
private boolean parent = true;
private boolean grandparent = false;
*/
private int relativeFlags =
IRI.SAMEDOCUMENT | IRI.ABSOLUTE | IRI.CHILD | IRI.PARENT;
/**
Answer the form of the URI after relativisation according to the relativeFlags set
by properties. If the flags are 0 or the base URI is null, answer the original URI.
Throw an exception if the URI is "bad" and we demandGoodURIs.
*/
protected String relativize( String uri ) {
return relativeFlags != 0 && baseURI != null
? relativize( baseURI, uri )
: checkURI( uri );
}
/**
Answer the relative form of the URI against the base, according to the relativeFlags.
*/
private String relativize( IRI base, String uri ) {
// errors?
return base.relativize( uri, relativeFlags).toString();
}
/**
Answer the argument URI, but if we demandGoodURIs and it isn't good, throw
a JenaException that encapsulates a MalformedURIException. There doesn't
appear to be a convenient URI.checkGood() kind of method, alas.
*/
private String checkURI( String uri ) {
if (demandGoodURIs) {
IRI iri = factory.create( uri );
if (iri.hasViolation(false) )
throw new BadURIException( "Only well-formed absolute URIrefs can be included in RDF/XML output: "
+ (iri.violations(false).next()).getShortMessage());
}
return uri;
}
/**
Answer true iff prefix is a "legal" prefix to use, ie, is empty [for the default namespace]
or an NCName that does not start with "xml" and does not match the reserved-to-Jena
pattern.
*/
private boolean checkLegalPrefix( String prefix ) {
if (prefix.equals(""))
return true;
if (prefix.toLowerCase().startsWith( "xml" ))
logger.warn( "Namespace prefix '" + prefix + "' is reserved by XML." );
else if (!XMLChar.isValidNCName(prefix))
logger.warn( "'" + prefix + "' is not a legal namespace prefix." );
else if (jenaNamespace.matcher(prefix).matches())
logger.warn( "Namespace prefix '" + prefix + "' is reserved by Jena." );
else
return true;
return false;
}
static private String flags2str(int f) {
StringBuffer oldValue = new StringBuffer(64);
if ( (f&IRI.SAMEDOCUMENT)!=0 )
oldValue.append( "same-document, " );
if ( (f&IRI.NETWORK)!=0 )
oldValue.append( "network, ");
if ( (f&IRI.ABSOLUTE)!=0 )
oldValue.append("absolute, ");
if ( (f&IRI.CHILD)!=0 )
oldValue.append("relative, ");
if ((f&IRI.PARENT)!=0)
oldValue.append("parent, ");
if ((f&IRI.GRANDPARENT)!=0)
oldValue.append("grandparent, ");
if (oldValue.length() > 0)
oldValue.setLength(oldValue.length()-2);
return oldValue.toString();
}
public static int str2flags(String pv){
StringTokenizer tkn = new StringTokenizer(pv,", ");
int rslt = 0;
while ( tkn.hasMoreElements() ) {
String flag = tkn.nextToken();
if ( flag.equals("same-document") )
rslt |= IRI.SAMEDOCUMENT;
else if ( flag.equals("network") )
rslt |= IRI.NETWORK;
else if ( flag.equals("absolute") )
rslt |= IRI.ABSOLUTE;
else if ( flag.equals("relative") )
rslt |= IRI.CHILD;
else if ( flag.equals("parent") )
rslt |= IRI.PARENT;
else if ( flag.equals("grandparent") )
rslt |= IRI.GRANDPARENT;
else
logger.warn(
"Incorrect property value for relativeURIs: " + flag
);
}
return rslt;
}
}