336 lines
16 KiB
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() );
|
|
}
|
|
|
|
}
|