eu.scape_project.planning.xml.PlanParser.java Source code

Java tutorial

Introduction

Here is the source code for eu.scape_project.planning.xml.PlanParser.java

Source

/*******************************************************************************
 * Copyright 2006 - 2012 Vienna University of Technology,
 * Department of Software Technology and Interactive Systems, IFS
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 ******************************************************************************/
package eu.scape_project.planning.xml;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.digester3.CallMethodRule;
import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.NodeCreateRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import eu.scape_project.planning.model.Alternative;
import eu.scape_project.planning.model.AlternativesDefinition;
import eu.scape_project.planning.model.CollectionProfile;
import eu.scape_project.planning.model.Decision;
import eu.scape_project.planning.model.DetailedExperimentInfo;
import eu.scape_project.planning.model.DigitalObject;
import eu.scape_project.planning.model.Evaluation;
import eu.scape_project.planning.model.ExecutablePlanDefinition;
import eu.scape_project.planning.model.FormatInfo;
import eu.scape_project.planning.model.ImportanceWeighting;
import eu.scape_project.planning.model.Parameter;
import eu.scape_project.planning.model.Plan;
import eu.scape_project.planning.model.PlanDefinition;
import eu.scape_project.planning.model.PlanProperties;
import eu.scape_project.planning.model.PlanState;
import eu.scape_project.planning.model.PlatoException;
import eu.scape_project.planning.model.Policy;
import eu.scape_project.planning.model.PolicyNode;
import eu.scape_project.planning.model.PreservationActionDefinition;
import eu.scape_project.planning.model.ProjectBasis;
import eu.scape_project.planning.model.RequirementsDefinition;
import eu.scape_project.planning.model.ResourceDescription;
import eu.scape_project.planning.model.SampleObject;
import eu.scape_project.planning.model.SampleRecordsDefinition;
import eu.scape_project.planning.model.Transformation;
import eu.scape_project.planning.model.TriggerDefinition;
import eu.scape_project.planning.model.Values;
import eu.scape_project.planning.model.XcdlDescription;
import eu.scape_project.planning.model.measurement.Attribute;
import eu.scape_project.planning.model.measurement.CriterionCategory;
import eu.scape_project.planning.model.measurement.EvaluationScope;
import eu.scape_project.planning.model.measurement.Measure;
import eu.scape_project.planning.model.measurement.Measurement;
import eu.scape_project.planning.model.scales.BooleanScale;
import eu.scape_project.planning.model.scales.FloatRangeScale;
import eu.scape_project.planning.model.scales.FloatScale;
import eu.scape_project.planning.model.scales.FreeStringScale;
import eu.scape_project.planning.model.scales.IntRangeScale;
import eu.scape_project.planning.model.scales.IntegerScale;
import eu.scape_project.planning.model.scales.OrdinalScale;
import eu.scape_project.planning.model.scales.PositiveFloatScale;
import eu.scape_project.planning.model.scales.PositiveIntegerScale;
import eu.scape_project.planning.model.scales.YanScale;
import eu.scape_project.planning.model.transform.NumericTransformer;
import eu.scape_project.planning.model.transform.OrdinalTransformer;
import eu.scape_project.planning.model.tree.Leaf;
import eu.scape_project.planning.model.tree.Node;
import eu.scape_project.planning.model.tree.ObjectiveTree;
import eu.scape_project.planning.model.tree.PolicyTree;
import eu.scape_project.planning.model.tree.TemplateTree;
import eu.scape_project.planning.model.values.BooleanValue;
import eu.scape_project.planning.model.values.FloatRangeValue;
import eu.scape_project.planning.model.values.FloatValue;
import eu.scape_project.planning.model.values.FreeStringValue;
import eu.scape_project.planning.model.values.IntRangeValue;
import eu.scape_project.planning.model.values.IntegerValue;
import eu.scape_project.planning.model.values.OrdinalValue;
import eu.scape_project.planning.model.values.PositiveFloatValue;
import eu.scape_project.planning.model.values.PositiveIntegerValue;
import eu.scape_project.planning.model.values.YanValue;
import eu.scape_project.planning.xml.plan.BinaryDataWrapper;
import eu.scape_project.planning.xml.plan.ChangeLogFactory;
import eu.scape_project.planning.xml.plan.EnumConverter;
import eu.scape_project.planning.xml.plan.ExperimentWrapper;
import eu.scape_project.planning.xml.plan.GoDecisionFactory;
import eu.scape_project.planning.xml.plan.OrdinalTransformerMappingFactory;
import eu.scape_project.planning.xml.plan.PlanStateFactory;
import eu.scape_project.planning.xml.plan.RecommendationWrapper;
import eu.scape_project.planning.xml.plan.SampleAggregationModeFactory;
import eu.scape_project.planning.xml.plan.TransformationModeFactory;
import eu.scape_project.planning.xml.plan.TriggerFactory;
import eu.scape_project.planning.xml.plan.XMLDataWrapper;

/**
 * Creates preservation plans and templates from their XML representations.
 * 
 * @author Michael Kraxner
 * 
 */
public class PlanParser {

    private static final Logger log = LoggerFactory.getLogger(PlanParser.class);

    /**
     * Used by digester.
     */
    private List<Plan> plans;

    /**
     * Used by digester.
     */
    private List<TemplateTree> templates;

    private final ValidatingParserFactory validatingParserFactory = new ValidatingParserFactory();

    /**
     * Deserialises the plans stored in the file. - the representation must be
     * of the {@link PlanXMLConstants#PLATO_SCHEMA_VERSION current version}.
     * 
     * @param file
     *            the file to parse
     * @return a list of plans
     * @throws PlatoException
     *             if an error occurred
     */
    public List<Plan> importProjects(final String file) throws PlatoException {
        try {
            FileInputStream in = new FileInputStream(file);
            return importProjects(in);
        } catch (FileNotFoundException e) {
            throw new PlatoException("IMPORT FAILED: could not find file " + file, e);
        }
    }

