Project

General

Profile

1
package eu.dnetlib.data.collective.transformation.engine.core;
2

    
3
import java.io.Reader;
4
import java.io.StringReader;
5
import java.io.StringWriter;
6
import java.util.*;
7
import javax.xml.transform.*;
8
import javax.xml.transform.stream.StreamResult;
9
import javax.xml.transform.stream.StreamSource;
10

    
11
import eu.dnetlib.data.collective.transformation.TransformationException;
12
import eu.dnetlib.data.collective.transformation.core.schema.SchemaInspector;
13
import eu.dnetlib.data.collective.transformation.rulelanguage.RuleLanguageParser;
14
import eu.dnetlib.data.collective.transformation.rulelanguage.Rules;
15
import eu.dnetlib.data.collective.transformation.utils.NamespaceContextImpl;
16
import net.sf.saxon.expr.instruct.TerminationException;
17
import net.sf.saxon.lib.FeatureKeys;
18
import org.apache.commons.logging.Log;
19
import org.apache.commons.logging.LogFactory;
20
import org.dom4j.*;
21
import org.dom4j.io.SAXReader;
22
import org.springframework.core.io.Resource;
23

    
24
// import net.sf.saxon.event.MessageWarner;
25

    
26
/**
27
 * @author jochen
28
 */
