de.interactive_instruments.ShapeChange.Options.java Source code

Java tutorial

Introduction

Here is the source code for de.interactive_instruments.ShapeChange.Options.java

Source

/**
 * ShapeChange - processing application schemas for geographic information
 *
 * This file is part of ShapeChange. ShapeChange takes a ISO 19109
 * Application Schema from a UML model and translates it into a
 * GML Application Schema or other implementation representations.
 *
 * Additional information about the software can be found at
 * http://shapechange.net/
 *
 * (c) 2002-2013 interactive instruments GmbH, Bonn, Germany
 *
 * This program 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.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contact:
 * interactive instruments GmbH
 * Trierer Strasse 70-72
 * 53115 Bonn
 * Germany
 */

package de.interactive_instruments.ShapeChange;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.SystemUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import de.interactive_instruments.ShapeChange.AIXMSchemaInfos.AIXMSchemaInfo;
import de.interactive_instruments.ShapeChange.Model.PackageInfo;
import de.interactive_instruments.ShapeChange.Model.Stereotypes;
import de.interactive_instruments.ShapeChange.Model.StereotypesCacheSet;
import de.interactive_instruments.ShapeChange.Model.TaggedValues;
import de.interactive_instruments.ShapeChange.Model.TaggedValuesCacheArray;
import de.interactive_instruments.ShapeChange.Model.TaggedValuesCacheMap;
import de.interactive_instruments.ShapeChange.Target.Target;
import de.interactive_instruments.ShapeChange.Target.FeatureCatalogue.FeatureCatalogue;

public class Options {

    //
    // Constants
    //

    /** Parser feature ids. */
    public static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
    public static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
    public static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
    public static final String W3C_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
    public static final String W3C_RDFS = "http://www.w3.org/2000/01/rdf-schema#";
    public static final String W3C_OWL = "http://www.w3.org/2002/07/owl#";
    public static final String W3C_OWL2XML = "http://www.w3.org/2006/12/owl2-xml#";
    public static final String DC = "http://purl.org/dc/elements/1.1/";
    public static final String OGC_GEOSPARQL = "http://www.opengis.net/geosparql#";
    public static final String W3C_SKOS = "http://www.w3.org/2004/02/skos/core#";

    public static final String SCRS_NS = "http://www.interactive-instruments.de/ShapeChange/Result";
    public static final String SCAI_NS = "http://www.interactive-instruments.de/ShapeChange/AppInfo";
    public static final String SCHEMATRON_NS = "http://purl.oclc.org/dsdl/schematron";
    public static final String DGIWGSP_NSABR = "gmldgiwgsp";
    public static final String DGIWGSP_NS = "http://www.dgiwg.org/gml/3.2/profiles/spatial/1.0/";

    // TODO move these to each target
    public static final String DEF_NS = "http://www.interactive-instruments.de/ShapeChange/Definitions/0.5";
    public static final String XTRASERVER_NS = "http://www.interactive-instruments.de/namespaces/XtraServer";
    public static final String GDBDOC_NS = "http://www.interactive-instruments.de/namespaces/GdbDoc";

    /* Target Java Classes */
    /**
     *
     */
    public static final String TargetXmlSchemaClass = "de.interactive_instruments.ShapeChange.Target.XmlSchema.XmlSchema";
    public static final String TargetFOL2SchematronClass = "de.interactive_instruments.ShapeChange.Target.FOL2Schematron.FOL2Schematron";
    public static final String TargetJsonSchemaClass = "de.interactive_instruments.ShapeChange.Target.JSON.JsonSchema";
    public static final String TargetFeatureCatalogueClass = "de.interactive_instruments.ShapeChange.Target.FeatureCatalogue.FeatureCatalogue";
    public static final String TargetOWLISO19150Class = "de.interactive_instruments.ShapeChange.Target.Ontology.OWLISO19150";
    public static final String TargetRDFClass = "de.interactive_instruments.ShapeChange.Target.Ontology.RDF";
    public static final String TargetSQLClass = "de.interactive_instruments.ShapeChange.Target.SQL.SqlDdl";
    public static final String TargetArcGISWorkspaceClass = "de.interactive_instruments.ShapeChange.Target.ArcGISWorkspace.ArcGISWorkspace";
    public static final String TargetReplicationSchemaClass = "de.interactive_instruments.ShapeChange.Target.ReplicationSchema.ReplicationXmlSchema";
    public static final String TargetApplicationSchemaMetadata = "de.interactive_instruments.ShapeChange.Target.Metadata.ApplicationSchemaMetadata";

    /** XML Schema encoding rules */
    public static final String ISO19136_2007 = "iso19136_2007".toLowerCase();
    public static final String ISO19139_2007 = "iso19139_2007".toLowerCase();
    public static final String ISO19136_2007_SHAPECHANGE_1_0 = "iso19136_2007_ShapeChange_1.0_Extensions"
            .toLowerCase();
    public static final String ISO19136_2007_INSPIRE = "iso19136_2007_INSPIRE_Extensions".toLowerCase();
    public static final String ISO19136_2007_NO_GML = "iso19136_2007_NoGmlBaseTypes".toLowerCase();
    public static final String ISO19150_2014 = "iso19150_2014".toLowerCase();
    public static final String SQL = "sql";
    public static final String SWECOMMON2 = "ogcSweCommon2".toLowerCase();
    public static final String NOT_ENCODED = "notEncoded".toLowerCase();
    public static final String GSIP_ENC = "gsip".toLowerCase();

    /** Well known stereotypes */
    public static final String[] classStereotypes = { "codelist", "enumeration", "datatype", "featuretype", "type",
            "basictype", "interface", "union", "abstract", "fachid", "schluesseltabelle", "adeelement",
            "featureconcept", "attributeconcept", "valueconcept", "aixmextension" };
    public static final String[] assocStereotypes = { "disjoint" };
    /**
     * NOTE: stereotype "identifier" is deprecated
     */
    public static final String[] propertyStereotypes = { "voidable", "identifier", "version", "property",
            "estimated" };
    public static final String[] packageStereotypes = { "application schema", "bundle", "leaf" };
    public static final String[] depStereotypes = { "import", "include" };

    /** Carriage Return and Line Feed characters. */
    public static final String CRLF = "\r\n";

    /** Model types. */
    public static final int XMI10 = 1;
    public static final int EA7 = 2;
    public static final int GSIP = 3;
    public static final int GENERIC = 4;

    /** SQL targets. TODO move the relevant targets */
    public static final int NONE = 0;
    // public static final int SFSQL = 1;
    public static final int ORACLE10 = 2;
    public static final int POSTGIS = 3;
    public static final int GDB = 4;

    /** Global vs local declarations. */
    public static final int GLOBAL = 1;
    public static final int LOCAL = 2;

    /** Class categories. */
    public static final int UNKNOWN = -1;
    public static final int FEATURE = 1;
    public static final int CODELIST = 2;
    public static final int ENUMERATION = 3;
    public static final int MIXIN = 4;
    public static final int DATATYPE = 5;
    public static final int OBJECT = 6;
    public static final int GMLOBJECT = 6; // for backwards compatibility
    public static final int BASICTYPE = 7;
    public static final int UNION = 8;
    // 2013-10-14: UNIONDIRECT has been retired, use UNION and
    // ClassInfo.isUnionDirect() instead
    // public static final int UNIONDIRECT = 9;
    public static final int OKSTRAKEY = 11;
    public static final int OKSTRAFID = 12;
    public static final int FEATURECONCEPT = 13;
    public static final int ATTRIBUTECONCEPT = 14;
    public static final int VALUECONCEPT = 15;
    public static final int AIXMEXTENSION = 16;

    /* These constants are used when loading diagrams from the input model */
    public static final String ELEMENT_NAME_KEY_FOR_DIAGRAM_MATCHING = "NAME";
    public static final String IMAGE_INCLUSION_CLASS_REGEX = "NAME";
    public static final String IMAGE_INCLUSION_PACKAGE_REGEX = "NAME";

    /** Constants of the ShapeChangeConfiguration */
    public static final String INPUTELEMENTID = "INPUT";

    /**
     * Defines the name of the configuration parameter via which a
     * comma-separated list of package names to exclude from the model loading
     * can be provided.
     */
    public static final String PARAM_INPUT_EXCLUDED_PACKAGES = "excludedPackages";

    /**
     * Defines the name of the input parameter that provides the location of an
     * excel file with constraints (currently only SBVR rules are supported)
     * that shall be loaded before postprocessing the input model.
     */
    public static final String PARAM_CONSTRAINT_EXCEL_FILE = "constraintExcelFile";

    public static final String PARAM_ONLY_DEFERRABLE_OUTPUT_WRITE = "onlyDeferrableOutputWrite";
    public static final String PARAM_USE_STRING_INTERNING = "useStringInterning";
    public static final String PARAM_LANGUAGE = "language"; // TODO document

    /**
     * If set to array?, ShapeChange will use a memory optimized implementation
     * of tagged values when processing the model.Use this option when
     * processing very large models. ShapeChange can process 100+MB sized models
     * without problem. However, if processing involves many transformations and
     * target derivations you may hit a memory limit, which is determined by the
     * maximum amount of memory you can assign to the Java process in which
     * ShapeChange is running. On Windows machines that were used for
     * development, that limit was near 1.1GB.
     */
    public static final String PARAM_TAGGED_VALUE_IMPL = "taggedValueImplementation";

    /**
     * (applies to EA7 input only) Defines if global identifiers of model
     * elements shall be loaded ("true") or not. Default is to not load them.
     */
    public static final String PARAM_LOAD_GLOBAL_IDENTIFIERS = "loadGlobalIdentifiers";

    /**
     * Set this input parameter to <code>true</code> if constraints shall only
     * be loaded for classes and properties from the schemas selected for
     * processing (and ignoring all constraints from other packages).
     * <p>
     * Don't make use of this parameter if one of the classes from the selected
     * schema packages extends another class from an external package (e.g. an
     * ISO package) and needs to inherit constraints from that class!
     * <p>
     * This parameter is primarily a convenience mechanism to avoid loading and
     * parsing constraints from external packages (especially ISO packages) that
     * are irrelevant for processing. So on the one hand this can speed up model
     * loading. On the other hand, it can prevent messages about constraints
     * that were parsed from the elements of an external package from appearing
     * in the log.
     */
    public static final String PARAM_LOAD_CONSTRAINT_FOR_SEL_SCHEMAS_ONLY = "loadConstraintsForSelectedSchemasOnly";

    // Application schema defaults (namespace and version)
    public String xmlNamespaceDefault = "FIXME";
    public String xmlNamespaceAbbreviationDefault = "FIXME";
    public String appSchemaVersion = "unknown";

    // Name of the configuration file
    public String configFile = null;

    /** GML core namespaces */
    public String GML_NS = "http://www.opengis.net/gml/3.2";
    public static String GMLEXR_NS = "http://www.opengis.net/gml/3.3/exr";

    // Default target version of GML
    // Note: the use of ISO/TS 19139 requires the use of GML 3.2
    // This parameter is deprecated and should not be used
    // TODO remove usages from external targets
    public String gmlVersion = "3.2";

    /**
     * If set to true, schemas (from the model) will be processed in some stable
     * (not random) order. To change the processing order of the classes in a
     * specific Target use the targetParameter "sortedOutput" in the
     * configuration file.
     */
    public boolean sortedSchemaOutput = false;

    // Bugfixes:

    // Fix for bugs in Rose
    public boolean roseBugFixDuplicateGlobalDataTypes = true;

    // Fix for bugs in EA
    public boolean eaBugFixWrongID = true;
    public boolean eaBugFixPublicPackagesAreMarkedAsPrivate = true;
    public boolean eaIncludeExtentsions = true;

    // Fix for bugs in ArgoUML
    public boolean argoBugFixMissingDOCTYPE = false;

    // Temporary directory for ShapeChange run
    public static final String DEFAULT_TMP_DIR_PATH = "temp";
    public static final String TMP_DIR_PATH_PARAM = "tmpDirectory";
    protected File tmpDir = null;

    /**
     * Map of targets to generate from the input model. Keys are the names of
     * the Java classes which must implement the Target interface and values are
     * the requested processing modes, one of "enabled", "disabled",
     * "diagnostics-only".
     */
    protected HashMap<String, String> fTargets = new HashMap<String, String>();

    /** Hash table for additional parameters */
    protected HashMap<String, String> fParameters = new HashMap<String, String>();

    /**
     * Hash table for string replacements. Must not be reset, because
     * replacement information is not contained in the configuration itself!
     */
    protected HashMap<String, String> fReplace = new HashMap<String, String>();

    /** Hash table for type and element mappings */
    protected HashMap<String, MapEntry> fTypeMap = new HashMap<String, MapEntry>();

    /**
     * Key: type + "#" + xsdEncodingRule
     * <p>
     * Value: MapEntry with:
     * <ul>
     * <li>rule: ("direct")</li>
     * <li>p1: xmlType</li>
     * <li>(optionally) p2: xmlTypeType+"/"+xmlTypeContent</li>
     * </ul>
     */
    protected HashMap<String, MapEntry> fBaseMap = new HashMap<String, MapEntry>();
    protected HashMap<String, MapEntry> fElementMap = new HashMap<String, MapEntry>();
    protected HashMap<String, MapEntry> fAttributeMap = new HashMap<String, MapEntry>();
    protected HashMap<String, MapEntry> fAttributeGroupMap = new HashMap<String, MapEntry>();

    /** Map of type mapping tables for non-XMLSchema targets */
    protected HashMap<String, HashMap<String, MapEntry>> fTargetTypeMap = new HashMap<String, HashMap<String, MapEntry>>();

    /** Hash table for all stereotype and tag aliases */
    protected HashMap<String, String> fStereotypeAliases = new HashMap<String, String>();
    protected HashMap<String, String> fTagAliases = new HashMap<String, String>();

