📄 n3jenawritercommon.java
字号:
/*
* (c) Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 Hewlett-Packard Development Company, LP
* [See end of file]
*/
package com.hp.hpl.jena.n3;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.JenaRuntime;
import com.hp.hpl.jena.util.iterator.* ;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.shared.JenaException;
import com.hp.hpl.jena.vocabulary.OWL ;
import com.hp.hpl.jena.vocabulary.XSD ;
import com.hp.hpl.jena.vocabulary.RDF ;
import java.util.* ;
import java.io.* ;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.* ;
/** Common framework for implementing N3 writers.
*
* @author Andy Seaborne
* @version $Id: N3JenaWriterCommon.java,v 1.36 2007/01/02 11:48:32 andy_seaborne Exp $
*/
public class N3JenaWriterCommon implements RDFWriter
{
static Log logger = LogFactory.getLog(N3JenaWriterCommon.class) ;
// N3 writing proceeds in 2 stages.
// First, it analysis the model to be written to extract information
// that is going to be specially formatted (RDF lists, one ref anon nodes)
// Second do the writing walk.
// The simple N3 writer does nothing during preparation.
Map writerPropertyMap = null ;
final boolean doAbbreviatedBaseURIref = getBooleanValue("abbrevBaseURI", true) ;
boolean alwaysAllocateBNodeLabel = false ;
// Common variables
RDFErrorHandler errorHandler = null;
static final String NS_W3_log = "http://www.w3.org/2000/10/swap/log#" ;
Map prefixMap = new HashMap() ; // Prefixes to actually use
Map bNodesMap = null ; // BNodes seen.
int bNodeCounter = 0 ;
// Specific properties that have a short form.
static Map wellKnownPropsMap = new HashMap() ;
static {
wellKnownPropsMap.put(NS_W3_log+"implies", "=>" ) ;
wellKnownPropsMap.put(OWL.sameAs.getURI(), "=" ) ;
wellKnownPropsMap.put(RDF.type.getURI(), "a" ) ;
}
// Work variables controlling the output
IndentedWriter out = null ;
String baseURIref = null ;
String baseURIrefHash = null ;
// Min spacing of items
int minGap = getIntValue("minGap", 1) ;
String minGapStr = pad(minGap) ;
// Gap from subject to property
int indentProperty = getIntValue("indentProperty", 6) ;
// Width of property before wrapping.
// This is not necessarily a control of total width
// e.g. the pretty writer may be writing properties inside indented one ref bNodes
int widePropertyLen = getIntValue("widePropertyLen", 20) ;
// Column for property when an object follows a property on the same line
int propertyCol = getIntValue("propertyColumn", 8) ;
// Minimum gap from property to object when object on a new line.
int indentObject = propertyCol ;
// If a subject is shorter than this, the first property may go on same line.
int subjectColumn = getIntValue("subjectColumn", indentProperty) ;
// Require shortSubject < subjectCol (strict less than)
int shortSubject = subjectColumn-minGap;
boolean useWellKnownPropertySymbols = getBooleanValue("usePropertySymbols", true) ;
boolean allowTripleQuotedStrings = getBooleanValue("useTripleQuotedStrings", true) ;
boolean allowDoubles = getBooleanValue("useDoubles", true) ;
boolean allowDecimals = getBooleanValue("useDecimals", true) ;
// ----------------------------------------------------
// Jena RDFWriter interface
public RDFErrorHandler setErrorHandler(RDFErrorHandler errHandler)
{
RDFErrorHandler old = errorHandler;
errorHandler = errHandler;
return old;
}
public Object setProperty(String propName, Object propValue)
{
if ( ! ( propValue instanceof String ) )
{
logger.warn("N3.setProperty: Property for '"+propName+"' is not a string") ;
propValue = propValue.toString() ;
}
// Store absolute name of property
propName = absolutePropName(propName) ;
if ( writerPropertyMap == null )
writerPropertyMap = new HashMap() ;
Object oldValue = writerPropertyMap.get(propName);
writerPropertyMap.put(propName, propValue);
return oldValue;
}
/** Write the model out in N3. The writer should be one suitable for UTF-8 which
* excludes a PrintWriter or a FileWriter which use default character set.
*
* Examples:
* <pre>
* try {
* Writer w = new BufferedWriter(new OutputStreamWriter(output, "UTF-8")) ;
* model.write(w, base) ;
* try { w.flush() ; } catch (IOException ioEx) {}
* } catch (java.io.UnsupportedEncodingException ex) {} //UTF-8 is required so can't happen
* </pre>
* or
* <pre>
* try {
* OutputStream out = new FileOutputStream(file) ;
* Writer w = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")) ;
* model.write(w, base) ;
* }
* catch (java.io.UnsupportedEncodingException ex) {}
* catch (java.io.FileNotFoundException noFileEx) { ... }
* </pre>
* @see #write(Model,Writer,String)
*/
public void write(Model baseModel, Writer _out, String base)
{
if (!(_out instanceof BufferedWriter))
_out = new BufferedWriter(_out);
out = new IndentedWriter(_out);
if ( base != null )
{
baseURIref = base ;
if ( !base.endsWith("#") &&! isOpaque(base) )
baseURIrefHash = baseURIref+"#" ;
}
processModel(baseModel) ;
}
/** Write the model out in N3, encoded in in UTF-8
* @see #write(Model,Writer,String)
*/
public synchronized void write(Model model, OutputStream output, String base)
{
try {
Writer w = new BufferedWriter(new OutputStreamWriter(output, "UTF-8")) ;
write(model, w, base) ;
try { w.flush() ; } catch (IOException ioEx) {}
} catch (java.io.UnsupportedEncodingException ex)
{
System.err.println("Failed to create UTF-8 writer") ;
}
}
// ----------------------------------------------------
// The assumed processing model is:
// Writing N3 involves ordering the graph into:
// -- Subjects
// -- Property lists within subjects
// -- Object lists with in properties
// A derived class may choose to intercept and implement at any of these levels.
// Standard layout is:
// subject
// property1 value1 ;
// property2 value2 ;
// property3 value3 .
// Normal hook points for subclasses.
protected void startWriting() {}
protected void finishWriting() {}
protected void prepare(Model model) {}
protected void processModel(Model baseModel)
{
prefixMap = baseModel.getNsPrefixMap() ;
Model model = ModelFactory.withHiddenStatements( baseModel );
bNodesMap = new HashMap() ;
// If no base defined for the model, but one given to writer,
// then use this.
String base2 = (String)prefixMap.get("") ;
if ( base2 == null && baseURIrefHash != null )
prefixMap.put("", baseURIrefHash) ;
for ( Iterator iter = prefixMap.keySet().iterator() ; iter.hasNext() ; )
{
String prefix = (String)iter.next() ;
if ( prefix.indexOf('.') != -1 )
iter.remove() ;
}
startWriting() ;
prepare(model) ;
writeHeader(model) ;
writePrefixes(model) ;
if (prefixMap.size() != 0)
out.println();
// Do the output.
writeModel(model) ;
// Release intermediate memory - allows reuse of a writer
finishWriting() ;
bNodesMap = null ;
}
protected void writeModel(Model model)
{
// Needed only for no prefixes, no blank first line.
boolean doingFirst = true;
ResIterator rIter = listSubjects(model);
for (; rIter.hasNext();)
{
// Subject:
// First - it is something we will write out as a structure in an object field?
// That is, a RDF list or the object of exactly one statement.
Resource subject = rIter.nextResource();
if ( skipThisSubject(subject) )
{
if (N3JenaWriter.DEBUG)
out.println("# Skipping: " + formatResource(subject));
continue;
}
// We really are going to print something via writeTriples
if (doingFirst)
doingFirst = false;
else
out.println();
writeOneGraphNode(subject) ;
}
rIter.close();
}
protected ResIterator listSubjects(Model model) { return model.listSubjects(); }
protected void writeOneGraphNode(Resource subject)
{
// New top level item.
// Does not take effect until newline.
out.incIndent(indentProperty) ;
writeSubject(subject);
writePropertiesForSubject(subject) ;
out.decIndent(indentProperty) ;
out.println(" .");
}
protected void writePropertiesForSubject(Resource subj)
{
ClosableIterator iter = preparePropertiesForSubject(subj);
// For each property.
for (; iter.hasNext();)
{
Property property = (Property) iter.next();
// Object list
writeObjectList(subj, property);
if (iter.hasNext())
out.println(" ;");
}
iter.close();
}
// Hook called on every resource.
// Since there is spacing bewteen resource frames, need to know
// whether an item will cause any output.
protected boolean skipThisSubject(Resource r) { return false ; }
// This is the hook called within writeModel.
// NB May not be at the top level (indent = 0)
protected void writeSubject(Resource subject)
{
String subjStr = formatResource(subject);
out.print(subjStr);
// May be very short : if so, stay on this line.
// Currently at end of subject.
// NB shortSubject is (subjectColumn-minGap) so there is a gap.
if (subjStr.length() < shortSubject )
{
out.print(pad(subjectColumn - subjStr.length()) );
}
else
// Does not fit this line.
out.println();
}
protected void writeHeader(Model model)
{
if (baseURIref != null && !baseURIref.equals("") )
out.println("# Base: " + baseURIref);
}
protected void writePrefixes(Model model)
{
for (Iterator pIter = prefixMap.keySet().iterator(); pIter.hasNext();)
{
String p = (String) pIter.next();
String u = (String) prefixMap.get(p);
// Special cases: N3 handling of base names.
if (doAbbreviatedBaseURIref && p.equals(""))
{
if (baseURIrefHash != null && u.equals(baseURIrefHash))
u = "#";
if (baseURIref != null && u.equals(baseURIref))
u = "";
}
String tmp = "@prefix " + p + ": ";
out.print(tmp);
out.print(pad(16 - tmp.length()));
// NB Starts with a space to ensure a gap.
out.println(" <" + u + "> .");
}
}
protected void writeObjectList(Resource resource, Property property)
{
String propStr = formatProperty(property) ;
// if (wellKnownPropsMap.containsKey(property.getURI()))
// propStr = (String) wellKnownPropsMap.get(property.getURI());
// else
// propStr = formatResource(property);
// Write with object lists as clusters of statements with the same property
// Looks more like a machine did it but fewer bad cases.
StmtIterator sIter = resource.listProperties(property);
for (; sIter.hasNext();)
{
Statement stmt = sIter.nextStatement() ;
String objStr = formatNode(stmt.getObject()) ;
out.print(propStr);
out.incIndent(indentObject);
if ( (propStr.length()+minGap) <= widePropertyLen )
{
// Property col allows for min gap but widePropertyLen > propertyCol
// (which looses alignment - this is intentional.
// Ensure there is at least min gap.
int padding = calcPropertyPadding(propStr) ;
out.print(pad(padding)) ;
// if ( propStr.length() < propertyWidth )
// out.print( pad(propertyCol-minGap-propStr.length()) ) ;
// out.print(minGapStr) ;
}
else
// Does not fit this line.
out.println();
// Write one object - simple writing.
out.print(objStr) ;
out.decIndent(indentObject);
if ( sIter.hasNext() )
{
out.println(" ;") ;
}
}
sIter.close() ;
}
protected String formatNode(RDFNode node)
{
if (node instanceof Literal)
return formatLiteral((Literal) node);
else
return formatResource((Resource)node) ;
}
protected void writeObject(RDFNode node)
{
if (node instanceof Literal)
{
writeLiteral((Literal) node);
return;
}
Resource rObj = (Resource) node;
out.print(formatResource(rObj));
}
protected void writeLiteral(Literal literal)
{
out.print(formatLiteral(literal)) ;
}
protected ClosableIterator preparePropertiesForSubject(Resource r)
{
// Properties to do.
Set properties = new HashSet() ;
StmtIterator sIter = r.listProperties();
for ( ; sIter.hasNext() ; )
properties.add(sIter.nextStatement().getPredicate()) ;
sIter.close() ;
return WrappedIterator.create(properties.iterator()) ;
}
// Utility operations
protected String formatResource(Resource r)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -