<commit_before>/*
 * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
 *
 * 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.opensaml.xml.io;

import javax.xml.namespace.QName;

import org.apache.log4j.Logger;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.Namespace;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.util.DatatypeHelper;
import org.opensaml.xml.util.XMLConstants;
import org.opensaml.xml.util.XMLHelper;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * An thread safe abstract unmarshaller. This unmarshaller will:
 * <ul>
 * <li>Unmarshalling namespace decleration attributes</li>
 * <li>Unmarshalling schema instance type (xsi:type) decleration attributes</li>
 * <li>Delegating to child classes element, text, and attribute processing</li>
 * </ul>
 * 
 * <strong>NOTE:</strong> In the case of Text nodes this unmarshaller will use {@link org.w3c.dom.Text#getWholeText()}
 * to retrieve the textual content. This is probably exceptable in almost all cases, if, however, you need to deal with
 * elements that contain multiple text node children you will need to override
 * {@link #unmarshallTextContent(XMLObject, Text)} and do "the right thing" for your implementation.
 */
public abstract class AbstractXMLObjectUnmarshaller implements Unmarshaller {

    /** Logger */
    private static Logger log = Logger.getLogger(AbstractXMLObjectUnmarshaller.class);

    /** The target name and namespace for this unmarshaller. */
    private QName targetQName;

    /** Factory for XMLObjectBuilders */
    private XMLObjectBuilderFactory xmlObjectBuilderFactory;

    /** Factory for creating unmarshallers for child elements */
    private UnmarshallerFactory unmarshallerFactory;

    /**
     * Constructor.
     */
    protected AbstractXMLObjectUnmarshaller() {
        xmlObjectBuilderFactory = Configuration.getBuilderFactory();
        unmarshallerFactory = Configuration.getUnmarshallerFactory();
    }

    /**
     * This constructor supports checking a DOM Element to be unmarshalled, either element name or schema type, against
     * a given namespace/local name pair.
     * 
     * @param targetNamespaceURI the namespace URI of either the schema type QName or element QName of the elements this
     *            unmarshaller operates on
     * @param targetLocalName the local name of either the schema type QName or element QName of the elements this
     *            unmarshaller operates on
     * 
     * @throws IllegalArgumentException if any of the arguments are null (or empty in the case of String parameters)
     */
    protected AbstractXMLObjectUnmarshaller(String targetNamespaceURI, String targetLocalName)
            throws IllegalArgumentException {
        if (DatatypeHelper.isEmpty(targetNamespaceURI)) {
            throw new IllegalArgumentException("Target Namespace URI may not be null or an empty");
        }

        if (DatatypeHelper.isEmpty(targetLocalName)) {
            throw new IllegalArgumentException("Target Local Name may not be null or an empty");
        }
        targetQName = XMLHelper.constructQName(targetNamespaceURI, targetLocalName, null);

        xmlObjectBuilderFactory = Configuration.getBuilderFactory();
        unmarshallerFactory = Configuration.getUnmarshallerFactory();
    }

    /** {@inheritDoc} */
    public XMLObject unmarshall(Element domElement) throws UnmarshallingException {
        if (log.isDebugEnabled()) {
            log.debug("Starting to unmarshall DOM element " + XMLHelper.getNodeQName(domElement));
        }

        checkElementIsTarget(domElement);

        XMLObject xmlObject = buildXMLObject(domElement);

        if (log.isDebugEnabled()) {
            log.debug("Unmarshalling attributes of DOM Element " + XMLHelper.getNodeQName(domElement));
        }
        NamedNodeMap attributes = domElement.getAttributes();
        Node attribute;
        for (int i = 0; i < attributes.getLength(); i++) {
            attribute = attributes.item(i);

            // These should allows be attribute nodes, but just in case...
            if (attribute.getNodeType() == Node.ATTRIBUTE_NODE) {
                unmarshallAttribute(xmlObject, (Attr) attribute);
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("Unmarshalling other child nodes of DOM Element " + XMLHelper.getNodeQName(domElement));
        }
        NodeList childNodes = domElement.getChildNodes();
        Node childNode;
        for (int i = 0; i < childNodes.getLength(); i++) {
            childNode = childNodes.item(i);

            if (childNode.getNodeType() == Node.ATTRIBUTE_NODE) {
                unmarshallAttribute(xmlObject, (Attr) childNode);
            } else if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                unmarshallChildElement(xmlObject, (Element) childNode);
            } else if (childNode.getNodeType() == Node.TEXT_NODE) {
                unmarshallTextContent(xmlObject, (Text) childNode);
            }
        }

        xmlObject.setDOM(domElement);
        return xmlObject;
    }

