/******************************************************************************* * Copyright (c) 2012 IBM Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v. 1.0 which accompanies this distribution. * * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Keith Wells - initial API and implementation * Sam Padgett - initial API and Implementation * Jim Conallen - initial API and implementation * *******************************************************************************/ package org.eclipse.lyo.samples.sharepoint.store; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.lyo.samples.sharepoint.core.IConstants; import org.eclipse.lyo.samples.sharepoint.store.ShareValue.ShareValueType; import org.eclipse.lyo.samples.sharepoint.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; public class XmlFormatter { private ShareResource resource = null; private Map<String,String> namespacePrefixes = new HashMap<String,String>(); private Map<String,Element> otherStatementIds = new HashMap<String,Element>(); private Map<String,Element> idElm = new HashMap<String,Element>(); private Map<String,Element> blankNodes = new HashMap<String,Element>(); private Document document; private static final String RDF_LI_PREFIX = "http://www.w3.org/1999/02/22-rdf-syntax-ns#_"; private static int RDF_LI_PREFIX_LEN = RDF_LI_PREFIX.length(); static public String formatResource(ShareResource resource, String rdfType ) throws ShareServerException { XmlFormatter formatter = new XmlFormatter(); return formatter.format(resource, rdfType); } public void addNamespacePrefix(String ns, String prefix) { this.namespacePrefixes.put(ns, prefix); } public String format(ShareResource resource, String rdfType ) throws ShareServerException { this.resource = resource; if( rdfType != null && !resource.isRdfType(rdfType) ) { throw new ShareServerException("Cannot format resource (" + resource.getUri() + ")to XML with type " + rdfType); } initPrefixes(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); factory.setNamespaceAware(true); try{ DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.newDocument(); Element rdf = document.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_PTERM_RDF); document.appendChild(rdf); rdf.setAttribute(IConstants.XML_BASE, resource.getUri()); String[] nlp; if( rdfType == null ){ nlp = new String[]{ IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_TERM_DESCRIPTION, IConstants.RDF_PREFIX }; } else { nlp = this.extractNsLocal(rdfType); } Element resRoot = document.createElementNS(nlp[0], nlp[2] + ':' + nlp[1] ); rdf.appendChild(resRoot); resRoot.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_ABOUT, resource.getUri()); List<ShareStatement> statements = resource.getStatements(); // find all reified statements in a first pass of statements. Looking for statement with type rdf:statement // these indicate a link with an anchor, so we should deal with it specially Iterator<ShareStatement> i = statements.iterator(); while( i.hasNext() ) { // rdfTypePredicate, rdfStatementPredicate, false, oslcResource.getResource()); ShareStatement statement = i.next(); String predicate = statement.getPredicate(); ShareValue object = statement.getObject(); if( IConstants.RDF_TYPE_STATEMENT.equals(object.stringValue()) && IConstants.RDF_TYPE.equals(predicate) ) { createOtherDescription(statement, resource, rdf); } if( object.isBlankNode() ) { // find what type this is pointing to. If none use rdf:Description Element elm = null; List<ShareStatement> stmts = resource.getStatements(object.stringValue(), IConstants.RDF_TYPE, null); if( stmts.isEmpty() ) { elm = document.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_PTERM_DESCRIPTION); } else { String typeUri = stmts.get(0).getObject().stringValue(); nlp = this.extractNsLocal(typeUri); elm = document.createElementNS(nlp[0], nlp[2] + ':' + nlp[1]); } this.blankNodes.put(statement.getObject().stringValue(), elm); } } //now get the rest of the statements for (ShareStatement statement : statements) { String subject = statement.getSubject(); String predicate = statement.getPredicate(); if( subject.equals(resource.getUri()) ) { // normal first level property String nsLocal[] = extractNsLocal(predicate); String ns = nsLocal[0]; String local = nsLocal[1]; String prefix = nsLocal[2]; Element elm = document.createElementNS(ns, prefix + ':' + local); resRoot.appendChild(elm); ShareValue value = statement.getObject(); setValue(elm, value); String predObj = extractPredObj(statement); Element descrElm = this.otherStatementIds.get(predObj); if( descrElm != null ) { String id = descrElm.getAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TERM_ABOUT); if( id.length()>1 ) { elm.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_PTERM_ID, id.substring(1)); } } } else if( statement.isBNode() ) { Element elm = this.blankNodes.get(subject); if( elm != null && !predicate.toString().equals(IConstants.RDF_TYPE) ) { Element elm2 = document.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_LI); elm.appendChild(elm2); ShareValue value = statement.getObject(); setValue(elm2, value); } } else { if( !isReifiedStatementProperty(predicate) ) { // now put it in the right description String strSubj = subject.toString(); String id = extractUrlFragment(strSubj); Element descrElm = this.idElm.get(id); if( descrElm != null ) { String[] nsLocal = extractNsLocal(predicate); String ns = nsLocal[0]; String local = nsLocal[1]; String prefix = nsLocal[2]; Element elm = document.createElementNS(ns, prefix + ':' + local); ShareValue value = statement.getObject(); setValue(elm, value); descrElm.appendChild(elm); } } } } // set namespaces and prefixes setNamespaces(rdf); return XmlUtils.prettyPrint(document); } catch( Exception e ) { throw new ShareServerException(e); } } private Element constructInlinedElement(String blankNodeSubject) throws ShareServerException { String type; List<ShareStatement> stmts = resource.getStatements(blankNodeSubject, IConstants.RDF_TYPE, null); if( stmts.isEmpty() ) { type = IConstants.RDF_TYPE_DESCRIPTION; } else { type = stmts.get(0).getObject().stringValue(); } String[] nlp = this.extractNsLocal(type); Element inlinedElm = document.createElementNS(nlp[0], nlp[2] + ':' + nlp[1]); stmts = resource.getStatements(blankNodeSubject, null, null); if( IConstants.RDF_TYPE_SEQ.equals(type) || IConstants.RDF_TYPE_SEQ.equals(type) ) { // we need to order all the children Collections.sort(stmts, new Comparator<ShareStatement>(){ @Override public int compare(ShareStatement s1, ShareStatement s2) { int i1 = 0; int i2 = 0; String p1 = s1.getPredicate(); if( p1.startsWith(RDF_LI_PREFIX) ) { try{ i1 = Integer.parseInt(p1.substring(RDF_LI_PREFIX_LEN) ); }catch( NumberFormatException e ){} } String p2 = s2.getPredicate(); if( p2.startsWith(RDF_LI_PREFIX) ) { try{ i2 = Integer.parseInt(p2.substring(RDF_LI_PREFIX_LEN) ); }catch( NumberFormatException e ){} } return i1-i2; }}); } for (ShareStatement statement : stmts) { String predicate = statement.getPredicate(); if( !IConstants.RDF_TYPE.equals(predicate) ) { nlp = extractNsLocal(predicate); if( IConstants.RDF_TYPE_SEQ.equals(type) || IConstants.RDF_TYPE_SEQ.equals(type) ) { Element childElm = document.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_LI ); setValue(childElm, statement.getObject()); inlinedElm.appendChild(childElm); } else { Element childElm = document.createElementNS(nlp[0], nlp[2] + ':' + nlp[1]); setValue(childElm, statement.getObject()); inlinedElm.appendChild(childElm); } } } return inlinedElm; } /** * * For the given URI a string array is returned with the namespace of the URI in the first * spot, the local value in the second and the namespace prefx to use in the third. * @param uri * @return */ private String[] extractNsLocal(String uri) { int pos = uri.lastIndexOf('#'); if( pos < 0 ) { pos = uri.lastIndexOf('/'); } String namespace = uri.substring(0,pos+1); String local = uri.substring(pos+1); String prefix = this.getPrefix(namespace); return new String[] { namespace, local, prefix }; } private void createOtherDescription(ShareStatement statementStatement, ShareResource resource, Element rdf) throws ShareServerException{ Document doc = rdf.getOwnerDocument(); // get subject and use to get all the other statement in the reification String subject = statementStatement.getSubject(); List<ShareStatement> preds = resource.getStatements(subject, IConstants.RDF_PREDICATE, null); if( preds.size() == 0 ) return; // nothing to do, not a full reification, maybe we could log this. ShareStatement stPred = preds.get(0); List<ShareStatement> objs = resource.getStatements(subject, IConstants.RDF_OBJECT, null); if( objs.size() == 0 ) return; // nothing to do, not a full reification, maybe we could log this. ShareStatement stObj = objs.get(0); Element otherDescrElm = doc.createElementNS(IConstants.RDF_NAMESPACE, IConstants.RDF_TYPE_PTERM_DESCRIPTION); rdf.appendChild(otherDescrElm); String id = extractUrlFragment(subject); otherDescrElm.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_ABOUT, '#' + id); String predObjStr = concatPredObj(stPred.getObject().stringValue(), stObj.getObject().stringValue() ); otherStatementIds.put(predObjStr, otherDescrElm); idElm.put(id, otherDescrElm); } private String extractUrlFragment(String uri) { int pos = uri.lastIndexOf('#'); if( pos > 0 ) { return uri.substring(pos+1); } return null; } private String concatPredObj(String p, String o ) { return '[' + p + ',' + o + ']'; } private String extractPredObj(ShareStatement statement){ String p = statement.getPredicate(); String o = statement.getObject().stringValue(); return concatPredObj(p, o); } private void setValue( Element elm, ShareValue value ) throws ShareServerException { if( value.getType() == ShareValueType.BLANK_NODE ) { Element inlinedElm = this.constructInlinedElement(value.stringValue()); elm.appendChild(inlinedElm); } else if( value.getType() == ShareValueType.URI ) { elm.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_RESOURCE, value.stringValue()); } else if( value.getType() == ShareValueType.STRING ) { elm.setTextContent( value.stringValue()); } else { String datatype = value.rdfDataType(); if( datatype != null ) { elm.setAttributeNS(IConstants.RDF_NAMESPACE, IConstants.RDF_PTERM_DATATYPE, datatype); } elm.setTextContent( value.stringValue()); } } private String getPrefix(String namespace) { String prefix = namespacePrefixes.get(namespace); if( prefix == null ) { // then find the next available prefix int i=0; String pre = "pr" + i; //$NON-NLS-1$ Collection<String> prefixes = namespacePrefixes.values(); while( prefixes.contains(pre) ) { i++; pre = "pr" + i; //$NON-NLS-1$ } namespacePrefixes.put(namespace, pre); prefix = pre; } return prefix; } private void setNamespaces(Element root) { Set<String> namespaces = namespacePrefixes.keySet(); for (String namespace : namespaces) { root.setAttribute( IConstants.XMLNS + ':' + namespacePrefixes.get(namespace), namespace); } } private void initPrefixes() { namespacePrefixes.put(IConstants.RDF_NAMESPACE, IConstants.RDF_PREFIX); namespacePrefixes.put(IConstants.OSLC_NAMESPACE, IConstants.OSLC_PREFIX); namespacePrefixes.put(IConstants.DCTERMS_NAMESPACE, IConstants.DCTERMS_PREFIX); namespacePrefixes.put(IConstants.SHARE_NAMESPACE, IConstants.SHARE_PREFIX); } static private List<String> reifiedStatementProperties = null; static { reifiedStatementProperties = new ArrayList<String>(); reifiedStatementProperties.add(IConstants.RDF_STATEMENT); reifiedStatementProperties.add(IConstants.RDF_SUBJECT); reifiedStatementProperties.add(IConstants.RDF_PREDICATE); reifiedStatementProperties.add(IConstants.RDF_OBJECT); reifiedStatementProperties.add(IConstants.RDF_TYPE); } static public boolean isReifiedStatementProperty(String uri) { return reifiedStatementProperties.contains(uri); } }