    /**
     * can be used to set data for a specific ByteStream, for example for
     * two-pass XML import. but: NOT USED AT THE MOMENT, and probably will not
     * be needed soon!
     * 
     * @param byteStreamID
     * @param data
     *            private void insertData(int byteStreamID, byte[] data) {
     *            ByteStream b = em.find(ByteStream.class, byteStreamID); if (b
     *            == null) {
     *            log.error("INCONSISTENCY: bytestream with ID "+byteStreamID
     *            +" not found!"); return; } b.setData(data); em.persist(b);
     *            em.flush(); b = null; em.clear(); }
     */

    /**
     * Used by the digester every time a project has been parsed.
     * 
     * @param p
     *            the plan to add to the list of processed plans
     */
    public void setProject(final Plan p) {
        plans.add(p);
    }

    /**
     * Used by the digester every time a template has been parsed.
     * 
     * @param t
     *            the template tree to add to the list of templates
     */
    public void setTemplate(final TemplateTree t) {
        templates.add(t);
    }

    /**
     * Imports the XML representation of templates.
     * 
     * @param in
     *            the input stream to read from
     * @return a list of read templates.
     * @throws PlatoException
     *             if the template cannot be parsed
     */
    public List<TemplateTree> importTemplates(final InputStream in) throws PlatoException {

        try {
            Digester digester = new Digester();
            // digester.setValidating(true);
            StrictErrorHandler errorHandler = new StrictErrorHandler();
            digester.setErrorHandler(errorHandler);

            // At the moment XML files for template tree's are only used
            // internally,
            // later we will define a schema and use it also for validation

            digester.push(this);

            digester.addObjectCreate("*/template", TemplateTree.class);
            digester.addSetProperties("*/template");
            digester.addSetRoot("*/template", "setTemplate");
            // digester.addSetNext("*/template/name", "setName");
            // digester.addSetNext("*/template/owner", "setOwner");

            PlanParser.addTreeParsingRulesToDigester(digester);

            digester.addObjectCreate("*/template/node", Node.class);
            digester.addSetProperties("*/template/node");
            digester.addSetNext("*/template/node", "addChild");

            digester.setUseContextClassLoader(true);

            templates = new ArrayList<TemplateTree>();
            digester.parse(in);
            // FIXME:
            /*
             * for (TemplateTree t : templates) { log.info(t.getName() +
             * t.getOwner()); }
             */

            return templates;
        } catch (Exception e) {
            throw new PlatoException("Failed to parse template tree.", e);
        }
    }

    /**
     * Imports the XML representation of plans from the given input stream.
     * 
     * @param in
     *            the input stream to read from
     * @return list of read plans
     * @throws PlatoException
     *             if the plan cannot be parsed
     */
    public List<Plan> importProjects(final InputStream in) throws PlatoException {
        try {

            SAXParser parser = validatingParserFactory.getValidatingParser();
            parser.setProperty(ValidatingParserFactory.JAXP_SCHEMA_SOURCE, PlanXMLConstants.PLAN_SCHEMAS);

            Digester digester = new Digester(parser);

            SchemaResolver schemaResolver = new SchemaResolver();

            schemaResolver
                    .addSchemaLocation(PlanXMLConstants.PLATO_SCHEMA_URI, PlanXMLConstants.PLATO_SCHEMA_LOCATION)
                    .addSchemaLocation(PlanXMLConstants.PAP_SCHEMA_URI, PlanXMLConstants.PAP_SCHEMA_LOCATION)
                    .addSchemaLocation(PlanXMLConstants.TAVERNA_SCHEMA_URI,
                            PlanXMLConstants.TAVERNA_SCHEMA_LOCATION);

            digester.setEntityResolver(schemaResolver);
            digester.setErrorHandler(new StrictErrorHandler());
            digester.setNamespaceAware(true);
            digester.push(this);

            PlanParser.addRules(digester);

            digester.setUseContextClassLoader(true);
            plans = new ArrayList<Plan>();

            // finally parse the XML representation with all created rules
            digester.parse(in);

            for (Plan plan : plans) {
                String projectName = plan.getPlanProperties().getName();
                if ((projectName != null) && (!"".equals(projectName))) {
                    /*
                     * establish links from values to scales. For all(!)
                     * alternatives: An alternative could have be discarded
                     * after some measurements have already been added.
                     */
                    plan.getTree().initValues(plan.getAlternativesDefinition().getAlternatives(),
                            plan.getSampleRecordsDefinition().getRecords().size(), true);
                    /*
                     * establish references of Experiment.uploads
                     */
                    HashMap<String, SampleObject> records = new HashMap<String, SampleObject>();
                    for (SampleObject record : plan.getSampleRecordsDefinition().getRecords()) {
                        records.put(record.getShortName(), record);
                    }
                    for (Alternative alt : plan.getAlternativesDefinition().getAlternatives()) {
                        if ((alt.getExperiment() != null) && (alt.getExperiment() instanceof ExperimentWrapper)) {
                            alt.setExperiment(((ExperimentWrapper) alt.getExperiment()).getExperiment(records));
                        }
                    }

                    // CHECK NUMERIC TRANSFORMER THRESHOLDS
                    for (Leaf l : plan.getTree().getRoot().getAllLeaves()) {
                        eu.scape_project.planning.model.transform.Transformer t = l.getTransformer();
                        if (t != null && t instanceof NumericTransformer) {
                            NumericTransformer nt = (NumericTransformer) t;
                            if (!nt.checkOrder()) {
                                StringBuffer sb = new StringBuffer("NUMERICTRANSFORMER THRESHOLD ERROR ");
                                sb.append(l.getName()).append("::NUMERICTRANSFORMER:: ");
                                sb.append(nt.getThreshold1()).append(" ").append(nt.getThreshold2()).append(" ")
                                        .append(nt.getThreshold3()).append(" ").append(nt.getThreshold4())
                                        .append(" ").append(nt.getThreshold5());
                                log.error(sb.toString());
                            }
                        }
                    }

                    /*
                     * establish references to selected alternative
                     */
                    HashMap<String, Alternative> alternatives = new HashMap<String, Alternative>();
                    for (Alternative alt : plan.getAlternativesDefinition().getAlternatives()) {
                        alternatives.put(alt.getName(), alt);
                    }
                    if ((plan.getRecommendation() != null)
                            && (plan.getRecommendation() instanceof RecommendationWrapper)) {
                        plan.setRecommendation(
                                ((RecommendationWrapper) plan.getRecommendation()).getRecommendation(alternatives));
                    }
                    if ((plan.getPlanProperties().getState() == PlanState.ANALYSED)
                            && ((plan.getRecommendation() == null)
                                    || (plan.getRecommendation().getAlternative() == null))) {
                        /*
                         * This project is NOT completely analysed
                         */
                        plan.getPlanProperties().setState(PlanState.valueOf(PlanState.ANALYSED.getValue() - 1));
                    }

                } else {
                    throw new PlatoException("Could not find any project data.");
                }
            }
        } catch (Exception e) {
            throw new PlatoException("Failed to import plans.", e);
        }

        return plans;
    }