    /** Hash table for all descriptor sources */
    protected HashMap<String, String> fDescriptorSources = new HashMap<String, String>();

    /** Known descriptors */
    public static enum Descriptor {
        ALIAS("alias"), PRIMARYCODE("primaryCode"), DOCUMENTATION("documentation"), DEFINITION(
                "definition"), DESCRIPTION("description"), EXAMPLE("example"), LEGALBASIS(
                        "legalBasis"), DATACAPTURESTATEMENT("dataCaptureStatement"), LANGUAGE("language");

        private String name = null;

        Descriptor(String n) {
            name = n;
        };

        public String toString() {
            return name;
        };
    }

    public static final String DERIVED_DOCUMENTATION_DEFAULT_TEMPLATE = "[[definition]]";
    public static final String DERIVED_DOCUMENTATION_DEFAULT_NOVALUE = "";

    public static final String LF = System.getProperty("line.separator");
    public static final String DERIVED_DOCUMENTATION_INSPIRE_TEMPLATE = "-- Name --" + LF + "[[alias]]" + LF + LF
            + "-- Definition --" + LF + "[[definition]]" + LF + LF + "-- Description --" + LF + "[[description]]";

    /**
     * Hash table for external namespaces
     * <p>
     * key: namespace abbreviation / prefix; value: MapEntry (arg1: namespace,
     * arg2: location)
     */
    protected HashMap<String, MapEntry> fNamespaces = new HashMap<String, MapEntry>();

    /** Hash table for packages */
    protected HashMap<String, MapEntry> fPackages = new HashMap<String, MapEntry>();

    /**
     * Hash table for schema locations
     * <p>
     * key: namespace, value: location
     */
    protected HashMap<String, String> fSchemaLocations = new HashMap<String, String>();

    /** Hash table for schema requirements and conversion rules */
    protected HashSet<String> fAllRules = new HashSet<String>();
    protected HashSet<String> fRulesInEncRule = new HashSet<String>();

    /** Hash table for encoding rule extensions */
    protected HashMap<String, String> fExtendsEncRule = new HashMap<String, String>();

    /** documentation separators */
    protected String extractSeparator = null;
    protected String definitionSeparator = null;
    protected String descriptionSeparator = null;
    protected String nameSeparator = null;

    public String extractSeparator() {
        if (extractSeparator == null)
            extractSeparator = parameter("extractSeparator");
        return extractSeparator;
    }

    public String definitionSeparator() {
        if (definitionSeparator == null)
            definitionSeparator = parameter("definitionSeparator");
        return definitionSeparator;
    }

    public String descriptionSeparator() {
        if (descriptionSeparator == null)
            descriptionSeparator = parameter("descriptionSeparator");
        return descriptionSeparator;
    }

    public String nameSeparator() {
        if (nameSeparator == null)
            nameSeparator = parameter("nameSeparator");
        return nameSeparator;
    }

    // determines, if unset class descriptors are inherited from a superclass of the same name
    boolean getDescriptorsFromSupertypesInitialised = false;
    boolean getDescriptorsFromSupertypes = false;

    public boolean getDescriptorsFromSupertypes() {
        if (!getDescriptorsFromSupertypesInitialised) {
            getDescriptorsFromSupertypesInitialised = true;
            String s = parameter("inheritDocumentation");
            // still support deprecated parameter in the FeatureCatalogue target for backward compatibility
            if (s == null || !s.equals("true"))
                s = parameter(FeatureCatalogue.class.getName(), "inheritDocumentation");
            if (s != null && s.equals("true"))
                getDescriptorsFromSupertypes = true;
        }
        return getDescriptorsFromSupertypes;
    }

    // determines, if unset package, class or property descriptors are inherited from a dependency
    boolean getDescriptorsFromDependencyInitialised = false;
    boolean getDescriptorsFromDependency = true;

    public boolean getDescriptorsFromDepedency() {
        if (!getDescriptorsFromDependencyInitialised) {
            getDescriptorsFromDependencyInitialised = true;
            String s = parameter("documentationFromDependency");
            if (s != null && s.equals("false"))
                getDescriptorsFromDependency = false;
        }
        return getDescriptorsFromDependency;
    }

    // /**
    // * List of transformer configurations that directly reference the input
    // * element.
    // */
    // protected List<TransformerConfiguration> transformerConfigurations =
    // null;
    //
    // /**
    // * List of all target configurations that directly reference the input
    // * element.
    // */
    // protected List<TargetConfiguration> inputTargetConfigurations = null;

    /**
     * True, if constraints shall be created for properties, else false.
     */
    protected boolean constraintCreationForProperties = true;

    /**
     * True, if xxxEncodingRule tagged values shall be ignored (because the
     * model is wrong and needs cleanup), else false.
     */
    protected boolean ignoreEncodingRuleTaggedValues = false;

    protected boolean useStringInterning = false;
    protected boolean loadGlobalIds = false;
    protected String language = "en";

    /**
     * Set of class stereotypes for which constraints shall be created; null if
     * constraints for all classes shall be created.
     */
    protected HashSet<Integer> classTypesToCreateConstraintsFor = null;

    protected InputConfiguration inputConfig = null;
    protected Map<String, String> dialogParameters = null;
    protected Map<String, String> logParameters = null;
    protected ProcessConfiguration currentProcessConfig = null;
    protected List<TargetConfiguration> inputTargetConfigs = new ArrayList<TargetConfiguration>();
    protected List<TransformerConfiguration> inputTransformerConfigs = new ArrayList<TransformerConfiguration>();
    private String inputId = null;

    /**
     * Flag used to determine if a reset is invoked during loading (in which
     * case input as well as dialog and log parameters should be populated) or
     * before executing a transformation / target process.
     */
    private boolean resetUponLoadFlag = true;
    private List<TargetConfiguration> targetConfigs = null;

    protected File imageTmpDir = null;

    private Map<String, AIXMSchemaInfo> schemaInfos;

    /**
     * @return True, if constraints shall be created for properties, else false.
     */
    public boolean isConstraintCreationForProperties() {
        return this.constraintCreationForProperties;
    }

    /**
     * Determines if AIXM schema are being processed, which require special
     * treatment (due to the AIXM extension mechanism and because AIXM feature
     * types are dynamic features).
     *
     * @return <code>true</code> if the input configuration element has
     *         parameter 'isAIXM' with value 'true' (ignoring case), else
     *         <code>false</code>.
     */
    public boolean isAIXM() {
        return this.parameter("isAIXM") != null && this.parameter("isAIXM").equalsIgnoreCase("true");
    }

    public boolean isLoadConstraintsForSelectedSchemasOnly() {

        return this.parameter(PARAM_LOAD_CONSTRAINT_FOR_SEL_SCHEMAS_ONLY) != null
                && this.parameter(PARAM_LOAD_CONSTRAINT_FOR_SEL_SCHEMAS_ONLY).equalsIgnoreCase("true");
    }

    /**
     * Determines if only deferrable output writing shall be executed. This can
     * be used to transform the temporary XML with feature catalogue information
     * in a separate ShapeChange run. That run does no longer need to read the
     * UML model and can thus be executed using 64bit Java, which supports
     * bigger heap sizes that may be required to transform huge XML files.
     *
     * @return <code>true</code> if the input configuration element has
     *         parameter {@value #PARAM_ONLY_DEFERRABLE_OUTPUT_WRITE} with value
     *         'true' (ignoring case), else <code>false</code>.
     */
    public boolean isOnlyDeferrableOutputWrite() {
        return this.parameter(PARAM_ONLY_DEFERRABLE_OUTPUT_WRITE) != null
                && this.parameter(PARAM_ONLY_DEFERRABLE_OUTPUT_WRITE).equalsIgnoreCase("true");
    }

    /**
     * @return True, if xxxEncodingRule tagged values shall be ignored (because
     *         the model is wrong and needs cleanup), else false.
     */
    public boolean ignoreEncodingRuleTaggedValues() {
        return this.ignoreEncodingRuleTaggedValues;
    }

    public boolean isClassTypeToCreateConstraintsFor(int classCategory) {
        if (this.classTypesToCreateConstraintsFor == null)
            return true;
        else
            return this.classTypesToCreateConstraintsFor.contains(new Integer(classCategory));
    }

    /** A map entry. */
    protected void addTypeMapEntry(String k1, String k2, String s1, String s2) {
        fTypeMap.put(k1 + "#" + k2, new MapEntry(s1, s2));
    }

    protected void addTypeMapEntry(String k1, String k2, String s1, String s2, String s3) {
        fTypeMap.put(k1 + "#" + k2, new MapEntry(s1, s2, s3));
    }

    protected void addTypeMapEntry(String k1, String k2, String s1, String s2, String s3, String s4) {
        fTypeMap.put(k1 + "#" + k2, new MapEntry(s1, s2, s3, s4));
    }

    public MapEntry typeMapEntry(String k1, String k2) {
        String rule = k2;
        MapEntry me = null;
        while (me == null && rule != null) {
            me = fTypeMap.get(k1 + "#" + rule);
            rule = extendsEncRule(rule);
        }
        return me;
    }

    public void addTargetTypeMapEntry(String cls, String type, String rule, String ttype, String param) {
        HashMap<String, MapEntry> fclass = fTargetTypeMap.get(cls);
        if (fclass == null) {
            fclass = new HashMap<String, MapEntry>();
            fTargetTypeMap.put(cls, fclass);
        }
        fclass.put(type + "#" + rule, new MapEntry(rule, ttype, param));
    }

    public MapEntry targetTypeMapEntry(String cls, String type, String rule) {
        HashMap<String, MapEntry> fclass = fTargetTypeMap.get(cls);
        if (fclass == null)
            return null;
        MapEntry me = null;
        while (me == null && rule != null) {
            me = fclass.get(type + "#" + rule);
            rule = extendsEncRule(rule);
        }
        return me;
    }

    protected void addBaseMapEntry(String k1, String k2, String s1, String s2) {
        fBaseMap.put(k1 + "#" + k2, new MapEntry(s1, s2));
    }

    /**
     * Adds a new MapEntry to fBaseMap.
     * <p>
     * The key is k1 + "#" + k2.
     * <p>
     * The value is a MapEntry with:
     * <ul>
     * <li>rule: s1</li>
     * <li>p1: s2</li>
     * <li>p2: s3</li>
     * </ul>
     *
     * @param k1
     *            type
     * @param k2
     *            xsdEncodingRule
     * @param s1
     *            ("direct")
     * @param s2
     *            xmlType
     * @param s3
     *            xmlTypeType+"/"+xmlTypeContent
     */
    protected void addBaseMapEntry(String k1, String k2, String s1, String s2, String s3) {
        fBaseMap.put(k1 + "#" + k2, new MapEntry(s1, s2, s3));
    }

    /**
     * Tries to find a MapEntry that defines the mapping of a type to its
     * xmlType, based upon the given encoding rule or any rules it extends.
     *
     * @param k1
     *            type name
     * @param k2
     *            encoding rule name
     * @return MapEntry with rule=("direct"), p1=xmlType and (optionally, can be
     *         null) p2=xmlTypeType+"/"+xmlTypeContent
     */
    public MapEntry baseMapEntry(String k1, String k2) {
        String rule = k2;
        MapEntry me = null;
        while (me == null && rule != null) {
            me = fBaseMap.get(k1 + "#" + rule);
            rule = extendsEncRule(rule);
        }
        return me;
    }

    protected void addElementMapEntry(String k1, String k2, String s1, String s2) {
        fElementMap.put(k1 + "#" + k2, new MapEntry(s1, s2));
    }

    protected void addElementMapEntry(String k1, String k2, String s1, String s2, String s3) {
        fElementMap.put(k1 + "#" + k2, new MapEntry(s1, s2, s3));
    }

    public MapEntry elementMapEntry(String k1, String k2) {
        String rule = k2;
        MapEntry me = null;
        while (me == null && rule != null) {
            me = fElementMap.get(k1 + "#" + rule);
            rule = extendsEncRule(rule);
        }
        return me;
    }

    protected void addAttributeMapEntry(String k1, String k2, String s1) {
        fAttributeMap.put(k1 + "#" + k2, new MapEntry(s1));
    }

    public MapEntry attributeMapEntry(String k1, String k2) {
        String rule = k2;
        MapEntry me = null;
        while (me == null && rule != null) {
            me = fAttributeMap.get(k1 + "#" + rule);
            rule = extendsEncRule(rule);
        }
        return me;
    }

    protected void addAttributeGroupMapEntry(String k1, String k2, String s1) {
        fAttributeGroupMap.put(k1 + "#" + k2, new MapEntry(s1));
    }

    public MapEntry attributeGroupMapEntry(String k1, String k2) {
        String rule = k2;
        MapEntry me = null;
        while (me == null && rule != null) {
            me = fAttributeGroupMap.get(k1 + "#" + rule);
            rule = extendsEncRule(rule);
        }
        return me;
    }

    protected void addTarget(String k1, String k2) {
        fTargets.put(k1, k2);
    }

    public Vector<String> targets() {
        Vector<String> res = new Vector<String>();
        for (String t : fTargets.keySet()) {
            res.add(t);
        }
        return res;
    }

    public String targetMode(String tn) {
        if (tn == null)
            return "disabled";

        String s = fTargets.get(tn);
        if (s == null)
            return "disabled";

        return s;
    }

    public String setTargetMode(String tn, String mode) {
        return fTargets.put(tn, mode);
    }

    /**
     * @param k1
     * @return the value of the parameter with the given name, or
     *         <code>null</code> if the parameter does not exist
     */
    public String parameter(String k1) {
        return fParameters.get(k1);
    }

    public String parameter(String t, String k1) {
        return fParameters.get(t + "::" + k1);
    }

    /** This returns the names of all parms whose names match a regex pattern */
    public String[] parameterNamesByRegex(String t, String regex) {
        HashSet<String> pnames = new HashSet<String>();
        int lt2 = t.length() + 2;
        for (Entry<String, String> e : fParameters.entrySet()) {
            String key = e.getKey();
            if (key.startsWith(t + "::")) {
                if (Pattern.matches(regex, key.substring(lt2)))
                    pnames.add(key.substring(lt2));
            }
        }
        return pnames.toArray(new String[0]);
    }

