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

org.fabric3.introspection.xml.DefaultLoaderHelper Maven / Gradle / Ivy

/*
 * Fabric3
 * Copyright (c) 2009-2013 Metaform Systems
 *
 * Fabric3 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version, with the
 * following exception:
 *
 * Linking this software statically or dynamically with other
 * modules is making a combined work based on this software.
 * Thus, the terms and conditions of the GNU General Public
 * License cover the whole combination.
 *
 * As a special exception, the copyright holders of this software
 * give you permission to link this software with independent
 * modules to produce an executable, regardless of the license
 * terms of these independent modules, and to copy and distribute
 * the resulting executable under terms of your choice, provided
 * that you also meet, for each linked independent module, the
 * terms and conditions of the license of that module. An
 * independent module is a module which is not derived from or
 * based on this software. If you modify this software, you may
 * extend this exception to your version of the software, but
 * you are not obligated to do so. If you do not wish to do so,
 * delete this exception statement from your version.
 *
 * Fabric3 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the
 * GNU General Public License along with Fabric3.
 * If not, see .
 *
 * ----------------------------------------------------
 *
 * Portions originally based on Apache Tuscany 2007
 * licensed under the Apache 2.0 license.
 *
 */
package org.fabric3.introspection.xml;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.oasisopen.sca.Constants;
import org.oasisopen.sca.annotation.Constructor;
import org.oasisopen.sca.annotation.Property;
import org.oasisopen.sca.annotation.Reference;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

import org.fabric3.host.Namespaces;
import org.fabric3.model.type.PolicyAware;
import org.fabric3.model.type.component.Multiplicity;
import org.fabric3.model.type.component.Target;
import org.fabric3.model.type.definitions.Intent;
import org.fabric3.spi.generator.policy.PolicyRegistry;
import org.fabric3.spi.introspection.IntrospectionContext;
import org.fabric3.spi.introspection.xml.InvalidPrefixException;
import org.fabric3.spi.introspection.xml.InvalidQNamePrefix;
import org.fabric3.spi.introspection.xml.InvalidTargetException;
import org.fabric3.spi.introspection.xml.InvalidValue;
import org.fabric3.spi.introspection.xml.LoaderHelper;

import static javax.xml.stream.XMLStreamConstants.CDATA;
import static javax.xml.stream.XMLStreamConstants.CHARACTERS;
import static javax.xml.stream.XMLStreamConstants.COMMENT;
import static javax.xml.stream.XMLStreamConstants.DTD;
import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
import static javax.xml.stream.XMLStreamConstants.ENTITY_REFERENCE;
import static javax.xml.stream.XMLStreamConstants.PROCESSING_INSTRUCTION;
import static javax.xml.stream.XMLStreamConstants.SPACE;
import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
import static org.fabric3.model.type.component.Multiplicity.ONE_N;
import static org.fabric3.model.type.component.Multiplicity.ONE_ONE;
import static org.fabric3.model.type.component.Multiplicity.ZERO_ONE;

/**
 * Default implementation of the loader helper.
 */
public class DefaultLoaderHelper implements LoaderHelper {
    private DocumentBuilderFactory documentBuilderFactory;
    private PolicyRegistry policyRegistry;
    private boolean strictValidation;

    public DefaultLoaderHelper() {
        documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
    }

    @Constructor
    public DefaultLoaderHelper(@Reference PolicyRegistry policyRegistry) {
        this();
        this.policyRegistry = policyRegistry;
    }

    @Property(required = false)
    public void setStrictValidation(boolean strictValidation) {
        this.strictValidation = strictValidation;
    }

    public String loadKey(XMLStreamReader reader) {
        String key = reader.getAttributeValue(Namespaces.F3, "key");
        if (key == null) {
            return null;
        }

        int index = key.indexOf(':');
        if (index != -1 && !key.startsWith("{")) {
            // treat the key as a QName
            String prefix = key.substring(0, index);
            String localPart = key.substring(index + 1);
            String ns = reader.getNamespaceContext().getNamespaceURI(prefix);
            key = "{" + ns + "}" + localPart;
        }
        return key;
    }


    public void loadPolicySetsAndIntents(PolicyAware policyAware, XMLStreamReader reader, IntrospectionContext context) {
        try {
            Set intentNames = parseListOfQNames(reader, "requires");
            validateIntents(intentNames, reader, context);
            policyAware.setIntents(intentNames);
            policyAware.setPolicySets(policySets(reader));

        } catch (InvalidPrefixException e) {
            String prefix = e.getPrefix();
            URI uri = context.getContributionUri();
            Location location = reader.getLocation();
            InvalidQNamePrefix failure =
                    new InvalidQNamePrefix("The prefix " + prefix + " specified in contribution " + uri + " is invalid", location);
            context.addError(failure);
        }
    }

