net.xy.jcms.controller.configurations.parser.UsecaseParser.java Source code

Java tutorial

Introduction

Here is the source code for net.xy.jcms.controller.configurations.parser.UsecaseParser.java

Source

/**
 * This file is part of XY.JCms, Copyright 2010 (C) Xyan Kruse, Xyan@gmx.net, Xyan.kilu.de
 * 
 * XY.JCms 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.
 * 
 * XY.JCms 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 XY.JCms. If not, see <http://www.gnu.org/licenses/>.
 */
package net.xy.jcms.controller.configurations.parser;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.lang.StringUtils;

import net.xy.jcms.controller.configurations.Configuration;
import net.xy.jcms.controller.configurations.Configuration.ConfigurationType;
import net.xy.jcms.controller.configurations.TemplateConfiguration;
import net.xy.jcms.controller.configurations.pool.ControllerPool;
import net.xy.jcms.controller.usecase.Controller;
import net.xy.jcms.controller.usecase.Parameter;
import net.xy.jcms.controller.usecase.Usecase;
import net.xy.jcms.shared.JCmsHelper;

/**
 * parses an usecase xml configuration file
 * 
 * @author Xyan
 * 
 */
public class UsecaseParser {
    /**
     * implicite referenced laoding mechanism for template linking.
     */
    private static final Map<ConfigurationType, String> CONFIG_POSTFIXES = new EnumMap<ConfigurationType, String>(
            ConfigurationType.class);
    static {
        CONFIG_POSTFIXES.put(ConfigurationType.ControllerConfiguration, ".controller.properties");
        CONFIG_POSTFIXES.put(ConfigurationType.MessageConfiguration, ".messages.properties");
        CONFIG_POSTFIXES.put(ConfigurationType.RenderKitConfiguration, ".renderer.properties");
        CONFIG_POSTFIXES.put(ConfigurationType.UIConfiguration, ".ui.properties");
        CONFIG_POSTFIXES.put(ConfigurationType.TemplateConfiguration, ".templates.properties");
    }

    /**
     * parses usecases out from an xml file
     * 
     * @param in
     * @param loader
     *            used for retrieving configuration included resources and also
     *            for retrieving the controllers
     * @return value
     * @throws XMLStreamException
     * @throws ClassNotFoundException
     */
    public static Usecase[] parse(final InputStream in, final ClassLoader loader)
            throws XMLStreamException, ClassNotFoundException {
        final XMLInputFactory factory = XMLInputFactory.newInstance();
        factory.setProperty("javax.xml.stream.isCoalescing", true);
        // not supported by the reference implementation
        // factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.TRUE);
        final XMLStreamReader parser = factory.createXMLStreamReader(in);
        while (parser.hasNext()) {
            final int event = parser.next();
            if (event == XMLStreamConstants.START_ELEMENT && parser.getName().getLocalPart().equals("usecases")) {
                return parseUsecases(parser, loader);
            }
        }
        throw new IllegalArgumentException("No usecases section found. [" + parser.getLocation() + "]");
    }

    /**
     * method for parsing single usecase xml files. one per file.
     * 
     * @param in
     * @param loader
     * @return parsed usecase
     * @throws XMLStreamException
     * @throws ClassNotFoundException
     */
    public static Usecase parseSingle(final InputStream in, final ClassLoader loader)
            throws XMLStreamException, ClassNotFoundException {
        final XMLInputFactory factory = XMLInputFactory.newInstance();
        factory.setProperty("javax.xml.stream.isCoalescing", true);
        // not supported by the reference implementation
        // factory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.TRUE);
        final XMLStreamReader parser = factory.createXMLStreamReader(in);
        while (parser.hasNext()) {
            final int event = parser.next();
            if (event == XMLStreamConstants.START_ELEMENT && parser.getName().getLocalPart().equals("usecase")) {
                return parseUsecase(parser, loader);
            }
        }
        throw new IllegalArgumentException("No usecases section found. [" + parser.getLocation() + "]");
    }

    /**
     * parses usecase section
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     * @throws ClassNotFoundException
     */
    private static Usecase[] parseUsecases(final XMLStreamReader parser, final ClassLoader loader)
            throws XMLStreamException, ClassNotFoundException {
        final List<Usecase> cases = new ArrayList<Usecase>();
        while (parser.nextTag() == XMLStreamConstants.START_ELEMENT) {
            if (parser.getLocalName().equals("usecase")) {
                cases.add(parseUsecase(parser, loader));
            } else {
                throw new IllegalArgumentException(
                        "Syntax error nothing allowed between Usecase sections. [" + parser.getLocation() + "]");
            }
        }
        return cases.toArray(new Usecase[cases.size()]);
    }