    public void setParameter(String k1, String s1) {
        String s = replaceValue(s1);
        if (s != null)
            fParameters.put(k1, s);
        else
            fParameters.put(k1, s1);
    }

    public void setParameter(String t, String k1, String s1) {
        String s = replaceValue(s1);
        if (s != null)
            fParameters.put(t + "::" + k1, s);
        else
            fParameters.put(t + "::" + k1, s1);
    }

    public String replaceValue(String k1) {
        return fReplace.get(k1);
    }

    public void setReplaceValue(String k1, String s1) {
        fReplace.put(k1, s1);
    }

    /**
     * Adds a stereotype alias mapping.
     *
     * @param alias
     *            - the stereotype alias (in lower case)
     * @param wellknown
     *            - the wellknown stereotype (in lower case) to which the alias
     *            maps
     */
    protected void addStereotypeAlias(String alias, String wellknown) {
        fStereotypeAliases.put(alias, wellknown);
    }

    /**
     * Retrieves the wellknown stereotype to which the given alias maps, or
     * <code>null</code> if no such mapping exists. The alias will automatically
     * be converte to lower case to look up the mapping (the according key
     * values in the stereotype map have also been converted to lower case).
     *
     * @param alias
     *            stereotype for which a mapping to a wellknown stereotype is
     *            being looked up
     * @return the wellknown stereotype to which the alias maps, or
     *         <code>null</code> if no such mapping exists
     */
    public String stereotypeAlias(String alias) {
        return fStereotypeAliases.get(alias.toLowerCase());
    }

    /**
     * Adds a tag alias mapping.
     * 
     * @param alias
     *            - the tag alias (in lower case)
     * @param wellknown
     *            - the wellknown tag (in lower case) to which the alias
     *            maps
     */
    protected void addTagAlias(String alias, String wellknown) {
        fTagAliases.put(alias, wellknown);
    }

    /**
     * Retrieves the wellknown tag to which the given alias maps, or
     * <code>null</code> if no such mapping exists. The alias will automatically
     * be converte to lower case to look up the mapping (the according key
     * values in the tag map have also been converted to lower case).
     * 
     * @param alias
     *            tag for which a mapping to a wellknown tag is
     *            being looked up
     * @return the wellknown tag to which the alias maps, or
     *         <code>null</code> if no such mapping exists
     */
    public String tagAlias(String alias) {
        return fTagAliases.get(alias.toLowerCase());
    }

    /**
     * Adds a descriptor-source mapping.
     * 
     * @param descriptor
     *            - the descriptor (in lower case)
     * @param source
     *            - the source (in lower case) 
     */
    protected void addDescriptorSource(String descriptor, String source) {
        fDescriptorSources.put(descriptor, source);
    }

    /**
     * Retrieves the wellknown stereotype to which the given alias maps, or
     * <code>null</code> if no such mapping exists. The alias will automatically
     * be converte to lower case to look up the mapping (the according key
     * values in the stereotype map have also been converted to lower case).
     * 
     * @param descriptor
     *            - the descriptor (in lower case)
     * @return the source where the descriptor is represented in the model or
     *         <code>null</code> if no such mapping exists
     */
    public String descriptorSource(String descriptor) {
        return fDescriptorSources.get(descriptor.toLowerCase());
    }

    /**
     * @param k1
     *            namespace abbreviation / prefix
     * @param s1
     *            namespace
     * @param s2
     *            location
     */
    protected void addNamespace(String k1, String s1, String s2) {
        fNamespaces.put(k1, new MapEntry(s1, s2));
    }

    protected void addRule(String rule) {
        fAllRules.add(rule.toLowerCase());
    }

    public boolean hasRule(String rule) {
        return fAllRules.contains(rule.toLowerCase());
    }

    protected void addRule(String rule, String encRule) {
        fRulesInEncRule.add(rule.toLowerCase() + "#" + encRule.toLowerCase());
    }

    public boolean hasRule(String rule, String encRule) {
        boolean res = false;
        while (!res && encRule != null) {
            res = fRulesInEncRule.contains(rule.toLowerCase() + "#" + encRule.toLowerCase());
            encRule = extendsEncRule(encRule);
        }
        return res;
    }

    public boolean matchesEncRule(String encRule, String baseRule) {
        while (encRule != null) {
            if (encRule.equalsIgnoreCase(baseRule))
                return true;
            encRule = extendsEncRule(encRule);
        }
        return false;
    }

    protected void addExtendsEncRule(String rule1, String rule2) {
        fExtendsEncRule.put(rule1.toLowerCase(), rule2.toLowerCase());
    }

    protected String extendsEncRule(String rule1) {
        return fExtendsEncRule.get(rule1.toLowerCase());
    }

    protected void addPackage(String k1, String s1, String s2, String s3, String s4) {
        fPackages.put(k1, new MapEntry(s1, s2, s3, s4));
    }

    /**
     * @param k1
     *            namespace
     * @param s1
     *            location
     */
    public void addSchemaLocation(String k1, String s1) {
        // This will overwrite any previously existing value for the given key
        // k1. Order of schema location additions thus is important.
        fSchemaLocations.put(k1, s1);
    }

    /**
     * @param k1
     *            nsabr (namespace abbreviation / prefix)
     * @return the MapEntry belonging to the nsabr (with namespace as 'rule' and
     *         location as 'p1') - or <code>null</code> if the nsabr is unknown
     */
    protected MapEntry namespace(String k1) {
        MapEntry me = fNamespaces.get(k1);
        return me;
    }

    /**
     * Gets the namespace abbreviation / prefix for a given namespace as
     * declared via the configuration, or <code>null</code> if the configuration
     * does not contain information about the namespace.
     *
     * @param ns
     * @return
     */
    public String nsabrForNamespace(String ns) {

        for (String nsabr : fNamespaces.keySet()) {
            MapEntry me = fNamespaces.get(nsabr);
            if (me.rule.equals(ns)) {
                return nsabr;
            }
        }
        return null;
    }

    /**
     * @param k1
     *            nsabr (namespace abbreviation / prefix)
     * @return the full namespace
     */
    public String fullNamespace(String k1) {
        MapEntry me = fNamespaces.get(k1);
        if (me != null) {
            return me.rule;
        }
        return null;
    }

    public String nsOfPackage(String k1) {
        MapEntry me = fPackages.get(k1);
        if (me != null) {
            return me.rule;
        }
        return null;
    }

    public String nsabrOfPackage(String k1) {
        MapEntry me = fPackages.get(k1);
        if (me != null) {
            return me.p1;
        }
        return null;
    }

    public String xsdOfPackage(String k1) {
        MapEntry me = fPackages.get(k1);
        if (me != null) {
            return me.p2;
        }
        return null;
    }

    public String versionOfPackage(String k1) {
        MapEntry me = fPackages.get(k1);
        if (me != null) {
            return me.p3;
        }
        return null;
    }

    /**
     * @param k1
     *            - namespace
     * @return schema location, if defined, else <code>null</code>
     */
    public String schemaLocationOfNamespace(String k1) {
        String loc = fSchemaLocations.get(k1);
        /*
         * note, schema location may be omitted / null; example: DGIWG spatial
         * profile is not available online
         */
        return loc;
    }

    public String nameOfTarget(int targetId) {

        for (TargetIdentification ti : TargetIdentification.values()) {

            if (ti.getId() == targetId) {
                return ti.getName();
            }
        }

        return "Unknown (" + targetId + ")";

        // switch (targetId) {
        // case 0:
        // return "-reserved-";
        // case 1:
        // return "XML Schema";
        // case 2:
        // return "-reserved-";
        // case 3:
        // return "RDF";
        // case 4:
        // return "Definitions";
        // case 5:
        // return "Excel Mapping";
        // case 6:
        // return "KML XSLT";
        // case 7:
        // return "JSON Schema";
        // case 8:
        // return "Code List Dictionary";
        // case 9:
        // return "Feature Catalogue";
        // case 10:
        // // FIXME shouldn't this be something like "SQL DDL"?
        // return "Decoder";
        // case 13:
        // return "Replication XML Schema";
        //
        // // FIXME hevan: it seems that not all targets in ShapeChange are in
        // this switch-statement
        //
        // case 401:
        // return "Objektartenkatalog";
        // case 402:
        // return "AAA-Profil (3AP)";
        // case 404:
        // return "AAA-Modellart (3AM)";
        // }
        //
        // return "Unknown (" + targetId + ")";
    }

    public void loadConfiguration() throws ShapeChangeAbortException {

        InputStream configStream = null;
        if (configFile == null) {
            // load minimal configuration, if no configuration file has been
            // provided
            configFile = "/config/minimal.xml";
            configStream = getClass().getResourceAsStream(configFile);
            if (configStream == null) {
                configFile = "src/main/resources" + configFile;
                File file = new File(configFile);
                if (file.exists())
                    try {
                        configStream = new FileInputStream(file);
                    } catch (FileNotFoundException e1) {
                        throw new ShapeChangeAbortException("Minimal configuration file not found: " + configFile);
                    }
                else {
                    URL url;
                    String configURL = "http://shapechange.net/resources/config/minimal.xml";
                    try {
                        url = new URL(configURL);
                        configStream = url.openStream();
                    } catch (MalformedURLException e) {
                        throw new ShapeChangeAbortException("Minimal configuration file not accessible from: "
                                + configURL + " (malformed URL)");
                    } catch (IOException e) {
                        throw new ShapeChangeAbortException(
                                "Minimal configuration file not accessible from: " + configURL + " (IO error)");
                    }
                }
            }
        } else {
            File file = new File(configFile);
            if (file == null || !file.exists()) {
                try {
                    configStream = (new URL(configFile)).openStream();
                } catch (MalformedURLException e) {
                    throw new ShapeChangeAbortException(
                            "No configuration file found at " + configFile + " (malformed URL)");
                } catch (IOException e) {
                    throw new ShapeChangeAbortException(
                            "No configuration file found at " + configFile + " (IO exception)");
                }
            } else {
                try {
                    configStream = new FileInputStream(file);
                } catch (FileNotFoundException e) {
                    throw new ShapeChangeAbortException("No configuration file found at " + configFile);
                }
            }
            if (configStream == null) {
                throw new ShapeChangeAbortException("No configuration file found at " + configFile);
            }
        }

        DocumentBuilder builder = null;
        ShapeChangeErrorHandler handler = null;
        try {
            System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
                    "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            factory.setValidating(true);
            factory.setFeature("http://apache.org/xml/features/validation/schema", true);
            factory.setIgnoringElementContentWhitespace(true);
            factory.setIgnoringComments(true);
            factory.setXIncludeAware(true);
            factory.setFeature("http://apache.org/xml/features/xinclude/fixup-base-uris", false);
            builder = factory.newDocumentBuilder();
            handler = new ShapeChangeErrorHandler();
            builder.setErrorHandler(handler);
        } catch (FactoryConfigurationError e) {
            throw new ShapeChangeAbortException("Unable to get a document builder factory.");
        } catch (ParserConfigurationException e) {
            throw new ShapeChangeAbortException("XML Parser was unable to be configured.");
        }

        // parse file
        try {
            Document document = builder.parse(configStream);
            if (handler.errorsFound()) {
                throw new ShapeChangeAbortException("Invalid configuration file.");
            }

            // parse input element specific content
            NodeList nl = document.getElementsByTagName("input");
            Element inputElement = (Element) nl.item(0);
            if (inputElement.hasAttribute("id")) {
                inputId = inputElement.getAttribute("id").trim();
                if (inputId.length() == 0) {
                    inputId = null;
                }
            } else {
                inputId = Options.INPUTELEMENTID;
            }

            Map<String, String> inputParameters = new HashMap<String, String>();
            nl = inputElement.getElementsByTagName("parameter");
            for (int j = 0; j < nl.getLength(); j++) {
                Element e = (Element) nl.item(j);
                String key = e.getAttribute("name");
                String val = e.getAttribute("value");
                inputParameters.put(key, val);
            }

            Map<String, String> stereotypeAliases = new HashMap<String, String>();
            nl = inputElement.getElementsByTagName("StereotypeAlias");
            for (int j = 0; j < nl.getLength(); j++) {
                Element e = (Element) nl.item(j);
                String key = e.getAttribute("alias");
                String val = e.getAttribute("wellknown");

                // case shall be ignored
                key = key.toLowerCase();
                val = val.toLowerCase();

                stereotypeAliases.put(key, val);
            }

            Map<String, String> tagAliases = new HashMap<String, String>();
            nl = inputElement.getElementsByTagName("TagAlias");
            for (int j = 0; j < nl.getLength(); j++) {
                Element e = (Element) nl.item(j);
                String key = e.getAttribute("alias");
                String val = e.getAttribute("wellknown");

                // case not to be ignored for tagged values at the moment
                // key = key.toLowerCase();
                // val = val.toLowerCase();

                tagAliases.put(key, val);
            }

            Map<String, String> descriptorSources = new HashMap<String, String>();
            nl = inputElement.getElementsByTagName("DescriptorSource");
            for (int j = 0; j < nl.getLength(); j++) {
                Element e = (Element) nl.item(j);
                String key = e.getAttribute("descriptor");
                String val = e.getAttribute("source");

                // case shall be ignored for descriptor and source
                key = key.toLowerCase();
                val = val.toLowerCase();

                if (val.equals("sc:extract")) {
                    String s = e.getAttribute("token");
                    val += "#" + (s == null ? "" : s);
                } else if (val.equals("tag")) {
                    String s = e.getAttribute("tag");
                    val += "#" + (s == null ? "" : s);
                }

                descriptorSources.put(key, val);
            }

            Map<String, PackageInfoConfiguration> packageInfos = parsePackageInfos(inputElement);

            this.inputConfig = new InputConfiguration(inputId, inputParameters, stereotypeAliases, tagAliases,
                    descriptorSources, packageInfos);

            // parse dialog specific parameters
            nl = document.getElementsByTagName("dialog");
            if (nl != null && nl.getLength() != 0) {
                for (int k = 0; k < nl.getLength(); k++) {
                    Node node = nl.item(k);
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        Element dialogElement = (Element) node;
                        this.dialogParameters = parseParameters(dialogElement, "parameter");
                    }
                }
            }

            // parse log specific parameters
            nl = document.getElementsByTagName("log");
            if (nl != null && nl.getLength() != 0) {
                for (int k = 0; k < nl.getLength(); k++) {
                    Node node = nl.item(k);
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        Element logElement = (Element) node;
                        this.logParameters = parseParameters(logElement, "parameter");
                    }
                }
            }