    /**
     * Checks that the given DOM Element's XSI type or namespace qualified element name matches the target QName of this
     * unmarshaller.
     * 
     * @param domElement the DOM element to check
     * 
     * @throws UnmarshallingException thrown if the DOM Element does not match the target of this unmarshaller
     */
    protected void checkElementIsTarget(Element domElement) throws UnmarshallingException {
        QName elementName = XMLHelper.getNodeQName(domElement);

        if (targetQName == null) {
            if (log.isDebugEnabled()) {
                log.debug("Targeted QName checking is not available for this unmarshaller, DOM Element " + elementName
                        + " was not verified");
            }

            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("Checking that " + elementName + " meets target criteria.");
        }

        QName type = XMLHelper.getXSIType(domElement);

        if (type != null && type.equals(targetQName)) {
            if (log.isDebugEnabled()) {
                log.debug(elementName + " schema type matches target.");
            }
            return;
        } else {
            if (elementName.equals(targetQName)) {
                if (log.isDebugEnabled()) {
                    log.debug(elementName + " element name matches target.");
                }
                return;
            } else {
                String errorMsg = "This unmarshaller only operations on " + targetQName + " elements not "
                        + elementName;
                log.error(errorMsg);
                throw new UnmarshallingException(errorMsg);
            }
        }
    }

    /**
     * Constructs the XMLObject that the given DOM Element will be unmarshalled into. If the DOM element has an XML
     * Schema type defined this method will attempt to retrieve an XMLObjectBuilder, from the factory given at
     * construction time, using the schema type. If no schema type is present or no builder is registered with the
     * factory for the schema type, the elements QName is used. Once the builder is found the XMLObject is create by
     * invoking {@link XMLObjectBuilder#buildObject(String, String, String)}. Extending classes may wish to override
     * this logic if more than just schema type or element name (e.g. element attributes or content) need to be used to
     * determine which XMLObjectBuilder should be used to create the XMLObject.
     * 
     * @param domElement the DOM Element the created XMLObject will represent<commit_msg>fix log message typo<commit_after>/*
 * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
 *
 * 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.opensaml.xml.io;

import javax.xml.namespace.QName;

import org.apache.log4j.Logger;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.Namespace;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.util.DatatypeHelper;
import org.opensaml.xml.util.XMLConstants;
import org.opensaml.xml.util.XMLHelper;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

/**
 * An thread safe abstract unmarshaller. This unmarshaller will:
 * <ul>
 * <li>Unmarshalling namespace decleration attributes</li>
 * <li>Unmarshalling schema instance type (xsi:type) decleration attributes</li>
 * <li>Delegating to child classes element, text, and attribute processing</li>
 * </ul>
 * 
 * <strong>NOTE:</strong> In the case of Text nodes this unmarshaller will use {@link org.w3c.dom.Text#getWholeText()}
 * to retrieve the textual content. This is probably exceptable in almost all cases, if, however, you need to deal with
 * elements that contain multiple text node children you will need to override
 * {@link #unmarshallTextContent(XMLObject, Text)} and do "the right thing" for your implementation.
 */
public abstract class AbstractXMLObjectUnmarshaller implements Unmarshaller {

    /** Logger */
    private static Logger log = Logger.getLogger(AbstractXMLObjectUnmarshaller.class);

    /** The target name and namespace for this unmarshaller. */
    private QName targetQName;

    /** Factory for XMLObjectBuilders */
    private XMLObjectBuilderFactory xmlObjectBuilderFactory;

    /** Factory for creating unmarshallers for child elements */
    private UnmarshallerFactory unmarshallerFactory;

    /**
     * Constructor.
     */
    protected AbstractXMLObjectUnmarshaller() {
        xmlObjectBuilderFactory = Configuration.getBuilderFactory();
        unmarshallerFactory = Configuration.getUnmarshallerFactory();
    }

