All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.openehealth.ipf.commons.xml.XqjTransmogrifier Maven / Gradle / Ivy
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openehealth.ipf.commons.xml;
import com.saxonica.xqj.SaxonXQConnection;
import com.saxonica.xqj.SaxonXQDataSource;
import lombok.Getter;
import lombok.Setter;
import net.sf.saxon.Configuration;
import org.openehealth.ipf.commons.core.modules.api.Transmogrifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.xquery.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* XQuery transformer similar to the XsltTransmogrifier
*
* @author Stefan Ivanov
*
* @param
* output type
*/
public class XqjTransmogrifier extends AbstractCachingXmlProcessor implements Transmogrifier {
private final static Logger LOG = LoggerFactory.getLogger(XqjTransmogrifier.class);
private static final ConcurrentMap> XQUERY_CACHE = new ConcurrentHashMap<>();
private static final Configuration XQUERY_GLOBAL_CONFIG;
private static final SaxonXQDataSource DATA_SOURCE;
static {
XQUERY_GLOBAL_CONFIG = new Configuration();
XQUERY_GLOBAL_CONFIG.setResourceResolver(new ClasspathResourceResolver(XQUERY_GLOBAL_CONFIG.getResourceResolver()));
DATA_SOURCE = new SaxonXQDataSource(XQUERY_GLOBAL_CONFIG);
}
private final Class outputFormat;
private SaxonXQConnection connection;
@Getter @Setter private Map staticParams;
@SuppressWarnings("unchecked")
public XqjTransmogrifier() {
this((Class) String.class);
}
/**
* @param outputFormat
* currently supported: String, Writer, OutputStream
*/
public XqjTransmogrifier(Class outputFormat) {
this(outputFormat, null, null);
}
/**
* @param outputFormat
* currently supported: String, Writer, OutputStream
* @param classLoader
* class loader for resource retrieval, may be null
*/
public XqjTransmogrifier(Class outputFormat, ClassLoader classLoader) {
this(outputFormat, classLoader, null);
}
/**
* @param outputFormat
* currently supported: String, Writer, OutputStream
* @param globalParams
* static XQuery parameters
*/
public XqjTransmogrifier(Class outputFormat, Map globalParams) {
this(outputFormat, null, globalParams);
}
/**
* @param outputFormat
* currently supported: String, Writer, OutputStream
* @param classLoader
* class loader for resource retrieval, may be null
* @param globalParams
* static XQuery parameters
*/
public XqjTransmogrifier(
Class outputFormat,
ClassLoader classLoader,
Map globalParams)
{
super(classLoader);
this.outputFormat = outputFormat;
if (globalParams != null) {
for (var entry : globalParams.entrySet()) {
XQUERY_GLOBAL_CONFIG.setConfigurationProperty(entry.getKey(), entry.getValue());
}
}
}
@Override
protected ConcurrentMap> getCache() {
return XQUERY_CACHE;
}
/**
* Converts a Source into a typed result using a XQuery processor.
*
* The XQ resource location is mandatory in the first extra parameter. Other
* parameters may be passed as a Map in the second parameter.
*
* @see Transmogrifier#zap(java.lang.Object, java.lang.Object[])
*/
@Override
public T zap(Source source, Object... params) {
var accessor = ResultHolderFactory.create(outputFormat);
if (accessor == null) throw new IllegalArgumentException("Format " + outputFormat + " is not supported");
var result = accessor.createResult();
doZap(source, result, params);
return accessor.getResult();
}
private void doZap(Source source, Result result, Object... params) {
XQResultSequence seq = null;
try {
var expression = resource(params);
expression.bindDocument(XQConstants.CONTEXT_ITEM, source, null);
bindExpressionContext(expression, staticParams);
bindExpressionContext(expression, resourceParameters(params));
seq = expression.executeQuery();
seq.writeSequenceToResult(result);
} catch (Exception e) {
throw new RuntimeException("XQuery processing failed", e);
} finally {
if (seq != null && !seq.isClosed()) {
try {
seq.close();
} catch (XQException e) {
LOG.trace("XQLTransmogrifier didn't return a value.", e);
}
}
}
}
private void bindExpressionContext(XQDynamicContext exp, Map params) throws XQException {
if (params == null) {
return;
}
for (var entry : params.entrySet()) {
if (entry.getKey().equalsIgnoreCase(RESOURCE_LOCATION)) {
continue;
}
var value = entry.getValue();
if (value instanceof java.lang.String) {
exp.bindString(new QName(entry.getKey()), (String) value, null);
} else if (value instanceof javax.xml.transform.Source) {
exp.bindDocument(new QName(entry.getKey()), (Source) entry.getValue(), null);
} else if (value instanceof Boolean) {
exp.bindBoolean(new QName(entry.getKey()), (Boolean) entry.getValue(), null);
} else {
exp.bindAtomicValue(new QName(entry.getKey()), (String) entry.getValue(), null);
}
}
}
/**
* This method had to be overridden because {@link XQPreparedExpression} objects
* are not thread-safe, thus an additional replication step is necessary.
*/
@Override
protected XQPreparedExpression resource(Object... params) throws Exception {
var expression = super.resource(params);
return getConnection().copyPreparedExpression(expression);
}
@Override
public XQPreparedExpression createResource(Object... params) {
var resourceLocation = resourceLocation(params);
LOG.debug("Create new template for {}", resourceLocation);
try {
var stream = resourceContent(params).getInputStream();
return getConnection().prepareExpression(stream);
} catch (Exception e) {
throw new IllegalArgumentException("The resource "
+ resourceLocation + " is not valid", e);
}
}
synchronized private SaxonXQConnection getConnection() {
if (connection == null) {
connection = (SaxonXQConnection) DATA_SOURCE.getConnection();
}
return connection;
}
}