All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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





© 2015 - 2024 Weber Informatics LLC | Privacy Policy