    /**
     * This constructor supports checking a DOM Element to be unmarshalled, either element name or schema type, against
     * a given namespace/local name pair.
     * 
     * @param targetNamespaceURI the namespace URI of either the schema type QName or element QName of the elements this
     *            unmarshaller operates on
     * @param targetLocalName the local name of either the schema type QName or element QName of the elements this
     *            unmarshaller operates on
     * 
     * @throws IllegalArgumentException if any of the arguments are null (or empty in the case of String parameters)
     */
    protected AbstractXMLObjectUnmarshaller(String targetNamespaceURI, String targetLocalName)
            throws IllegalArgumentException {
        if (DatatypeHelper.isEmpty(targetNamespaceURI)) {
            throw new IllegalArgumentException("Target Namespace URI may not be null or an empty");
        }

        if (DatatypeHelper.isEmpty(targetLocalName)) {
            throw new IllegalArgumentException("Target Local Name may not be null or an empty");
        }
        targetQName = XMLHelper.constructQName(targetNamespaceURI, targetLocalName, null);

        xmlObjectBuilderFactory = Configuration.getBuilderFactory();
        unmarshallerFactory = Configuration.getUnmarshallerFactory();
    }

    /** {@inheritDoc} */
    public XMLObject unmarshall(Element domElement) throws UnmarshallingException {
        if (log.isDebugEnabled()) {
            log.debug("Starting to unmarshall DOM element " + XMLHelper.getNodeQName(domElement));
        }

        checkElementIsTarget(domElement);

        XMLObject xmlObject = buildXMLObject(domElement);

        if (log.isDebugEnabled()) {
            log.debug("Unmarshalling attributes of DOM Element " + XMLHelper.getNodeQName(domElement));
        }
        NamedNodeMap attributes = domElement.getAttributes();
        Node attribute;
        for (int i = 0; i < attributes.getLength(); i++) {
            attribute = attributes.item(i);

            // These should allows be attribute nodes, but just in case...
            if (attribute.getNodeType() == Node.ATTRIBUTE_NODE) {
                unmarshallAttribute(xmlObject, (Attr) attribute);
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("Unmarshalling other child nodes of DOM Element " + XMLHelper.getNodeQName(domElement));
        }
        NodeList childNodes = domElement.getChildNodes();
        Node childNode;
        for (int i = 0; i < childNodes.getLength(); i++) {
            childNode = childNodes.item(i);

            if (childNode.getNodeType() == Node.ATTRIBUTE_NODE) {
                unmarshallAttribute(xmlObject, (Attr) childNode);
            } else if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                unmarshallChildElement(xmlObject, (Element) childNode);
            } else if (childNode.getNodeType() == Node.TEXT_NODE) {
                unmarshallTextContent(xmlObject, (Text) childNode);
            }
        }

        xmlObject.setDOM(domElement);
        return xmlObject;
    }

    /**
     * Checks that the given DOM Element's XSI type or namespace qualified element name matches the target QName of this
     * unmarshaller.
     * 
     * @param domElement the DOM element to check
     * 
     * @throws UnmarshallingException thrown if the DOM Element does not match the target of this unmarshaller
     */
    protected void checkElementIsTarget(Element domElement) throws UnmarshallingException {
        QName elementName = XMLHelper.getNodeQName(domElement);

        if (targetQName == null) {
            if (log.isDebugEnabled()) {
                log.debug("Targeted QName checking is not available for this unmarshaller, DOM Element " + elementName
                        + " was not verified");
            }

            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("Checking that " + elementName + " meets target criteria.");
        }

        QName type = XMLHelper.getXSIType(domElement);

        if (type != null && type.equals(targetQName)) {
            if (log.isDebugEnabled()) {
                log.debug(elementName + " schema type matches target.");
            }
            return;
        } else {
            if (elementName.equals(targetQName)) {
                if (log.isDebugEnabled()) {
                    log.debug(elementName + " element name matches target.");
                }
                return;
            } else {
                String errorMsg = "This unmarshaller only operates on " + targetQName + " elements not "
                        + elementName;
                log.error(errorMsg);
                throw new UnmarshallingException(errorMsg);
            }
        }
    }

    /**
     * Constructs the XMLObject that the given DOM Element will be unmarshalled into. If the DOM element has an XML
     * Schema type defined this method will attempt to retrieve an XMLObjectBuilder, from the factory given at
     * construction time, using the schema type. If no schema type is present or no builder is registered with the
     * factory for the schema type, the elements QName is used. Once the builder is found the XMLObject is create by
     * invoking {@link XMLObjectBuilder#buildObject(String, String, String)}. Extending classes may wish to override
     * this logic if more than just schema type or element name (e.g. element attributes or content) need to be used to
     * determine which XMLObjectBuilder should be used to create the XMLObject.
     * 
     * @param domElement the DOM Element the created XMLObject will represent
{'fraction_non_alphanumeric': 0.08099281515349445, 'fraction_numerical': 0.0021376402826435484, 'mean_word_length': 2.798376184032476, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 40}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5710150', 'n_tokens_mistral': 4295, 'n_tokens_neox': 4230, 'n_words': 1462}