    /**
     * parse the usecase subsections
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     * @throws ClassNotFoundException
     */
    private static Usecase parseUsecase(final XMLStreamReader parser, final ClassLoader loader)
            throws XMLStreamException, ClassNotFoundException {
        if (parser.getAttributeCount() != 1) {
            throw new IllegalArgumentException(
                    "There are to much or few attributes specified for usecase. [" + parser.getLocation() + "]");
        }
        final String id = parser.getAttributeValue(0);
        String description = null;
        Parameter[] parameterList = {};
        Controller[] controllerList = {};
        Configuration<?>[] configurationList = {};
        while (parser.nextTag() == XMLStreamConstants.START_ELEMENT) {
            if (parser.getLocalName().equals("description")) {
                description = parseDescription(parser);
            } else if (parser.getLocalName().equals("parameter")) {
                parameterList = parseParameters(parser);
            } else if (parser.getLocalName().equals("controller")) {
                controllerList = parseControllers(parser, loader);
            } else if (parser.getLocalName().equals("configurations")) {
                configurationList = parseConfigurations(parser, loader);
            } else {
                throw new IllegalArgumentException(
                        "Syntax error nothing allowed between Usecase sections. [" + parser.getLocation() + "]");
            }
        }
        if (StringUtils.isBlank(description) || description.length() < 32) {
            throw new IllegalArgumentException(
                    "Description is empty or insufficient please add more details. [" + parser.getLocation() + "]");
        }
        return new Usecase(id, description, parameterList, controllerList, configurationList);
    }

    /**
     * parses the description field
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     */
    private static String parseDescription(final XMLStreamReader parser) throws XMLStreamException {
        parser.next();
        final String text = parser.getText();
        parser.nextTag();
        return text;
    }

    /**
     * parses parameter entries
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     */
    private static Parameter[] parseParameters(final XMLStreamReader parser) throws XMLStreamException {
        final List<Parameter> params = new ArrayList<Parameter>();
        while (parser.nextTag() == XMLStreamConstants.START_ELEMENT) {
            if (parser.getLocalName().equals("param")) {
                params.add(parseParameter(parser));
            } else {
                throw new IllegalArgumentException(
                        "Syntax error nothing allowed between param deffinitions. [" + parser.getLocation() + "]");
            }
        }
        return params.toArray(new Parameter[params.size()]);
    }

    /**
     * parses an parameter deffinition
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     */
    private static Parameter parseParameter(final XMLStreamReader parser) throws XMLStreamException {
        if (parser.getAttributeCount() != 2) {
            throw new IllegalArgumentException(
                    "There are to much or few attributes specified for param. [" + parser.getLocation() + "]");
        }
        String key = null, valueType = null;
        for (int i = 0; i < 2; i++) {
            if (parser.getAttributeLocalName(i).equals("key")) {
                key = parser.getAttributeValue(i);
            } else if (parser.getAttributeLocalName(i).equals("valueType")) {
                valueType = parser.getAttributeValue(i);
            }
        }
        if (StringUtils.isBlank(key)) {
            throw new IllegalArgumentException("Param key name is missing [" + parser.getLocation() + "]");
        } else if (StringUtils.isBlank(valueType)) {
            throw new IllegalArgumentException("Param value type is missing [" + parser.getLocation() + "]");
        }
        parser.nextTag();
        return new Parameter(key, valueType);
    }

    /**
     * parses an controller section
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     * @throws ClassNotFoundException
     */
    private static Controller[] parseControllers(final XMLStreamReader parser, final ClassLoader loader)
            throws XMLStreamException, ClassNotFoundException {
        final List<Controller> controller = new LinkedList<Controller>();
        while (parser.nextTag() == XMLStreamConstants.START_ELEMENT) {
            if (parser.getLocalName().equals("class")) {
                controller.add(parseController(parser, loader));
            } else {
                throw new IllegalArgumentException("Syntax error nothing allowed between controller deffinitions. ["
                        + parser.getLocation() + "]");
            }
        }
        return controller.toArray(new Controller[controller.size()]);
    }

