org.ow2.mind.plugin.ExtensionPointImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ow2.mind.plugin.ExtensionPointImpl.java

Source

/**
 * Copyright (C) 2010 STMicroelectronics
 *
 * This file is part of "Mind Compiler" is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public License 
 * as published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact: mind@ow2.org
 *
 * Authors: Matthieu Leclercq
 * Contributors: 
 */

package org.ow2.mind.plugin;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.regex.Pattern;

import org.objectweb.fractal.adl.CompilerError;
import org.objectweb.fractal.adl.error.GenericErrors;
import org.w3c.dom.Element;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.wutka.dtd.DTD;
import com.wutka.dtd.DTDAny;
import com.wutka.dtd.DTDAttribute;
import com.wutka.dtd.DTDCardinal;
import com.wutka.dtd.DTDChoice;
import com.wutka.dtd.DTDDecl;
import com.wutka.dtd.DTDElement;
import com.wutka.dtd.DTDEmpty;
import com.wutka.dtd.DTDItem;
import com.wutka.dtd.DTDName;
import com.wutka.dtd.DTDParser;
import com.wutka.dtd.DTDSequence;

class ExtensionPointImpl implements ExtensionPoint {

    private static final String EXTENSION_ELEMENT_NAME = "extension";
    private static final String ID_ATTR_NAME = "id";
    private static final String NAME_ATTR_NAME = "name";
    private static final String DTD_ATTR_NAME = "dtd";

    private final String id;
    private final String qualifiedId;
    private final String name;
    private final Plugin plugin;
    private final DTD dtd;
    private final List<Extension> extensions;
    private final WeakHashMap<DTDElement, Pattern> patternCache = new WeakHashMap<DTDElement, Pattern>();

    ExtensionPointImpl(final PluginImpl plugin, final Element element) {
        this.plugin = plugin;

        id = element.getAttribute(ID_ATTR_NAME);
        if (id == null) {
            throw new CompilerError(GenericErrors.INTERNAL_ERROR,
                    "Invalid extenstion point, missing id in '" + element.getBaseURI() + "'.");
        }
        qualifiedId = plugin.getId() + "." + id;
        name = element.getAttribute(NAME_ATTR_NAME);

        extensions = new ArrayList<Extension>();

        final String dtd = element.getAttribute(DTD_ATTR_NAME);
        if (dtd != null && dtd.length() > 0) {
            final URL dtdURL;
            try {
                final URL pluginURL = plugin.getDescriptorURL();
                if (pluginURL.getProtocol().equals("jar")) {
                    String path = pluginURL.getPath();
                    path = path.substring(0, path.lastIndexOf('!'));
                    dtdURL = new URL(pluginURL.getProtocol(), pluginURL.getHost(), pluginURL.getPort(),
                            path + "!/" + dtd);
                } else {
                    dtdURL = pluginURL.toURI().resolve(dtd).normalize().toURL();
                }
            } catch (final MalformedURLException e) {
                throw new CompilerError(GenericErrors.INTERNAL_ERROR, e,
                        "Can't find extension point DTD '" + dtd + "'.");
            } catch (final URISyntaxException e) {
                throw new CompilerError(GenericErrors.INTERNAL_ERROR, e,
                        "Can't find extension point DTD '" + dtd + "'.");
            }

            try {
                this.dtd = new DTDParser(new InputStreamReader(dtdURL.openStream())).parse();
            } catch (final IOException e) {
                throw new CompilerError(GenericErrors.INTERNAL_ERROR, e,
                        "Can't read extension point DTD '" + dtd + "'.");
            }
            if (this.dtd.elements.get(EXTENSION_ELEMENT_NAME) == null) {
                throw new CompilerError(GenericErrors.GENERIC_ERROR, "Invalid DTD '" + dtd
                        + "' missing definition of element '" + EXTENSION_ELEMENT_NAME + "'.");
            }

        } else {
            this.dtd = null;
        }
    }

    public String getId() {
        return id;
    }

    public String getQualifiedId() {
        return qualifiedId;
    }

    public String getName() {
        return name;
    }

    public Plugin getPlugin() {
        return plugin;
    }

    public Iterable<Extension> getExtensions() {
        return Iterables.unmodifiableIterable(extensions);
    }

    public Iterable<ConfigurationElement> getConfigurationElements() {
        return Iterables.unmodifiableIterable(Iterables
                .concat(Iterables.transform(extensions, new Function<Extension, Iterable<ConfigurationElement>>() {
                    public Iterable<ConfigurationElement> apply(final Extension from) {
                        return from.getConfigurationElements();
                    }
                })));

    }

