/*
* Created on Mar 28, 2005
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package org.mindswap.swoop.utils.rdfapi;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import org.mindswap.swoop.utils.owlapi.QNameShortFormProvider;
import org.semanticweb.owl.io.vocabulary.RDFVocabularyAdapter;
import org.semanticweb.owl.io.vocabulary.XMLSchemaSimpleDatatypeVocabulary;
import org.semanticweb.owl.model.OWLException;
import edu.unika.aifb.rdf.api.model.Literal;
import edu.unika.aifb.rdf.api.model.Model;
import edu.unika.aifb.rdf.api.model.ModelException;
import edu.unika.aifb.rdf.api.model.NodeFactory;
import edu.unika.aifb.rdf.api.model.RDFNode;
import edu.unika.aifb.rdf.api.model.Resource;
import edu.unika.aifb.rdf.api.model.Statement;
import edu.unika.aifb.rdf.api.util.RDFConstants;
/**
* @author ronwalf
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class RDFWriter {
protected URI base;
protected Model model;
protected XMLWriter xml;
private Map bnodes;
private Map bnodeCount;
private Set namespaces;
private Comparator nodeComparator;
private NodeFactory nodeFactory;
private RDFVocabularyAdapter rdf = RDFVocabularyAdapter.INSTANCE;
private Set rendered;
private QNameShortFormProvider qnames;
private Map subjects; /* Map of subjects to property maps*/
private int level;
private int maxLevel = 1;
private static int bnodeNumber = 0;
public RDFWriter(XMLWriter xml, Model model, QNameShortFormProvider qnames) throws OWLException {
reset();
this.model = model;
this.nodeComparator = new NodeComparator();
try {
this.nodeFactory = model.getNodeFactory();
} catch (ModelException e) {
throw new OWLException(e);
}
this.qnames = qnames;
this.xml = xml;
}
public void addNamespacePrefix(String prefix, String namespace) {
qnames.setMapping(prefix, namespace.toString());
}
protected void addNamespaces() throws URISyntaxException, ModelException {
for (Iterator resourceIter = namespaces.iterator(); resourceIter.hasNext();) {
Resource resource = (Resource) resourceIter.next();
String qname = qnames.shortForm(new URI(resource.getURI()), false);
if (qname == null)
continue;
String prefix = qname.substring(0, qname.indexOf(":"));
String namespace = qnames.getURI(prefix);
xml.addNamespace(prefix, namespace);
xml.addEntity(prefix, namespace);
}
}
protected Map buildPropertyMap(Resource subject) throws ModelException {
Map properties = new TreeMap(nodeComparator);
Model propModel = model.find(subject, null, null);
for (Iterator statementIter = propModel.iterator(); statementIter
.hasNext();) {
Statement statement = (Statement) statementIter.next();
Resource property = statement.predicate();
RDFNode object = statement.object();
Collection objects = (Collection) properties.get(property);
if (objects == null) {
objects = new TreeSet(nodeComparator);
}
objects.add(object);
properties.put(property, objects);
}
return properties;
}
protected void collectInformation() throws URISyntaxException, ModelException {
subjects = new TreeMap(nodeComparator);
for (Iterator iter = model.iterator(); iter.hasNext();) {
Statement statement = (Statement) iter.next();
Resource subject = statement.subject();
Resource predicate = statement.predicate();
RDFNode object = statement.object();
if (subjects.get(subject) == null) {
subjects.put(subject, buildPropertyMap(subject));
}
if (SwoopResourceImpl.isAnonymous(subject) && (bnodes.get(subject) == null)) {
bnodes.put(subject, "b"+(bnodeNumber++));
}
/* Count BNodes referenced as objects */
if (SwoopResourceImpl.isAnonymous(object)) {
if (bnodes.get(object) == null) {
bnodes.put(object, "b"+(bnodeNumber++));
}
Integer count = (Integer) bnodeCount.get(object);
if (count == null) {
count = new Integer(1);
} else {
count = new Integer(count.intValue() + 1);
}
bnodeCount.put(object, count);
}
namespaces.add(predicate);
if (predicate.getURI().equals(rdf.getInstanceOf())) {
if (!SwoopResourceImpl.isAnonymous(object)) {
namespaces.add(object);
}
}
}
}
private Resource genResource(String uri) throws OWLException {
try {
return nodeFactory.createResource(uri);
} catch (ModelException e) {
throw new OWLException(e);
}
}
protected boolean isList(RDFNode node) throws ModelException {
if (!(node instanceof Resource))
return false;
// Node is either anonymous or rdf:Nil
Resource resource = (Resource) node;
if (!SwoopResourceImpl.isAnonymous(node)) {
if (rdf.getNil().equals(resource.getURI())) {
return true;
}
return false;
}
// Node is referenced exactly once.
Integer count = (Integer) bnodeCount.get(resource);
if ((count == null) || (count.intValue() != 1)) {
return false;
}
Map propertyMap = (Map) subjects.get(resource);
// Can only be two properties off of subject
if (propertyMap == null || propertyMap.size() != 2) {
return false;
}
// Should be only one element for rdf:first
Set firstSet = (Set) propertyMap.get(nodeFactory.createResource(RDFConstants.RDF_FIRST));
if (firstSet == null || firstSet.size() != 1) {
return false;
}
// Of that one element, it should be a resource
for (Iterator firstIter = firstSet.iterator(); firstIter.hasNext();) {
RDFNode first = (RDFNode) firstIter.next();
if (!(first instanceof Resource)) {
return false;
}
}
// There should only be one element for rdf:rest
Set nextSet = (Set) propertyMap.get(nodeFactory.createResource(RDFConstants.RDF_REST));
if (nextSet == null || nextSet.size() != 1) {
return false;
}
RDFNode child = null;
for (Iterator nextIter = nextSet.iterator(); nextIter.hasNext();) {
child = (RDFNode) nextIter.next();
break;
}
// child should not be rendered all ready.
if (rendered.contains(child)) {
return false;
}
// rdf:rest should be a list.
return isList(child);
}
protected boolean propertyBNode(RDFNode value) throws IOException {
if (SwoopResourceImpl.isAnonymous(value)) {
xml.addAttribute(RDFVocabularyAdapter.RDF, "nodeID", (String) bnodes.get(value));
return true;
}
return false;
}
protected boolean propertyList(RDFNode value) throws OWLException, IOException, URISyntaxException, ModelException {
if (!isList(value)) {
return false;
}
Vector elements = new Vector();
Resource current = (Resource) value;
while (!rdf.getNil().equals(current.getURI())) {
rendered.add(current);
Map propertyMap = (Map) subjects.get(current);
Set firstSet = (Set) propertyMap.get(nodeFactory.createResource(RDFConstants.RDF_FIRST));
for (Iterator firstIter = firstSet.iterator(); firstIter.hasNext();) {
elements.add(firstIter.next());
}
Set restSet = (Set) propertyMap.get(nodeFactory.createResource(RDFConstants.RDF_REST));
for (Iterator restIter = restSet.iterator(); restIter.hasNext();) {
current = (Resource) restIter.next();
break;
}
}
// Write list
xml.addAttribute(RDFVocabularyAdapter.RDF, "parseType", "Collection");
for (Iterator elementIter = elements.iterator(); elementIter.hasNext();) {
Resource element = (Resource) elementIter.next();
if (shouldNest(element)) {
serializeSubject(element);
} else {
xml.startElement(RDFVocabularyAdapter.RDF, "Description");
if (SwoopResourceImpl.isAnonymous(element)) {
xml.addAttribute(RDFVocabularyAdapter.RDF, "nodeID", (String)bnodes.get(element));
} else {
xml.addAttribute(RDFVocabularyAdapter.RDF, "about", new URI(element.getURI()));
}
xml.endElement();
}
}
return true;
}
protected boolean propertyLiteral(RDFNode value) throws ModelException, IOException, URISyntaxException {
if (value instanceof Literal) {
Literal valueLit = (Literal) value;
String datatype = valueLit.getDatatype();
String language = valueLit.getLanguage();
if (datatype != null) {
xml.addAttribute(RDFVocabularyAdapter.RDF, "datatype", new URI(datatype));
} else if (language != null) {
xml.addAttribute(null, "xml:lang", language);
}
xml.writeData(valueLit.getLabel());
return true;
}
return false;
}
protected boolean propertyNested(RDFNode value) throws IOException, OWLException {
if (!(value instanceof Resource) || !shouldNest(value)) {
return false;
}
serializeSubject((Resource) value);
return true;
}
protected boolean propertyReference(RDFNode value) throws URISyntaxException, ModelException, IOException {
if ((value instanceof Resource) && !SwoopResourceImpl.isAnonymous(value)) {
URI valueURI = new URI(((Resource)value).getURI());
xml.addAttribute(RDFVocabularyAdapter.RDF, "resource", valueURI);
return true;
}
return false;
}
protected boolean propertyXMLLiteral(RDFNode value) {
return false;
}
private void reset() {
bnodes = new HashMap();
bnodeCount = new HashMap();
namespaces = new HashSet();
rendered = new HashSet();
level = 0;
}
public void serializeAll() throws IOException, OWLException {
serializeAll(null);
}
public void serializeAll(String comment) throws IOException, OWLException {
boolean started = false;
for (Iterator subjectIter = subjects.keySet().iterator(); subjectIter.hasNext();) {
Resource subject = (Resource) subjectIter.next();
if (!started && !rendered.contains(subject)) {
if (comment != null) {
xml.writeComment(comment);
}
started = true;
}
serializeSubject(subject);
}
}
protected void serializeProperty(Resource property, RDFNode value) throws URISyntaxException, ModelException, IOException, OWLException {
URI propertyURI = new URI(property.getURI());
startElement(propertyURI);
if (!propertyList(value)
&& !propertyNested(value)
&& !propertyBNode(value)
&& !propertyReference(value)
&& !propertyXMLLiteral(value)
&& !propertyLiteral(value)) {
throw new OWLException("Could not serialize property "+property+" with value "+value);
}
xml.endElement();
}
public void serializeSubject(Resource subject) throws IOException, OWLException {
if (rendered.contains(subject) || (subjects.get(subject) == null)) {
return;
}
try {
level++;
rendered.add(subject);
Map propertyMap = (Map) subjects.get(subject);
Collection types = (Collection) propertyMap.get(genResource(rdf
.getInstanceOf()));
if (types == null) {
types = Collections.EMPTY_SET;
}
boolean started = false;
for (Iterator typeIter = types.iterator(); typeIter.hasNext();) {
RDFNode typeNode = (RDFNode) typeIter.next();
if (typeNode instanceof Resource
&& !SwoopResourceImpl.isAnonymous(typeNode)) {
started = true;
types.remove(typeNode);
startElement(new URI(((Resource) typeNode).getURI()));
break;
}
}
if (!started) {
xml.startElement(RDFVocabularyAdapter.RDF, "Description");
}
/* Add identifier */
if (SwoopResourceImpl.isAnonymous(subject)) {
String nodeid = (String) bnodes.get(subject);
Integer count = (Integer) bnodeCount.get(subject);
if ((nodeid != null) && (count != null)) {
if ((count.intValue() > 1) || ((count.intValue() == 1) && (level == 1))) {
xml.addAttribute(RDFVocabularyAdapter.RDF, "nodeID", nodeid);
}
}
} else {
xml.addAttribute(RDFVocabularyAdapter.RDF, "about", new URI(subject.getURI()));
}
/* Write short literal properties */
for (Iterator propIter = propertyMap.keySet().iterator(); propIter.hasNext();) {
Resource property = (Resource) propIter.next();
URI propertyURI = new URI(property.getURI());
Collection values = (Collection) propertyMap.get(property);
if (values.size() > 1) {
continue;
}
for (Iterator valueIter = values.iterator(); valueIter.hasNext();) {
RDFNode value = (RDFNode) valueIter.next();
if (value instanceof Literal) {
Literal valueLit = (Literal) value;
if (valueLit.getDatatype()==null
&& valueLit.getLanguage()==null
&& valueLit.toString().length() < 40) {
xml.addAttribute(qnames.findPrefixURI(propertyURI),
qnames.findLocal(propertyURI), valueLit.toString());
values.remove(valueLit);
break;
}
}
}
}
/* Write all properties */
for (Iterator propIter = propertyMap.keySet().iterator(); propIter.hasNext();) {
Resource property = (Resource) propIter.next();
Collection values = (Collection) propertyMap.get(property);
for (Iterator valueIter = values.iterator(); valueIter.hasNext();) {
RDFNode value = (RDFNode) valueIter.next();
serializeProperty(property, value);
}
}
xml.endElement();
} catch (URISyntaxException e) {
throw new OWLException(e);
} catch (ModelException e) {
throw new OWLException(e);
} finally {
level--;
}
}
public void serializeSubjects(Collection subjects) throws IOException, OWLException {
serializeSubjects(subjects, null);
}
public void serializeSubjects(Collection subjects, String comment) throws IOException, OWLException {
boolean started = false;
for (Iterator subjectIter = subjects.iterator(); subjectIter.hasNext();) {
Resource subject = (Resource) subjectIter.next();
if (!started && !rendered.contains(subject)) {
if (comment != null) {
xml.writeComment(comment);
}
started = true;
}
serializeSubject(subject);
}
}
public void setMaxLevel(int level) {
maxLevel = level;
}
private boolean shouldNest(RDFNode node) {
if (node instanceof Resource)
return shouldNest((Resource) node);
return false;
}
private boolean shouldNest(Resource resource) {
if (rendered.contains(resource) || (subjects.get(resource) == null)) {
return false;
}
if (SwoopResourceImpl.isAnonymous(resource)) {
return true;
}
if (level >= maxLevel) {
return false;
}
return true;
}
public void startDocument() throws IOException, OWLException {
try {
collectInformation();
addNamespaces();
xml.addEntity("xsd", XMLSchemaSimpleDatatypeVocabulary.XS);
xml.startDocument();
xml.startElement(RDFVocabularyAdapter.RDF, "RDF");
} catch (ModelException e) {
throw new OWLException(e);
} catch (URISyntaxException e) {
throw new OWLException(e);
}
}
private void startElement(URI uri) throws IOException {
xml.startElement(qnames.findPrefixURI(uri), qnames.findLocal(uri));
}
public void endDocument() throws OWLException {
try {
xml.endDocument();
} catch (Exception e) {
throw new OWLException(e);
}
}
/**
* @param base
*/
public void setBase(URI base) {
this.base = base;
nodeComparator = new NodeComparator(base.toString());
xml.setBase(base);
}
}