    public Set parseListOfQNames(XMLStreamReader reader, String attribute) throws InvalidPrefixException {
        Set qNames = new HashSet();
        String val = reader.getAttributeValue(null, attribute);
        if (val != null) {
            StringTokenizer tok = new StringTokenizer(val);
            while (tok.hasMoreElements()) {
                qNames.add(createQName(tok.nextToken(), reader));
            }
        }
        return qNames;
    }

    public QName createQName(String name, XMLStreamReader reader) throws InvalidPrefixException {
        QName qName;
        int index = name.indexOf(':');
        if (index != -1) {
            String prefix = name.substring(0, index);
            String localPart = name.substring(index + 1);
            String ns = reader.getNamespaceContext().getNamespaceURI(prefix);
            if (ns == null) {
                throw new InvalidPrefixException("Invalid prefix: " + prefix, prefix, reader);
            }
            qName = new QName(ns, localPart, prefix);
        } else {
            String prefix = "";
            String ns = reader.getNamespaceURI();
            qName = new QName(ns, name, prefix);
        }
        return qName;
    }

    public URI parseUri(String target) throws URISyntaxException {
        if (target == null) {
            return null;
        }

        int index = target.lastIndexOf('/');
        if (index == -1) {
            return new URI(target);
        } else {
            String uri = target.substring(0, index);
            String fragment = target.substring(index + 1);
            return new URI(uri + '#' + fragment);
        }
    }

    public Target parseTarget(String target, XMLStreamReader reader) throws InvalidTargetException {
        if (target == null) {
            return null;
        }
        String[] tokens = target.split("/");
        if (tokens.length == 1) {
            return new Target(tokens[0]);
        } else if (tokens.length == 2) {
            return new Target(tokens[0], tokens[1]);
        } else if (tokens.length == 3) {
            return new Target(tokens[0], tokens[1], tokens[2]);
        } else {
            throw new InvalidTargetException("Invalid target format: " + target, target, reader);
        }
    }

    public List parseListOfUris(XMLStreamReader reader, String attribute) throws URISyntaxException {
        String value = reader.getAttributeValue(null, attribute);
        if (value == null || value.length() == 0) {
            return null;
        } else {
            StringTokenizer tok = new StringTokenizer(value);
            List result = new ArrayList(tok.countTokens());
            while (tok.hasMoreTokens()) {
                result.add(parseUri(tok.nextToken().trim()));
            }
            return result;
        }
    }

    public boolean canNarrow(Multiplicity first, Multiplicity second) {
        switch (second) {
        case ONE_ONE:
            return ONE_ONE == first;
        case ONE_N:
            return ONE_ONE == first || ONE_N == first;
        case ZERO_N:
            return true;
        case ZERO_ONE:
            return ONE_ONE == first || ZERO_ONE == first;
        }
        return false;

    }

    public Document loadPropertyValues(XMLStreamReader reader) throws XMLStreamException {
        Document document = createDocument();

        int depth = 0;
        Element root = document.createElementNS("", "values");
        for (int i = 0; i < reader.getAttributeCount(); i++) {
            root.setAttributeNS(reader.getAttributeNamespace(i),
                                reader.getAttributeLocalName(i),
                                reader.getAttributeValue(i));
        }
        populateNamespaces(reader, root);
        document.appendChild(root);
        Node element = root;

        while (true) {
            int next = reader.next();
            switch (next) {
            case START_ELEMENT:
                String namespace = reader.getNamespaceURI();
                String name = reader.getLocalName();

                if (depth == 0) {
                    if (!"value".equals(name)) {
                        element = document.getDocumentElement();
                    }
                }

                Element child = document.createElementNS(namespace, name);

                if (element != null) {
                    element.appendChild(child);
                } else {
                    document.appendChild(child);
                }
                int count = reader.getAttributeCount();
                for (int i = 0; i < count; i++) {
                    String attrNamespace = reader.getAttributeNamespace(i);
                    String attrName = reader.getAttributeLocalName(i);
                    String attrValue = reader.getAttributeValue(i);
                    if (attrNamespace == null) {
                        child.setAttribute(attrName, attrValue);
                    } else {
                        child.setAttributeNS(attrNamespace, attrName, attrValue);
                    }
                }
                element = child;
                depth++;
                break;
            case CHARACTERS:
            case CDATA:
                String value = reader.getText();
                if (value.trim().length() == 0) {
                    // empty, skip node
                    break;
                }
                if (depth == 0) {
                    // simple value, e.g. val
                    element = document.createElement("value");
                    root.appendChild(element);
                }
                Text text = document.createTextNode(value);
                element.appendChild(text);
                break;
            case END_ELEMENT:
                QName elementName = reader.getName();
                String localPart = elementName.getLocalPart();
                String ns = elementName.getNamespaceURI();
                if (localPart.equals("property") && ("".equals(ns) || Constants.SCA_NS.equals(ns))) {
                    return document;
                }
                depth--;
                if (depth == 0) {
                    // property has multiple values, reset the current element and document
                    element = root;
                } else {
                    element = element.getParentNode();
                }
                break;
            case XMLStreamConstants.END_DOCUMENT:
                return document;

            case ENTITY_REFERENCE:
            case COMMENT:
            case SPACE:
            case PROCESSING_INSTRUCTION:
            case DTD:
                break;
            }
        }
    }