    /**
     * Adds rules to the digester to parse the a plan XML.
     * 
     * @param digester
     *            the digester
     * @throws ParserConfigurationException
     *             if an error occurred
     */
    private static void addRules(Digester digester) throws ParserConfigurationException {

        ConvertUtils.register(new EnumConverter<EvaluationScope>(EvaluationScope.class), EvaluationScope.class);
        // start with a new file
        digester.addObjectCreate("*/plan", Plan.class);
        digester.addSetProperties("*/plan");
        digester.addSetRoot("*/plan", "setProject");

        digester.addFactoryCreate("*/changelog", ChangeLogFactory.class);
        digester.addSetNext("*/changelog", "setChangeLog");

        digester.addObjectCreate("*/plan/properties", PlanProperties.class);
        digester.addSetProperties("*/plan/properties");
        digester.addSetNext("*/plan/properties", "setPlanProperties");
        digester.addCallMethod("*/plan/properties/description", "setDescription", 0);
        digester.addCallMethod("*/plan/properties/owner", "setOwner", 0);

        digester.addFactoryCreate("*/plan/properties/state", PlanStateFactory.class);
        digester.addSetNext("*/plan/properties/state", "setState");

        PlanParser.addCreateUpload(digester, "*/plan/properties/report", "setReportUpload", DigitalObject.class);

        digester.addObjectCreate("*/plan/basis", ProjectBasis.class);
        digester.addSetProperties("*/plan/basis");
        digester.addSetNext("*/plan/basis", "setProjectBasis");
        digester.addCallMethod("*/plan/basis/applyingPolicies", "setApplyingPolicies", 0);
        digester.addCallMethod("*/plan/basis/designatedCommunity", "setDesignatedCommunity", 0);
        digester.addCallMethod("*/plan/basis/mandate", "setMandate", 0);

        digester.addCallMethod("*/plan/basis/documentTypes", "setDocumentTypes", 0);
        digester.addCallMethod("*/plan/basis/identificationCode", "setIdentificationCode", 0);
        digester.addCallMethod("*/plan/basis/organisationalProcedures", "setOrganisationalProcedures", 0);
        digester.addCallMethod("*/plan/basis/planningPurpose", "setPlanningPurpose", 0);
        digester.addCallMethod("*/plan/basis/planRelations", "setPlanRelations", 0);
        digester.addCallMethod("*/plan/basis/preservationRights", "setPreservationRights", 0);
        digester.addCallMethod("*/plan/basis/referenceToAgreements", "setReferenceToAgreements", 0);

        // define common rule for triggers, for all */triggers/...!
        // also used for PlanDefinition
        digester.addObjectCreate("*/triggers", TriggerDefinition.class);
        digester.addSetNext("*/triggers", "setTriggers");
        // every time a */triggers/trigger is encountered:
        digester.addFactoryCreate("*/triggers/trigger", TriggerFactory.class);
        digester.addSetNext("*/triggers/trigger", "setTrigger");

        //
        // Policy Tree
        //
        digester.addObjectCreate("*/plan/basis/policyTree", PolicyTree.class);
        digester.addSetProperties("*/plan/basis/policyTree");
        digester.addSetNext("*/plan/basis/policyTree", "setPolicyTree");

        digester.addObjectCreate("*/plan/basis/policyTree/policyNode", PolicyNode.class);
        digester.addSetProperties("*/plan/basis/policyTree/policyNode");
        digester.addSetNext("*/plan/basis/policyTree/policyNode", "setRoot");

        digester.addObjectCreate("*/policyNode/policyNode", PolicyNode.class);
        digester.addSetProperties("*/policyNode/policyNode");
        digester.addSetNext("*/policyNode/policyNode", "addChild");

        digester.addObjectCreate("*/policyNode/policy", Policy.class);
        digester.addSetProperties("*/policyNode/policy");
        digester.addSetNext("*/policyNode/policy", "addChild");

        //
        // Sample Records
        //

        digester.addObjectCreate("*/plan/sampleRecords", SampleRecordsDefinition.class);
        digester.addSetProperties("*/plan/sampleRecords");
        digester.addSetNext("*/plan/sampleRecords", "setSampleRecordsDefinition");

        digester.addCallMethod("*/plan/sampleRecords/samplesDescription", "setSamplesDescription", 0);

        // - records
        digester.addObjectCreate("*/record", SampleObject.class);
        digester.addSetProperties("*/record");
        digester.addSetNext("*/record", "addRecord");

        digester.addCallMethod("*/record/description", "setDescription", 0);
        digester.addCallMethod("*/record/originalTechnicalEnvironment", "setOriginalTechnicalEnvironment", 0);

        digester.addObjectCreate("*/record/data", BinaryDataWrapper.class);
        digester.addSetTop("*/record/data", "setData");
        digester.addCallMethod("*/record/data", "setFromBase64Encoded", 0);

        // set up an general rule for all jhove strings!
        digester.addObjectCreate("*/jhoveXML", BinaryDataWrapper.class);
        digester.addSetTop("*/jhoveXML", "setString");
        digester.addCallMethod("*/jhoveXML", "setFromBase64Encoded", 0);
        digester.addCallMethod("*/jhoveXML", "setMethodName", 1, new String[] { "java.lang.String" });
        digester.addObjectParam("*/jhoveXML", 0, "setJhoveXMLString");

        // set up general rule for all fitsXMLs
        digester.addObjectCreate("*/fitsXML", BinaryDataWrapper.class);
        digester.addSetTop("*/fitsXML", "setString");
        digester.addCallMethod("*/fitsXML", "setFromBase64Encoded", 0);
        digester.addCallMethod("*/fitsXML", "setMethodName", 1, new String[] { "java.lang.String" });
        digester.addObjectParam("*/fitsXML", 0, "setFitsXMLString");

        digester.addObjectCreate("*/formatInfo", FormatInfo.class);
        digester.addSetProperties("*/formatInfo");
        digester.addSetNext("*/formatInfo", "setFormatInfo");

        PlanParser.addCreateUpload(digester, "*/record/xcdlDescription", "setXcdlDescription",
                XcdlDescription.class);

        // - collection profile
        digester.addObjectCreate("*/plan/sampleRecords/collectionProfile", CollectionProfile.class);
        digester.addSetProperties("*/plan/sampleRecords/collectionProfile");
        digester.addSetNext("*/plan/sampleRecords/collectionProfile", "setCollectionProfile");

        digester.addCallMethod("*/plan/sampleRecords/collectionProfile/collectionID", "setCollectionID", 0);
        digester.addCallMethod("*/plan/sampleRecords/collectionProfile/description", "setDescription", 0);
        digester.addCallMethod("*/plan/sampleRecords/collectionProfile/numberOfObjects", "setNumberOfObjects", 0);
        digester.addCallMethod("*/plan/sampleRecords/collectionProfile/typeOfObjects", "setTypeOfObjects", 0);
        digester.addCallMethod("*/plan/sampleRecords/collectionProfile/expectedGrowthRate", "setExpectedGrowthRate",
                0);
        digester.addCallMethod("*/plan/sampleRecords/collectionProfile/retentionPeriod", "setRetentionPeriod", 0);
        PlanParser.addCreateUpload(digester, "*/plan/sampleRecords/collectionProfile/profile", "setProfile",
                DigitalObject.class);

        // requirements definition
        digester.addObjectCreate("*/plan/requirementsDefinition", RequirementsDefinition.class);
        digester.addSetProperties("*/plan/requirementsDefinition");
        digester.addSetNext("*/plan/requirementsDefinition", "setRequirementsDefinition");

        digester.addCallMethod("*/plan/requirementsDefinition/description", "setDescription", 0);

        // - uploads
        digester.addObjectCreate("*/plan/requirementsDefinition/uploads", ArrayList.class);
        digester.addSetNext("*/plan/requirementsDefinition/uploads", "setUploads");
        PlanParser.addCreateUpload(digester, "*/plan/requirementsDefinition/uploads/upload", "add",
                DigitalObject.class);

        // alternatives
        digester.addObjectCreate("*/plan/alternatives", AlternativesDefinition.class);
        digester.addSetProperties("*/plan/alternatives");
        digester.addCallMethod("*/plan/alternatives/description", "setDescription", 0);
        digester.addSetNext("*/plan/alternatives", "setAlternativesDefinition");

        digester.addObjectCreate("*/plan/alternatives/alternative", Alternative.class);
        digester.addSetProperties("*/plan/alternatives/alternative");
        digester.addSetNext("*/plan/alternatives/alternative", "addAlternative");
        // - action
        digester.addObjectCreate("*/plan/alternatives/alternative/action", PreservationActionDefinition.class);
        digester.addSetProperties("*/plan/alternatives/alternative/action");
        digester.addBeanPropertySetter("*/plan/alternatives/alternative/action/descriptor");
        digester.addBeanPropertySetter("*/plan/alternatives/alternative/action/parameterInfo");

        digester.addSetNext("*/plan/alternatives/alternative/action", "setAction");

        digester.addCallMethod("*/plan/alternatives/alternative/description", "setDescription", 0);

        // - - params
        digester.addObjectCreate("*/plan/alternatives/alternative/action/params", LinkedList.class);
        digester.addSetNext("*/plan/alternatives/alternative/action/params", "setParams");

        digester.addObjectCreate("*/plan/alternatives/alternative/action/params/param", Parameter.class);
        digester.addSetProperties("*/plan/alternatives/alternative/action/params/param");
        digester.addSetNext("*/plan/alternatives/alternative/action/params/param", "add");
        // - resource description
        digester.addObjectCreate("*/resourceDescription", ResourceDescription.class);
        digester.addSetProperties("*/resourceDescription");
        digester.addSetNext("*/resourceDescription", "setResourceDescription");

        digester.addCallMethod("*/resourceDescription/configSettings", "setConfigSettings", 0);
        digester.addCallMethod("*/resourceDescription/necessaryResources", "setNecessaryResources", 0);
        digester.addCallMethod("*/resourceDescription/reasonForConsidering", "setReasonForConsidering", 0);

        // - experiment
        digester.addObjectCreate("*/experiment", ExperimentWrapper.class);
        digester.addSetProperties("*/experiment");
        digester.addSetNext("*/experiment", "setExperiment");
        digester.addCallMethod("*/experiment/description", "setDescription", 0);
        digester.addCallMethod("*/experiment/settings", "setSettings", 0);
        PlanParser.addCreateUpload(digester, "*/experiment/workflow", "setWorkflow", DigitalObject.class);

        PlanParser.addCreateUpload(digester, "*/experiment/results/result", null, DigitalObject.class);
        PlanParser.addCreateUpload(digester, "*/result/xcdlDescription", "setXcdlDescription",
                XcdlDescription.class);

        // call function addUpload of ExperimentWrapper
        CallMethodRule r = new CallMethodRule(1, "addResult", 2); // method
                                                                  // with
                                                                  // two
                                                                  // params
                                                                  // every time */experiment/uploads/upload is encountered
        digester.addRule("*/experiment/results/result", r);
        // use attribute "key" as first param
        digester.addCallParam("*/experiment/results/result", 0, "key");
        // and the object on stack (DigitalObject) as the second
        digester.addCallParam("*/experiment/results/result", 1, true);

        // addCreateUpload(digester,
        // "*/experiment/xcdlDescriptions/xcdlDescription", null,
        // XcdlDescription.class);
        // // call function addXcdlDescription of ExperimentWrapper
        // r = new CallMethodRule(1, "addXcdlDescription", 2); //method with
        // two
        // params
        // // every time */experiment/xcdlDescriptions/xcdlDescription is
        // encountered
        // digester.addRule("*/experiment/xcdlDescriptions/xcdlDescription",
        // r);
        // // use attribute "key" as first param
        // digester.addCallParam("*/experiment/xcdlDescriptions/xcdlDescription",
        // 0 , "key");http://fue.onb.ac.at/abo/data
        // // and the object on stack (DigitalObject) as the second
        // digester.addCallParam("*/experiment/xcdlDescriptions/xcdlDescription",1,true);

        digester.addObjectCreate("*/experiment/detailedInfos/detailedInfo", DetailedExperimentInfo.class);
        digester.addSetProperties("*/experiment/detailedInfos/detailedInfo");
        digester.addBeanPropertySetter("*/experiment/detailedInfos/detailedInfo/programOutput");
        digester.addBeanPropertySetter("*/experiment/detailedInfos/detailedInfo/cpr");

        // call function "addDetailedInfo" of ExperimentWrapper
        r = new CallMethodRule(1, "addDetailedInfo", 2); // method with two
                                                         // params
                                                         // every time */experiment/detailedInfos/detailedInfo is encountered
        digester.addRule("*/experiment/detailedInfos/detailedInfo", r);
        // use attribute "key" as first param
        digester.addCallParam("*/experiment/detailedInfos/detailedInfo", 0, "key");
        // and the object on stack as second parameter
        digester.addCallParam("*/experiment/detailedInfos/detailedInfo", 1, true);

        // read contained measurements:
        digester.addObjectCreate("*/detailedInfo/measurements/measurement", Measurement.class);
        digester.addSetProperties("*/detailedInfo/measurements/measurement");
        digester.addSetNext("*/detailedInfo/measurements/measurement", "put");
        // values are defined with wild-cards, and therefore set
        // automatically

        /*
         * for each value type a set of rules because of FreeStringValue we need
         * to store the value as XML-element instead of an attribute naming them
         * "ResultValues" wasn't nice too
         */
        PlanParser.addCreateValue(digester, BooleanValue.class, "setValue");
        PlanParser.addCreateValue(digester, FloatRangeValue.class, "setValue");
        PlanParser.addCreateValue(digester, IntegerValue.class, "setValue");
        PlanParser.addCreateValue(digester, IntRangeValue.class, "setValue");
        PlanParser.addCreateValue(digester, OrdinalValue.class, "setValue");
        PlanParser.addCreateValue(digester, PositiveFloatValue.class, "setValue");
        PlanParser.addCreateValue(digester, PositiveIntegerValue.class, "setValue");
        PlanParser.addCreateValue(digester, YanValue.class, "setValue");
        PlanParser.addCreateValue(digester, FreeStringValue.class, "setValue");

        // go no go decision
        digester.addObjectCreate("*/plan/decision", Decision.class);
        digester.addSetProperties("*/plan/decision");
        digester.addSetNext("*/plan/decision", "setDecision");

        digester.addCallMethod("*/plan/decision/actionNeeded", "setActionNeeded", 0);
        digester.addCallMethod("*/plan/decision/reason", "setReason", 0);

        digester.addFactoryCreate("*/plan/decision/goDecision", GoDecisionFactory.class);
        digester.addSetNext("*/plan/decision/goDecision", "setDecision");

        // evaluation
        digester.addObjectCreate("*/plan/evaluation", Evaluation.class);
        digester.addSetProperties("*/plan/evaluation");
        digester.addSetNext("*/plan/evaluation", "setEvaluation");

        digester.addCallMethod("*/plan/evaluation/comment", "setComment", 0);

        // importance weighting
        digester.addObjectCreate("*/plan/importanceWeighting", ImportanceWeighting.class);
        digester.addSetProperties("*/plan/importanceWeighting");
        digester.addSetNext("*/plan/importanceWeighting", "setImportanceWeighting");

        digester.addCallMethod("*/plan/importanceWeighting/comment", "setComment", 0);

        // recommendation
        digester.addObjectCreate("*/plan/recommendation", RecommendationWrapper.class);
        digester.addSetProperties("*/plan/recommendation");
        digester.addSetNext("*/plan/recommendation", "setRecommendation");

        digester.addCallMethod("*/plan/recommendation/reasoning", "setReasoning", 0);
        digester.addCallMethod("*/plan/recommendation/effects", "setEffects", 0);

        // transformation
        digester.addObjectCreate("*/plan/transformation", Transformation.class);
        digester.addSetProperties("*/plan/transformation");
        digester.addSetNext("*/plan/transformation", "setTransformation");

        digester.addCallMethod("*/plan/transformation/comment", "setComment", 0);

        // Tree
        /*
         * Some rules for tree parsing are necessary for importing templates
         * too, that's why they are added by this static method.
         */
        PlanParser.addTreeParsingRulesToDigester(digester);

        digester.addObjectCreate("*/leaf/evaluation", HashMap.class);
        digester.addSetNext("*/leaf/evaluation", "setValueMap");
        /*
         * The valueMap has an entry for each (considered) alternative ... and
         * for each alternative there is a list of values, one per SampleObject.
         * Note: The digester uses a stack, therefore the rule to put the list
         * of values to the valueMap must be added after the rule for adding the
         * values to the list.
         */

        /*
         * 2. and for each alternative there is a list of values, one per
         * SampleObject
         */
        digester.addObjectCreate("*/leaf/evaluation/alternative", Values.class);
        digester.addCallMethod("*/leaf/evaluation/alternative/comment", "setComment", 0);

        /*
         * for each result-type a set of rules they are added to the valueMap by
         * the rules above
         */
        PlanParser.addCreateResultValue(digester, BooleanValue.class);
        PlanParser.addCreateResultValue(digester, FloatValue.class);
        PlanParser.addCreateResultValue(digester, FloatRangeValue.class);
        PlanParser.addCreateResultValue(digester, IntegerValue.class);
        PlanParser.addCreateResultValue(digester, IntRangeValue.class);
        PlanParser.addCreateResultValue(digester, OrdinalValue.class);
        PlanParser.addCreateResultValue(digester, PositiveFloatValue.class);
        PlanParser.addCreateResultValue(digester, PositiveIntegerValue.class);
        PlanParser.addCreateResultValue(digester, YanValue.class);
        PlanParser.addCreateResultValue(digester, FreeStringValue.class);

        /*
         * 1. The valueMap has an entry for each (considered) alternative ...
         */
        // call put of the ValueMap (HashMap)
        r = new CallMethodRule(1, "put", 2);
        digester.addRule("*/leaf/evaluation/alternative", r);
        digester.addCallParam("*/leaf/evaluation/alternative", 0, "key");
        digester.addCallParam("*/leaf/evaluation/alternative", 1, true);

        // digester.addObjectCreate("*/plan/executablePlan/planWorkflow",
        // ExecutablePlanContentWrapper.class);
        // digester.addSetProperties("*/plan/executablePlan/planWorkflow");
        // digester.addSetNext("*/plan/executablePlan/planWorkflow",
        // "setRecommendation");

        // Executable plan definition
        digester.addObjectCreate("*/plan/executablePlan", ExecutablePlanDefinition.class);
        digester.addSetProperties("*/plan/executablePlan");
        digester.addCallMethod("*/plan/executablePlan/objectPath", "setObjectPath", 0);
        digester.addCallMethod("*/plan/executablePlan/toolParameters", "setToolParameters", 0);
        digester.addCallMethod("*/plan/executablePlan/triggersConditions", "setTriggersConditions", 0);
        digester.addCallMethod("*/plan/executablePlan/validateQA", "setValidateQA", 0);
        PlanParser.addCreateUpload(digester, "*/plan/executablePlan/workflow", "setT2flowExecutablePlan",
                DigitalObject.class);

        digester.addSetNext("*/plan/executablePlan", "setExecutablePlanDefinition");

        // Preservation action plan
        digester.addObjectCreate("*/plan/preservationActionPlan", DigitalObject.class);
        digester.addSetNext("*/plan/preservationActionPlan", "setPreservationActionPlan");

        digester.addCallMethod("*/plan/preservationActionPlan", "setContentType", 1);
        digester.addObjectParam("*/plan/preservationActionPlan", 0, "application/xml");
        digester.addCallMethod("*/plan/preservationActionPlan", "setFullname", 1);
        digester.addObjectParam("*/plan/preservationActionPlan", 0, PreservationActionPlanGenerator.FULL_NAME);
        digester.addSetNext("*/plan/preservationActionPlan", "setPreservationActionPlan");

        PlanParser.addCreateXMLData(digester, "*/plan/preservationActionPlan", "setData", "setChangeLog");

        //
        // Import Planets executable plan if present
        //
        // object-create rules are called at the beginning element-tags, in
        // the
        // same order as defined
        // first create the wrapper
        // digester.addObjectCreate("*/plan/executablePlan/planWorkflow",
        // NodeContentWrapper.class);
        // // then an element for workflowConf
        // digester.addRule("*/plan/executablePlan/planWorkflow/workflowConf",
        // new NodeCreateRule());
        //
        // // CallMethod and SetNext rules are called at closing element-tags,
        // // (last
        // // in - first out!)
        //
        // CallMethodRule rr = new CallMethodRule(1, "setNodeContent", 2);
        // digester.addRule("*/plan/executablePlan/planWorkflow/workflowConf",
        // rr);
        // // right below the wrapper is an instance of
        // // ExecutablePlanDefinition
        // digester.addCallParam("*/plan/executablePlan/planWorkflow/workflowConf",
        // 0, 1);
        // // provide the name of the setter method
        // digester.addObjectParam("*/plan/executablePlan/planWorkflow/workflowConf",
        // 1, "setExecutablePlan");
        //
        // // the generated node is not accessible as CallParam (why?!?), but
        // // available for addSetNext
        // digester.addSetNext("*/plan/executablePlan/planWorkflow/workflowConf",
        // "setNode");
        //
        // //
        // // Import EPrints executable plan if present
        // //
        // digester.addObjectCreate("*/plan/executablePlan/eprintsPlan",
        // NodeContentWrapper.class);
        // // then an element for workflowConf
        // digester.addRule("*/plan/executablePlan/eprintsPlan", new
        // NodeCreateRule());
        //
        // CallMethodRule rr2 = new CallMethodRule(1,
        // "setNodeContentEPrintsPlan", 2);
        // digester.addRule("*/plan/executablePlan/eprintsPlan", rr2);
        // // right below the wrapper is an instance of
        // // ExecutablePlanDefinition
        // digester.addCallParam("*/plan/executablePlan/eprintsPlan", 0, 1);
        // // provide the name of the setter method
        // digester.addObjectParam("*/plan/executablePlan/eprintsPlan", 1,
        // "setEprintsExecutablePlan");

        // digester.addSetNext("*/plan/executablePlan/eprintsPlan", "setNode");

        // Plan definition
        digester.addObjectCreate("*/plan/planDefinition", PlanDefinition.class);
        digester.addSetProperties("*/plan/planDefinition");
        digester.addSetNext("*/plan/planDefinition", "setPlanDefinition");

        digester.addCallMethod("*/plan/planDefinition/costsIG", "setCostsIG", 0);
        digester.addCallMethod("*/plan/planDefinition/costsPA", "setCostsPA", 0);
        digester.addCallMethod("*/plan/planDefinition/costsPE", "setCostsPE", 0);
        digester.addCallMethod("*/plan/planDefinition/costsQA", "setCostsQA", 0);
        digester.addCallMethod("*/plan/planDefinition/costsREI", "setCostsREI", 0);
        digester.addCallMethod("*/plan/planDefinition/costsRemarks", "setCostsRemarks", 0);
        digester.addCallMethod("*/plan/planDefinition/costsRM", "setCostsRM", 0);
        digester.addCallMethod("*/plan/planDefinition/costsTCO", "setCostsTCO", 0);
        digester.addCallMethod("*/plan/planDefinition/responsibleExecution", "setResponsibleExecution", 0);
        digester.addCallMethod("*/plan/planDefinition/responsibleMonitoring", "setResponsibleMonitoring", 0);

        digester.addObjectCreate("*/plan/planDefinition/triggers", TriggerDefinition.class);
        digester.addSetNext("*/plan/planDefinition/triggers", "setTriggers");
        // every time a */plan/basis/triggers/trigger is encountered:
        digester.addFactoryCreate("*/plan/planDefinition/triggers/trigger", TriggerFactory.class);
        digester.addSetNext("*/plan/planDefinition/triggers/trigger", "setTrigger");
    }

