-
-
Notifications
You must be signed in to change notification settings - Fork 266
/
XsltTransform.java
139 lines (125 loc) · 4.59 KB
/
XsltTransform.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package org.basex.query.func.xslt;
import static org.basex.query.QueryError.*;
import static org.basex.util.Token.*;
import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import org.basex.build.xml.*; // for CatalogWrapper
import org.basex.io.*;
import org.basex.io.out.*;
import org.basex.io.serial.*;
import org.basex.query.*;
import org.basex.query.value.item.*;
import org.basex.query.value.node.*;
import org.basex.util.*;
import org.basex.util.options.*;
/**
* Function implementation.
*
* @author BaseX Team 2005-22, BSD License
* @author Christian Gruen
* @author Liam Quin
*/
public class XsltTransform extends XsltFn {
/** XSLT Options. */
public static final class XsltOptions extends Options {
/** Cache flag. */
public static final BooleanOption CACHE = new BooleanOption("cache", false);
}
@Override
public Item item(final QueryContext qc, final InputInfo ii) throws QueryException {
try {
final Str result = (Str) transform(qc, true);
return new DBNode(new IOContent(result.string()));
} catch(final IOException ex) {
throw IOERR_X.get(info, ex);
}
}
/**
* Performs an XSL transformation.
* @param qc query context
* @param simple simple processing (no report generation)
* @return item (map or string)
* @throws QueryException query exception
*/
final Item transform(final QueryContext qc, final boolean simple) throws QueryException {
final IO in = read(0, qc), xsl = read(1, qc);
final Options opts = toOptions(2, new Options(), qc);
final XsltOptions xopts = toOptions(3, new XsltOptions(), qc);
final ArrayOutput result = new ArrayOutput();
final PrintStream errPS = System.err;
final ArrayOutput err = new ArrayOutput();
final XsltReport xr = simple ? null : new XsltReport(qc);
try {
// redirect errors
System.setErr(new PrintStream(err));
final StreamSource ss = xsl.streamSource();
final String key = xopts.get(XsltOptions.CACHE) ? ss.getSystemId() : null;
// retrieve new or cached templates object
Templates templates = key != null ? MAP.get(key) : null;
final URIResolver ur = CatalogWrapper.getURIResolver(qc.context.options);
if(templates == null) {
// no templates object cached: create new instance
final TransformerFactory tf = TransformerFactory.newInstance();
// assign catalog resolver (if defined)
if(ur != null) tf.setURIResolver(ur);
templates = tf.newTemplates(ss);
if(key != null) MAP.put(key, templates);
}
// create transformer, assign catalog resolver (if defined)
final Transformer tr = templates.newTransformer();
if(ur != null) tr.setURIResolver(ur);
// bind parameters
opts.free().forEach(tr::setParameter);
// do transformation and return result
if(simple) {
tr.transform(in.streamSource(), new StreamResult(result));
return Str.get(result.finish());
}
xr.register(tr);
tr.transform(in.streamSource(), new StreamResult(result));
xr.addMessage();
} catch(final IllegalArgumentException ex) {
// Saxon raises runtime exceptions for illegal parameters
if(simple) throw XSLT_ERROR_X.get(info, ex);
xr.addError(Str.get(Util.message(ex)));
} catch(final TransformerException ex) {
Util.debug(ex);
// catch transformation errors, throw them again or add them to report
byte[] error = trim(utf8(err.toArray(), Prop.ENCODING));
if(error.length == 0) {
Throwable th = ex;
while(th.getCause() != null) th = th.getCause();
error = token(th.getLocalizedMessage());
}
if(simple) throw XSLT_ERROR_X.get(info, error);
xr.addError(Str.get(error));
xr.addMessage();
} finally {
System.setErr(errPS);
}
xr.addResult(result.finish());
return xr.finish();
}
/**
* Returns an input reference (possibly cached) to the specified input.
* @param i index of argument
* @param qc query context
* @return item
* @throws QueryException query exception
*/
private IO read(final int i, final QueryContext qc) throws QueryException {
final Item item = toNodeOrAtomItem(i, qc);
if(item instanceof ANode) {
try {
final IO io = new IOContent(item.serialize(SerializerMode.NOINDENT.get()).finish());
io.name(string(((ANode) item).baseURI()));
return io;
} catch(final QueryIOException ex) {
throw ex.getCause(info);
}
}
if(item.type.isStringOrUntyped()) return toIO(toToken(item));
throw STRNOD_X_X.get(info, item.type, item);
}
}