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

org.jooq.impl.XMLasDOMBinding Maven / Gradle / Ivy

There is a newer version: 3.19.9
Show newest version
/*
 * 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.
 *
 * Other licenses:
 * -----------------------------------------------------------------------------
 * Commercial licenses for this work are available. These replace the above
 * ASL 2.0 and offer limited warranties, support, maintenance, and commercial
 * database integrations.
 *
 * For more information, please visit: http://www.jooq.org/licenses
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package org.jooq.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.sql.SQLXML;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.jooq.Converter;

import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * A binding that binds {@link Node} types to {@link SQLXML} types from your database.
 *
 * @author Lukas Eder
 */
public class XMLasDOMBinding extends AbstractVarcharBinding {

    /**
     * Generated UID
     */
    private static final long serialVersionUID = -2153155338260706262L;

    private final Converter converter;

    public XMLasDOMBinding() {
        this.converter = new Converter() {

            /**
             * Generated UID
             */
            private static final long serialVersionUID = -2153155338260706262L;

            @Override
            public Node from(Object t) {
                return t == null ? null : XMLasDOMBinding.fromString("" + t);
            }

            @Override
            public Object to(Node u) {
                return u == null ? null : XMLasDOMBinding.toString(u);
            }

            @Override
            public Class fromType() {
                return Object.class;
            }

            @Override
            public Class toType() {
                return Node.class;
            }
        };
    }

    @Override
    public final Converter converter() {
        return converter;
    }

    // ------------------------------------------------------------------------
    // The following logic originates from jOOX
    // ------------------------------------------------------------------------

    /**
     * Transform an {@link Node} into a String.
     */
    static final String toString(Node node) {
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            Source source = new DOMSource(node);
            Result target = new StreamResult(out);
            transformer.transform(source, target);
            return out.toString("UTF-8");
        }
        catch (Exception e) {
            return "[ ERROR IN toString() : " + e.getMessage() + " ]";
        }
    }

    /**
     * Create a new DOM element in an independent document
     */
    public static Document fromString(String name) {
        Document document = builder().newDocument();
        DocumentFragment fragment = createContent(document, name);

        if (fragment != null) {
            document.appendChild(fragment);
        }
        else {
            document.appendChild(document.createElement(name));
        }

        return document;
    }

    /**
     * Get a namespace-aware document builder
     */
    public static DocumentBuilder builder() {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            // -----------------------------------------------------------------
            // [#4592] FIX START: Prevent OWASP attack vectors
            try {
                factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            }
            catch (ParserConfigurationException ignore) {}

            try {
                factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
            }
            catch (ParserConfigurationException ignore) {}

            try {
                factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            }
            catch (ParserConfigurationException ignore) {}

            factory.setXIncludeAware(false);
            factory.setExpandEntityReferences(false);
            // [#4592] FIX END
            // -----------------------------------------------------------------

            // [#9] [#107] In order to take advantage of namespace-related DOM
            // features, the internal builder should be namespace-aware
            factory.setNamespaceAware(true);

            DocumentBuilder builder = factory.newDocumentBuilder();
            return builder;
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Create some content in the context of a given document
     *
     * @return 
    *
  • A {@link DocumentFragment} if text is * well-formed.
  • *
  • null, if text is plain text or not * well formed
  • *
*/ static final DocumentFragment createContent(Document doc, String text) { // Text might hold XML content if (text != null && text.contains("<")) { DocumentBuilder builder = builder(); try { // [#128] Trimming will get rid of leading and trailing whitespace, which would // otherwise cause a HIERARCHY_REQUEST_ERR raised by the parser text = text.trim(); // There is a processing instruction. We can safely assume // valid XML and parse it as such if (text.startsWith("" + text + ""; Document parsed = builder.parse(new InputSource(new StringReader(wrapped))); DocumentFragment fragment = parsed.createDocumentFragment(); NodeList children = parsed.getDocumentElement().getChildNodes(); // appendChild removes children also from NodeList! while (children.getLength() > 0) { fragment.appendChild(children.item(0)); } return (DocumentFragment) doc.importNode(fragment, true); } } // This does not occur catch (IOException ignore) {} // The XML content is invalid catch (SAXException ignore) {} } // Plain text or invalid XML return null; } }