/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.dmn.backend.marshalling.v1_1.xstream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringReader; import java.io.Writer; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.stream.StreamResult; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.core.ClassLoaderReference; import com.thoughtworks.xstream.io.xml.AbstractPullReader; import com.thoughtworks.xstream.io.xml.QNameMap; import com.thoughtworks.xstream.io.xml.StaxDriver; import com.thoughtworks.xstream.io.xml.StaxWriter; import org.kie.dmn.api.marshalling.v1_1.DMNExtensionRegister; import org.kie.dmn.api.marshalling.v1_1.DMNMarshaller; import org.kie.dmn.backend.marshalling.CustomStaxReader; import org.kie.dmn.backend.marshalling.CustomStaxWriter; import org.kie.dmn.model.v1_1.Artifact; import org.kie.dmn.model.v1_1.Association; import org.kie.dmn.model.v1_1.AuthorityRequirement; import org.kie.dmn.model.v1_1.Binding; import org.kie.dmn.model.v1_1.BusinessContextElement; import org.kie.dmn.model.v1_1.BusinessKnowledgeModel; import org.kie.dmn.model.v1_1.Context; import org.kie.dmn.model.v1_1.ContextEntry; import org.kie.dmn.model.v1_1.DMNElement; import org.kie.dmn.model.v1_1.DMNElementReference; import org.kie.dmn.model.v1_1.Decision; import org.kie.dmn.model.v1_1.DecisionRule; import org.kie.dmn.model.v1_1.DecisionService; import org.kie.dmn.model.v1_1.DecisionTable; import org.kie.dmn.model.v1_1.Definitions; import org.kie.dmn.model.v1_1.ElementCollection; import org.kie.dmn.model.v1_1.Expression; import org.kie.dmn.model.v1_1.FunctionDefinition; import org.kie.dmn.model.v1_1.Import; import org.kie.dmn.model.v1_1.ImportedValues; import org.kie.dmn.model.v1_1.InformationItem; import org.kie.dmn.model.v1_1.InformationRequirement; import org.kie.dmn.model.v1_1.InputClause; import org.kie.dmn.model.v1_1.InputData; import org.kie.dmn.model.v1_1.Invocation; import org.kie.dmn.model.v1_1.ItemDefinition; import org.kie.dmn.model.v1_1.KnowledgeRequirement; import org.kie.dmn.model.v1_1.KnowledgeSource; import org.kie.dmn.model.v1_1.LiteralExpression; import org.kie.dmn.model.v1_1.NamedElement; import org.kie.dmn.model.v1_1.OrganizationUnit; import org.kie.dmn.model.v1_1.OutputClause; import org.kie.dmn.model.v1_1.PerformanceIndicator; import org.kie.dmn.model.v1_1.Relation; import org.kie.dmn.model.v1_1.TextAnnotation; import org.kie.dmn.model.v1_1.UnaryTests; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.InputSource; public class XStreamMarshaller implements DMNMarshaller { private static Logger logger = LoggerFactory.getLogger( XStreamMarshaller.class ); private List<DMNExtensionRegister> extensionRegisters = new ArrayList<>(); private static StaxDriver staxDriver; static { QNameMap qmap = new QNameMap(); // TODO what-if a given file is setting the default namespace to something else than DMN ? qmap.setDefaultNamespace("http://www.omg.org/spec/DMN/20151101/dmn.xsd"); // qmap.registerMapping(new javax.xml.namespace.QName("http://www.omg.org/spec/FEEL/20140401", "feel", "feel"), "dddecision"); // FIXME still not sure how to output non-used ns like 'feel:' staxDriver = new StaxDriver() { public AbstractPullReader createStaxReader(XMLStreamReader in) { return new CustomStaxReader(getQnameMap(), in); } public StaxWriter createStaxWriter(XMLStreamWriter out, boolean writeStartEndDocument) throws XMLStreamException { return new CustomStaxWriter(getQnameMap(), out, writeStartEndDocument, isRepairingNamespace(), getNameCoder()); } }; staxDriver.setQnameMap(qmap); staxDriver.setRepairingNamespace(false); } public XStreamMarshaller() { } public XStreamMarshaller (List<DMNExtensionRegister> extensionRegisters) { this.extensionRegisters.addAll(extensionRegisters); } @Override public Definitions unmarshal(String xml) { return unmarshal( new StringReader( xml ) ); } @Override public Definitions unmarshal(Reader isr) { try { XStream xStream = newXStream(); Definitions def = (Definitions) xStream.fromXML( isr ); return def; } catch ( Exception e ) { logger.error( "Error unmarshalling DMN model from reader.", e ); } return null; } @Override public String marshal(Object o) { try { XStream xStream = newXStream(); String xml = xStream.toXML(o); return xml; } catch ( Exception e ) { logger.error( "Error marshalling DMN model to XML.", e ); } return null; } @Override public void marshal(Object o, Writer out) { try { out.write( marshal( o ) ); } catch ( Exception e ) { logger.error( "Error marshalling DMN model to XML.", e ); } } /** * Unnecessary as was a tentative UTF-8 preamble output but still not working. */ @Deprecated public void marshalMarshall(Object o) { marshalMarshall(o, System.out); } /** * Unnecessary as was a tentative UTF-8 preamble output but still not working. */ @Deprecated public void marshalMarshall(Object o, OutputStream out) { try { XStream xStream = newXStream(); out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n".getBytes()); OutputStreamWriter ows = new OutputStreamWriter(out, "UTF-8"); xStream.toXML(o, ows); } catch ( Exception e ) { e.printStackTrace(); } } /** * Unnecessary as the stax driver custom anon as static definition is embedding the indentation. */ @Deprecated public static String formatXml(String xml){ try{ Transformer serializer= SAXTransformerFactory.newInstance().newTransformer(); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); Source xmlSource=new SAXSource(new InputSource(new ByteArrayInputStream(xml.getBytes()))); StreamResult res = new StreamResult(new ByteArrayOutputStream()); serializer.transform(xmlSource, res); return new String(((ByteArrayOutputStream)res.getOutputStream()).toByteArray()); }catch(Exception e){ return xml; } } private XStream newXStream() { XStream xStream = new XStream( null, staxDriver, new ClassLoaderReference( Definitions.class.getClassLoader() ) ); xStream.alias( "artifact", Artifact.class ); xStream.alias( "definitions", Definitions.class ); xStream.alias( "inputData", InputData.class ); xStream.alias( "decision", Decision.class ); xStream.alias( "variable", InformationItem.class ); xStream.alias( "informationRequirement", InformationRequirement.class ); xStream.alias( "requiredInput", DMNElementReference.class ); xStream.alias( "literalExpression", LiteralExpression.class ); // TODO will need to remove dups and find missing element not captured? xStream.alias("DMNElement", DMNElement.class ); xStream.alias("allowedValues", UnaryTests.class ); xStream.alias("artifact", Artifact.class ); xStream.alias("association", Association.class ); xStream.alias("authorityRequirement", AuthorityRequirement.class ); xStream.alias("authorityRequirement", AuthorityRequirement.class ); xStream.alias("authorityRequirement", AuthorityRequirement.class ); xStream.alias("binding", Binding.class ); xStream.alias("businessContextElement", BusinessContextElement.class ); xStream.alias("businessKnowledgeModel", BusinessKnowledgeModel.class ); xStream.alias("column", InformationItem.class ); xStream.alias("context", Context.class ); xStream.alias("contextEntry", ContextEntry.class ); xStream.alias("decision", Decision.class ); xStream.alias("decisionMade", DMNElementReference.class ); xStream.alias("decisionMaker", DMNElementReference.class ); xStream.alias("decisionOwned", DMNElementReference.class ); xStream.alias("decisionOwner", DMNElementReference.class ); xStream.alias("decisionService", DecisionService.class ); xStream.alias("decisionTable", DecisionTable.class ); xStream.alias("defaultOutputEntry", LiteralExpression.class ); xStream.alias("definitions", Definitions.class ); xStream.alias("drgElement", DMNElementReference.class ); // xStream.alias("drgElement", DRGElement.class ); ambiguity, also referring to top-level xsd element just under xsd root. xStream.alias("elementCollection", ElementCollection.class ); xStream.alias("elementCollection", ElementCollection.class ); xStream.alias("encapsulatedDecision", DMNElementReference.class ); xStream.alias("encapsulatedLogic", FunctionDefinition.class ); xStream.alias("expression", Expression.class ); xStream.alias("formalParameter", InformationItem.class ); xStream.alias("functionDefinition", FunctionDefinition.class ); xStream.alias("impactedPerformanceIndicator", DMNElementReference.class ); xStream.alias("impactingDecision", DMNElementReference.class ); xStream.alias("import", Import.class ); xStream.alias("import", Import.class ); xStream.alias("importedElement", String.class ); // TODO where? xStream.alias("importedValues", ImportedValues.class ); xStream.alias("informationItem", InformationItem.class ); xStream.alias("informationRequirement", InformationRequirement.class ); xStream.alias("input", InputClause.class ); xStream.alias("inputData", DMNElementReference.class ); xStream.alias("inputData", InputData.class ); xStream.alias("inputDecision", DMNElementReference.class ); xStream.alias("inputEntry", UnaryTests.class ); xStream.alias("inputExpression", LiteralExpression.class ); xStream.alias("inputValues", UnaryTests.class ); xStream.alias("invocation", Invocation.class ); xStream.alias("itemComponent", ItemDefinition.class ); xStream.alias("itemDefinition", ItemDefinition.class ); xStream.alias("itemDefinition", ItemDefinition.class ); xStream.alias("knowledgeRequirement", KnowledgeRequirement.class ); xStream.alias("knowledgeRequirement", KnowledgeRequirement.class ); xStream.alias("knowledgeSource", KnowledgeSource.class ); xStream.alias("literalExpression", LiteralExpression.class ); xStream.alias("namedElement", NamedElement.class ); xStream.alias("organizationUnit", OrganizationUnit.class ); xStream.alias("output", OutputClause.class ); xStream.alias("outputDecision", DMNElementReference.class ); xStream.alias("outputEntry", LiteralExpression.class ); xStream.alias("outputValues", UnaryTests.class ); xStream.alias("owner", DMNElementReference.class ); xStream.alias("parameter", InformationItem.class ); xStream.alias("performanceIndicator", PerformanceIndicator.class ); xStream.alias("relation", Relation.class ); xStream.alias("requiredAuthority", DMNElementReference.class ); xStream.alias("requiredDecision", DMNElementReference.class ); xStream.alias("requiredDecision", DMNElementReference.class ); xStream.alias("requiredInput", DMNElementReference.class ); xStream.alias("requiredInput", DMNElementReference.class ); xStream.alias("requiredKnowledge", DMNElementReference.class ); xStream.alias("rule", DecisionRule.class ); xStream.alias("sourceRef", DMNElementReference.class ); xStream.alias("supportedObjective", DMNElementReference.class ); xStream.alias("targetRef", DMNElementReference.class ); xStream.alias("textAnnotation", TextAnnotation.class ); xStream.alias("type", String.class ); // TODO where? xStream.alias("typeRef", QName.class ); xStream.alias("usingProcess", DMNElementReference.class ); xStream.alias("usingTask", DMNElementReference.class ); xStream.alias("variable", InformationItem.class ); xStream.alias("variable", InformationItem.class ); xStream.alias("variable", InformationItem.class ); xStream.alias("variable", InformationItem.class ); // xStream.alias("allowedAnswers", xsd:string.class ); // xStream.alias("description", xsd:string.class ); // xStream.alias("question", xsd:string.class ); // xStream.alias("text", xsd:string.class ); // xStream.alias("text", xsd:string.class ); // xStream.alias("text", xsd:string.class ); xStream.alias("row", org.kie.dmn.model.v1_1.List.class ); xStream.alias("list", org.kie.dmn.model.v1_1.List.class ); xStream.alias("extensionElements", DMNElement.ExtensionElements.class); // Manually imported TEXT = String xStream.alias( LiteralExpressionConverter.TEXT, String.class ); xStream.alias( TextAnnotationConverter.TEXT, String.class ); xStream.alias( UnaryTestsConverter.TEXT, String.class ); xStream.alias( DecisionConverter.QUESTION, String.class ); xStream.alias( DecisionConverter.ALLOWED_ANSWERS, String.class ); xStream.alias( DMNElementConverter.DESCRIPTION, String.class ); xStream.registerConverter(new AssociationConverter( xStream ) ); xStream.registerConverter(new AuthorityRequirementConverter( xStream ) ); xStream.registerConverter(new BindingConverter( xStream ) ); xStream.registerConverter(new BusinessKnowledgeModelConverter( xStream ) ); xStream.registerConverter(new ContextConverter( xStream ) ); xStream.registerConverter(new ContextEntryConverter( xStream ) ); xStream.registerConverter(new DecisionConverter( xStream ) ); xStream.registerConverter(new DecisionRuleConverter( xStream ) ); xStream.registerConverter(new DecisionTableConverter( xStream ) ); xStream.registerConverter(new DefinitionsConverter( xStream ) ); xStream.registerConverter(new DMNElementReferenceConverter( xStream ) ); xStream.registerConverter(new FunctionDefinitionConverter( xStream ) ); xStream.registerConverter(new ImportConverter( xStream ) ); xStream.registerConverter(new ImportedValuesConverter( xStream ) ); xStream.registerConverter(new InformationItemConverter( xStream ) ); xStream.registerConverter(new InformationRequirementConverter( xStream ) ); xStream.registerConverter(new InputClauseConverter( xStream ) ); xStream.registerConverter(new InputDataConverter( xStream ) ); xStream.registerConverter(new InvocationConverter( xStream ) ); xStream.registerConverter(new ItemDefinitionConverter( xStream ) ); xStream.registerConverter(new KnowledgeRequirementConverter( xStream ) ); xStream.registerConverter(new KnowledgeSourceConverter( xStream ) ); xStream.registerConverter(new LiteralExpressionConverter( xStream ) ); xStream.registerConverter(new OrganizationUnitConverter( xStream ) ); xStream.registerConverter(new OutputClauseConverter( xStream ) ); xStream.registerConverter(new PerformanceIndicatorConverter( xStream ) ); xStream.registerConverter(new RelationConverter( xStream ) ); xStream.registerConverter(new TextAnnotationConverter( xStream ) ); xStream.registerConverter(new UnaryTestsConverter( xStream ) ); xStream.registerConverter(new QNameConverter()); xStream.registerConverter(new DMNListConverter( xStream ) ); xStream.registerConverter(new ElementCollectionConverter( xStream ) ); xStream.registerConverter(new ExtensionElementsConverter( xStream, extensionRegisters ) ); xStream.ignoreUnknownElements(); if(extensionRegisters != null && !extensionRegisters.isEmpty()) { for(DMNExtensionRegister extensionRegister : extensionRegisters) { extensionRegister.registerExtensionConverters(xStream); } } return xStream; } }