    public Iterable<ConfigurationElement> getConfigurationElements(final String name) {
        return Iterables.filter(getConfigurationElements(), new Predicate<ConfigurationElement>() {
            public boolean apply(final ConfigurationElement input) {
                return input.getName().equals(name);
            }
        });
    }

    protected void bindExtension(final Extension extension) {
        extensions.add(extension);

        if (dtd != null) {
            checkElementContent(extension.getConfigurationElements(),
                    (DTDElement) dtd.elements.get(EXTENSION_ELEMENT_NAME));
        }
    }

    @SuppressWarnings("unchecked")
    void checkElement(final ConfigurationElement element) throws CompilerError {
        final String elementName = element.getName();
        final DTDElement dtdElement = (DTDElement) dtd.elements.get(elementName);
        if (dtdElement == null) {
            throw new CompilerError(GenericErrors.GENERIC_ERROR,
                    "Invalid element name '" + elementName + "' in extension.");
        }
        final Map<String, DTDAttribute> dtdAttributes = new HashMap<String, DTDAttribute>(dtdElement.attributes);
        final Map<String, String> attributes = element.getAttributes();
        for (final String attrName : attributes.keySet()) {
            final DTDAttribute dtdAttr = dtdAttributes.remove(attrName);
            if (dtdAttr == null) {
                throw new CompilerError(GenericErrors.GENERIC_ERROR,
                        "Invalid attribute name '" + attrName + "' in extension.");
            }
        }
        // scan remaining dtdAttribute and check their contingency
        for (final DTDAttribute dtdAttr : dtdAttributes.values()) {
            if (dtdAttr.defaultValue != null) {
                ((ConfigurationElementImpl) element).setAttribute(dtdAttr.name, dtdAttr.defaultValue);
            } else if (dtdAttr.getDecl().equals(DTDDecl.REQUIRED)) {
                throw new CompilerError(GenericErrors.GENERIC_ERROR,
                        "Missing attribute name '" + dtdAttr.name + "' in extension.");
            }
        }

        checkElementContent(element.getChildren(), dtdElement);
    }

    void checkElementContent(final Iterable<ConfigurationElement> children, final DTDElement dtdElement)
            throws CompilerError {
        if (!(dtdElement.content instanceof DTDEmpty || dtdElement.content instanceof DTDAny)) {
            final Pattern pattern = getPattern(dtdElement);
            if (!pattern.matcher(buildChildrenSequence(children)).matches()) {
                throw new CompilerError(GenericErrors.GENERIC_ERROR,
                        "Invalid content of element '" + dtdElement.name + "' in extension.");
            }

            for (final ConfigurationElement child : children) {
                checkElement(child);
            }
        }
    }

    String buildChildrenSequence(final Iterable<ConfigurationElement> children) {
        final StringBuilder sb = new StringBuilder();
        for (final ConfigurationElement child : children) {
            sb.append(",").append(child.getName());
        }
        return sb.toString();
    }

    Pattern getPattern(final DTDElement element) {
        Pattern pattern = patternCache.get(element);
        if (pattern == null) {
            final StringBuilder sb = new StringBuilder();
            buildRegExp(element.content, sb);
            pattern = Pattern.compile(sb.toString());
            patternCache.put(element, pattern);
        }
        return pattern;
    }

    void buildRegExp(final DTDItem item, final StringBuilder sb) {
        if (item instanceof DTDName) {
            sb.append("(,").append(((DTDName) item).value).append(")");
        } else if (item instanceof DTDChoice) {
            sb.append("(");
            final DTDItem[] subItems = ((DTDChoice) item).getItem();
            for (int i = 0; i < subItems.length; i++) {
                buildRegExp(subItems[i], sb);
                if (i < subItems.length - 1)
                    sb.append("|");
            }
            sb.append(")");
        } else if (item instanceof DTDSequence) {
            sb.append("(");
            final DTDItem[] subItems = ((DTDSequence) item).getItem();
            for (final DTDItem subItem : subItems) {
                buildRegExp(subItem, sb);
            }
            sb.append(")");
        } else {
            throw new CompilerError(GenericErrors.GENERIC_ERROR, "Invalid DTD in extension point '" + id + ".");
        }

        if (item.cardinal == DTDCardinal.ONEMANY) {
            sb.append("+");
        } else if (item.cardinal == DTDCardinal.ZEROMANY) {
            sb.append("*");
        } else if (item.cardinal == DTDCardinal.OPTIONAL) {
            sb.append("?");
        }
    }
}