eu.planets_project.pp.plato.action.workflow.EvaluateExperimentsAction.java Source code

Java tutorial

Introduction

Here is the source code for eu.planets_project.pp.plato.action.workflow.EvaluateExperimentsAction.java

Source

/*******************************************************************************
 * Copyright (c) 2006-2010 Vienna University of Technology, 
 * Department of Software Technology and Interactive Systems
 *
 * All rights reserved. This program and the accompanying
 * materials are made available under the terms of the
 * Apache License, Version 2.0 which accompanies
 * this distribution, and is available at
 * http://www.apache.org/licenses/LICENSE-2.0 
 *******************************************************************************/

package eu.planets_project.pp.plato.action.workflow;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.faces.application.FacesMessage;

import org.apache.commons.logging.Log;
import org.jboss.annotation.ejb.cache.Cache;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.RaiseEvent;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.faces.FacesMessages;

import eu.planets_project.pp.plato.action.interfaces.IEvaluateExperiments;
import eu.planets_project.pp.plato.action.interfaces.ITransformMeasuredValues;
import eu.planets_project.pp.plato.action.interfaces.IWorkflowStep;
import eu.planets_project.pp.plato.bean.BooleanCapsule;
import eu.planets_project.pp.plato.evaluation.IActionEvaluator;
import eu.planets_project.pp.plato.evaluation.IObjectEvaluator;
import eu.planets_project.pp.plato.evaluation.IStatusListener;
import eu.planets_project.pp.plato.evaluation.MiniRED;
import eu.planets_project.pp.plato.model.Alternative;
import eu.planets_project.pp.plato.model.DigitalObject;
import eu.planets_project.pp.plato.model.EvaluationStatus;
import eu.planets_project.pp.plato.model.PlanState;
import eu.planets_project.pp.plato.model.SampleObject;
import eu.planets_project.pp.plato.model.Values;
import eu.planets_project.pp.plato.model.measurement.MeasurableProperty;
import eu.planets_project.pp.plato.model.tree.Leaf;
import eu.planets_project.pp.plato.model.tree.Node;
import eu.planets_project.pp.plato.model.tree.TreeNode;
import eu.planets_project.pp.plato.model.values.Value;
import eu.planets_project.pp.plato.services.characterisation.jhove.JHoveAdaptor;
import eu.planets_project.pp.plato.services.characterisation.jhove.tree.JHoveTree;
import eu.planets_project.pp.plato.util.MeasurementInfoUri;
import eu.planets_project.pp.plato.util.PlatoLogger;
import eu.planets_project.pp.plato.validators.INodeValidator;
import eu.planets_project.pp.plato.validators.ITreeValidator;
import eu.planets_project.pp.plato.validators.TreeValidator;

/**
 * Implements actions for workflow step 'Evaluate Experiments', i.e. enable the user
 * to enter the evaluation result per alternative and sample record. The user has to enter
 * evaluation result for all leaves in the objective tree.
 * @author Hannes Kulovits
 */
