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
|
}
|