📄 basexmlwriter.java
字号:
/*
* (c) Copyright 2000, 2001, 2002, 2002, 2003, 2004, 2005, 2006, 2007 Hewlett-Packard Development Company, LP
* All rights reserved.
* [See end of file]
* $Id: BaseXMLWriter.java,v 1.66 2007/06/07 17:07:01 jeremy_carroll Exp $
*/
package com.hp.hpl.jena.xmloutput.impl;
import java.io.*;
import java.util.*;
import java.util.regex.Pattern;
import org.apache.commons.logging.*;
import org.apache.xerces.util.XMLChar;
import com.hp.hpl.jena.JenaRuntime;
import com.hp.hpl.jena.iri.*;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.rdf.model.impl.RDFDefaultErrorHandler;
import com.hp.hpl.jena.rdf.model.impl.ResourceImpl;
import com.hp.hpl.jena.rdf.model.impl.Util;
import com.hp.hpl.jena.shared.*;
import com.hp.hpl.jena.util.CharEncoding;
import com.hp.hpl.jena.util.FileUtils;
import com.hp.hpl.jena.vocabulary.*;
import com.hp.hpl.jena.xmloutput.RDFXMLWriterI;
/**
* 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 presentationall
* <li>errorHandler
* </ul>
*
* @author jjcnee
* @version Release='$Name: $' Revision='$Revision: 1.66 $' Date='$Date: 2007/06/07 17:07:01 $'
*/
abstract public class BaseXMLWriter implements RDFXMLWriterI {
private static final String newline =
JenaRuntime.getSystemProperty( "line.separator" );
public BaseXMLWriter() {
setupMaps();
}
private static Log xlogger = LogFactory.getLog( BaseXMLWriter.class );
protected static SimpleLogger logger = new SimpleLogger() {
public void warn(String s) {
xlogger.warn(s);
}
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 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 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 = (String) 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 nameSpaces = new Relation();
private Map ns;
private PrefixMapping modelPrefixMapping;
private Set 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 m = model.getNsPrefixMap();
Iterator it = m.entrySet().iterator();
while (it.hasNext())
{
Map.Entry e = (Map.Entry) it.next();
// String key = (String) e.getKey();
String value = (String) e.getValue();
String already = this.getPrefixFor( value );
if (already == null)
{ this.setNsPrefix( model.getNsURIPrefix( value ), value );
if (writingAllModelPrefixNamespaces) this.addNameSpace( value ); }
}
}
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 prefixesUsed = new HashSet();
setFromWriterSystemProperties( ns, prefixesUsed );
setFromGivenNamespaces( ns, prefixesUsed );
}
}
private void setFromWriterSystemProperties( Map ns, Set prefixesUsed ) {
Iterator it = namespacesNeeded.iterator();
while (it.hasNext()) {
String uri = (String) it.next();
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 ns, Set prefixesUsed ) {
Iterator it = namespacesNeeded.iterator();
while (it.hasNext()) {
String uri = (String) it.next();
if (ns.containsKey(uri))
continue;
String val = null;
Set s = nameSpaces.forward(uri);
if (s != null) {
Iterator it2 = s.iterator();
if (it2.hasNext())
val = (String) 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 )
{
Set s = nameSpaces.backward( uri );
if (s != null && s.size() == 1) return (String) s.iterator().next();
return null;
}
String xmlnsDecl() {
workOutNamespaces();
StringBuffer result = new StringBuffer();
Iterator it = ns.entrySet().iterator();
while (it.hasNext()) {
Map.Entry ent = (Map.Entry) it.next();
String prefix = (String) ent.getValue();
String uri = (String) 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.splitNamespace( 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 = (String) 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.fatal("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.
*/
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 out The Writer to which the serialization should be sent.
* @param baseModel The model to be written.
* @param base the base URI for relative URI calculations. <code>
* null</code> means use only absolute URI's.
*/
synchronized public void write(Model baseModel, Writer out, String base)
{
Model model = ModelFactory.withHiddenStatements( baseModel );
setupNamespaces( baseModel, 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 baseModel, Model model )
{
this.namespacesNeeded = new HashSet();
this.ns = null;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -