package eu.dnetlib.data.collective.transformation.engine.core; import java.io.StringReader; import java.io.StringWriter; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import java.util.Set; import javax.xml.namespace.NamespaceContext; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import eu.dnetlib.data.collective.transformation.core.schema.SchemaElement; import eu.dnetlib.data.collective.transformation.core.schema.SchemaInspector; import eu.dnetlib.data.collective.transformation.core.xsl.XslConstructor; import eu.dnetlib.data.collective.transformation.core.xsl.XsltConstants; import eu.dnetlib.data.collective.transformation.core.xsl.XslElement; import eu.dnetlib.data.collective.transformation.rulelanguage.IRule; import eu.dnetlib.data.collective.transformation.rulelanguage.RuleLanguageParser; import eu.dnetlib.data.collective.transformation.rulelanguage.Rules; import eu.dnetlib.data.collective.transformation.rulelanguage.util.Converter; /** * @author jochen * */ public class StylesheetBuilder { private static final Log log = LogFactory.getLog(StylesheetBuilder.class); private SchemaInspector schemaInspector; private RuleLanguageParser ruleLanguageParser; private NamespaceContext namespaceContext; // implicit rule for deleted records private final String elementNameIndicatingDeletedRecords = "header"; private final String attributeNameIndicatingDeletedRecords = "status"; private final String attributeValueIndicatingDeletedRecords = "deleted"; private final String elementNameAbout = "about"; public String createTemplate(){ if (schemaInspector == null || ruleLanguageParser == null || namespaceContext == null){ throw new IllegalStateException("StylesheetBuidler is not initialized with schemaInspector or ruleLanguageParser or namespaceContext."); } if (!schemaInspector.isInspected()){ throw new IllegalStateException("schemaInspector must first inspect in order to create a stylesheet."); } StringBuilder builder = new StringBuilder(); XslElement templateRoot = new XslElement("templateroot"); templateRoot.addBoundPrefix(XsltConstants.nsXsl); templateRoot.addAllBoundPrefixes(Converter.getBoundPrefixes(this.ruleLanguageParser.getNamespaceDeclarations())); XslElement template = new XslElement(XsltConstants.template); template.addAttribute("match", "/"); Map> ruleMapping = this.ruleLanguageParser.getElementMappingRules(); Map variableRuleMapping = this.ruleLanguageParser.getVariableMappingRules(); Map templateRuleMapping = this.ruleLanguageParser.getTemplateMappingRules(); Queue templateQueue = new LinkedList(); XslElement rootField = new XslElement(schemaInspector.getRootElement()); XslConstructor xslConstructor = new XslConstructor(); /** * * */ // Iterator keyIterator = ruleMapping.keySet().iterator(); // while(keyIterator.hasNext()){ // System.out.println("stylesheetbuilder: key: " + keyIterator.next()); // } /* * */ int templateCounter = 1; int standaloneTemplateCounter = 1; // write variables at the beginning of the templateRoot for (String variable: variableRuleMapping.keySet()){ IRule currentVariableRule = variableRuleMapping.get(variable); templateRoot.addEnclosedElements(xslConstructor.writeOutVariableRule((Rules)currentVariableRule)); } Map targetFieldTemplateMap = new LinkedHashMap(); for (String keyTemplate: templateRuleMapping.keySet()){ IRule currentTemplateRule = templateRuleMapping.get(keyTemplate); targetFieldTemplateMap.put(((Rules)currentTemplateRule).getFunctionCall().getParameters().get("elementName"), "templName" + standaloneTemplateCounter); templateRoot.addEnclosedElements(xslConstructor.writeOutRecursiveTemplate((Rules)currentTemplateRule , "templName" + (standaloneTemplateCounter++), this.ruleLanguageParser.getNamespaceDeclarations()).asXml()); } XslElement chooseField = new XslElement(XsltConstants.choose); XslElement whenField = new XslElement(XsltConstants.when, "test", "//" + this.elementNameIndicatingDeletedRecords + "/@" + this.attributeNameIndicatingDeletedRecords + "='" + this.attributeValueIndicatingDeletedRecords + "'"); XslElement otherwiseField = new XslElement(XsltConstants.otherwise); String templateAboutName = "applyAbout"; // write schema elements for (SchemaElement element: schemaInspector.getChildElements()){ if (!element.containsSimpleType()){ String complexTypeTemplateName = "apply" + (templateCounter++); XslElement complexTypeTemplate = new XslElement(XsltConstants.template, "name", complexTypeTemplateName); // will contain only other elements XslElement childField = new XslElement(XsltConstants.element, "name", element.getName()); if (element.getName().equals(this.elementNameIndicatingDeletedRecords)){ XslElement ifField = new XslElement(XsltConstants.ifCondition, "test", "//" + this.elementNameIndicatingDeletedRecords + "/@" + this.attributeNameIndicatingDeletedRecords); XslElement attributeField = new XslElement(XsltConstants.attribute, "name", this.attributeNameIndicatingDeletedRecords); XslElement valueofField = new XslElement(XsltConstants.valueOf, "select", "//" + this.elementNameIndicatingDeletedRecords + "/@" + this.attributeNameIndicatingDeletedRecords); attributeField.addEnclosedElements(valueofField.asXml()); ifField.addEnclosedElements(attributeField.asXml()); childField.addEnclosedElements(ifField.asXml()); } if (element.getName().equals(this.elementNameAbout)){ XslElement templateAbout = new XslElement(XsltConstants.template, "name", templateAboutName); templateAbout.addEnclosedElements(xslConstructor.writeOutApplyAbout()); templateQueue.add(templateAbout.asXml()); } for (SchemaElement childElement: element.getChildList()){ String currentKey = getPrefixedElementName(childElement); if (childElement.containsSimpleType()){ log.debug("currentKey: " + currentKey); if (ruleMapping.containsKey(currentKey)){ for (IRule currentRule: ruleMapping.get(currentKey)){ if (currentRule instanceof Rules){ log.debug(" has Set? " + ((Rules)currentRule).hasSet() ); log.debug(" has Condition? " + ((Rules)currentRule).hasCondition() ); if ( !((Rules)currentRule).hasCondition()){ if ( !((Rules)currentRule).hasSet() ){ if (currentRule.definesTemplateMatch()){ String templateName = ((Rules)currentRule).getTemplateMatch(); XslElement subTemplate = new XslElement(XsltConstants.template, "match", templateName); subTemplate.addEnclosedElements(xslConstructor.writeOutRuleCopy((Rules)currentRule, currentKey)); templateQueue.add(subTemplate.asXml()); childField.addEnclosedElements(xslConstructor.writeOutApplyTemplates(((Rules)currentRule).getProperties().getProperty("applyTemplateSelectExpression"))); }else{ childField.addEnclosedElements(xslConstructor.writeOutRule((Rules)currentRule, currentKey)); } }else{ childField.addEnclosedElements(xslConstructor.writeOutRuleComplex((Rules)currentRule, currentKey)); } }else{ // has condition if ( ((Rules)currentRule).getCondition().isPrimary((Rules)currentRule) && ((Rules)currentRule).getUniqueName().equals( ((Rules)currentRule).getCondition().getSecondaryRule().getUniqueName() ) ){ if ( ((Rules)currentRule).getCondition().getApplyExpression() != null ){ String templateName = "apply" + (templateCounter++); XslElement subTemplate = new XslElement(XsltConstants.template, "name", templateName); subTemplate.addEnclosedElements(xslConstructor.writeOutApplyConditionalTemplateChoose((Rules)currentRule)); templateQueue.add(subTemplate.asXml()); childField.addEnclosedElements(xslConstructor.writeOutCallTemplate(templateName)); }else{ // a condition with alternative rules for the same output elements childField.addEnclosedElements(xslConstructor.writeOutConditionalChoose((Rules) currentRule)); } }else if ( ! ((Rules)currentRule).getCondition().getPrimaryRule().getUniqueName().equals( ((Rules)currentRule).getCondition().getSecondaryRule().getUniqueName() ) ){ // a condition with alternative rules for distinct output elements if ( ((Rules)currentRule).getCondition().getApplyExpression() != null ){ // has apply expression String templateName = "apply" + (templateCounter++); XslElement subTemplate = new XslElement(XsltConstants.template, "name", templateName); subTemplate.addEnclosedElements(xslConstructor.writeOutApplyConditionalTemplateIf((Rules)currentRule, false)); templateQueue.add(subTemplate.asXml()); childField.addEnclosedElements(xslConstructor.writeOutCallTemplate(templateName)); }else{ childField.addEnclosedElements(xslConstructor.writeOutConditionalIf((Rules) currentRule)); } } } }else{ // only Rules instances are supported } } }else if (targetFieldTemplateMap.containsKey(currentKey)){ childField.addEnclosedElements(xslConstructor.writeOutCallTemplate(targetFieldTemplateMap.get(currentKey))); }else{ // no rule defined, check if element is mandatory if (childElement.isMandatory()){ XslElement emptyField = new XslElement(currentKey); childField.addEnclosedElements(emptyField.asXml()); } } }else{ // complex-type elements if (ruleMapping.containsKey(currentKey)){ for (IRule currentRule: ruleMapping.get(currentKey)){ if (currentRule instanceof Rules){ if ( !((Rules)currentRule).hasCondition() ){ log.debug("stylesheetbuilder.complexType NO CONDITION: " + childElement.getName()); childField.addEnclosedElements(xslConstructor.writeOutRuleComplex((Rules)currentRule, currentKey)); }else{ // log.debug("stylesheetbuilder.complexType HAS CONDITION: ONLY PARTLY IMPLEMENTED !!!!!!!!!!"); if ( ((Rules)currentRule).getCondition().isPrimary((Rules)currentRule) && ((Rules)currentRule).getUniqueName().equals( ((Rules)currentRule).getCondition().getSecondaryRule().getUniqueName() ) ){ // log.debug("condition: complex rule with same output elements"); if ( ((Rules)currentRule).getCondition().getApplyExpression() != null ){ log.debug("APPLY expression rules for complex-type elements NOT YET SUPPORTED"); // log.debug("complex rule with apply expression: NOT YET IMPLEMENTED !!!!!!!!!!!!"); }else{ // a condition with alternative rules for the same output elements childField.addEnclosedElements(xslConstructor.writeOutConditionalChooseComplex((Rules) currentRule)); } }else if ( ! ((Rules)currentRule).getCondition().getPrimaryRule().getUniqueName().equals( ((Rules)currentRule).getCondition().getSecondaryRule().getUniqueName() ) ){ log.debug("CURRENTLY UNSUPPORTED!!!"); // a condition with alternative rules for distinct output elements if ( ((Rules)currentRule).getCondition().getApplyExpression() != null ){ log.debug("APPLY expression rules for complex-type elements NOT YET SUPPORTED"); String templateName = "apply" + (templateCounter++); XslElement subTemplate = new XslElement(XsltConstants.template, "name", templateName); subTemplate.addEnclosedElements(xslConstructor.writeOutApplyConditionalTemplateIf((Rules)currentRule, true)); templateQueue.add(subTemplate.asXml()); childField.addEnclosedElements(xslConstructor.writeOutCallTemplate(templateName)); // // has apply expression // String templateName = "apply" + (templateCounter++); // XslElement subTemplate = new XslElement(XsltConstants.template, "name", templateName); // subTemplate.addEnclosedElements(xslConstructor.writeOutApplyConditionalTemplateIf((Rules)currentRule)); // templateQueue.add(subTemplate.asXml()); // childField.addEnclosedElements(xslConstructor.writeOutCallTemplate(templateName)); }else{ childField.addEnclosedElements(xslConstructor.writeOutConditionalIfComplex((Rules) currentRule)); } } } } } } } } if ( !(childField.isEmpty() && !element.isMandatory()) ){ complexTypeTemplate.addEnclosedElements(childField.asXml()); templateQueue.add(complexTypeTemplate.asXml()); if (element.getName().equals(this.elementNameIndicatingDeletedRecords)){ whenField.addEnclosedElements(xslConstructor.writeOutCallTemplate(complexTypeTemplateName)); } otherwiseField.addEnclosedElements(xslConstructor.writeOutCallTemplate(complexTypeTemplateName)); // rootField.addEnclosedElements(childField.asXml()); } } } chooseField.addEnclosedElements(whenField.asXml()); otherwiseField.addEnclosedElements(xslConstructor.writeOutCallTemplate(templateAboutName)); chooseField.addEnclosedElements(otherwiseField.asXml()); rootField.addEnclosedElements(chooseField.asXml()); template.addEnclosedElements(rootField.asXml()); templateRoot.addEnclosedElements(template.asXml()); // add sub-templates from queue for (String templateCode: templateQueue){ templateRoot.addEnclosedElements(templateCode); } builder.append(templateRoot.asXml()); log.debug(dumpStylesheetTemplate(builder.toString())); return builder.toString(); } String dumpStylesheetTemplate(String aTemplate){ StringWriter w = new StringWriter(); Source s = new StreamSource(new StringReader(aTemplate)); Result r = new StreamResult(w); Transformer t; try { t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.METHOD, "xml"); t.setOutputProperty(OutputKeys.INDENT, "yes"); t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); t.transform(s, r); } catch (Exception e) { log.fatal(e); } return w.toString(); } /** * @param schemaInspector the schemaInspector to set */ public void setSchemaInspector(SchemaInspector schemaInspector) { this.schemaInspector = schemaInspector; } /** * @return the schemaInspector */ public SchemaInspector getSchemaInspector() { return schemaInspector; } /** * @return the ruleLanguageParser */ public RuleLanguageParser getRuleLanguageParser() { return ruleLanguageParser; } /** * @param ruleLanguageParser the ruleLanguageParser to set */ public void setRuleLanguageParser(RuleLanguageParser ruleLanguageParser) { this.ruleLanguageParser = ruleLanguageParser; } /** * @param namespaceContext the namespaceContext to set */ public void setNamespaceContext(NamespaceContext namespaceContext) { this.namespaceContext = namespaceContext; } /** * @return the namespaceContext */ public NamespaceContext getNamespaceContext() { return namespaceContext; } private String getPrefixedElementName(SchemaElement aElement){ return ( this.namespaceContext.getPrefix(aElement.getTargetNamespace()) + ":" + aElement.getName() ); } }