eu.planets_project.pp.plato.services.characterisation.xcl.XclPropertiesExplorer.java Source code

Java tutorial

Introduction

Here is the source code for eu.planets_project.pp.plato.services.characterisation.xcl.XclPropertiesExplorer.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.services.characterisation.xcl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.xml.sax.SAXException;

import eu.planets_project.pp.plato.model.ObjectProperty;
import eu.planets_project.pp.plato.model.characterisation.xcl.XCLObjectProperty;
import eu.planets_project.pp.plato.model.measurement.Metric;
import eu.planets_project.pp.plato.model.scales.Scale;
import eu.planets_project.pp.plato.services.PlatoServiceException;
import eu.planets_project.pp.plato.services.characterisation.ICharacterisationService;
import eu.planets_project.pp.plato.util.CommandExecutor;
import eu.planets_project.pp.plato.util.OS;
import eu.planets_project.pp.plato.util.PlatoLogger;
import eu.planets_project.pp.plato.xml.StrictErrorHandler;

/**
 * This class deals with the current implementation of the PP5 characterisation tool.
 * Based on a list of pronom unique identifiers (puid) it returns all measurable properties.
 *
 * @author Hannes Kulovits
 */
public class XclPropertiesExplorer implements ICharacterisationService, Serializable {

    private static final long serialVersionUID = 3499698474788470264L;

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

    private Set<ObjectProperty> propertiesSet;
    private List<String> warnings;
    private String format;
    private String status;
    private String error;

    private String xclExplorerPath;

    private String makeTempDir() {
        String tempDir = OS.getTmpPath() + "xclexplorer" + System.nanoTime() + "/";
        new File(tempDir).mkdir();
        return tempDir;
    }

    public XclPropertiesExplorer() {

        xclExplorerPath = System.getenv("XCLEXPLORER_HOME");
        if (xclExplorerPath != null) {
            xclExplorerPath = xclExplorerPath + (xclExplorerPath.endsWith(File.separator) ? "" : File.separator);
        } else {
            log.error("Environment variable XCLEXPLORER_HOME is not defined!");
        }
    }

    /**
     * @param PUIDs colon seperated list of "planets puids", i.e. pronom unique identifieres using an
     *        underscore character instead of a forward slash. The list must end with a colon.
     *        Example: fmt_10:fmt_13:
     */
    public List<ObjectProperty> characterise(String PUIDs, List<String> warnings) throws PlatoServiceException {

        if (("".equals(PUIDs)) || (xclExplorerPath == null)) {
            return new ArrayList<ObjectProperty>();
        }

        String xcelString;
        String tempDir = makeTempDir();
        String command = xclExplorerPath + "XCLExplorer " + PUIDs + " -o " + tempDir;

        try {
            CommandExecutor cmdExecutor = new CommandExecutor();
            cmdExecutor.setWorkingDirectory(xclExplorerPath);
            try {
                int exitStatus = cmdExecutor.runCommand(command);
                // r.setSuccess(exitStatus == 0);
                //r.setReport(cmdExecutor.getCommandError());
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }

            String outputFile = tempDir + "fpm.fpm";

            // we use a set as we don't want dublicate properties
            propertiesSet = new HashSet<ObjectProperty>();
            this.warnings = warnings;
            status = null;
            format = null;
            error = null;

            Digester digester = new Digester();
            digester.setValidating(false);
            digester.setErrorHandler(new StrictErrorHandler());

            digester.push(this);

            digester.addCallMethod("*/XCLExplorer/fpmError", "setError", 0);

            // maybe there is a warning in the property "status"
            digester.addSetProperties("*/XCLExplorer/format");
            digester.addCallMethod("*/XCLExplorer/format", "addWarning", 0);

            digester.addObjectCreate("*/XCLExplorer/format/property", XCLObjectProperty.class);
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/id", "propertyId");
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/name", "name");
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/description", "description");
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/unit", "unit");
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/type", "type");

            digester.addObjectCreate("*/XCLExplorer/format/property/metrics/m", Metric.class);
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/metrics/m/mId", "metricId");
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/metrics/m/mName", "name");
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/metrics/m/mDescription", "description");
            digester.addBeanPropertySetter("*/XCLExplorer/format/property/metrics/m/mType", "type");
            digester.addSetNext("*/XCLExplorer/format/property/metrics/m", "addMetric");

            digester.addSetNext("*/XCLExplorer/format/property", "addProperty");

            try {
                digester.setUseContextClassLoader(true);

                digester.parse(new FileInputStream(outputFile));

                if (error != null) {
                    throw new PlatoServiceException("XCLExplorer failed: " + error);
                }

                return new ArrayList<ObjectProperty>(propertiesSet);

            } catch (IOException e) {
                throw new PlatoServiceException("The response of XCLExplorer is invalid.", e);
            } catch (SAXException e) {
                throw new PlatoServiceException("The response of XCLExplorer is invalid.", e);
            } finally {
                new File(tempDir + "fpm.fpm").delete();
                new File(tempDir).delete();
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return new ArrayList<ObjectProperty>();
        }
    }

    /**
     * Adds the ObjectProperty to the propertiesSet.
     * Used to parse the fpmResponse.
     *  
     * @param p
     */
    public void addProperty(ObjectProperty p) {
        propertiesSet.add(p);
    }

    /**
     * Adds the status info to the {@link #warnings}.
     * Used to parse the fpmResponse.
     * 
     * @param s
     */
    public void setStatus(String s) {
        status = s;
    }

    public void setPuid(String f) {
        format = f;
    }

    public void addWarning(String s) {
        if ((status != null) && !("".equals(status)))
            if ("unavailable".equals(status))
                warnings.add(
                        "At the moment the characterisation service at hki.uni-koeln.de does not support the format "
                                + format + ".");
            else
                warnings.add(format + ": " + status);

        format = null;
        status = null;
    }

    public static Scale adjustCriterionToMetric(Metric metric) {
        if (metric == null)
            return null;
        /*
         * Plato's Scales are more precise than the result types of XCL metrics: 
         * There are not only int, float and boolean values, but a distinction is drawn between
         * positive and negative numbers.
         * Therefore we have to choose the scale according to the metric itself.  
         */
        Scale scale = MetricToScaleMapping.getScale(metric.getMetricId().trim());
        return scale;

    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }
}