            // add standard rules, just so that configured rules can be
            // validated
            addStandardRules();

            // Load transformer configurations (if any are provided in the
            // configuration file)
            Map<String, TransformerConfiguration> transformerConfigs = parseTransformerConfigurations(document);

            // Load target configurations
            this.targetConfigs = parseTargetConfigurations(document);

            this.resetFields();
            // this.resetUponLoadFlag = false;

            // TBD discuss if there's a better way to support rule and
            // requirements matching for the input model
            // TBD apparently the matching requires all
            // applicable encoding rules to be known up front
            for (TargetConfiguration tgtConfig : targetConfigs) {

                // System.out.println(tgtConfig);
                String className = tgtConfig.getClassName();
                String mode = tgtConfig.getProcessMode().name();

                // set targets and their mode; if a target occurs multiple
                // times, keep the enabled one(s)
                if (!this.fTargets.containsKey(className)) {
                    addTarget(className, mode);

                } else {
                    if (this.fTargets.get(className).equals(ProcessMode.disabled)) {
                        // set targets and their mode; if a target occurs
                        // multiple times, keep the enabled one(s)
                        addTarget(className, mode);
                    }
                }

                // ensure that we have all the rules from all non-disabled
                // targets
                // we repeat this for the same target (if it is not disabled) to
                // ensure that we get the union of all encoding rules
                if (!tgtConfig.getProcessMode().equals(ProcessMode.disabled)) {
                    for (ProcessRuleSet prs : tgtConfig.getRuleSets().values()) {
                        String nam = prs.getName();
                        String ext = prs.getExtendedRuleSetName();
                        addExtendsEncRule(nam, ext);

                        if (prs.hasAdditionalRules()) {
                            for (String rule : prs.getAdditionalRules()) {
                                addRule(rule, nam);
                            }
                        }
                    }
                }

                /*
                 * looks like we also need parameters like defaultEncodingRule
                 * !!! IF THERE ARE DIFFERENT DEFAULT ENCODING RULES FOR
                 * DIFFERENT TARGETS (WITH SAME CLASS) THIS WONT WORK!!!
                 */
                for (String paramName : tgtConfig.getParameters().keySet()) {
                    setParameter(className, paramName, tgtConfig.getParameters().get(paramName));
                }

                // in order for the input model load not to produce warnings,
                // we also need to load the map entries
                if (tgtConfig instanceof TargetXmlSchemaConfiguration) {

                    TargetXmlSchemaConfiguration config = (TargetXmlSchemaConfiguration) tgtConfig;

                    // add xml schema namespace information
                    for (XmlNamespace xns : config.getXmlNamespaces()) {
                        addNamespace(xns.getNsabr(), xns.getNs(), xns.getLocation());
                        // if (xns.getLocation() != null) {
                        addSchemaLocation(xns.getNs(), xns.getLocation());
                        // }
                    }

                    // add xsd map entries
                    for (XsdMapEntry xsdme : config.getXsdMapEntries()) {

                        for (String xsdEncodingRule : xsdme.getEncodingRules()) {

                            String type = xsdme.getType();
                            String xmlPropertyType = xsdme.getXmlPropertyType();
                            String xmlElement = xsdme.getXmlElement();
                            String xmlTypeContent = xsdme.getXmlTypeContent();
                            String xmlTypeNilReason = xsdme.getXmlTypeNilReason();
                            String xmlType = xsdme.getXmlType();
                            String xmlTypeType = xsdme.getXmlTypeType();
                            String xmlAttribute = xsdme.getXmlAttribute();
                            String xmlAttributeGroup = xsdme.getXmlAttributeGroup();

                            if (xmlPropertyType != null) {
                                if (xmlPropertyType.equals("_P_") && xmlElement != null) {
                                    addTypeMapEntry(type, xsdEncodingRule, "propertyType", xmlElement);
                                } else if (xmlPropertyType.equals("_MP_") && xmlElement != null) {
                                    addTypeMapEntry(type, xsdEncodingRule, "metadataPropertyType", xmlElement);
                                } else {
                                    addTypeMapEntry(type, xsdEncodingRule, "direct", xmlPropertyType,
                                            xmlTypeType + "/" + xmlTypeContent, xmlTypeNilReason);
                                }
                            }
                            if (xmlElement != null) {
                                addElementMapEntry(type, xsdEncodingRule, "direct", xmlElement);
                            }
                            if (xmlType != null) {
                                addBaseMapEntry(type, xsdEncodingRule, "direct", xmlType,
                                        xmlTypeType + "/" + xmlTypeContent);
                            }
                            if (xmlAttribute != null) {
                                addAttributeMapEntry(type, xsdEncodingRule, xmlAttribute);
                            }
                            if (xmlAttributeGroup != null) {
                                addAttributeGroupMapEntry(type, xsdEncodingRule, xmlAttributeGroup);
                            }
                        }
                    }
                } else {

                    // add map entries for Target (no need to do this for
                    // transformers)
                    for (ProcessMapEntry pme : tgtConfig.getMapEntries()) {
                        addTargetTypeMapEntry(tgtConfig.getClassName(), pme.getType(), pme.getRule(),
                                pme.getTargetType(), pme.getParam());
                    }
                }
            }

            // create "tree"
            for (TargetConfiguration tgtConfig : targetConfigs) {
                for (String inputIdref : tgtConfig.getInputIds()) {
                    if (inputIdref.equals(getInputId())) {
                        this.inputTargetConfigs.add(tgtConfig);
                    } else {
                        transformerConfigs.get(inputIdref).addTarget(tgtConfig);
                    }
                }
            }

            for (TransformerConfiguration trfConfig : transformerConfigs.values()) {
                String inputIdref = trfConfig.getInputId();
                if (inputIdref.equals(getInputId())) {
                    this.inputTransformerConfigs.add(trfConfig);
                } else {
                    transformerConfigs.get(inputIdref).addTransformer(trfConfig);
                }
            }

