dnet-core/dnet-data-services/src/main/java/eu/dnetlib/data/collective/transformation/engine/core/StylesheetBuilder.java

336 lines
16 KiB
Java

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<String, Set<IRule>> ruleMapping = this.ruleLanguageParser.getElementMappingRules();
Map<String, IRule> variableRuleMapping = this.ruleLanguageParser.getVariableMappingRules();
Map<String, IRule> templateRuleMapping = this.ruleLanguageParser.getTemplateMappingRules();
Queue<String> templateQueue = new LinkedList<String>();
XslElement rootField = new XslElement(schemaInspector.getRootElement());
XslConstructor xslConstructor = new XslConstructor();
/**
* *
*/
// Iterator<String> 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<String, String> targetFieldTemplateMap = new LinkedHashMap<String, String>();
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() );
}
}