    /**
     * Create a rule for reading an upload entry for the given location
     * <code>pattern</code>, and use the <code>method</code> to set the upload
     * object.
     * 
     * @param digester
     *            the digester
     * @param pattern
     *            the location pattern
     * @param method
     *            a method name of the parent object
     * @param objectType
     *            class of object to create
     */
    private static void addCreateUpload(final Digester digester, final String pattern, final String method,
            final Class<?> objectType) {
        digester.addObjectCreate(pattern, objectType);
        digester.addSetProperties(pattern);
        if ((method != null) && (!"".equals(method))) {
            digester.addSetNext(pattern, method);
        }
        /*
         * Note: It is not possible to read element data, process it and pass it
         * to a function with a simple digester Rule, neither you can define a
         * factory to read the data of an element.
         * 
         * So we have to do it the other way round: (remember: the function
         * added last is executed first!)
         */
        // 1. Create a BinaryDataWrapper if a <data> element is encountered
        digester.addObjectCreate(pattern + "/data", BinaryDataWrapper.class);
        // 3. Finally call setData on the BinaryDataWrapper(!) on top with the
        // object next to top as argument
        // The BinaryDataWrapper will call setData on to object next to top with
        // the previously read and decoded data
        digester.addSetTop(pattern + "/data", "setData");
        // 2. Call setFromBase64Encoded on the BinaryDataWrapper to read the
        // elements content
        digester.addCallMethod(pattern + "/data", "setFromBase64Encoded", 0);

    }