    /**
     * parses the controller itself
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     * @throws ClassNotFoundException
     */
    private static Controller parseController(final XMLStreamReader parser, final ClassLoader loader)
            throws XMLStreamException, ClassNotFoundException {
        if (parser.getAttributeCount() != 2 && parser.getAttributeCount() != 1) {
            throw new IllegalArgumentException(
                    "There are to much or few attributes specified for class. [" + parser.getLocation() + "]");
        }
        String path = null;
        final EnumSet<ConfigurationType> obmitedConfigurations = EnumSet.noneOf(ConfigurationType.class);
        for (int i = 0; i < parser.getAttributeCount(); i++) {
            if (parser.getAttributeLocalName(i).equals("path")) {
                path = parser.getAttributeValue(i);
            } else if (parser.getAttributeLocalName(i).equals("obmitConfig")) {
                final String[] configs = parser.getAttributeValue(i).split(",");
                for (final String config : configs) {
                    obmitedConfigurations.add(ConfigurationType.valueOf(config.trim()));
                }
            }
        }
        if (StringUtils.isBlank(path)) {
            throw new IllegalArgumentException("Classpath must be set [" + parser.getLocation() + "]");
        }
        parser.nextTag();
        return new Controller(ControllerPool.get(path, loader), obmitedConfigurations);
    }

    /**
     * parses the configuration section
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     */
    private static Configuration<?>[] parseConfigurations(final XMLStreamReader parser, final ClassLoader loader)
            throws XMLStreamException {
        final List<Configuration<?>> configs = new ArrayList<Configuration<?>>();
        while (parser.nextTag() == XMLStreamConstants.START_ELEMENT) {
            if (parser.getLocalName().equals("configuration")) {
                final Configuration<?> config = parseConfiguration(parser, loader);
                if (config != null) {
                    configs.add(config);
                    if (config instanceof TemplateConfiguration) {
                        try {
                            configs.addAll(loadFragmentDependencies((TemplateConfiguration) config, loader));
                        } catch (final IOException e) {
                            throw new IllegalArgumentException(
                                    "An implicite referenced config couldn't be loaded pls have a look.", e);
                        } catch (final Exception e) {
                            throw new IllegalArgumentException(
                                    "An implicite referenced config has caused some error please verify format and parsing",
                                    e);
                        }
                    }
                }
            } else {
                throw new IllegalArgumentException(
                        "Syntax error nothing allowed between configuration deffinitions. [" + parser.getLocation()
                                + "]");
            }
        }
        return configs.toArray(new Configuration[configs.size()]);
    }

    /**
     * this method checks if a included fragment is associated with dependend
     * configurations destinguished from filename. this is done for one level.
     * 
     * @param config
     * @param loader
     * @return list of additional configs
     * @throws IOException
     *             in case of loading an resource failed
     */
    private static Collection<? extends Configuration<?>> loadFragmentDependencies(
            final TemplateConfiguration config, final ClassLoader loader) throws IOException {
        final List<Configuration<?>> configs = new ArrayList<Configuration<?>>();
        for (final Entry<String, String> e : config.getSources().entrySet()) {
            for (final Entry<ConfigurationType, String> resConf : CONFIG_POSTFIXES.entrySet()) {
                final URL url = loader.getResource(e.getValue().trim() + resConf.getValue().trim());
                if (url != null) {
                    configs.add(Configuration.initByStream(resConf.getKey(), JCmsHelper.loadResource(url, loader),
                            loader, e.getKey()));
                }
            }
        }
        return configs;
    }

    /**
     * parses the configuration itself
     * 
     * @param parser
     * @return
     * @throws XMLStreamException
     */
    private static Configuration<?> parseConfiguration(final XMLStreamReader parser, final ClassLoader loader)
            throws XMLStreamException {
        Configuration<?> config = null;
        if (parser.getAttributeCount() != 1 && parser.getAttributeCount() != 2) {
            throw new IllegalArgumentException("There are to much or few attributes specified for configuration. ["
                    + parser.getLocation() + "]");
        }
        ConfigurationType type = null;
        String include = null;
        for (int i = 0; i < parser.getAttributeCount(); i++) {
            if (parser.getAttributeLocalName(i).equals("type")) {
                type = ConfigurationType.valueOf(parser.getAttributeValue(i).trim());
            } else if (parser.getAttributeLocalName(i).equals("include")) {
                include = parser.getAttributeValue(i);
            }
        }
        if (type == null) {
            throw new IllegalArgumentException("Configuration type must be set [" + parser.getLocation() + "]");
        }
        if (include != null) {
            config = Configuration.initConfigurationByInclude(type, include, loader);
        } else {
            config = getConfigurationByBody(type, parser, loader);
        }

        if (config == null) {
            throw new IllegalArgumentException(
                    "Configuration could not be retrieved [" + parser.getLocation() + "]");
        }
        parser.nextTag();
        return config;
    }

    /**
     * inits an config from the xml body
     * 
     * @param type
     * @param parser
     * @return
     * @throws XMLStreamException
     */
    private static Configuration<?> getConfigurationByBody(final ConfigurationType type,
            final XMLStreamReader parser, final ClassLoader loader) throws XMLStreamException {
        parser.next();
        final String text = parser.getText();
        return Configuration.initByString(type, text, loader);
    }

}