29
public class TransformationImpl implements ITransformation {
30

    
31
	private static final String rootElement = "record";
32
	private final Log log = LogFactory.getLog(TransformationImpl.class);
33
	protected RuleLanguageParser ruleLanguageParser;
34
	private Document xslDoc;
35
	private SAXReader reader = new SAXReader();
36
	private Transformer transformer;
37
	private Transformer transformerFailed;
38
	private StylesheetBuilder stylesheetBuilder;
39
	// cache static transformation results, valid for one transformation job
40
	private Map<String, String> staticResults = new LinkedHashMap<String, String>();
41
	private Map<String, String> jobConstantMap = new HashMap<String, String>();
42
	//	private MessageWarner mw = new MessageWarner();
43

    
44
	//@javax.annotation.Resource(name = "template")
45
	private Resource template;
46

    
47
	private Resource schema;
48

    
49
	private Source xsltSyntaxcheckFailed;
50
	//@Autowired
51
	private TransformerFactory saxonTransformerFactory;
52

    
53
	public TransformationImpl(TransformerFactory saxonTransformerFactory){
54
		this.saxonTransformerFactory = saxonTransformerFactory;
55
	}
56

    
57
	/**
58
	 * initializes the transformation with the underlying XSL-template
59
	 */
60
	public void init() {
61
		try {
62
			xslDoc = reader.read(template.getInputStream());
63
			Resource xslResource = template.createRelative(XSLSyntaxcheckfailed);
64
			String systemId = xslResource.getURL().toExternalForm();
65
			xsltSyntaxcheckFailed = new StreamSource(xslResource.getInputStream(), systemId);
66

    
67
		} catch (Throwable e) {
68
			log.error("cannot initialize this transformation.", e);
69
			throw new IllegalStateException(e);
70
		}
71

    
72
	}
73

    
74
	public void addJobConstant(String aKey, String aValue) {
75
		this.jobConstantMap.put(aKey, aValue);
76
	}
77

    
78
	/**
79
	 * creates a new Transformer object using a stylesheet based on the transformation rules
80
	 */
81
	public void configureTransformation() throws TransformerConfigurationException {
82
		saxonTransformerFactory.setAttribute(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, Boolean.TRUE);
83

    
84
		Templates templates = null;
85
		if (this.ruleLanguageParser.isXslStylesheet()) {
86
			templates = saxonTransformerFactory.newTemplates(new StreamSource(new StringReader(ruleLanguageParser.getXslStylesheet())));
87
		} else {
88
			templates = saxonTransformerFactory.newTemplates(new StreamSource(createStylesheet()));
89
		}
90

    
91
		transformer = templates.newTransformer();
92
		//((net.sf.saxon.Controller)transformer).setMessageEmitter(mw);
93
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
94
		transformer.setOutputProperty(OutputKeys.METHOD, "xml");
95
		transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
96

    
97
		Templates templateFailed = saxonTransformerFactory.newTemplates(xsltSyntaxcheckFailed);
98
		transformerFailed = templateFailed.newTransformer();
99
		//((net.sf.saxon.Controller)transformerFailed).setMessageEmitter(mw);
100
	}
101

    
102
	/* (non-Javadoc)
103
	 * @see eu.dnetlib.data.collective.transformation.engine.core.ITransformation#transformRecord(java.lang.String, int)
104
	 */
105
	public String transformRecord(String record, int index) throws TerminationException, TransformationException {
106
		try {
107
			StreamSource s = new StreamSource(new StringReader(record));
108
			StringWriter writer = new StringWriter();
109
			StreamResult r = new StreamResult(writer);
110
			transformer.setParameter("index", index);
111
			transformer.transform(s, r);
112
			return writer.toString();
113
		} catch (TerminationException e) {
114
			log.debug(e.getLocalizedMessage());
115
			throw e;
116
		} catch (TransformerException e) {
117
			log.error(e);
118
			throw new TransformationException(e);
119
		}
120
	}
121

    
122
	public String transformRecord(String record, Map<String, String> parameters) throws TerminationException, TransformationException {
123
		try {
124
			StreamSource s = new StreamSource(new StringReader(record));
125
			StringWriter writer = new StringWriter();
126
			StreamResult r = new StreamResult(writer);
127
			for (String key : parameters.keySet()) {
128
				log.debug(String.format("Add (%s, %s) ", key, parameters.get(key)));
129
				transformer.setParameter(key, parameters.get(key));
130
			}
131
			transformer.transform(s, r);
132
			return writer.toString();
133
		} catch (TerminationException e) {
134
			log.debug(e.getLocalizedMessage());
135
			throw e;
136
		} catch (TransformerException e) {
137
			log.error(e);
138
			throw new TransformationException(e);
139
		}
140
	}
141

    
142
	public String transformRecord(String record, String stylesheetName) throws TransformationException {
143
		if (!stylesheetName.equals(XSLSyntaxcheckfailed))
144
			throw new IllegalArgumentException("in TransformationImpl: stylesheetname " + stylesheetName + " is unsupported!");
145
		try {
146
			StreamSource s = new StreamSource(new StringReader(record));
147
			StringWriter w = new StringWriter();
148
			StreamResult r = new StreamResult(w);
149
			transformerFailed.transform(s, r);
150
			return w.toString();
151
		} catch (TransformerException e) {
152
			log.error(e);
153
			throw new TransformationException(e);
154
		}
155
	}
156

    
157
	public String dumpStylesheet() {
158
		return xslDoc.asXML();
159

    
160
		//		StringWriter writer = new StringWriter();
161
		//		try {
162
		//			Transformer tXsl = transformer; //.newTransformer();
163
		//			tXsl.setOutputProperty(OutputKeys.INDENT, "yes");
164
		//			tXsl.setOutputProperty(OutputKeys.METHOD, "xml");
165
		//			tXsl.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
166
		//
167
		//		StreamResult r = new StreamResult(writer);
168
		//		Source s = new StreamSource(new StringReader(xslDoc.asXML()));
169
		//			tXsl.transform(s, r);
170
		//		} catch (TransformerException e) {
171
		//			// TODO Auto-generated catch block
172
		//			e.printStackTrace();
173
		//		}
174
		//		return writer.toString();
175
	}
176

    
177
	/**
178
	 * @return the resource to access the XSL template
179
	 */
180
	public Resource getTemplate() {
181
		return template;
182
	}
183

    
184
	/**
185
	 * sets the XSL template
186
	 *
187
	 * @param template - resource to access the XSL template
188
	 */
189
	public void setTemplate(Resource template) {
190
		this.template = template;
191
	}
192

    
193
	public RuleLanguageParser getRuleLanguageParser() {
194
		return ruleLanguageParser;
195
	}
196

    
197
	public void setRuleLanguageParser(RuleLanguageParser ruleLanguageParser) {
198
		this.ruleLanguageParser = ruleLanguageParser;
199
	}
200

    
201
	/**
202
	 * @return the stylesheetBuilder
203
	 */
204
	public StylesheetBuilder getStylesheetBuilder() {
205
		return stylesheetBuilder;
206
	}
207

    
208
	/**
209
	 * @param stylesheetBuilder the stylesheetBuilder to set
210
	 */
211
	public void setStylesheetBuilder(StylesheetBuilder stylesheetBuilder) {
212
		this.stylesheetBuilder = stylesheetBuilder;
213
	}
214

    
215
	/**
216
	 * @return the transformation rules as String object
217
	 */
218
	protected String getTransformationRules() {
219
		// add job-properties to the rules as variables
220
		for (String key : jobConstantMap.keySet()) {
221
			Rules r = new Rules();
222
			r.setVariable(key);
223
			r.setConstant("'" + jobConstantMap.get(key) + "'");
224
			ruleLanguageParser.getVariableMappingRules().put(JOBCONST_DATASINKID, r);
225
		}
226
		if (this.stylesheetBuilder == null) {
227
			// create DMF compliant stylesheet builder
228
			this.stylesheetBuilder = new StylesheetBuilder(saxonTransformerFactory);
229
			this.stylesheetBuilder.setRuleLanguageParser(this.ruleLanguageParser);
230
			NamespaceContextImpl namespaceContext = new NamespaceContextImpl();
231
			for (String prefix : ruleLanguageParser.getNamespaceDeclarations().keySet()) {
232
				namespaceContext.addNamespace(prefix, ruleLanguageParser.getNamespaceDeclarations().get(prefix));
233
			}
234
			SchemaInspector inspector = new SchemaInspector();
235
			try {
236
				inspector.inspect(this.schema.getURL(), rootElement);
237
			} catch (Exception e) {
238
				throw new IllegalStateException(e);
239
			}
240
			this.stylesheetBuilder.setNamespaceContext(namespaceContext);
241
			this.stylesheetBuilder.setSchemaInspector(inspector);
242
		}
243
		return this.stylesheetBuilder.createTemplate();
244
	}
245

    
246
	/**
247
	 * creates a stylesheet from transformation rules;
248
	 * <p>don't call this method multiple times, unless transformation configuration changes, then re-init and configure transformation</p>
249
	 *
250
	 * @return the stylesheet
251
	 */
252
	private Reader createStylesheet() {
253
		try {
254
			Document rulesDoc = DocumentHelper.parseText(getTransformationRules());
255
			for (String key : this.ruleLanguageParser.getNamespaceDeclarations().keySet()) {
256
				xslDoc.getRootElement().addNamespace(key, this.ruleLanguageParser.getNamespaceDeclarations().get(key));
257
			}
258
			@SuppressWarnings("unchecked")
259
			List<Node> nodes = rulesDoc.getRootElement().selectNodes("//xsl:template");
260

    
261
			@SuppressWarnings("unchecked")
262
			List<Node> varNodes = rulesDoc.getRootElement().selectNodes("/templateroot/xsl:param");
263
			for (Node node : varNodes) {
264
				xslDoc.getRootElement().add(((Element) node).detach());
265
			}
266

    
267
			//			xslDoc.getRootElement().add(rulesDoc.getRootElement().selectSingleNode("//xsl:param[@name='var1']").detach());
268
			for (Node node : nodes) {
269
				xslDoc.getRootElement().add(((Element) node).detach()); //  (rulesDoc.getRootElement().aget);
270
			}
271
		} catch (DocumentException e) {
272
			log.error("error in creating stylesheet: " + e);
273
			throw new IllegalStateException(e);
274
		}
275
		String xsl = xslDoc.asXML();
276
		log.debug(xsl);
277
		return new StringReader(xsl);
278
	}
279

    
280
	/**
281
	 * @return the schema
282
	 */
283
	public Resource getSchema() {
284
		return schema;
285
	}
286

    
287
	/**
288
	 * @param schema the schema to set
289
	 */
290
	public void setSchema(Resource schema) {
291
		this.schema = schema;
292
	}
293

    
294
	@Override
295
	public Map<String, String> getStaticTransformationResults() {
296
		return this.staticResults;
297
	}
298

    
299
	@Override
300
	public Map<String, String> getJobProperties() {
301
		// TODO Auto-generated method stub
302
		return this.jobConstantMap;
303
	}
304

    
305
	@Override
306
	public Properties getLogInformation() {
307
		// TODO Auto-generated method stub
308
		return null;
309
	}
310

    
311
}
(3-3/3)