    /**
     * Create a rule for parsing an element for the given location
     * <code>pattern</code>, write it the XML as binary data, and use the
     * <code>dataSetterMethod</code> to set the data in the parent.
     * 
     * @param digester
     *            the digester
     * @param pattern
     *            the location pattern
     * @param dataMethod
     *            a method name of the parent object that will be called to set
     *            the binary data or null to use XMLDataWrapper's default
     * @throws ParserConfigurationException
     *             if the digester could not be configured
     */
    private static void addCreateXMLData(final Digester digester, final String pattern, String dataMethod,
            String changeLogMethod) throws ParserConfigurationException {

        // 1. Create a XMLDataWrapper
        digester.addObjectCreate(pattern, XMLDataWrapper.class);

        if (changeLogMethod != null) {
            // 6. Finally call setChangeLog on the XMLDataWrapper on top with
            // the object next to top as argument.
            digester.addSetTop(pattern, "setChangeLog");
            // 5. Set change log setter method on XMLDataWrapper
            digester.addCallMethod(pattern, "setChangeLogMethodName", 1);
            digester.addObjectParam(pattern, 0, changeLogMethod);
        }

        // 4. Finally call setData on the XMLDataWrapper on top with the
        // object next to top as argument.
        digester.addSetTop(pattern, "setData");
        // 3. Set data setter method on XMLDataWrapper
        if (dataMethod != null) {
            digester.addCallMethod(pattern, "setMethodName", 1);
            digester.addObjectParam(pattern, 0, dataMethod);
        }
        // 2. Create element from XML and set the element in the XMLDataWrapper
        NodeCreateRule nodeCreateRule = new NodeCreateRule();
        digester.addRule(pattern, nodeCreateRule);
        digester.addSetNext(pattern, "setEncoded");

    }