            // Determine constraint creation handling parameters:
            String classTypesToCreateConstraintsFor = parameter("classTypesToCreateConstraintsFor");
            if (classTypesToCreateConstraintsFor != null) {
                classTypesToCreateConstraintsFor = classTypesToCreateConstraintsFor.trim();
                if (classTypesToCreateConstraintsFor.length() > 0) {
                    String[] stereotypes = classTypesToCreateConstraintsFor.split("\\W*,\\W*");
                    this.classTypesToCreateConstraintsFor = new HashSet<Integer>();
                    for (String stereotype : stereotypes) {
                        String sForCons = stereotype.toLowerCase();
                        if (sForCons.equals("enumeration")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(ENUMERATION));
                        } else if (sForCons.equals("codelist")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(CODELIST));
                        } else if (sForCons.equals("schluesseltabelle")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(OKSTRAKEY));
                        } else if (sForCons.equals("fachid")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(OKSTRAFID));
                        } else if (sForCons.equals("datatype")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(DATATYPE));
                        } else if (sForCons.equals("union")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(UNION));
                        } else if (sForCons.equals("featureconcept")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(FEATURECONCEPT));
                        } else if (sForCons.equals("attributeconcept")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(ATTRIBUTECONCEPT));
                        } else if (sForCons.equals("valueconcept")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(VALUECONCEPT));
                        } else if (sForCons.equals("interface")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(MIXIN));
                        } else if (sForCons.equals("basictype")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(BASICTYPE));
                        } else if (sForCons.equals("adeelement")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(FEATURE));
                        } else if (sForCons.equals("featuretype")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(FEATURE));
                        } else if (sForCons.equals("type")) {
                            this.classTypesToCreateConstraintsFor.add(new Integer(OBJECT));
                        } else {
                            this.classTypesToCreateConstraintsFor.add(new Integer(UNKNOWN));
                        }
                    }
                }
            }

            String constraintCreationForProperties = parameter("constraintCreationForProperties");
            if (constraintCreationForProperties != null) {
                if (constraintCreationForProperties.trim().equalsIgnoreCase("false")) {
                    this.constraintCreationForProperties = false;
                }
            }

            /*
             * TODO add documentation
             */
            String ignoreEncodingRuleTaggedValues = parameter("ignoreEncodingRuleTaggedValues");

            if (ignoreEncodingRuleTaggedValues != null) {
                if (ignoreEncodingRuleTaggedValues.trim().equalsIgnoreCase("true")) {
                    this.ignoreEncodingRuleTaggedValues = true;
                }
            }

            String useStringInterning_value = parameter(PARAM_USE_STRING_INTERNING);

            if (useStringInterning_value != null && useStringInterning_value.trim().equalsIgnoreCase("true")) {
                this.useStringInterning = true;
            }

            String loadGlobalIds_value = this.parameter(PARAM_LOAD_GLOBAL_IDENTIFIERS);

            if (loadGlobalIds_value != null && loadGlobalIds_value.trim().equalsIgnoreCase("true")) {
                this.loadGlobalIds = true;
            }

            String language_value = inputConfig.getParameters().get(PARAM_LANGUAGE);

            if (language_value != null && !language_value.trim().isEmpty()) {
                this.language = language_value.trim().toLowerCase();
            }

        } catch (SAXException e) {
            String m = e.getMessage();
            if (m != null) {
                throw new ShapeChangeAbortException(
                        "Error while loading configuration file: " + System.getProperty("line.separator") + m);
            } else {
                e.printStackTrace(System.err);
                throw new ShapeChangeAbortException("Error while loading configuration file: "
                        + System.getProperty("line.separator") + System.err);
            }
        } catch (IOException e) {
            String m = e.getMessage();
            if (m != null) {
                throw new ShapeChangeAbortException(
                        "Error while loading configuration file: " + System.getProperty("line.separator") + m);
            } else {
                e.printStackTrace(System.err);
                throw new ShapeChangeAbortException("Error while loading configuration file: "
                        + System.getProperty("line.separator") + System.err);
            }
        }

        MapEntry nsme = namespace("gml");
        if (nsme != null) {
            GML_NS = nsme.rule;
        }
    }

    /**
     *
     */
    public void resetFields() {

        // reset fields

        fTargets = new HashMap<String, String>();
        fParameters = new HashMap<String, String>();
        fTypeMap = new HashMap<String, MapEntry>();
        fBaseMap = new HashMap<String, MapEntry>();
        fElementMap = new HashMap<String, MapEntry>();
        fAttributeMap = new HashMap<String, MapEntry>();
        fAttributeGroupMap = new HashMap<String, MapEntry>();
        fTargetTypeMap = new HashMap<String, HashMap<String, MapEntry>>();
        fStereotypeAliases = new HashMap<String, String>();
        fTagAliases = new HashMap<String, String>();
        fDescriptorSources = new HashMap<String, String>();
        fNamespaces = new HashMap<String, MapEntry>();
        fPackages = new HashMap<String, MapEntry>();
        fSchemaLocations = new HashMap<String, String>();
        fAllRules = new HashSet<String>();
        fRulesInEncRule = new HashSet<String>();
        fExtendsEncRule = new HashMap<String, String>();

        // repopulate fields

        // set standard parameters first
        setStandardParameters();

        addStandardRules();

        if (resetUponLoadFlag) {
            // add all parameters from input
            for (String key : inputConfig.getParameters().keySet()) {
                setParameter(key, inputConfig.getParameters().get(key));
            }

            // add all stereotype aliases
            for (String key : inputConfig.getStereotypeAliases().keySet()) {
                addStereotypeAlias(key, inputConfig.getStereotypeAliases().get(key));
            }

            // add all tag aliases
            for (String key : inputConfig.getTagAliases().keySet()) {
                addTagAlias(key, inputConfig.getTagAliases().get(key));
            }

            // add all descriptor sources
            for (String key : inputConfig.getDescriptorSources().keySet()) {
                addDescriptorSource(key, inputConfig.getDescriptorSources().get(key));
            }

            // add all package infos
            for (PackageInfoConfiguration pic : inputConfig.getPackageInfos().values()) {
                addPackage(pic.getPackageName(), pic.getNs(), pic.getNsabr(), pic.getXsdDocument(),
                        pic.getVersion());
                // ensure that the package information is also added to the
                // schema location map
                addSchemaLocation(pic.getNs(), pic.getXsdDocument());
            }

            // add all dialog parameters
            if (dialogParameters != null) {
                for (String key : dialogParameters.keySet()) {
                    setParameter(key, dialogParameters.get(key));
                }
            }

            // add all log parameters
            if (logParameters != null) {
                for (String key : logParameters.keySet()) {
                    setParameter(key, logParameters.get(key));
                }
            }
        }

        if (currentProcessConfig != null) {

            this.gmlVersion = currentProcessConfig.getGmlVersion();

            // no need to set all the fields for transformers, because they
            // retrieve them directly

            // initialise fields for common targets and xml schema targets
            if (currentProcessConfig instanceof TargetConfiguration) {
                // add all parameters from current process configuration (only
                // required
                // for targets)
                for (String name : currentProcessConfig.getParameters().keySet()) {
                    setParameter(currentProcessConfig.getClassName(), name,
                            currentProcessConfig.getParameters().get(name));
                }

                // add target mode
                addTarget(currentProcessConfig.getClassName(), currentProcessConfig.getProcessMode().name());

                // add encoding rules
                for (ProcessRuleSet prs : currentProcessConfig.getRuleSets().values()) {
                    String nam = prs.getName();
                    String ext = prs.getExtendedRuleSetName();
                    addExtendsEncRule(nam, ext);
                    if (prs.hasAdditionalRules()) {
                        for (String rule : prs.getAdditionalRules()) {
                            addRule(rule, nam);
                        }
                    }
                }
            }

            if (currentProcessConfig.getClassName().equals(TargetFOL2SchematronClass)
                    && currentProcessConfig.getParameters().get("defaultEncodingRule") != null) {
                /*
                 * The execution of the FOL2Schematron target only generates
                 * Schematron, not XML Schema. We want the derivation of
                 * Schematron code to take into account the default encoding
                 * rule that we configured in the FOL2Schematron target. Because
                 * Schematron generation depends on how the XML Schema of model
                 * elements looks like, we must take the XSD encoding into
                 * account. We have a standard value for the XML Schema default
                 * encoding rule, and encoding rule checks for 'xsd' depend upon
                 * that specific parameter. However, the parameter is bound to
                 * the XMLSchema target class name. Thus if we have a
                 * defaultEncodingRule set in the FOL2Schematron target we must
                 * overwrite that parameter for the XML Schema target.
                 */
                this.setParameter(TargetXmlSchemaClass, "defaultEncodingRule",
                        currentProcessConfig.getParameters().get("defaultEncodingRule"));
            }

            if (currentProcessConfig instanceof TargetXmlSchemaConfiguration) {

                TargetXmlSchemaConfiguration config = (TargetXmlSchemaConfiguration) currentProcessConfig;

                // add xml schema namespace information
                for (XmlNamespace xns : config.getXmlNamespaces()) {
                    addNamespace(xns.getNsabr(), xns.getNs(), xns.getLocation());
                    // if (xns.getLocation() != null) {
                    addSchemaLocation(xns.getNs(), xns.getLocation());
                    // }
                }

                // add xsd map entries
                for (XsdMapEntry xsdme : config.getXsdMapEntries()) {

                    for (String xsdEncodingRule : xsdme.getEncodingRules()) {

                        String type = xsdme.getType();
                        String xmlPropertyType = xsdme.getXmlPropertyType();
                        String xmlElement = xsdme.getXmlElement();
                        String xmlTypeContent = xsdme.getXmlTypeContent();
                        String xmlTypeNilReason = xsdme.getXmlTypeNilReason();
                        String xmlType = xsdme.getXmlType();
                        String xmlTypeType = xsdme.getXmlTypeType();
                        String xmlAttribute = xsdme.getXmlAttribute();
                        String xmlAttributeGroup = xsdme.getXmlAttributeGroup();

                        if (xmlPropertyType != null) {
                            if (xmlPropertyType.equals("_P_") && xmlElement != null) {
                                addTypeMapEntry(type, xsdEncodingRule, "propertyType", xmlElement);
                            } else if (xmlPropertyType.equals("_MP_") && xmlElement != null) {
                                addTypeMapEntry(type, xsdEncodingRule, "metadataPropertyType", xmlElement);
                            } else {
                                addTypeMapEntry(type, xsdEncodingRule, "direct", xmlPropertyType,
                                        xmlTypeType + "/" + xmlTypeContent, xmlTypeNilReason);
                            }
                        }
                        if (xmlElement != null) {
                            addElementMapEntry(type, xsdEncodingRule, "direct", xmlElement);
                        }
                        if (xmlType != null) {
                            addBaseMapEntry(type, xsdEncodingRule, "direct", xmlType,
                                    xmlTypeType + "/" + xmlTypeContent);
                        }
                        if (xmlAttribute != null) {
                            addAttributeMapEntry(type, xsdEncodingRule, xmlAttribute);
                        }
                        if (xmlAttributeGroup != null) {
                            addAttributeGroupMapEntry(type, xsdEncodingRule, xmlAttributeGroup);
                        }
                    }
                }
            } else {

                // add map entries for Target (no need to do this for
                // transformers)
                for (ProcessMapEntry pme : currentProcessConfig.getMapEntries()) {
                    addTargetTypeMapEntry(currentProcessConfig.getClassName(), pme.getType(), pme.getRule(),
                            pme.getTargetType(), pme.getParam());
                }

                // TODO store namespace info
            }
        }

        MapEntry nsme = namespace("gml");
        if (nsme != null) {
            GML_NS = nsme.rule;
        }

    }

    private void setStandardParameters() {
        setParameter("reportLevel", "INFO");
        setParameter("xsltFile", "src/main/resources/xslt/result.xsl");
        setParameter("appSchemaName", "");
        setParameter("appSchemaNameRegex", "");
        setParameter("appSchemaNamespaceRegex", "");
        setParameter("publicOnly", "true");
        setParameter("inputFile", "http://shapechange.net/resources/test/test.xmi");
        setParameter("inputModelType", "XMI10");
        setParameter("logFile", "log.xml");
        setParameter("representTaggedValues", "");
        setParameter("addTaggedValues", "");
        setParameter("extractSeparator", "--IMPROBABLE--DUMMY--SEPARATOR--");
        setParameter("definitionSeparator", "-- Definition --");
        setParameter("descriptionSeparator", "-- Description --");
        setParameter("nameSeparator", "-- Name --");
        setParameter("outputDirectory", SystemUtils.getUserDir().getPath());
        setParameter("sortedSchemaOutput", "true");
        setParameter("sortedOutput", "true");
        setParameter("oclConstraintTypeRegex", "(OCL|Invariant)");
        setParameter("folConstraintTypeRegex", "(SBVR)");
        setParameter(Options.TargetXmlSchemaClass, "defaultEncodingRule", Options.ISO19136_2007);
        setParameter(Options.TargetXmlSchemaClass, "gmlVersion", "3.2");
        setParameter(Options.TargetOWLISO19150Class, "defaultEncodingRule", Options.ISO19150_2014);
        setParameter(Options.TargetSQLClass, "defaultEncodingRule", Options.SQL);
    }

    private Map<String, PackageInfoConfiguration> parsePackageInfos(Element inputElement) {

        Map<String, PackageInfoConfiguration> result = new HashMap<String, PackageInfoConfiguration>();

        NodeList nl = inputElement.getElementsByTagName("PackageInfo");

        for (int j = 0; j < nl.getLength(); j++) {

            Element e = (Element) nl.item(j);

            String name = e.getAttribute("packageName");
            String nsabr = e.getAttribute("nsabr");
            String ns = e.getAttribute("ns");
            String xsdDocument = e.getAttribute("xsdDocument");
            String version = e.getAttribute("version");

            PackageInfoConfiguration pic = new PackageInfoConfiguration(name, nsabr, ns, xsdDocument, version);

            result.put(name, pic);
        }

        return result;
    }

    private List<TargetConfiguration> parseTargetConfigurations(Document configurationDocument)
            throws ShapeChangeAbortException {

        List<TargetConfiguration> tgtConfigs = new ArrayList<TargetConfiguration>();

        NodeList tgtsNl = configurationDocument.getElementsByTagName("targets");

        for (int i = 0; i < tgtsNl.getLength(); i++) {
            Node tgtsN = tgtsNl.item(i);
            NodeList tgtNl = tgtsN.getChildNodes();

            // look for all Target/TargetXmlSchema elements in the "targets"
            // Node
            for (int j = 0; j < tgtNl.getLength(); j++) {
                Node tgtN = tgtNl.item(j);

                if (tgtN.getNodeType() == Node.ELEMENT_NODE) {
                    Element tgtE = (Element) tgtN;

                    // parse content of target element

                    // get name of config element for decision which type of
                    // TargetConfiguration to instantiate later on
                    String tgtType = tgtE.getNodeName();

                    // get class name
                    String tgtConfigName = tgtE.getAttribute("class");

                    // get mode
                    ProcessMode tgtMode = this.parseMode(tgtE);

                    Map<String, String> processParameters = parseParameters(tgtE, "targetParameter");

                    // now look up all ProcessRuleSet elements, if there are
                    // any
                    Map<String, ProcessRuleSet> processRuleSets = parseRuleSets(tgtE, "EncodingRule", true);

                    // get the target inputs - can be null, then set it to
                    // global input element
                    Set<String> tgtConfigInputs;
                    if (tgtE.hasAttribute("inputs")) {
                        String[] inputs = tgtE.getAttribute("inputs").split("\\s");
                        tgtConfigInputs = new HashSet<String>(Arrays.asList(inputs));
                    } else {
                        tgtConfigInputs = new HashSet<String>();
                        tgtConfigInputs.add(getInputId());
                    }

                    Element advancedProcessConfigurations = parseAdvancedProcessConfigurations(tgtE);

                    TargetConfiguration tgtConfig;
                    if (tgtType.equals("Target")) {
                        // now look up all ProcessMapEntry elements, if there
                        // are any
                        List<ProcessMapEntry> processMapEntries = parseProcessMapEntries(tgtE, "MapEntry");

                        // also parse namespaces, if there are any
                        List<Namespace> namespaces = parseNamespaces(tgtE);

                        // create target config and add it to list
                        tgtConfig = new TargetConfiguration(tgtConfigName, tgtMode, processParameters,
                                processRuleSets, processMapEntries, tgtConfigInputs, namespaces,
                                advancedProcessConfigurations);

                    } else if (tgtType.equals("TargetOwl")) {

                        // now look up all ProcessMapEntry elements, if there
                        // are any
                        List<ProcessMapEntry> processMapEntries = parseProcessMapEntries(tgtE, "MapEntry");

                        // also parse namespaces, if there are any
                        List<Namespace> namespaces = parseNamespaces(tgtE);

                        // also parse stereotype mappings, if there are any
                        Map<String, String> stereotypeMappings = parseStereotypeMappings(tgtE);

                        // create target owl config and add it to list
                        tgtConfig = new TargetOwlConfiguration(tgtConfigName, tgtMode, processParameters,
                                processRuleSets, processMapEntries, tgtConfigInputs, namespaces, stereotypeMappings,
                                advancedProcessConfigurations);
                    } else {
                        // We're dealing with a TargetXmlSchema element

                        List<XsdMapEntry> xsdMapEntries = parseXsdMapEntries(tgtE);

                        List<XmlNamespace> xmlNamespaces = parseXmlNamespaces(tgtE);

                        tgtConfig = new TargetXmlSchemaConfiguration(tgtConfigName, tgtMode, processParameters,
                                processRuleSets, null, xsdMapEntries, xmlNamespaces, tgtConfigInputs,
                                advancedProcessConfigurations);
                    }
                    tgtConfigs.add(tgtConfig);
                }
            }
        }
        return tgtConfigs;
    }

    private Element parseAdvancedProcessConfigurations(Element processElement) {

        NodeList apcNl = processElement.getElementsByTagName("advancedProcessConfigurations");
        Node apcN;
        Element apcE;

        if (apcNl != null && apcNl.getLength() != 0) {

            for (int k = 0; k < apcNl.getLength(); k++) {

                apcN = apcNl.item(k);
                if (apcN.getNodeType() == Node.ELEMENT_NODE) {

                    apcE = (Element) apcN;

                    /*
                     * there can only be one 'advancedProcessConfigurations'
                     * element
                     */
                    return apcE;
                }
            }
        }

        // no 'advancedProcessConfigurations' element found
        return null;
    }

    private Map<String, String> parseStereotypeMappings(Element targetElement) {

        Map<String, String> result = new HashMap<String, String>();

        NodeList smNl = targetElement.getElementsByTagName("StereotypeMapping");
        Node smN;
        Element smE;

        if (smNl != null && smNl.getLength() != 0) {

            for (int k = 0; k < smNl.getLength(); k++) {

                smN = smNl.item(k);
                if (smN.getNodeType() == Node.ELEMENT_NODE) {

                    smE = (Element) smN;

                    String wellknownStereotype = smE.getAttribute("wellknown");
                    String mapsTo = smE.getAttribute("mapsTo");

                    result.put(wellknownStereotype.toLowerCase(), mapsTo);
                }
            }
        }
        return result;
    }

    private List<Namespace> parseNamespaces(Element targetElement) {

        List<Namespace> result = new ArrayList<Namespace>();

        NodeList namespacesNl = targetElement.getElementsByTagName("Namespace");
        Node namespaceN;
        Element namespaceE;

        if (namespacesNl != null && namespacesNl.getLength() != 0) {

            for (int k = 0; k < namespacesNl.getLength(); k++) {

                namespaceN = namespacesNl.item(k);
                if (namespaceN.getNodeType() == Node.ELEMENT_NODE) {

                    namespaceE = (Element) namespaceN;

                    String nsabr = namespaceE.getAttribute("nsabr");
                    String ns = namespaceE.getAttribute("ns");
                    String location = namespaceE.hasAttribute("location")
                            ? location = namespaceE.getAttribute("location")
                            : null;

                    result.add(new Namespace(nsabr, ns, location));
                }
            }
        }
        return result;
    }

    private Map<String, String> parseParameters(Element processElement, String parameterElementTagName)
            throws ShapeChangeAbortException {

        Map<String, String> result = new HashMap<String, String>();
        NodeList parametersNl = processElement.getElementsByTagName(parameterElementTagName);
        Node parameterN;
        Element parameterE;

        if (parametersNl != null && parametersNl.getLength() != 0) {

            for (int k = 0; k < parametersNl.getLength(); k++) {

                parameterN = parametersNl.item(k);
                if (parameterN.getNodeType() == Node.ELEMENT_NODE) {

                    parameterE = (Element) parameterN;

                    result.put(parameterE.getAttribute("name"), parameterE.getAttribute("value"));
                }

            }
        }

        String s = result.get("gmlVersion");
        if (s != null) {
            if (s.equals("3.3") || s.equals("3.2") || s.equals("3.1") || s.equals("2.1"))
                gmlVersion = s;
            else {
                throw new ShapeChangeAbortException("Unknown value for gmlVersion: " + s);
            }
        }

        return result;
    }

    private List<XmlNamespace> parseXmlNamespaces(Element targetXmlSchemaElement) {

        List<XmlNamespace> result = new ArrayList<XmlNamespace>();

        NodeList xmlNamespacesNl = targetXmlSchemaElement.getElementsByTagName("XmlNamespace");
        Node xmlNamespaceN;
        Element xmlNamespaceE;

        if (xmlNamespacesNl != null && xmlNamespacesNl.getLength() != 0) {

            for (int k = 0; k < xmlNamespacesNl.getLength(); k++) {

                xmlNamespaceN = xmlNamespacesNl.item(k);
                if (xmlNamespaceN.getNodeType() == Node.ELEMENT_NODE) {

                    xmlNamespaceE = (Element) xmlNamespaceN;

                    String nsabr = xmlNamespaceE.getAttribute("nsabr");
                    String ns = xmlNamespaceE.getAttribute("ns");
                    String location = xmlNamespaceE.hasAttribute("location")
                            ? location = xmlNamespaceE.getAttribute("location")
                            : null;
                    String packageName = xmlNamespaceE.hasAttribute("packageName")
                            ? xmlNamespaceE.getAttribute("packageName")
                            : null;

                    result.add(new XmlNamespace(nsabr, ns, location, packageName));
                }
            }
        }
        return result;
    }

    /**
     * Parses the given TargetXmlSchema element and returns a map of all
     * XsdMapEntries it contains.
     *
     * @param targetXmlSchemaElement
     *            The TargetXmlSchema element from the ShapeChangeConfiguration
     * @return map of the XsdMapEntries contained in the TargetXmlSchema element
     *         (key: XsdMapEntry type)
     */
    private List<XsdMapEntry> parseXsdMapEntries(Element targetXmlSchemaElement) {

        List<XsdMapEntry> result = new ArrayList<XsdMapEntry>();

        NodeList xsdMapEntriesNl = targetXmlSchemaElement.getElementsByTagName("XsdMapEntry");
        Node xsdMapEntryN;
        Element xsdMapEntryE;

        if (xsdMapEntriesNl != null && xsdMapEntriesNl.getLength() != 0) {

            for (int k = 0; k < xsdMapEntriesNl.getLength(); k++) {

                xsdMapEntryN = xsdMapEntriesNl.item(k);
                if (xsdMapEntryN.getNodeType() == Node.ELEMENT_NODE) {

                    xsdMapEntryE = (Element) xsdMapEntryN;

                    String type = xsdMapEntryE.getAttribute("type");

                    List<String> encodingRules = new ArrayList<String>();
                    String encodingRulesValue = xsdMapEntryE.getAttribute("xsdEncodingRules");
                    if (encodingRulesValue != null && encodingRulesValue.trim().length() > 0) {
                        encodingRulesValue = encodingRulesValue.toLowerCase();
                        encodingRules = new ArrayList<String>(Arrays.asList(encodingRulesValue.split("\\s")));
                    }

                    String xmlType = xsdMapEntryE.hasAttribute("xmlType") ? xsdMapEntryE.getAttribute("xmlType")
                            : null;

                    String xmlTypeContent = "";
                    if (xsdMapEntryE.hasAttribute("xmlTypeContent")) {
                        xmlTypeContent = xsdMapEntryE.getAttribute("xmlTypeContent").trim();
                    }
                    if (xmlTypeContent.length() == 0) {
                        xmlTypeContent = "complex";
                    }

                    String xmlTypeType = "";
                    if (xsdMapEntryE.hasAttribute("xmlTypeType")) {
                        xmlTypeType = xsdMapEntryE.getAttribute("xmlTypeType").trim();
                    }
                    if (xmlTypeType.length() == 0) {
                        xmlTypeType = "complex";
                    }

                    String xmlTypeNilReason = "";
                    if (xsdMapEntryE.hasAttribute("xmlTypeNilReason")) {
                        xmlTypeNilReason = xsdMapEntryE.getAttribute("xmlTypeNilReason").trim();
                    }
                    if (xmlTypeNilReason.length() == 0) {
                        if (xmlTypeType.equals("simple"))
                            xmlTypeNilReason = "false";
                        else
                            xmlTypeNilReason = "true";
                    }

                    String xmlElement = xsdMapEntryE.hasAttribute("xmlElement")
                            ? xsdMapEntryE.getAttribute("xmlElement")
                            : null;

                    String xmlPropertyType = xsdMapEntryE.hasAttribute("xmlPropertyType")
                            ? xsdMapEntryE.getAttribute("xmlPropertyType")
                            : null;

                    String xmlAttribute = xsdMapEntryE.hasAttribute("xmlAttribute")
                            ? xsdMapEntryE.getAttribute("xmlAttribute")
                            : null;

                    String xmlAttributeGroup = xsdMapEntryE.hasAttribute("xmlAttributeGroup")
                            ? xsdMapEntryE.getAttribute("xmlAttributeGroup")
                            : null;

                    String nsabr = xsdMapEntryE.hasAttribute("nsabr") ? xsdMapEntryE.getAttribute("nsabr") : null;

                    result.add(new XsdMapEntry(type, encodingRules, xmlType, xmlTypeContent, xmlTypeType,
                            xmlTypeNilReason, xmlElement, xmlPropertyType, xmlAttribute, xmlAttributeGroup, nsabr));
                }
            }
        }
        return result;
    }

    /**
     * Parses the given process element and returns a map of all map entries it
     * contains.
     *
     * @param processElement
     *            A Transformer or Target element from the
     *            ShapeChangeConfiguration
     * @param string
     * @return map of the map entries contained in the process element (key:
     *         ProcessMapEntry type)
     */
    private List<ProcessMapEntry> parseProcessMapEntries(Element processElement, String mapEntryElementTagName) {

        List<ProcessMapEntry> result = new ArrayList<ProcessMapEntry>();

        NodeList processMapEntriesNl = processElement.getElementsByTagName(mapEntryElementTagName);
        Node processMapEntryN;
        Element processMapEntryE;

        if (processMapEntriesNl != null && processMapEntriesNl.getLength() != 0) {

            for (int k = 0; k < processMapEntriesNl.getLength(); k++) {

                processMapEntryN = processMapEntriesNl.item(k);
                if (processMapEntryN.getNodeType() == Node.ELEMENT_NODE) {

                    processMapEntryE = (Element) processMapEntryN;

                    String type = processMapEntryE.getAttribute("type");
                    String rule = processMapEntryE.getAttribute("rule");
                    String targetType = processMapEntryE.hasAttribute("targetType")
                            ? processMapEntryE.getAttribute("targetType")
                            : null;
                    String param = processMapEntryE.hasAttribute("param") ? processMapEntryE.getAttribute("param")
                            : null;

                    result.add(new ProcessMapEntry(type, rule, targetType, param));
                }
            }
        }
        return result;
    }

    /**
     * Parses all transformer configuration aspects available in the given
     * ShapeChangeConfiguration document.
     *
     * @param configurationDocument
     * @throws ShapeChangeAbortException
     *
     */
    private Map<String, TransformerConfiguration> parseTransformerConfigurations(Document configurationDocument)
            throws ShapeChangeAbortException {

        Map<String, TransformerConfiguration> trfConfigs = new HashMap<String, TransformerConfiguration>();

        NodeList trfsNl = configurationDocument.getElementsByTagName("transformers");

        for (int i = 0; i < trfsNl.getLength(); i++) {
            Node trfsN = trfsNl.item(i);
            NodeList trfNl = trfsN.getChildNodes();

            // look for all Transformer elements in the "transformers" Node
            for (int j = 0; j < trfNl.getLength(); j++) {
                Node trfN = trfNl.item(j);

                if (trfN.getNodeType() == Node.ELEMENT_NODE) {
                    Element trfE = (Element) trfN;

                    // parse content of Transformer element

                    // get transformer id
                    String trfConfigId = trfE.getAttribute("id");

                    // get transformer class name
                    String trfConfigName = trfE.getAttribute("class");

                    // get transformer mode
                    ProcessMode trfMode = parseMode(trfE);

                    Map<String, String> processParameters = parseParameters(trfE, "ProcessParameter");

                    // now look up all ProcessRuleSet elements, if there are
                    // any
                    Map<String, ProcessRuleSet> processRuleSets = parseRuleSets(trfE, "ProcessRuleSet", false);

                    // now look up all ProcessMapEntry elements, if there
                    // are any
                    List<ProcessMapEntry> processMapEntries = parseProcessMapEntries(trfE, "ProcessMapEntry");

                    // parse tagged values, if any are defined
                    List<TaggedValueConfigurationEntry> taggedValues = parseTaggedValues(trfE);

                    // get the transformer input - can be null, then set it to
                    // global input element
                    String trfConfigInput;
                    if (trfE.hasAttribute("input")) {
                        trfConfigInput = trfE.getAttribute("input");
                    } else {
                        trfConfigInput = getInputId();
                    }

                    if (trfConfigInput.equals(trfConfigId)) {
                        throw new ShapeChangeAbortException(
                                "Attributes input and id are equal in a transformer configuration element (class: "
                                        + trfConfigName + ") which is not allowed.");
                    }

                    Element advancedProcessConfigurations = parseAdvancedProcessConfigurations(trfE);

                    // create transformer config and add it to list
                    TransformerConfiguration trfConfig = new TransformerConfiguration(trfConfigId, trfConfigName,
                            trfMode, processParameters, processRuleSets, processMapEntries, taggedValues,
                            trfConfigInput, advancedProcessConfigurations);

                    trfConfigs.put(trfConfig.getId(), trfConfig);
                }
            }
        }
        return trfConfigs;

    }

    /**
     * @param trfE
     *            "Transformer" element from the configuration
     * @return list of tagged values defined for the transformer or
     *         <code>null</code> if no tagged values are defined for it.
     */
    private List<TaggedValueConfigurationEntry> parseTaggedValues(Element trfE) {

        List<TaggedValueConfigurationEntry> result = new ArrayList<TaggedValueConfigurationEntry>();

        Element directTaggedValuesInTrf = null;

        /*
         * identify taggedValues element that is direct child of the Transformer
         * element - can be null
         */
        NodeList children = trfE.getChildNodes();
        if (children != null && children.getLength() != 0) {

            for (int k = 0; k < children.getLength(); k++) {

                Node n = children.item(k);
                if (n.getNodeType() == Node.ELEMENT_NODE && n.getNodeName().equals("taggedValues")) {
                    directTaggedValuesInTrf = (Element) n;
                    break;
                }
            }
        }

        if (directTaggedValuesInTrf != null) {

            NodeList taggedValuesNl = directTaggedValuesInTrf.getElementsByTagName("TaggedValue");
            Node taggedValueN;
            Element taggedValueE;

            if (taggedValuesNl != null && taggedValuesNl.getLength() > 0) {

                for (int k = 0; k < taggedValuesNl.getLength(); k++) {

                    taggedValueN = taggedValuesNl.item(k);
                    if (taggedValueN.getNodeType() == Node.ELEMENT_NODE) {

                        taggedValueE = (Element) taggedValueN;

                        String value = null;

                        if (taggedValueE.hasAttribute("value")) {
                            value = taggedValueE.getAttribute("value");
                        }

                        Pattern modelElementNamePattern = null;

                        if (taggedValueE.hasAttribute("modelElementName")) {
                            String modelElementName = taggedValueE.getAttribute("modelElementName");
                            modelElementNamePattern = Pattern.compile(modelElementName);
                        }

                        Pattern modelElementStereotypePattern = null;

                        if (taggedValueE.hasAttribute("modelElementStereotype")) {

                            String modelElementStereotype = taggedValueE.getAttribute("modelElementStereotype");
                            modelElementStereotypePattern = Pattern.compile(modelElementStereotype);
                        }

                        Pattern applicationSchemaNamePattern = null;

                        if (taggedValueE.hasAttribute("applicationSchemaName")) {

                            String applicationSchemaName = taggedValueE.getAttribute("applicationSchemaName");
                            applicationSchemaNamePattern = Pattern.compile(applicationSchemaName);
                        }

                        result.add(new TaggedValueConfigurationEntry(taggedValueE.getAttribute("name"), value,
                                modelElementStereotypePattern, modelElementNamePattern,
                                applicationSchemaNamePattern));

                    }
                }
            }
        }

        if (result.isEmpty())
            return null;
        else
            return result;
    }

    private ProcessMode parseMode(Element processElement) {

        ProcessMode result = ProcessMode.enabled;

        if (processElement.hasAttribute("mode")) {
            String mode = processElement.getAttribute("mode");
            if (mode.equalsIgnoreCase("diagnostics-only")) {
                mode = "diagnosticsonly";
            }
            result = ProcessMode.fromString(mode);
        }

        return result;
    }

    private Map<String, ProcessRuleSet> parseRuleSets(Element processElement, String ruleSetElementTagName,
            boolean checkRuleExistence) throws ShapeChangeAbortException {

        Map<String, ProcessRuleSet> result = new HashMap<String, ProcessRuleSet>();

        NodeList processRuleSetsNl = processElement.getElementsByTagName(ruleSetElementTagName);
        Node processRuleSetN;
        Element processRuleSetE;
        Set<String> ruleSetRules;

        if (processRuleSetsNl != null && processRuleSetsNl.getLength() != 0) {

            for (int k = 0; k < processRuleSetsNl.getLength(); k++) {

                processRuleSetN = processRuleSetsNl.item(k);

                if (processRuleSetN.getNodeType() == Node.ELEMENT_NODE) {

                    // parse rule set
                    processRuleSetE = (Element) processRuleSetN;

                    // parse name and extends
                    String processRuleSetName = processRuleSetE.getAttribute("name");
                    // String processRuleSetName = processRuleSetE
                    // .getAttribute("name").toLowerCase();
                    String extendedRuleName = processRuleSetE.getAttribute("extends");
                    if (extendedRuleName != null && extendedRuleName.trim().length() == 0) {
                        extendedRuleName = "*";
                    }
                    // extendedRuleName = extendedRuleName.toLowerCase();

                    // parse additional rules for rule set, if
                    // there are any
                    ruleSetRules = null;
                    NodeList processRuleSetRulesNl = processRuleSetE.getElementsByTagName("rule");
                    if (processRuleSetRulesNl != null && processRuleSetRulesNl.getLength() != 0) {

                        for (int l = 0; l < processRuleSetRulesNl.getLength(); l++) {
                            Node processRuleSetRuleN = processRuleSetRulesNl.item(l);
                            if (processRuleSetRuleN.getNodeType() == Node.ELEMENT_NODE) {
                                Element processRuleSetRuleE = (Element) processRuleSetRuleN;
                                if (ruleSetRules == null)
                                    ruleSetRules = new HashSet<String>();
                                String ruleName = processRuleSetRuleE.getAttribute("name");

                                // we do not need to check rules for
                                // transformers here
                                if (checkRuleExistence) {
                                    if (hasRule(ruleName))
                                        ruleSetRules.add(ruleName);
                                    else
                                        System.err.println("Warning while loading configuration file: Rule '"
                                                + ruleName
                                                + "' is unknown, but referenced from the configuration. This is only a problem, if "
                                                + "you know that the conversion rule is used in an encoding rule of your configuration.");
                                } else {
                                    ruleSetRules.add(ruleName);
                                }
                            }
                        }
                    }

                    result.put(processRuleSetName,
                            new ProcessRuleSet(processRuleSetName, extendedRuleName, ruleSetRules));

                }
            }
        }
        return result;
    }

    private void addStandardRules() {
        /*
         * mandatory rules
         */
        addRule("req-xsd-pkg-xsdDocument-unique");
        addRule("req-xsd-cls-name-unique");
        addRule("req-xsd-cls-ncname");
        addRule("req-xsd-prop-data-type");
        addRule("req-xsd-prop-value-type-exists");
        addRule("req-xsd-prop-ncname");
        addRule("rule-xsd-pkg-contained-packages");
        addRule("rule-xsd-pkg-dependencies");
        addRule("rule-xsd-cls-union-as-choice");
        addRule("rule-xsd-cls-unknown-as-object");
        addRule("rule-xsd-cls-sequence");
        addRule("rule-xsd-cls-object-element");
        addRule("rule-xsd-cls-type");
        addRule("rule-xsd-cls-property-type");
        addRule("rule-xsd-cls-local-properties");
        /*
         * Associate these with a core encoding rule
         */
        addRule("req-xsd-pkg-xsdDocument-unique", "*");
        addRule("req-xsd-cls-name-unique", "*");
        addRule("req-xsd-cls-ncname", "*");
        addRule("req-xsd-prop-data-type", "*");
        addRule("req-xsd-prop-value-type-exists", "*");
        addRule("req-xsd-prop-ncname", "*");
        addRule("rule-xsd-pkg-contained-packages", "*");
        addRule("rule-xsd-pkg-dependencies", "*");
        addRule("rule-xsd-cls-unknown-as-object", "*");
        addRule("rule-xsd-cls-object-element", "*");
        addRule("rule-xsd-cls-type", "*");
        addRule("rule-xsd-cls-property-type", "*");
        addRule("rule-xsd-cls-local-properties", "*");
        addRule("rule-xsd-cls-union-as-choice", "*");
        addRule("rule-xsd-cls-sequence", "*");
        /*
         * GML 3.2 / ISO 19136:2007 rules
         */
        addRule("req-xsd-cls-generalization-consistent");
        addRule("rule-xsd-all-naming-gml");
        addRule("rule-xsd-cls-global-enumeration");
        addRule("rule-xsd-cls-codelist-asDictionary");
        addRule("rule-xsd-cls-noPropertyType");
        addRule("rule-xsd-cls-byValuePropertyType");
        addRule("rule-xsd-cls-standard-gml-property-types");
        addRule("rule-xsd-pkg-gmlProfileSchema");
        addRule("rule-xsd-prop-defaultCodeSpace");
        addRule("rule-xsd-prop-inlineOrByReference");
        addRule("rule-xsd-prop-reverseProperty");
        addRule("rule-xsd-prop-targetElement");
        /*
         * add the iso19136_2007 encoding rule and extend the core encoding rule
         */
        addExtendsEncRule("iso19136_2007", "*");
        addRule("req-xsd-cls-generalization-consistent", "iso19136_2007");
        addRule("rule-xsd-all-naming-gml", "iso19136_2007");
        addRule("rule-xsd-cls-global-enumeration", "iso19136_2007");
        addRule("rule-xsd-cls-codelist-asDictionary", "iso19136_2007");
        addRule("rule-xsd-cls-standard-gml-property-types", "iso19136_2007");
        addRule("rule-xsd-cls-noPropertyType", "iso19136_2007");
        addRule("rule-xsd-cls-byValuePropertyType", "iso19136_2007");
        addRule("rule-xsd-pkg-gmlProfileSchema", "iso19136_2007");
        addRule("rule-xsd-prop-targetElement", "iso19136_2007");
        addRule("rule-xsd-prop-reverseProperty", "iso19136_2007");
        addRule("rule-xsd-prop-defaultCodeSpace", "iso19136_2007");
        addRule("rule-xsd-prop-inlineOrByReference", "iso19136_2007");
        /*
         * additional GML 3.3 rules
         */
        addRule("rule-xsd-cls-codelist-asDictionaryGml33");
        addRule("rule-xsd-rel-association-classes");
        /*
         * add the gml33 encoding rule and extend the core encoding rule
         */
        addExtendsEncRule("gml33", "*");
        addRule("req-xsd-cls-generalization-consistent", "gml33");
        addRule("rule-xsd-all-naming-gml", "gml33");
        addRule("rule-xsd-cls-global-enumeration", "gml33");
        addRule("rule-xsd-cls-codelist-asDictionaryGml33", "gml33");
        addRule("rule-xsd-cls-standard-gml-property-types", "gml33");
        addRule("rule-xsd-cls-noPropertyType", "gml33");
        addRule("rule-xsd-cls-byValuePropertyType", "gml33");
        addRule("rule-xsd-pkg-gmlProfileSchema", "gml33");
        addRule("rule-xsd-prop-targetElement", "gml33");
        addRule("rule-xsd-prop-reverseProperty", "gml33");
        addRule("rule-xsd-prop-defaultCodeSpace", "gml33");
        addRule("rule-xsd-prop-inlineOrByReference", "gml33");
        addRule("rule-xsd-rel-association-classes", "gml33");
        /*
         * ISO/TS 19139:2007 rules
         */
        addRule("rule-xsd-all-naming-19139");
        addRule("rule-xsd-cls-standard-19139-property-types");
        addRule("rule-xsd-cls-enum-object-element");
        addRule("rule-xsd-cls-enum-property-type");
        /*
         * add the iso19139_2007 encoding rule and extend the core encoding rule
         */
        addExtendsEncRule("iso19139_2007", "*");
        addRule("rule-xsd-cls-enum-object-element", "iso19139_2007");
        addRule("rule-xsd-cls-enum-property-type", "iso19139_2007");
        addRule("rule-xsd-cls-global-enumeration", "iso19139_2007");
        addRule("rule-xsd-cls-standard-19139-property-types", "iso19139_2007");
        addRule("rule-xsd-all-naming-19139", "iso19139_2007");
        /*
         * SWE Common Data Model 2.0 rules
         */
        addRule("rule-xsd-all-naming-swe");
        addRule("rule-xsd-prop-xsdAsAttribute");
        addRule("rule-xsd-prop-soft-typed");
        addRule("rule-xsd-cls-union-as-group-property-type");
        addRule("rule-xsd-cls-standard-swe-property-types");
        addRule("rule-xsd-prop-initialValue");
        /*
         * add the ogcSweCommon2 encoding rule and extend the core encoding rule
         */
        addExtendsEncRule("ogcSweCommon2", "*");
        addRule("req-xsd-cls-generalization-consistent", "ogcSweCommon2");
        addRule("rule-xsd-all-naming-swe", "ogcSweCommon2");
        addRule("rule-xsd-cls-global-enumeration", "ogcSweCommon2");
        addRule("rule-xsd-cls-codelist-asDictionary", "ogcSweCommon2");
        addRule("rule-xsd-cls-standard-swe-property-types", "ogcSweCommon2");
        addRule("rule-xsd-cls-noPropertyType", "ogcSweCommon2");
        addRule("rule-xsd-cls-byValuePropertyType", "ogcSweCommon2");
        addRule("rule-xsd-pkg-gmlProfileSchema", "ogcSweCommon2");
        addRule("rule-xsd-prop-targetElement", "ogcSweCommon2");
        addRule("rule-xsd-prop-reverseProperty", "ogcSweCommon2");
        addRule("rule-xsd-prop-defaultCodeSpace", "ogcSweCommon2");
        addRule("rule-xsd-prop-inlineOrByReference", "ogcSweCommon2");
        addRule("rule-xsd-prop-xsdAsAttribute", "ogcSweCommon2");
        addRule("rule-xsd-prop-soft-typed", "ogcSweCommon2");
        addRule("rule-xsd-cls-union-as-group-property-type", "ogcSweCommon2");
        addRule("rule-xsd-prop-initialValue", "ogcSweCommon2");

        /*
         * additional GML 2.1 rules
         */
        addRule("rule-xsd-all-gml21");
        addRule("rule-xsd-cls-codelist-anonymous-xlink");
        /*
         * add the gml21 encoding rule and extend the core encoding rule
         */
        addExtendsEncRule("gml21", "iso19136_2007");
        addRule("rule-xsd-all-gml21", "gml21");
        addRule("rule-xsd-cls-codelist-anonymous-xlink", "gml21");

        /*
         * addExtendsEncRule("gsip","*");
         * addRule("req-xsd-cls-mixin-supertypes","gsip");
         * addRule("req-xsd-cls-codelist-no-supertypes","gsip");
         * addRule("rule-xsd-cls-union-asCharacterString","gsip");
         * addRule("rule-xsd-cls-union-asGroup","gsip");
         * addRule("rule-xsd-cls-enum-supertypes","gsip");
         * addRule("rule-xsd-cls-enum-subtypes","gsip");
         * addRule("rule-xsd-cls-basictype","gsip");
         * addRule("rule-xsd-cls-union-direct","gsip");
         * addRule("rule-xsd-cls-codelist-constraints","gsip");
         * addRule("rule-xsd-cls-mixin-classes-as-group","gsip");
         * addRule("rule-xsd-cls-mixin-classes","gsip");
         * addRule("rule-xsd-prop-exclude-derived","gsip");
         * addRule("rule-xsd-prop-length-size-pattern","gsip");
         * addRule("rule-xsd-prop-xsdAsAttribute","gsip");
         * addRule("rule-xsd-prop-nillable","gsip");
         * addRule("rule-xsd-prop-nilReasonAllowed","gsip");
         * addRule("rule-xsd-prop-initialValue","gsip");
         * addRule("rule-xsd-prop-att-map-entry","gsip");
         * addRule("rule-xsd-pkg-schematron","gsip");
         * addRule("rule-xsd-all-tagged-values","gsip");
         */

        /*
         * non-standard extensions - requirements
         */
        addRule("req-all-all-documentation");
        addRule("req-all-prop-sequenceNumber");
        addRule("req-xsd-pkg-targetNamespace");
        addRule("req-xsd-pkg-xmlns");
        addRule("req-xsd-pkg-namespace-schema-only");
        addRule("rec-xsd-pkg-version");
        addRule("req-xsd-pkg-xsdDocument");
        addRule("req-xsd-pkg-dependencies");
        addRule("req-xsd-cls-codelist-asDictionary-true");
        addRule("req-xsd-cls-codelist-extensibility-values");
        addRule("req-xsd-cls-codelist-extensibility-vocabulary");
        addRule("req-xsd-cls-codelist-no-supertypes");
        addRule("req-xsd-cls-datatype-noPropertyType");
        addRule("req-xsd-cls-enum-no-supertypes");
        addRule("req-xsd-cls-mixin-supertypes");
        addRule("req-xsd-cls-mixin-supertypes-overrule");
        addRule("req-xsd-cls-objecttype-byValuePropertyType");
        addRule("req-xsd-cls-objecttype-noPropertyType");
        addRule("req-xsd-cls-suppress-no-properties");
        addRule("req-xsd-cls-suppress-subtype");
        addRule("req-xsd-cls-suppress-supertype");
        addRule("req-xsd-prop-codelist-obligation");
        /*
         * non-standard extensions - conversion rules
         */
        addRule("rule-xsd-all-notEncoded");
        addRule("rule-xsd-cls-adeelement");
        addRule("rule-xsd-cls-basictype");
        addRule("rule-xsd-cls-codelist-constraints");
        addRule("rule-xsd-cls-enum-subtypes");
        addRule("rule-xsd-cls-enum-supertypes");
        addRule("rule-xsd-cls-mixin-classes-as-group");
        addRule("rule-xsd-cls-mixin-classes");
        addRule("rule-xsd-cls-mixin-classes-non-mixin-supertypes");
        addRule("rule-xsd-cls-no-abstract-classes");
        addRule("rule-xsd-cls-no-base-class");
        addRule("rule-xsd-cls-no-gml-types");
        addRule("rule-xsd-cls-okstra-fid");
        addRule("rule-xsd-cls-okstra-lifecycle");
        addRule("rule-xsd-cls-okstra-schluesseltabelle");
        addRule("rule-xsd-cls-suppress");
        addRule("rule-xsd-cls-union-asCharacterString");
        addRule("rule-xsd-cls-union-asGroup");
        addRule("rule-xsd-cls-union-direct");
        addRule("rule-xsd-cls-union-direct-optionality");
        addRule("rule-xsd-prop-att-map-entry");
        addRule("rule-xsd-prop-exclude-derived");
        addRule("rule-xsd-prop-length-size-pattern");
        addRule("rule-xsd-prop-nillable");
        addRule("rule-xsd-prop-nilReasonAllowed");
        addRule("rule-xsd-prop-gmlArrayProperty");
        addRule("rule-xsd-prop-gmlListProperty");
        addRule("rule-xsd-prop-qualified-associations");
        addRule("rule-xsd-all-no-documentation");
        addRule("rule-xsd-cls-local-enumeration");
        addRule("rule-xsd-cls-local-basictype");
        addRule("rule-xsd-pkg-dgiwgsp");
        addRule("rule-xsd-pkg-schematron");
        addRule("rule-xsd-all-tagged-values");
        addRule("rule-xsd-cls-adehook");

        // AIXM specific rules
        addRule("rule-all-cls-aixmDatatype");
        // further rules of a more general nature
        addRule("rule-all-prop-uomAsAttribute");

        /*
         * JSON encoding rules
         */
        addExtendsEncRule("geoservices", "*");
        addExtendsEncRule("geoservices_extended", "*");

        /*
         * RDF encoding rules
         */
        addRule("rule-rdf-prop-parent");

        /*
         * SQL encoding rules
         */

        addRule("rule-sql-cls-feature-types");
        addRule("rule-sql-cls-object-types");
        addRule("rule-sql-cls-references-to-external-types");
        addRule("rule-sql-all-associativetables");
        addRule("rule-sql-prop-exclude-derived");
        addRule("rule-sql-cls-data-types");
        addRule("rule-sql-prop-check-constraints-for-enumerations");

        // declare rule sets
        addExtendsEncRule(SQL, "*");
        addRule("rule-sql-cls-feature-types", SQL);

        /*
         * OWL encoding rules
         */
        addExtendsEncRule("iso19150_2014", "*");

        // declare optional rules
        addRule("rule-owl-pkg-singleOntologyPerSchema");
        addRule("rule-owl-pkg-pathInOntologyName");
        addRule("rule-owl-all-constraints");
        addRule("rule-owl-cls-geosparql-features");
        addRule("rule-owl-cls-19150-2-features");
        addRule("rule-owl-cls-codelist-external");

        addRule("rule-owl-prop-suppress-cardinality-restrictions");
        addRule("rule-owl-prop-suppress-allValuesFrom-restrictions");
        addRule("rule-owl-prop-voidable-as-minCardinality0");
        addRule("rule-owl-all-suppress-dc-source");
        addRule("rule-owl-prop-suppress-asociation-names");
        addRule("rule-owl-pkg-app-schema-code");

        /*
         * ArcGIS workspace encoding rules
         */
        addRule("rule-arcgis-prop-initialValueByAlias");
        /*
         * extend core encoding rule with optional ArcGIS workspace rules
         */
        addRule("rule-arcgis-prop-initialValueByAlias", "*");

        /*
         * Replication schema encoding rules
         */
        addRule("rule-rep-prop-optional");
        addRule("rule-rep-prop-exclude-derived");
        addRule("rule-rep-cls-generate-objectidentifier");
        addRule("rule-rep-prop-maxLength-from-size");

        /*
         * Application schema metadata rules
         */
        addRule("rule-asm-all-identify-profiles");

        // // ==================
        // // Transformer rules
        // // ==================
        //
        // /*
        // * Flattener rules
        // */
        // addRule("rule-trf-all-removeType");
        // addRule("rule-trf-prop-flatten-ONINAs");
        // addRule("rule-trf-prop-optionality");
        // addRule("rule-trf-cls-flatten-inheritance");
        // addRule("rule-trf-prop-flatten-multiplicity");
        // addRule("rule-trf-prop-flatten-types");
        // addRule("rule-trf-all-flatten-name");
        // addRule("rule-trf-all-flatten-constraints");
        // addRule("rule-trf-prop-remove-name-and-alias-component");
        // addRule("rule-trf-prop-flatten-homogeneousgeometries");
        //
        // /*
        // * Profiler rules
        // */
        // addRule("rule-trf-profiling-preprocessing-profilesValueConsistencyCheck");
        // addRule("rule-trf-profiling-preprocessing-modelConsistencyCheck");
        // addRule("rule-trf-profiling-postprocessing-removeEmptyPackages");

    }

    /** Normalize a stereotype fetched from the model. */
    public String normalizeStereotype(String stereotype) {
        // Map stereotype alias to well-known stereotype
        String s = stereotypeAlias(stereotype.trim());
        if (s != null)
            return s;
        return stereotype;
    };

    /** Normalize a tag fetched from the model. */
    public String normalizeTag(String tag) {
        // Map tag alias to well-known tag
        String s = tagAlias(tag.trim());
        if (s != null)
            return s;
        return tag;
    };

    /*
     * Only process schemas in a namespace and name that matches a user-selected
     * pattern
     */
    public boolean skipSchema(Target target, PackageInfo pi) {
        String name = pi.name();
        String ns = pi.targetNamespace();

        // only process schemas with a given name
        String schemaFilter;
        if (target == null)
            schemaFilter = parameter("appSchemaName");
        else
            schemaFilter = parameter(target.getClass().getName(), "appSchemaName");
        if (schemaFilter != null && schemaFilter.length() > 0 && !schemaFilter.equals(name))
            return true;

        // only process schemas with a name that matches a user-selected pattern
        String appSchemaNameRegex;
        if (target == null)
            appSchemaNameRegex = parameter("appSchemaNameRegex");
        else
            appSchemaNameRegex = parameter(target.getClass().getName(), "appSchemaNameRegex");
        if (appSchemaNameRegex != null && appSchemaNameRegex != null && appSchemaNameRegex.length() > 0
                && !name.matches(appSchemaNameRegex))
            return true;

        // only process schemas in a namespace that matches a user-selected
        // pattern
        String appSchemaNamespaceRegex;
        if (target == null)
            appSchemaNamespaceRegex = parameter("appSchemaNamespaceRegex");
        else
            appSchemaNamespaceRegex = parameter(target.getClass().getName(), "appSchemaNamespaceRegex");
        if (appSchemaNamespaceRegex != null && appSchemaNamespaceRegex.length() > 0
                && !ns.matches(appSchemaNamespaceRegex))
            return true;

        return false;
    }

    public String targetClassName(String rule) {
        String[] ra = rule.toLowerCase().split("-", 4);
        if (ra.length != 4)
            return null;

        if (ra[1].equals("xsd"))
            return Options.TargetXmlSchemaClass;
        else if (ra[1].equals("json"))
            return Options.TargetJsonSchemaClass;
        else if (ra[1].equals("fc"))
            return Options.TargetFeatureCatalogueClass;
        else if (ra[1].equals("rdf"))
            return Options.TargetRDFClass;
        else if (ra[1].equals("sql"))
            return Options.TargetSQLClass;
        else if (ra[1].equals("owl"))
            return Options.TargetOWLISO19150Class;
        else if (ra[1].equals("arcgis"))
            return Options.TargetArcGISWorkspaceClass;
        else if (ra[1].equals("rep"))
            return Options.TargetReplicationSchemaClass;
        else if (ra[1].equals("asm"))
            return Options.TargetApplicationSchemaMetadata;

        return null;
    }

    public Options() {
        setStandardParameters();
    }

    /**
     * @return the inputTargetConfigs
     */
    public List<TargetConfiguration> getInputTargetConfigs() {
        return inputTargetConfigs;
    }

    /**
     * @return the inputTransformerConfigs
     */
    public List<TransformerConfiguration> getInputTransformerConfigs() {
        return inputTransformerConfigs;
    }

    /**
     * @param currentProcessConfig
     *            the currentProcessConfig to set
     */
    public void setCurrentProcessConfig(ProcessConfiguration currentProcessConfig) {
        this.currentProcessConfig = currentProcessConfig;
    }

    /**
     * @return the currentProcessConfig
     */
    public ProcessConfiguration getCurrentProcessConfig() {
        return currentProcessConfig;
    }

    public List<TargetConfiguration> getTargetConfigurations() {
        return this.targetConfigs;
    }

    /**
     * @return the value of the 'id' attribute of the 'input' configuration
     *         element (or the default value as defined by the
     *         {@link #INPUTELEMENTID} field), if the attribute was not set in
     *         the configuration.
     */
    public String getInputId() {
        return inputId;
    }

    /**
     * @return the temporary directory for the ShapeChange run; will be created
     *         if it does not already exist
     */
    public File getTmpDir() {

        if (tmpDir == null) {

            String tmpDirPath = this.parameter(TMP_DIR_PATH_PARAM);
            if (tmpDirPath == null) {
                tmpDirPath = DEFAULT_TMP_DIR_PATH;
            }

            tmpDir = new File(tmpDirPath);
        }

        if (!tmpDir.exists()) {
            try {
                FileUtils.forceMkdir(tmpDir);
            } catch (IOException e) {
                e.printStackTrace(System.err);
            }
        }

        return tmpDir;
    }

    /**
     * @return the directory in which images (i.e. diagrams) from the input
     *         model can be stored; if it did not exist yet, it will be created
     */
    public File imageTmpDir() {

        if (imageTmpDir == null) {

            imageTmpDir = new File(getTmpDir(), "images");
        }

        if (!imageTmpDir.exists()) {
            try {
                FileUtils.forceMkdir(imageTmpDir);
            } catch (IOException e) {
                e.printStackTrace(System.err);
            }
        }

        return imageTmpDir;
    }

    /**
     * @return the excludedPackages for the model (may be empty but not
     *         <code>null</code>).
     */
    public Set<String> getExcludedPackages() {

        Set<String> excludedPackages = new HashSet<String>();

        String value = parameter(PARAM_INPUT_EXCLUDED_PACKAGES);

        if (value != null) {

            value = value.trim();

            if (value.length() > 0) {

                String[] values = value.split(",");

                for (String v : values) {
                    excludedPackages.add(v.trim());
                }
            }
        }

        return excludedPackages;
    }

    /**
     * Store AIXM schema infos for global use.
     *
     * @param schemaInfos
     */
    public void setAIXMSchemaInfos(Map<String, AIXMSchemaInfo> schemaInfos) {
        this.schemaInfos = schemaInfos;
    }

    /**
     * Retrieve AIXM schema infos that have been stored previously.
     *
     * @return map with schema infos (key: info object id; value: AIXMSchemaInfo
     *         for the object)
     */
    public Map<String, AIXMSchemaInfo> getAIXMSchemaInfos() {
        return schemaInfos;
    }

    /**
     * Determine if global identifiers of model elements shall be loaded or not.
     * This depends upon the setting of the input element parameter
     * {@value #PARAM_LOAD_GLOBAL_IDENTIFIERS}.
     *
     * @return <code>true</code> if global identifiers shall be loaded, else
     *         <code>false</code>
     */
    public boolean isLoadGlobalIdentifiers() {

        return this.loadGlobalIds;
    }

    /**
     * 
     * @return RFC 5646 language code of the primary language to use in targets and transformers
     */
    public String language() {

        return this.language;
    }

    public boolean useStringInterning() {

        return this.useStringInterning;
    }

    /**
     * Depending upon whether or not string interning shall be used during
     * processing, this method interns the given string.
     *
     * @param string
     * @return
     */
    public String internalize(String string) {

        if (string == null) {
            return null;
        } else if (useStringInterning) {
            return string.intern();
        } else {
            return string;
        }
    }

    /**
     * Depending upon whether or not string interning shall be used during
     * processing, this method interns the given string array or an 
     * internized copy.
     *
     * @param array
     * @return
     */
    public String[] internalize(String[] array) {
        if (useStringInterning) {
            String[] result = new String[array.length];
            int i = 0;
            for (String s : array)
                result[i++] = s.intern();
            return result;
        } else {
            return array;
        }
    }

    /**
     * Depending on the tagged value implementation we want (map: default;
     * array: better memory footprint for large models) this returns the one
     * selected in the configuration.
     */
    private boolean useTaggedValuesArray() {
        String tvImpl = parameter(PARAM_TAGGED_VALUE_IMPL);
        return (tvImpl != null && tvImpl.equalsIgnoreCase("array"));
    }

    public TaggedValues taggedValueFactory() {
        TaggedValues result;
        if (useTaggedValuesArray()) {
            result = new TaggedValuesCacheArray(this);
        } else {
            result = new TaggedValuesCacheMap(this);
        }
        return result;
    }

    public TaggedValues taggedValueFactory(int size) {
        TaggedValues result;
        if (useTaggedValuesArray()) {
            result = size < 0 ? new TaggedValuesCacheArray(this) : new TaggedValuesCacheArray(size, this);
        } else {
            result = size < 0 ? new TaggedValuesCacheMap(this) : new TaggedValuesCacheMap(size, this);
        }
        return result;
    }

    /**
     * @param original
     * @param tagList
     * @return can be empty but not <code>null</code>
     */
    public TaggedValues taggedValueFactory(TaggedValues original, String tagList) {
        TaggedValues result;
        if (useTaggedValuesArray()) {
            result = new TaggedValuesCacheArray(original, tagList, this);
        } else {
            result = new TaggedValuesCacheMap(original, tagList, this);
        }
        return result;
    }

    /**
     * @param original
     * @return can be empty but not <code>null</code>
     */
    public TaggedValues taggedValueFactory(TaggedValues original) {
        TaggedValues result;
        if (useTaggedValuesArray()) {
            result = new TaggedValuesCacheArray(original, this);
        } else {
            result = new TaggedValuesCacheMap(original, this);
        }
        return result;
    }

    public Stereotypes stereotypesFactory() {
        return new StereotypesCacheSet(this);
    }

    public Stereotypes stereotypesFactory(Stereotypes stereotypes) {
        return new StereotypesCacheSet(stereotypes, this);
    }
}