@Stateful
@Scope(ScopeType.SESSION)
@Name("evalexperiments")
@Cache(org.jboss.ejb3.cache.NoPassivationCache.class)
public class EvaluateExperimentsAction extends AbstractWorkflowStep
        implements IEvaluateExperiments, INodeValidator, IStatusListener {
    /**
     * 
     */
    private static final long serialVersionUID = -3892845379219070784L;

    protected boolean needsClearEm() {
        return true;
    }

    private static final Log log = PlatoLogger.getLogger(EvaluateExperimentsAction.class);

    private StringBuffer evaluationLogBuffer;

    @In(create = true)
    ITransformMeasuredValues transform;

    @SuppressWarnings("unused")
    @Out(required = false)
    private JHoveTree jhoveTree1;

    @SuppressWarnings("unused")
    @Out(required = false)
    private JHoveTree jhoveTree2;

    /**
     * Creates from the SampleObject object, retrieved by injection, a Tree with all the
     * characteristics extracted from Jhove
     * 
     */
    public JHoveTree characteriseJHove(DigitalObject object) {
        JHoveTree jhoveTree = new JHoveTree();

        //returns an empty tree for null
        if (object == null) {
            return jhoveTree;
        }

        if (object.getJhoveXMLString() == null || "".equals(object.getJhoveXMLString())) {
            object.setJhoveXMLString(new JHoveAdaptor().describe(object));
        }
        if (object.getJhoveXMLString() != null && !"".equals(object.getJhoveXMLString())) {
            jhoveTree = new JHoveAdaptor().digestString(object.getFullname(), object.getJhoveXMLString());
        }

        return jhoveTree;
    }

    /**
     * the node currently selected in the objective tree.
     */
    @In(required = false)
    @Out(required = false)
    TreeNode node;

    public EvaluateExperimentsAction() {
        requiredPlanState = new Integer(PlanState.EXPERIMENT_PERFORMED);
    }

    /**
     * List of all leaves for which the evaluation settings shall be displayed to the user. 
     */
    @Out(required = false)
    @In(required = false)
    List<Leaf> leaves;

    @Out
    private List<MeasurableProperty> measurableProperties = new ArrayList<MeasurableProperty>();

    /**
     * Leaves displayed to the user.
     */
    @Out(required = false)
    @In(required = false)
    List<Leaf> errorleaves;

    @Out
    private BooleanCapsule hasAutomatedMeasurements = new BooleanCapsule();

    protected IWorkflowStep getSuccessor() {
        return transform;
    }

    /**
     * initialises the values in the tree and inits/clears the leaf list
     * @see AbstractWorkflowStep#init()
     */
    public void init() {
        clearLogBuffer();
        initLeafLists();
        boolean xcl = false;
        hasAutomatedMeasurements.setBool(false);
        Iterator<Leaf> iter = selectedPlan.getTree().getRoot().getAllLeaves().iterator();
        while (iter.hasNext()) {
            Leaf l = iter.next();
            if (l.isMapped()) {
                hasAutomatedMeasurements.setBool(true);

                if (l.getMeasurementInfo().getProperty().getName().startsWith("xcl/")) {
                    xcl = true;
                }
            }
        }
        if (xcl) {
            for (DigitalObject o : selectedPlan.getSampleRecordsDefinition().getRecords()) {
                if ((o.getXcdlDescription() == null) || !o.getXcdlDescription().isDataExistent()) {
                    FacesMessages.instance().add(FacesMessage.SEVERITY_INFO,
                            "Some XCL descriptions for samples are missing, XCL comparison will not work for these samples. ");
                    break;
                }
            }
            for (Alternative a : selectedPlan.getAlternativesDefinition().getAlternatives()) {
                for (DigitalObject r : a.getExperiment().getResults().values()) {
                    if ((r.getXcdlDescription() == null) || !r.getXcdlDescription().isDataExistent()) {
                        FacesMessages.instance().add(FacesMessage.SEVERITY_INFO,
                                "XCL descriptions for experiment results of " + a.getName() + " are missing, "
                                        + "XCL comparison will not work for these objects. ");
                        break;
                    }
                }
            }
        }

        // TODO for now this is ALWAYS reloaded when entering,
        // but should be cached in the future.
        MiniRED.getInstance().reloadEvaluators();
        refreshMeasurableProperties();

    }

    private void clearLogBuffer() {
        evaluationLogBuffer = new StringBuffer();
        Contexts.getEventContext().remove("evaluationMessage");
    }

    /**
     * @param record sample object the alternative has been carried out on
     * @param alternative alternative which has been carried out on the alternative (we determine the result object from the alternative)
     */
    public void setTreeFromRecordAltern(Object record, Object alternative) {

        if (!(record instanceof SampleObject) || !(alternative instanceof Alternative)) {
            return;
        }

        Alternative a = (Alternative) alternative;
        SampleObject o = (SampleObject) record;

        o = em.merge(o);
        jhoveTree1 = characteriseJHove(o);

        // get the result 
        // we have to merge the sample object back into the session to be able to access the byte stream
        DigitalObject result = em.merge(a.getExperiment().getResults().get(record));
        a.getExperiment().getResults().put(o, result);

        jhoveTree2 = characteriseJHove(result);
    }

    /**
     * Select a node or leaf from the tree.
     */
    public String select(Object ob) {
        initErrorLeaves();
        log.trace("Select Called with: " + ob.toString());
        if (ob instanceof Node) {
            log.debug("Setting all Leaves");
            leaves = ((Node) ob).getAllLeaves();
        } else if (ob instanceof Leaf) {
            log.debug("Setting leaf: " + ob.toString());
            leaves.clear();
            leaves.add((Leaf) ob);
        }
        return null;
    }

    /**
     * @see AbstractWorkflowStep#discard()
     */
    @Override
    @RaiseEvent("reload")
    public String discard() {
        String result = super.discard();
        init();
        return result;
    }

    /**
     * @see AbstractWorkflowStep#destroy()
     */
    @Destroy
    @Remove
    public void destroy() {
    }

    /**
     * @see eu.planets_project.pp.plato.action.workflow.AbstractWorkflowStep#validate()
     */
    public boolean validate(boolean showValidationErrors) {
        ITreeValidator validator = new TreeValidator();
        List<TreeNode> nodes = new ArrayList<TreeNode>();
        boolean valid = validator.validate(selectedPlan.getTree().getRoot(), this, nodes, showValidationErrors);
        if (!valid) {
            if (leaves == null) {
                leaves = new ArrayList<Leaf>();
            } else {
                leaves.clear();
            }
            //All invalid leaves should be in the list so that they are displayed
            for (TreeNode node : nodes) {
                if (node.isLeaf()) {
                    this.leaves.add((Leaf) node);
                }
            }
        }
        return valid;
    }

    /**
     * @see eu.planets_project.pp.plato.util.INodeValidator#validateNode(eu.planets_project.pp.plato.model.TreeNode, java.util.List, java.util.List)
     */
    public boolean validateNode(TreeNode node, List<String> nodelist, List<TreeNode> nodes) {
        return node.isCompletelyEvaluated(nodelist, nodes,
                selectedPlan.getAlternativesDefinition().getConsideredAlternatives());
    }

    /**
     * We have the rule that all evaluation settings have to be either changed or confirmed once
     * by the user.
     * This approve function makes it easier to confirm the settings for many leaves at once - 
     * It touches all currently displayed leaves so that they are marked as confirmed.
     * @see eu.planets_project.pp.plato.model.values.Value#touch()
     */
    public void approve() {
        for (Leaf leaf : leaves) {
            for (Values values : leaf.getValueMap().values()) {
                for (Value value : values.getList()) {
                    value.touch();
                }
            }
        }
    }

    /**
     * @see AbstractWorkflowStep#getWorkflowstepName()
     */
    protected String getWorkflowstepName() {
        return "evalexperiments";
    }

    /**
     * evaluates the given leaves automatically.
     * This is only possible for criteria, where information on the measurement has been defined.
     * The registered evaluators are applied one after an other, 
     * if an evaluator is able to measure a criterion, its value is applied and the criterion is excluded from further evaluation.
     * 
     * First per alternative all action related evaluators are called.
     * 
     * Then per alternative, for each sample object, all object/runtime related evaluators are called. 
     * 
     * @param leaves
     */
    private void evaluateLeaves(List<Leaf> leaves) {
        clearLogBuffer();

        // we evaluate measurements and have to assign each result to the corresponding leaf: build a map
        HashMap<MeasurementInfoUri, Leaf> measurementOfLeaf = new HashMap<MeasurementInfoUri, Leaf>();

        // list of measurements which shall be evaluated
        List<MeasurementInfoUri> allMeasurementsToEval = new LinkedList<MeasurementInfoUri>();

        for (Leaf l : leaves) {
            // measure this criterion automatically
            MeasurementInfoUri m = l.getMeasurementInfo().toMeasurementInfoUri();
            if ((m != null) && (m.getAsURI() != null)) {
                measurementOfLeaf.put(m, l);
                allMeasurementsToEval.add(m);
            }
        }

        try {
            // start evaluation:
            List<MeasurementInfoUri> measurementsToEval = new ArrayList<MeasurementInfoUri>();
            // first action evaluators  
            List<IActionEvaluator> actionEvaluators = MiniRED.getInstance().getActionEvaluationSequence();
            for (Alternative alternative : selectedPlan.getAlternativesDefinition().getConsideredAlternatives()) {
                // we want to evaluate each property only once, by the evaluator with the highest priority
                measurementsToEval.clear();
                measurementsToEval.addAll(allMeasurementsToEval);
                for (IActionEvaluator evaluator : actionEvaluators) {
                    Map<MeasurementInfoUri, Value> results = evaluator.evaluate(alternative, measurementsToEval,
                            this);
                    // apply all results
                    for (MeasurementInfoUri m : results.keySet()) {
                        Value value = results.get(m);
                        if (value != null) {
                            Leaf l = measurementOfLeaf.get(m);
                            value.setScale(l.getScale());
                            l.getValues(alternative.getName()).setValue(0, value);
                        }
                    }
                    // exclude evaluated leaves from further evaluation
                    measurementsToEval.removeAll(results.keySet());
                }
            }
            // then object evaluators
            List<IObjectEvaluator> objEvaluators = MiniRED.getInstance().getObjectEvaluationSequence();
            for (Alternative alternative : selectedPlan.getAlternativesDefinition().getConsideredAlternatives()) {
                // .. for all alternatives
                List<SampleObject> samples = selectedPlan.getSampleRecordsDefinition().getRecords();
                for (int i = 0; i < samples.size(); i++) {
                    // we want to evaluate each property only once, by the evaluator with the highest priority
                    measurementsToEval.clear();
                    measurementsToEval.addAll(allMeasurementsToEval);

                    for (IObjectEvaluator evaluator : objEvaluators) {
                        DigitalObject r = alternative.getExperiment().getResults().get(samples.get(i));
                        DigitalObject r2 = (r == null ? null : em.merge(r));
                        try {
                            Map<MeasurementInfoUri, Value> results = evaluator.evaluate(alternative,
                                    em.merge(samples.get(i)), r2, measurementsToEval, this);
                            // apply all results
                            for (MeasurementInfoUri m : results.keySet()) {
                                Value value = results.get(m);
                                if (value != null) {
                                    Leaf l = measurementOfLeaf.get(m);
                                    value.setScale(l.getScale());
                                    // add evaluation result for the current result-object!
                                    l.getValues(alternative.getName()).setValue(i, value);
                                }
                            }
                            // exclude evaluated leaves from further evaluation
                            measurementsToEval.removeAll(results.keySet());
                        } catch (Exception e) {
                            log.error("evaluator failed" + e.getMessage(), e);
                            continue;
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("Automated evaluation threw exception " + e.getMessage(), e);
            FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
                    "Automated evaluation failed:" + e.getMessage());
            updateStatus("Automated evaluation threw exception " + e.getMessage());
        }
        Contexts.getEventContext().set("evaluationMessage", evaluationLogBuffer.toString());
    }

    public void evaluateAll() {
        evaluateLeaves(selectedPlan.getTree().getRoot().getAllLeaves());
    }

    public void evaluate(Leaf leaf) {
        evaluateLeaves(Arrays.asList(leaf));
    }

    private void refreshMeasurableProperties() {
        measurableProperties.clear();
        measurableProperties.addAll(selectedPlan.getMeasurableProperties());
        for (MeasurableProperty p : measurableProperties) {
            log.debug("prop:: " + p.getName());
        }
    }

    public void updateStatus(String msg) {
        log.info(msg);
        evaluationLogBuffer.append(msg).append("\n");
    }

    @Override
    public String save() {
        // initialising the values for free text transformers
        for (Leaf l : selectedPlan.getTree().getRoot().getAllLeaves()) {
            l.initTransformer();
        }
        return super.save();
    }

    /**
     * 
     */
    private void initErrorLeaves() {
        if (errorleaves == null) {
            errorleaves = new ArrayList<Leaf>();
        } else {
            errorleaves.clear();
        }
    }

    /**
     * 
     */
    private void initLeafLists() {
        if (leaves == null) {
            leaves = new ArrayList<Leaf>();
        } else {
            leaves.clear();
        }
        initErrorLeaves();
    }

}