    public Document loadPropertyValue(String content) throws XMLStreamException {
        Document document = createDocument();

        Element root = document.createElement("values");
        document.appendChild(root);
        Element element = document.createElement("value");
        root.appendChild(element);
        Text text = document.createTextNode(content);
        element.appendChild(text);
        return document;
    }


    public Document transform(XMLStreamReader reader) throws XMLStreamException {

        if (reader.getEventType() != XMLStreamConstants.START_ELEMENT) {
            throw new XMLStreamException("The stream needs to be at the start of an element");
        }

        Document document = createDocument();

        QName rootName = reader.getName();
        Element root = createElement(reader, document, rootName);

        document.appendChild(root);

        while (true) {

            int next = reader.next();
            switch (next) {
            case START_ELEMENT:

                QName childName = new QName(reader.getNamespaceURI(), reader.getLocalName());
                Element child = createElement(reader, document, childName);

                root.appendChild(child);
                root = child;

                break;

            case CHARACTERS:
            case CDATA:
                Text text = document.createTextNode(reader.getText());
                root.appendChild(text);
                break;
            case END_ELEMENT:
                if (rootName.equals(reader.getName())) {
                    return document;
                }
                root = (Element) root.getParentNode();
            case ENTITY_REFERENCE:
            case COMMENT:
            case SPACE:
            case PROCESSING_INSTRUCTION:
            case DTD:
                break;
            }
        }
    }

    private void validateIntents(Set intentNames, XMLStreamReader reader, IntrospectionContext context) {
        if (!strictValidation || policyRegistry == null) {
            return;
        }
        Set excluded = new HashSet();
        // check for mutually exclusive intents
        for (QName name : intentNames) {
            Intent intent = policyRegistry.getDefinition(name, Intent.class);
            if (!intent.getExcludes().isEmpty()) {
                for (QName exclude : intent.getExcludes()) {
                    if (excluded.contains(exclude) || intentNames.contains(exclude)) {
                        Location location = reader.getLocation();
                        InvalidValue error = new InvalidValue("Mutually exclusive intents configured: " + exclude, location);
                        context.addError(error);
                    } else {
                        excluded.add(exclude);
                    }
                }
            }
        }
    }

    private Set policySets(XMLStreamReader reader) throws InvalidPrefixException {
        return parseListOfQNames(reader, "policySets");
    }

    private void populateNamespaces(XMLStreamReader reader, Element element) {
        for (int i = 0; i < reader.getNamespaceCount(); i++) {
            String prefix = reader.getNamespacePrefix(i);
            String uri = reader.getNamespaceURI(i);
            prefix = prefix == null || prefix.length() == 0 ? "xmlns" : "xmlns:" + prefix;
            element.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix, uri);
        }
    }

    private Document createDocument() throws XMLStreamException {
        DocumentBuilder builder = getDocumentBuilder();
        return builder.newDocument();
    }

    private DocumentBuilder getDocumentBuilder() throws XMLStreamException {
        try {
            return documentBuilderFactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new XMLStreamException(e);
        }
    }

    /**
     * Creates the element and populates namespace declarations and attributes.
     *
     * @param reader   the reader source
     * @param document the document to update
     * @param rootName the root element name
     * @return the element
     */
    private Element createElement(XMLStreamReader reader, Document document, QName rootName) {
        Element root = document.createElementNS(rootName.getNamespaceURI(), rootName.getLocalPart());

        // Handle namespace declarations
        for (int i = 0; i < reader.getNamespaceCount(); i++) {
            String prefix = reader.getNamespacePrefix(i);
            String uri = reader.getNamespaceURI(i);

            prefix = prefix == null ? "xmlns" : "xmlns:" + prefix;

            root.setAttribute(prefix, uri);
        }

        // Handle attributes
        for (int i = 0; i < reader.getAttributeCount(); i++) {

            String attributeNs = reader.getAttributeNamespace(i);
            String localName = reader.getAttributeLocalName(i);
            String value = reader.getAttributeValue(i);
            String attributePrefix = reader.getAttributePrefix(i);
            String qualifiedName = attributePrefix == null || attributePrefix.length() == 0 ? localName : attributePrefix + ":" + localName;

            if (attributeNs == null || attributeNs.length() == 0) {
                root.setAttribute(qualifiedName, value);
            } else {
                root.setAttributeNS(attributeNs, qualifiedName, value);
            }

        }
        return root;
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy