1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.openehealth.ipf.commons.xml;
17
18 import com.saxonica.xqj.SaxonXQConnection;
19 import com.saxonica.xqj.SaxonXQDataSource;
20 import lombok.Getter;
21 import lombok.Setter;
22 import net.sf.saxon.Configuration;
23 import org.openehealth.ipf.commons.core.modules.api.Transmogrifier;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import javax.xml.namespace.QName;
28 import javax.xml.transform.Result;
29 import javax.xml.transform.Source;
30 import javax.xml.xquery.*;
31 import java.io.InputStream;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.concurrent.ConcurrentHashMap;
35 import java.util.concurrent.ConcurrentMap;
36
37
38
39
40
41
42
43
44
45 public class XqjTransmogrifier<T> extends AbstractCachingXmlProcessor<XQPreparedExpression> implements Transmogrifier<Source, T> {
46 private final static Logger LOG = LoggerFactory.getLogger(XqjTransmogrifier.class);
47
48 private static final ConcurrentMap<String, Loader<XQPreparedExpression>> XQUERY_CACHE = new ConcurrentHashMap<>();
49 private static final Configuration XQUERY_GLOBAL_CONFIG;
50 private static final SaxonXQDataSource DATA_SOURCE;
51 static {
52 XQUERY_GLOBAL_CONFIG = new Configuration();
53 XQUERY_GLOBAL_CONFIG.setURIResolver(new ClasspathUriResolver(XQUERY_GLOBAL_CONFIG.getURIResolver()));
54 DATA_SOURCE = new SaxonXQDataSource(XQUERY_GLOBAL_CONFIG);
55 }
56
57 private final Class<T> outputFormat;
58 private SaxonXQConnection connection;
59
60 @Getter @Setter private Map<String, Object> staticParams;
61
62
63 @SuppressWarnings("unchecked")
64 public XqjTransmogrifier() {
65 this((Class<T>) String.class);
66 }
67
68
69
70
71
72 public XqjTransmogrifier(Class<T> outputFormat) {
73 this(outputFormat, null, null);
74 }
75
76
77
78
79
80
81
82 public XqjTransmogrifier(Class<T> outputFormat, ClassLoader classLoader) {
83 this(outputFormat, classLoader, null);
84 }
85
86
87
88
89
90
91
92 public XqjTransmogrifier(Class<T> outputFormat, Map<String, Object> globalParams) {
93 this(outputFormat, null, globalParams);
94 }
95
96
97
98
99
100
101
102
103
104 public XqjTransmogrifier(
105 Class<T> outputFormat,
106 ClassLoader classLoader,
107 Map<String, Object> globalParams)
108 {
109 super(classLoader);
110 this.outputFormat = outputFormat;
111
112 if (globalParams != null) {
113 for (Entry<String, Object> entry : globalParams.entrySet()) {
114 XQUERY_GLOBAL_CONFIG.setConfigurationProperty(entry.getKey(), entry.getValue());
115 }
116 }
117 }
118
119 @Override
120 protected ConcurrentMap<String, Loader<XQPreparedExpression>> getCache() {
121 return XQUERY_CACHE;
122 }
123
124
125
126
127
128
129
130
131
132 @Override
133 public T zap(Source source, Object... params) {
134 ResultHolder<T> accessor = ResultHolderFactory.create(outputFormat);
135 if (accessor == null) throw new IllegalArgumentException("Format " + outputFormat.getClass() + " is not supported");
136 Result result = accessor.createResult();
137 doZap(source, result, params);
138 return accessor.getResult();
139 }
140
141 private void doZap(Source source, Result result, Object... params) {
142 XQResultSequence seq = null;
143 try {
144 XQPreparedExpression expression = resource(params);
145 expression.bindDocument(XQConstants.CONTEXT_ITEM, source, null);
146 bindExpressionContext(expression, staticParams);
147 bindExpressionContext(expression, resourceParameters(params));
148 seq = expression.executeQuery();
149 seq.writeSequenceToResult(result);
150 } catch (Exception e) {
151 throw new RuntimeException("XQuery processing failed", e);
152 } finally {
153 if (seq != null && !seq.isClosed()) {
154 try {
155 seq.close();
156 } catch (XQException e) {
157 LOG.trace("XQLTransmogrifier didn't return a value.", e);
158 }
159 }
160 }
161 }
162
163 private void bindExpressionContext(XQDynamicContext exp, Map<String, Object> params) throws XQException {
164 if (params == null) {
165 return;
166 }
167 for (Entry<String, Object> entry : params.entrySet()) {
168 if (entry.getKey().equalsIgnoreCase(RESOURCE_LOCATION)) {
169 continue;
170 }
171 Object value = entry.getValue();
172 if (value instanceof java.lang.String) {
173 exp.bindString(new QName(entry.getKey()), (String) value, null);
174 } else if (value instanceof javax.xml.transform.Source) {
175 exp.bindDocument(new QName(entry.getKey()), (Source) entry.getValue(), null);
176 } else if (value instanceof Boolean) {
177 exp.bindBoolean(new QName(entry.getKey()), (Boolean) entry.getValue(), null);
178 } else {
179 exp.bindAtomicValue(new QName(entry.getKey()), (String) entry.getValue(), null);
180 }
181 }
182 }
183
184
185
186
187
188 @Override
189 protected XQPreparedExpression resource(Object... params) throws Exception {
190 XQPreparedExpression expression = super.resource(params);
191 return getConnection().copyPreparedExpression(expression);
192 }
193
194 @Override
195 public XQPreparedExpression createResource(Object... params) {
196 String resourceLocation = resourceLocation(params);
197 LOG.debug("Create new template for {}", resourceLocation);
198 try {
199 InputStream stream = resourceContent(params).getInputStream();
200 return getConnection().prepareExpression(stream);
201 } catch (Exception e) {
202 throw new IllegalArgumentException("The resource "
203 + resourceLocation + " is not valid", e);
204 }
205 }
206
207 synchronized private SaxonXQConnection getConnection() throws XQException {
208 if (connection == null) {
209 connection = (SaxonXQConnection) DATA_SOURCE.getConnection();
210 }
211 return connection;
212 }
213
214 }