PowsyblWriter.java
/**
* Copyright (c) 2017-2018, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.triplestore.impl.rdf4j;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import org.eclipse.rdf4j.common.xml.XMLUtil;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.util.Literals;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.model.vocabulary.XSD;
import org.eclipse.rdf4j.rio.RDFHandlerException;
import org.eclipse.rdf4j.rio.rdfxml.RDFXMLWriter;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
*/
public class PowsyblWriter extends RDFXMLWriter {
private String lastObjNamespace;
private String lastObjLocalName;
public PowsyblWriter(OutputStream out) {
super(out);
}
public PowsyblWriter(Writer writer) {
super(writer);
}
@Override
public void handleStatement(Statement st) {
checkWritingStarted();
Resource subj = st.getSubject();
IRI pred = st.getPredicate();
Value obj = st.getObject();
// Verify that an XML namespace-qualified name can be created for the predicate
String predString = pred.toString();
int predSplitIdx = XMLUtil.findURISplitIndex(predString);
if (predSplitIdx == -1) {
throw new RDFHandlerException("Unable to create XML namespace-qualified name for predicate: " + predString);
}
String predNamespace = predString.substring(0, predSplitIdx);
String predLocalName = predString.substring(predSplitIdx);
try {
if (!headerWritten) {
writeHeader();
}
// SUBJECT
if (!subj.equals(lastWrittenSubject)) {
writeNewSubject(subj, obj, st.getContext().stringValue());
} else {
writeLastSubject(obj, predNamespace, predLocalName);
}
// Don't write </rdf:Description> yet, maybe the next statement
// has the same subject.
} catch (IOException e) {
throw new RDFHandlerException(e);
}
}
private void writeNewSubject(Resource subj, Value obj, String ctxt) throws IOException {
flushPendingStatements();
String objString = obj.toString();
int objSplitIdx = XMLUtil.findURISplitIndex(objString);
if (objSplitIdx == -1) {
throw new RDFHandlerException("Unable to create XML namespace-qualified name for predicate: " + objString);
}
String objNamespace = objString.substring(0, objSplitIdx);
String objLocalName = objString.substring(objSplitIdx);
// Write new subject:
writeNewLine();
writeStartOfStartTag(objNamespace, objLocalName);
// FIXME This is hard-coded logic for processing CGMES data
IRI uri = (IRI) subj;
String attName = "ID";
String value = uri.toString();
String prefix = namespaceTable.get(uri.getNamespace());
if (uri.getNamespace().equals("urn:uuid:")) {
if (objLocalName.equals("FullModel")) {
attName = "about";
} else {
value = "_" + uri.getLocalName();
}
}
if (prefix != null && prefix.equals("data")) {
if (ctxt.contains("_SSH_")
|| ctxt.contains("_DY_") && objLocalName.equals("EnergyConsumer")
|| ctxt.contains("_TP_") && !objLocalName.equals("TopologicalNode")) {
attName = "about";
value = "#" + uri.getLocalName();
} else {
value = uri.getLocalName();
}
}
writeAttribute(RDF.NAMESPACE, attName, value);
writeEndOfStartTag();
writeNewLine();
lastWrittenSubject = subj;
lastObjNamespace = objNamespace;
lastObjLocalName = objLocalName;
}
private void writeLastSubject(Value obj, String predNamespace, String predLocalName) throws IOException {
// PREDICATE
writeIndent();
writeStartOfStartTag(predNamespace, predLocalName);
// OBJECT
if (obj instanceof Resource) {
writeResource(obj);
} else if (obj instanceof Literal) {
writeLiteral(obj);
writeEndTag(predNamespace, predLocalName);
}
writeNewLine();
}
private void writeResource(Value obj) throws IOException {
Resource objRes = (Resource) obj;
if (objRes instanceof BNode bNode) {
writeAttribute(RDF.NAMESPACE, "nodeID", getValidNodeId(bNode));
} else {
IRI uri = (IRI) objRes;
String value = uri.toString();
String prefix = namespaceTable.get(uri.getNamespace());
// FIXME review the use of hard-coded literal "data" for CGMES
if (prefix != null && prefix.equals("data")) {
value = "#" + uri.getLocalName();
}
writeAttribute(RDF.NAMESPACE, "resource", value);
}
writeEndOfEmptyTag();
}
private void writeLiteral(Value obj) throws IOException {
Literal objLit = (Literal) obj;
// datatype attribute
boolean isXMLLiteral = false;
// language attribute
if (Literals.isLanguageLiteral(objLit)) {
writeAttribute("xml:lang", objLit.getLanguage().orElse(""));
} else {
IRI datatype = objLit.getDatatype();
// Check if datatype is rdf:XMLLiteral
isXMLLiteral = datatype.equals(RDF.XMLLITERAL);
if (isXMLLiteral) {
writeAttribute(RDF.NAMESPACE, "parseType", "Literal");
} else if (!datatype.equals(XSD.STRING)) {
writeAttribute(RDF.NAMESPACE, "datatype", datatype.toString());
}
}
writeEndOfStartTag();
// label
if (isXMLLiteral) {
// Write XML literal as plain XML
writer.write(objLit.getLabel());
} else {
writeCharacterData(objLit.getLabel());
}
}
@Override
protected void flushPendingStatements()
throws IOException {
if (lastWrittenSubject != null) {
// The last statement still has to be closed:
writeEndTag(lastObjNamespace, lastObjLocalName);
lastWrittenSubject = null;
lastObjNamespace = null;
lastObjLocalName = null;
}
}
}