    /**
     * This method adds rules for name, properties, scales, modes and mappings
     * only! Rules for importing measured values of alternatives are defined
     * seperately in importProjects()! (Refactored to its own method by Kevin)
     * 
     * @param digester
     *            the digester
     */
    private static void addTreeParsingRulesToDigester(final Digester digester) {
        digester.addObjectCreate("*/plan/tree", ObjectiveTree.class);
        digester.addSetProperties("*/plan/tree");
        digester.addSetNext("*/plan/tree", "setTree");

        digester.addObjectCreate("*/node/node", Node.class);
        digester.addSetProperties("*/node/node");
        digester.addSetNext("*/node/node", "addChild");

        digester.addCallMethod("*/node/description", "setDescription", 0);

        digester.addObjectCreate("*/plan/tree/node", Node.class);
        digester.addSetProperties("*/plan/tree/node");
        digester.addSetNext("*/plan/tree/node", "setRoot");

        digester.addObjectCreate("*/leaf", Leaf.class);
        digester.addSetProperties("*/leaf");
        digester.addSetNext("*/leaf", "addChild");
        digester.addFactoryCreate("*/leaf/aggregationMode", SampleAggregationModeFactory.class);
        digester.addSetNext("*/leaf/aggregationMode", "setAggregationMode");

        digester.addCallMethod("*/leaf/description", "setDescription", 0);

        PlanParser.addMeasureRules(digester, "*/measure");

        /*
         * for each scale-type a set of rules
         */
        PlanParser.addCreateScale(digester, BooleanScale.class);
        PlanParser.addCreateScale(digester, FloatRangeScale.class);
        PlanParser.addCreateScale(digester, FloatScale.class);
        PlanParser.addCreateScale(digester, IntegerScale.class);
        PlanParser.addCreateScale(digester, IntRangeScale.class);
        PlanParser.addCreateScale(digester, OrdinalScale.class);
        PlanParser.addCreateScale(digester, PositiveFloatScale.class);
        PlanParser.addCreateScale(digester, PositiveIntegerScale.class);
        PlanParser.addCreateScale(digester, YanScale.class);
        PlanParser.addCreateScale(digester, FreeStringScale.class);

        /*
         * for each transformer type a set of rules
         */
        digester.addObjectCreate("*/leaf/numericTransformer", NumericTransformer.class);
        digester.addSetProperties("*/leaf/numericTransformer");
        digester.addFactoryCreate("*/leaf/numericTransformer/mode", TransformationModeFactory.class);
        digester.addSetNext("*/leaf/numericTransformer/mode", "setMode");
        digester.addBeanPropertySetter("*/leaf/numericTransformer/thresholds/threshold1", "threshold1");
        digester.addBeanPropertySetter("*/leaf/numericTransformer/thresholds/threshold2", "threshold2");
        digester.addBeanPropertySetter("*/leaf/numericTransformer/thresholds/threshold3", "threshold3");
        digester.addBeanPropertySetter("*/leaf/numericTransformer/thresholds/threshold4", "threshold4");
        digester.addBeanPropertySetter("*/leaf/numericTransformer/thresholds/threshold5", "threshold5");
        digester.addSetNext("*/leaf/numericTransformer", "setTransformer");

        // digester.addObjectCreate("*/numericTransformer/thresholds",
        // LinkedHashMap.class);
        // digester.addSetNext("*/numericTransformer/thresholds",
        // "setThresholds");
        // digester.addFactoryCreate("*/thresholds/threshold",
        // NumericTransformerThresholdFactory.class);

        digester.addObjectCreate("*/leaf/ordinalTransformer", OrdinalTransformer.class);
        digester.addSetProperties("*/leaf/ordinalTransformer");
        digester.addSetNext("*/leaf/ordinalTransformer", "setTransformer");

        digester.addObjectCreate("*/ordinalTransformer/mappings", LinkedHashMap.class);
        digester.addSetNext("*/ordinalTransformer/mappings", "setMapping");
        digester.addFactoryCreate("*/mappings/mapping", OrdinalTransformerMappingFactory.class);

        digester.addRule("*/mappings/mapping", new CallMethodRule(1, "put", 2)); // method
                                                                                 // with
                                                                                 // two
                                                                                 // params
        digester.addCallParam("*/mappings/mapping", 0, "ordinal"); // use
                                                                   // attribute
                                                                   // "ordinal"
                                                                   // as first
                                                                   // argument
        digester.addCallParam("*/mappings/mapping", 1, true); // and the object
                                                              // on the stack as
                                                              // second
    }

    private static void addMeasureRules(final Digester digester, final String pattern) {
        digester.addObjectCreate(pattern, Measure.class);
        digester.addSetNext(pattern, "setMeasure");
        digester.addSetProperties(pattern, "ID", "uri");
        digester.addBeanPropertySetter(pattern + "/name");
        digester.addBeanPropertySetter(pattern + "/description");

        PlanParser.addAttributeRules(digester, pattern + "/attribute");
        // scale will be set by global rule
    }

    private static void addCreateResultValue(final Digester digester, final Class c) {
        String name = c.getName();
        name = name.substring(name.lastIndexOf(".") + 1);
        name = name.substring(0, 1).toLowerCase() + name.substring(1);

        String pattern = "*/" + name.replace("Value", "Result");
        digester.addObjectCreate(pattern, c);
        digester.addSetProperties(pattern);
        digester.addBeanPropertySetter(pattern + "/value");
        digester.addBeanPropertySetter(pattern + "/comment");
        digester.addSetNext(pattern, "add");
    }

    private static void addCreateValue(final Digester digester, final Class c, final String setNextMethod) {
        String name = c.getName();
        name = name.substring(name.lastIndexOf(".") + 1);
        name = name.substring(0, 1).toLowerCase() + name.substring(1);

        String pattern = "*/" + name;
        digester.addObjectCreate(pattern, c);
        // digester.addSetProperties(pattern);
        digester.addBeanPropertySetter(pattern + "/value");
        digester.addSetNext(pattern, setNextMethod);
    }

    private static void addCreateScale(final Digester digester, final Class c) {
        String name = c.getName();
        name = name.substring(name.lastIndexOf(".") + 1);
        name = name.substring(0, 1).toLowerCase() + name.substring(1);

        String pattern = "*/" + name;
        digester.addObjectCreate(pattern, c);
        digester.addSetProperties(pattern);
        digester.addSetNext(pattern, "setScale");
    }

    private static void addAttributeRules(final Digester digester, final String pattern) {
        digester.addObjectCreate(pattern, Attribute.class);
        digester.addSetNext(pattern, "setAttribute");
        digester.addSetProperties(pattern, "ID", "uri");
        digester.addBeanPropertySetter(pattern + "/name");
        digester.addBeanPropertySetter(pattern + "/description");

        digester.addObjectCreate(pattern + "/category", CriterionCategory.class);
        digester.addSetProperties(pattern + "/category", "ID", "uri");
        digester.addSetProperties(pattern + "/category", "scope", "scope");
        digester.addBeanPropertySetter(pattern + "/category/name");
        digester.addSetNext(pattern + "/category", "setCategory");

    }
}