edu.isi.wings.catalog.component.api.impl.kb.ComponentReasoningKB.java Source code

Java tutorial

Introduction

Here is the source code for edu.isi.wings.catalog.component.api.impl.kb.ComponentReasoningKB.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 edu.isi.wings.catalog.component.api.impl.kb;

import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;

import edu.isi.wings.catalog.component.api.ComponentReasoningAPI;
import edu.isi.wings.catalog.component.classes.Component;
import edu.isi.wings.catalog.component.classes.ComponentInvocation;
import edu.isi.wings.catalog.component.classes.ComponentPacket;
import edu.isi.wings.catalog.component.classes.ComponentRole;
import edu.isi.wings.catalog.component.classes.requirements.ComponentRequirement;
import edu.isi.wings.catalog.data.classes.metrics.Metric;
import edu.isi.wings.catalog.data.classes.metrics.Metrics;
import edu.isi.wings.common.UuidGen;
import edu.isi.wings.common.kb.KBUtils;
import edu.isi.wings.ontapi.*;
import edu.isi.wings.ontapi.rules.KBRuleList;
import edu.isi.wings.workflow.template.api.ConstraintEngine;
import edu.isi.wings.workflow.template.api.impl.kb.ConstraintEngineKB;
import edu.isi.wings.workflow.template.classes.Role;
import edu.isi.wings.workflow.template.classes.sets.Binding;
import edu.isi.wings.workflow.template.classes.sets.ValueBinding;
import edu.isi.wings.workflow.template.classes.sets.WingsSet;
import edu.isi.wings.workflow.template.classes.variables.ComponentVariable;
import edu.isi.wings.workflow.template.classes.variables.Variable;
import edu.isi.wings.workflow.template.classes.variables.VariableType;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.util.*;

public class ComponentReasoningKB extends ComponentKB implements ComponentReasoningAPI {
    private Logger logger = Logger.getLogger(this.getClass());

    private HashMap<String, Component> ccache = new HashMap<String, Component>();
    private HashMap<String, KBRuleList> rulescache = new HashMap<String, KBRuleList>();
    private HashMap<String, ArrayList<String>> abscache = new HashMap<String, ArrayList<String>>();
    private HashMap<String, ArrayList<KBObject>> classcache = new HashMap<String, ArrayList<KBObject>>();
    private HashMap<String, ArrayList<KBTriple>> kbcache = new HashMap<String, ArrayList<KBTriple>>();

    private ArrayList<KBTriple> metricTriples;
    private ArrayList<KBObject> metricProps;

    public ComponentReasoningKB(Properties props) {
        super(props, true, false, true, false);
        this.initializeMetrics();
    }

    private void initializeMetrics() {
        KBObject rdfsProp = this.kb.getProperty(KBUtils.RDFS + "subPropertyOf");
        KBObject dcProp = this.kb.getProperty(this.dcns + "hasMetrics");
        KBObject dcPropD = this.kb.getProperty(this.dcns + "hasDataMetrics");

        // Get all metric properties
        this.metricTriples = this.kb.genericTripleQuery(null, rdfsProp, dcProp);
        // Get all data metric properties
        this.metricTriples.addAll(this.kb.genericTripleQuery(null, rdfsProp, dcPropD));

        // Get a list of all metrics and datametrics properties in the catalog
        this.metricProps = this.kb.getSubPropertiesOf(this.objPropMap.get("hasMetrics"), false);
        this.metricProps.addAll(this.kb.getSubPropertiesOf(this.dataPropMap.get("hasDataMetrics"), false));
    }

    protected KBObject copyObjectIntoKB(String id, KBObject obj, KBAPI tkb, String includeNS, String excludeNS,
            boolean direct) {
        // Add component to the temporary KB (add all its classes explicitly)
        for (KBTriple triple : this.getTriplesForObject(id, obj, includeNS, excludeNS, direct)) {
            tkb.addTriple(triple);
        }
        return tkb.getIndividual(id);
    }

    protected KBObject copyObjectClassesIntoKB(String id, KBObject obj, KBAPI tkb, String includeNS,
            String excludeNS, boolean direct) {
        // Add component to the temporary KB (add all its classes explicitly)
        for (KBTriple triple : this.getObjectClassTriples(id, obj, includeNS, excludeNS, direct)) {
            tkb.addTriple(triple);
        }
        return tkb.getIndividual(id);
    }

    protected ArrayList<String> getConcreteComponentsForAbstract(String id) {
        if (abscache.containsKey(id))
            return abscache.get(id);

        KBObject cls = this.kb.getClassOfInstance(this.kb.getIndividual(id));
        if (cls == null) {
            this.abscache.put(id, null);
            return null;
        }
        ArrayList<KBObject> insts = this.kb.getInstancesOfClass(cls, false);
        ArrayList<String> ids = new ArrayList<String>();
        for (KBObject inst : insts)
            ids.add(inst.getID());
        this.abscache.put(id, ids);
        return ids;
    }

    protected ArrayList<KBTriple> getTriplesForObject(String id, KBObject obj, String includeNS, String excludeNS,
            boolean direct) {
        ArrayList<KBTriple> triples = new ArrayList<KBTriple>();
        triples.add(ontologyFactory.getTriple(ontologyFactory.getObject(id),
                ontologyFactory.getObject(KBUtils.RDF + "type"), this.kb.getClassOfInstance(obj)));
        triples.addAll(getObjectClassTriples(id, obj, includeNS, excludeNS, direct));
        return triples;
    }

    protected ArrayList<KBTriple> getObjectClassTriples(String id, KBObject obj, String includeNS, String excludeNS,
            boolean direct) {
        // Add component to the temporary KB (add all its classes explicitly)
        ArrayList<KBTriple> triples = new ArrayList<KBTriple>();

        if (kbcache.containsKey(obj.getID())) {
            for (KBTriple triple : kbcache.get(obj.getID())) {
                triples.add(ontologyFactory.getTriple(ontologyFactory.getObject(id), triple.getPredicate(),
                        triple.getObject()));
            }
            return triples;
        }

        KBObject tobj = ontologyFactory.getObject(id);
        ArrayList<KBObject> objclses = this.getAllCachedClassesOfInstance(obj.getID(), direct);
        for (KBObject objcls : objclses) {
            if ((includeNS != null && objcls.getNamespace().equals(includeNS))
                    || (excludeNS != null && !objcls.getNamespace().equals(excludeNS))) {
                triples.add(
                        ontologyFactory.getTriple(tobj, ontologyFactory.getObject(KBUtils.RDF + "type"), objcls));
            }
        }
        kbcache.put(id, triples);
        return triples;
    }

    private ArrayList<KBObject> getAllClassesOfInstance(KBAPI tkb, String id) {
        ArrayList<KBObject> clses = new ArrayList<KBObject>();
        for (KBTriple t : tkb.genericTripleQuery(tkb.getResource(id), tkb.getProperty(KBUtils.RDF + "type"),
                null)) {
            clses.add(this.conceptMap.get(t.getObject().getID()));
        }
        return clses;
    }

    private ArrayList<KBObject> getAllCachedClassesOfInstance(String id, boolean direct) {
        String key = id + ":" + direct;
        if (classcache.containsKey(key))
            return classcache.get(key);
        ArrayList<KBObject> clses = kb.getAllClassesOfInstance(kb.getResource(id), direct);
        classcache.put(key, clses);
        return clses;
    }

    protected boolean checkTypeCompatibility(KBAPI tkb, String varid, String argid) {
        ArrayList<KBObject> varclses = this.getAllClassesOfInstance(tkb, varid);
        ArrayList<KBObject> argclses = this.getAllCachedClassesOfInstance(argid, true);
        for (KBObject argcls : argclses) {
            if (argcls.getNamespace().equals(this.dcdomns)) {
                for (KBObject varcls : varclses) {
                    if (varcls.getNamespace().equals(this.dcdomns)) {
                        if (!kb.hasSubClass(argcls, varcls) && !kb.hasSubClass(varcls, argcls))
                            return false;
                    }
                }
            }
        }
        return true;
    }

    protected boolean checkTypeCompatibility(ArrayList<String> varclassids, String argid) {
        ArrayList<KBObject> argclses = this.getAllCachedClassesOfInstance(argid, true);
        for (KBObject argcls : argclses) {
            if (argcls.getNamespace().equals(this.dcdomns)) {
                for (String varclassid : varclassids) {
                    KBObject varcl = this.conceptMap.get(varclassid);
                    if (varcl.getNamespace().equals(this.dcdomns)) {
                        if (!kb.hasSubClass(argcls, varcl) && !kb.hasSubClass(varcl, argcls))
                            return false;
                    }
                }
            }
        }
        return true;
    }

    /**
     * <b>Query 2.1</b><br/>
     * Get a list of Specialized Components with their IO Data Requirements
     * given a component (maybe abstract) and it's IO Data Requirements
     * 
     * @param details
     *            A ComponentDetails Object which contains:
     *            <ul>
     *            <li>component,
     *            <li>maps of component input arguments to template variables,
     *            <li>maps of component output arguments to template variables,
     *            <li>template variable descriptions (dods) - list of triples
     *            </ul>
     * @return list of ComponentDetails Objects for each specialized component
     */

    public ArrayList<ComponentPacket> specializeAndFindDataDetails(ComponentPacket details) {
        return findDataDetails(details, true, true);
    }

    /**
     * <b>Query 2.1b</b><br/>
     * Get inferred IO Data Requirements given a component (maybe abstract) and
     * it's given IO Data Requirements
     * 
     * @param details
     *            A ComponentDetails Object which contains:
     *            <ul>
     *            <li>component,
     *            <li>maps of component input arguments to template variables,
     *            <li>maps of component output arguments to template variables,
     *            <li>template variable descriptions (dods) - list of triples
     *            </ul>
     * @return ComponentDetails Object
     */
    public ComponentPacket findDataDetails(ComponentPacket details) {
        ArrayList<ComponentPacket> list = findDataDetails(details, false, true);
        if (list.size() > 0)
            return list.get(0);

        return null;
    }

    /**
     * Helper function which does the actual data requirement checking
     * 
     * @param details
     * @param specialize
     * @param useRules
     * @return
     */
    public ArrayList<ComponentPacket> findDataDetails(ComponentPacket details, boolean specialize,
            boolean useRules) {
        ArrayList<ComponentPacket> list = new ArrayList<ComponentPacket>();

        HashMap<String, KBObject> omap = this.objPropMap;
        HashMap<String, KBObject> dmap = this.dataPropMap;

        // Extract info from details object
        ComponentVariable c = details.getComponent();
        HashMap<String, Variable> roleMaps = details.getStringRoleMaps();
        HashMap<String, Role> varMaps = details.getStringVariableMap();
        ArrayList<KBTriple> redbox = details.getRequirements();

        // Get All component bindings
        String incompid = c.getID();
        Binding cb = c.getBinding();
        ArrayList<String> cbindings = new ArrayList<String>();
        if (!cb.isSet())
            cbindings.add(cb.getID());
        else {
            for (WingsSet s : cb) {
                cbindings.add(((Binding) s).getID());
            }
        }

        // Get List of all concrete components
        ArrayList<Component> ccomps = new ArrayList<Component>();
        for (String cbid : cbindings) {
            Component comp = this.getCachedComponent(cbid);
            if (comp == null) {
                logger.debug(cbid + " is not a valid component");
                details.addExplanations(cbid + " is not a valid component");
                details.setInvalidFlag(true);
                list.add(details);
                return list;
            }
            boolean isConcrete = (comp.getType() == Component.CONCRETE);
            if (!specialize) {
                // If no specialization required, add component as is
                ccomps.add(comp);
            } else if (isConcrete) {
                // If specialization required, but component is already concrete,
                // add as is
                ccomps.add(comp);
            } else {
                /* If the component is abstract, then get all it's concrete
                 * components. Example of how components are structured in ontology:
                 * absClass 
                 * - [abs (isConcrete: false)] 
                 * - conc1Class 
                 *    - [conc1 (isConcrete: true)] 
                 * - conc2Class 
                 *    - [conc2 (isConcrete: true)] 
                 * Note: Only 1 Component Instance per Class
                 */
                ArrayList<String> concreteids = this.getConcreteComponentsForAbstract(comp.getID());
                for (String concreteid : concreteids) {
                    Component instcomp = this.getCachedComponent(concreteid);
                    if (instcomp != null && instcomp.getType() == Component.CONCRETE) {
                        ccomps.add(instcomp);
                    }
                }
            }
        }

        logger.debug("Available components to check validity: " + ccomps);

        // For All concrete components :
        // - Get mapping of specialized arguments to variables
        // - Transfer "relevant" output variable properties to input variables
        // - Pass back the specialized component + specialized mappings +
        // modified red-box
        // - Handle *NEW* Arguments as well -> Create *NEW* DataVariables

        // Get Metrics property hierarchy triples for adding into the temporary
        // kb

        for (Component ccomp : ccomps) {
            HashMap<Role, Variable> sRoleMap = new HashMap<Role, Variable>();
            ArrayList<String> varids = new ArrayList<String>();

            // Create a new temporary kb
            KBAPI tkb = this.ontologyFactory.getKB(OntSpec.PLAIN);

            // Add the redbox (i.e. datavariable constraints) to the temporary
            // kb, along with domain knowledge about the data catalog
            tkb.addTriples(redbox);
            tkb.addTriples(domainKnowledge);

            KBObject ccompobj = this.kb.getIndividual(ccomp.getID());
            // Create a copy of the specialized component in the temporary kb
            KBObject tcomp = this.copyObjectIntoKB(incompid, ccompobj, tkb, this.pcdomns, null, false);

            boolean typesOk = true;

            // For all argument roles of the specialized component :
            ArrayList<ComponentRole> allArgs = new ArrayList<ComponentRole>(ccomp.getInputs());
            allArgs.addAll(ccomp.getOutputs());

            HashSet<String> explanations = new HashSet<String>();
            ComponentPacket cmr;
            ComponentVariable concreteComponent = new ComponentVariable(incompid);
            concreteComponent.setBinding(new Binding(ccomp.getID()));
            if (specialize)
                concreteComponent.setConcrete(true);
            else
                concreteComponent.setConcrete(c.isConcrete());

            ArrayList<String> inputRoles = new ArrayList<String>();

            for (ComponentRole arg : allArgs) {
                // Get the argument ID for the specialized argument
                String argid = arg.getRoleName();

                Variable var = roleMaps.get(argid);
                String varid = null;
                String roleid = null;
                if (var == null) {
                    // Create a new Variable for role if none exists currently
                    // varid = arg.getID()+"_"+ccomp.getName()+"_Variable";
                    varid = ccomp.getNamespace() + argid;
                    roleid = ccomp.getID() + "_" + argid + "_role";

                    short type = 0;
                    if (arg.isParam())
                        type = VariableType.PARAM;
                    else
                        type = VariableType.DATA;
                    var = new Variable(varid, type);
                } else {
                    varid = var.getID();
                    roleid = varMaps.get(var.getID()).getID();
                    // Make sure that the variable has a type that is either
                    // subsumed by the argument type, or that the argument type
                    // is subsumed by the variable type
                    if (!checkTypeCompatibility(tkb, varid, arg.getID())) {
                        logger.debug(arg.getID() + " is not type compatible with variable: " + varid);
                        explanations.add("INFO " + ccomp + " is not selectable because " + arg.getID()
                                + " is not type compatible with variable: " + varid);
                        typesOk = false;
                        break;
                    }
                }

                // Copy over the argument's classes to the variable
                KBObject argobj = this.kb.getIndividual(arg.getID());
                KBObject varobj = this.copyObjectClassesIntoKB(varid, argobj, tkb, this.dcdomns, null, false);

                // create hasArgumentID property for the variable
                tkb.addTriple(varobj, dmap.get("hasArgumentID"), tkb.createLiteral(argid));

                Role r = new Role(roleid);
                r.setRoleId(argid);
                r.setDimensionality(arg.getDimensionality());

                // Set variable data binding
                if (var.isDataVariable() && var.getBinding() != null && var.getBinding().getName() != null) {
                    tkb.addTriple(varobj, dmap.get("hasBindingID"), tkb.createLiteral(var.getBinding().getName()));
                } else {
                    tkb.addTriple(varobj, dmap.get("hasBindingID"), tkb.createLiteral(""));
                }

                // Set variable parameter binding (default if none set)
                if (var.isParameterVariable()) {
                    KBObject arg_value = null;
                    ValueBinding parambinding = (ValueBinding) var.getBinding();
                    if (parambinding != null && parambinding.getValue() != null) {
                        arg_value = tkb.createXSDLiteral(parambinding.getValueAsString(),
                                parambinding.getDatatype());
                    } else if (arg.getParamDefaultalue() != null) {
                        arg_value = tkb.createLiteral(arg.getParamDefaultalue());
                    }
                    if (arg_value != null) {
                        tkb.setPropertyValue(varobj, dmap.get("hasValue"), arg_value);
                    }
                }

                // assign this variable as an input or output to the component
                if (ccomp.getInputs().contains(arg)) {
                    inputRoles.add(r.getRoleId());
                    tkb.addTriple(tcomp, omap.get("hasInput"), varobj);
                } else {
                    tkb.addTriple(tcomp, omap.get("hasOutput"), varobj);
                }
                sRoleMap.put(r, var);
                varids.add(var.getID());
            }

            // Empty triple list returned if errors encountered below
            ArrayList<KBTriple> empty = new ArrayList<KBTriple>();

            // Return if there was some problem with types
            if (!typesOk) {
                logger.debug(ccomp + " is not selectable ");
                explanations.add("INFO " + ccomp + " is not selectable ");
                cmr = new ComponentPacket(concreteComponent, sRoleMap, empty);
                cmr.setInputRoles(inputRoles);
                cmr.addExplanations(explanations);
                cmr.setInvalidFlag(true);
                list.add(cmr);
                continue;
            }

            // ** Run Rules **
            if (useRules && ccomp.hasRules()) {
                // Redirect output to a byte stream
                ByteArrayOutputStream bost = new ByteArrayOutputStream();
                PrintStream oldout = System.out;
                System.setOut(new PrintStream(bost, true));

                // Run propagation rules on the temporary kb
                tkb.setRulePrefixes(this.rulePrefixes);
                tkb.applyRules(this.getCachedComponentRules(ccomp));
                //tkb.applyRulesFromString(allrules);

                // Get printouts from Rules and store as Explanations
                if (!bost.toString().equals("")) {
                    for (String exp : bost.toString().split("\\n")) {
                        explanations.add(exp);
                    }
                }
                // Set output back to original System.out
                System.setOut(oldout);
            }

            // Checking for invalidity
            KBObject invalidProp = tkb.getProperty(this.pcns + "isInvalid");
            KBObject isInvalid = tkb.getPropertyValue(tcomp, invalidProp);
            if (isInvalid != null && (Boolean) isInvalid.getValue()) {
                logger.debug(ccomp + " is not selectable ");
                explanations.add("INFO " + ccomp + " is not selectable ");
                cmr = new ComponentPacket(concreteComponent, sRoleMap, empty);
                cmr.setInputRoles(inputRoles);
                cmr.addExplanations(explanations);
                cmr.setInvalidFlag(true);
                list.add(cmr);
                continue;
            }

            // Set parameter values (if any)
            for (Variable var : roleMaps.values()) {
                if (var.isParameterVariable() && var.getBinding() == null) {
                    KBObject varobj = tkb.getResource(var.getID());
                    KBObject val = tkb.getPropertyValue(varobj, dmap.get("hasValue"));
                    if (val != null && val.getValue() != null) {
                        tkb.addTriple(varobj, tkb.getResource(this.wflowns + "hasParameterValue"), val);
                        var.setBinding(new ValueBinding(val.getValue(), val.getDataType()));
                    }
                }
            }

            // Create a constraint engine and get Relevant Constraints here
            ConstraintEngine cons = new ConstraintEngineKB(tkb, "");
            cons.addWhitelistedNamespace(this.dcdomns);
            cons.addWhitelistedNamespace(this.dcns);
            cons.addWhitelistedNamespace(this.wflowns);
            ArrayList<String> blacklistedIds = new ArrayList<String>();
            blacklistedIds.add(dmap.get("hasArgumentID").getID());
            blacklistedIds.add(dmap.get("hasBindingID").getID());
            blacklistedIds.add(this.dcns + "hasMetrics");
            blacklistedIds.add(this.dcns + "hasDataMetrics");
            blacklistedIds.add(this.pcns + "hasValue");

            for (String id : blacklistedIds)
                cons.addBlacklistedId(id);
            ArrayList<KBTriple> constraints = cons.getConstraints(varids);
            for (String id : blacklistedIds)
                cons.removeBlacklistedId(id);

            cmr = new ComponentPacket(concreteComponent, sRoleMap, constraints);
            cmr.setInputRoles(inputRoles);
            cmr.addExplanations(explanations);
            list.add(cmr);
        }
        return list;
    }

    private Component getCachedComponent(String compid) {
        // Get Component
        Component comp = null;
        if (ccache.containsKey(compid)) {
            comp = ccache.get(compid);
        } else {
            comp = this.getComponent(compid, true);
            ccache.put(compid, comp);
        }
        return comp;
    }

    private KBRuleList getCachedComponentRules(Component comp) {
        if (rulescache.containsKey(comp.getID()))
            return rulescache.get(comp.getID());
        String rulestr = "";
        for (String str : comp.getRules())
            rulestr += str + "\n";
        for (String str : comp.getInheritedRules())
            rulestr += str + "\n";
        KBRuleList rules = this.ontologyFactory.parseRules(rulestr);
        rulescache.put(comp.getID(), rules);
        return rules;
    }

    /**
     * <b>Query 4.2</b><br/>
     * This function is supposed to <b>SET</b> the DataSet Metrics, or Parameter
     * Values for the Variables that are passed in via the input/output maps as
     * part of details.<br/>
     * Variables will already be bound to dataObjects, so the function will have
     * to do something like the following :
     * 
     * <pre>
     * If Variable.isParameterVariable() Variable.setParameterValue(value)
     * If Variable.isDataVariable() Variable.getDataObjectBinding().setDataMetrics(xml)
     * </pre>
     * 
     * @param details
     *            A ComponentDetails Object which contains:
     *            <ul>
     *            <li>component,
     *            <li>maps of component input arguments to template variables,
     *            <li>maps of component output arguments to template variables,
     *            <li>template variable descriptions (dods) - list of triples
     *            </ul>
     * @return List of extra template variable descriptions (will mostly be
     *         empty in Q4.2 though)
     */
    public ArrayList<ComponentPacket> findOutputDataPredictedDescriptions(ComponentPacket details) {
        ArrayList<ComponentPacket> list = new ArrayList<ComponentPacket>();

        HashMap<String, KBObject> omap = this.objPropMap;
        HashMap<String, KBObject> dmap = this.dataPropMap;

        // If the component has no rules, then simplify !!

        // Extract info from details object
        ComponentVariable c = details.getComponent();
        HashMap<String, Variable> sRoleMap = details.getStringRoleMaps();
        HashMap<String, Boolean> noParamBindings = new HashMap<String, Boolean>();

        ArrayList<KBTriple> redbox = details.getRequirements();

        Component comp = this.getCachedComponent(c.getBinding().getID());
        if (comp == null) {
            logger.debug(c.getBinding().getID() + " is not a valid component");
            details.addExplanations(c.getBinding().getID() + " is not a valid component");
            details.setInvalidFlag(true);
            list.add(details);
            return list;
        }

        c.setRequirements(comp.getComponentRequirement());

        boolean typesOk = true;

        // Set default parameter values (if not already set)
        // - Also recheck type compatibility
        ArrayList<String> inputRoles = new ArrayList<String>();
        for (ComponentRole role : comp.getInputs()) {
            inputRoles.add(role.getRoleName());
            Variable v = sRoleMap.get(role.getRoleName());
            if (role.isParam()) {
                if (v.getBinding() == null) {
                    v.setBinding(new ValueBinding(role.getParamDefaultalue()));
                    noParamBindings.put(v.getID(), true);
                } else if (v.getBinding().getValue() == null) {
                    v.getBinding().setValue(role.getParamDefaultalue());
                    noParamBindings.put(v.getID(), true);
                }
            } else {
                ArrayList<String> varclassids = new ArrayList<String>();
                ArrayList<Metric> vartypes = v.getBinding().getMetrics().getMetrics().get(KBUtils.RDF + "type");
                if (vartypes != null) {
                    for (Metric m : vartypes) {
                        varclassids.add(m.getValueAsString());
                    }
                    // Check type compatibility of roles
                    if (!checkTypeCompatibility(varclassids, role.getID())) {
                        details.addExplanations("INFO " + comp + " is not selectable because " + role.getID()
                                + " is not type compatible with variable binding: " + v.getBinding());
                        typesOk = false;
                        break;
                    }
                }
            }
        }
        details.setInputRoles(inputRoles);

        if (!typesOk) {
            details.setInvalidFlag(true);
            list.add(details);
            return list;
        }

        if (!comp.hasRules()) {
            // No rules. Just set default parameter values (if not already set)
            list.add(details);
            return list;
        }

        // Create a new temporary KB store to run rules on
        KBAPI tkb = this.ontologyFactory.getKB(OntSpec.PLAIN);

        KBObject compobj = this.kb.getIndividual(comp.getID());

        // Add component to the temporary KB store (add all its classes
        // explicitly)
        KBObject tcomp = this.copyObjectIntoKB(comp.getID(), compobj, tkb, this.pcdomns, null, false);

        // Keep a map of variable object to variable name
        HashMap<Variable, String> variableNameMap = new HashMap<Variable, String>();

        for (String rolestr : sRoleMap.keySet()) {
            Variable var = sRoleMap.get(rolestr);
            // Map template variable to a temporary variable for running rules
            // - Reason is that the same variable may be used in multiple roles
            // and we want to distinguish them
            String variableName = var.getID() + "_" + rolestr;
            variableNameMap.put(var, variableName);
        }
        // Add the information from redbox to the temporary KB store
        // Cache varid to varobj
        HashMap<String, KBObject> varIDObjMap = new HashMap<String, KBObject>();
        for (Variable var : sRoleMap.values()) {
            KBObject varobj = tkb.getResource(variableNameMap.get(var));
            varIDObjMap.put(var.getID(), varobj);
        }
        // Add information from redbox
        for (KBTriple t : redbox) {
            KBObject subj = varIDObjMap.get(t.getSubject().getID());
            KBObject obj = varIDObjMap.get(t.getObject().getID());
            if (subj == null)
                subj = t.getSubject();
            if (obj == null)
                obj = t.getObject();
            tkb.addTriple(subj, t.getPredicate(), obj);
        }

        // Get a mapping of ArgID's to arg for the Component
        // Also note which roles are inputs
        HashMap<String, ComponentRole> argMaps = new HashMap<String, ComponentRole>();
        HashMap<String, Boolean> sInputRoles = new HashMap<String, Boolean>();
        for (ComponentRole role : comp.getInputs()) {
            argMaps.put(role.getRoleName(), role);
            sInputRoles.put(role.getRoleName(), true);
        }
        for (ComponentRole role : comp.getOutputs()) {
            argMaps.put(role.getRoleName(), role);
        }

        // Convert metrics to Property assertions in the Temporary KB
        for (String rolestr : sRoleMap.keySet()) {
            Variable var = sRoleMap.get(rolestr);
            ComponentRole arg = argMaps.get(rolestr);
            if (arg == null) {
                details.addExplanations("ERROR Component catalog cannot recognize role id " + rolestr);
                continue;
            }
            String variableName = variableNameMap.get(var);

            // Get a KBObject for the temporary variable
            KBObject varobj = tkb.getResource(variableName);

            if (var.isDataVariable()) {
                // If the variable is a data variable (& is bound)
                if (var.getBinding() != null) {
                    // Convert Metrics to PC properties in order to run rules
                    Metrics metrics = var.getBinding().getMetrics();
                    HashMap<String, ArrayList<Metric>> propValMap = metrics.getMetrics();
                    for (String propid : propValMap.keySet()) {
                        for (Metric tmp : propValMap.get(propid)) {
                            Object val = tmp.getValue();
                            String valstring = tmp.getValueAsString();
                            int type = tmp.getType();
                            String dtype = tmp.getDatatype();
                            KBObject metricProp = this.kb.getProperty(propid);
                            if (metricProp != null) {
                                //System.out.println(var.getName()+": " + propid + " = " +valstring);
                                if (type == Metric.URI) {
                                    // Object Property
                                    KBObject valobj = this.kb.getResource(valstring);
                                    if (valobj == null) {
                                        // TODO: Log and explain (make a utility
                                        // function)
                                        details.addExplanations(
                                                "ERROR Cannot Recognize Metrics Value " + valstring);
                                        continue;
                                    }
                                    // Copy over the object class into kb as well
                                    // (except where the object itself is a class)
                                    if (!metricProp.getID().equals(KBUtils.RDF + "type")) {
                                        valobj = this.copyObjectIntoKB(valobj.getID(), valobj, tkb, null, null,
                                                true);
                                        // Remove any existing values first
                                        for (KBTriple t : tkb.genericTripleQuery(varobj, metricProp, null))
                                            tkb.removeTriple(t);
                                    }
                                    // Add a Triple for the metric property value
                                    tkb.addTriple(varobj, metricProp, valobj);
                                } else if (type == Metric.LITERAL && val != null) {
                                    // Literal value
                                    KBObject tobj = dtype != null ? tkb.createXSDLiteral(valstring, dtype)
                                            : tkb.createLiteral(val);
                                    if (tobj != null) {
                                        // Remove any existing values first
                                        for (KBTriple t : tkb.genericTripleQuery(varobj, metricProp, null))
                                            tkb.removeTriple(t);
                                        // Add a Triple for the metric propertyvalue
                                        tkb.addTriple(varobj, metricProp, tobj);
                                    } else {
                                        details.addExplanations("ERROR Cannot Convert Metrics Value " + valstring);
                                        continue;
                                    }
                                }
                            } else {
                                // TODO: Log and explain (make a utility function)
                                details.addExplanations(
                                        "ERROR No Such Metrics Property Known to Component Catalog : " + propid);
                                continue;
                            }
                        }
                    }

                    // Create other standard PC properties on variable
                    // - hasDimensionSizes
                    // - hasBindingID
                    if (var.getBinding().isSet()) {
                        String dimensionSizes = "";
                        ArrayList<Binding> vbs = new ArrayList<Binding>();
                        vbs.add(var.getBinding());
                        while (!vbs.isEmpty()) {
                            Binding vb = vbs.remove(0);
                            if (vb.isSet()) {
                                for (WingsSet vs : vb) {
                                    vbs.add((Binding) vs);
                                }
                                if (!dimensionSizes.equals(""))
                                    dimensionSizes += ",";
                                dimensionSizes += vb.getSize();
                            }
                        }
                        tkb.setPropertyValue(varobj, dmap.get("hasDimensionSizes"),
                                tkb.createLiteral(dimensionSizes));
                    }

                    if (var.getBinding().getID() != null)
                        tkb.addTriple(varobj, dmap.get("hasBindingID"),
                                tkb.createLiteral(var.getBinding().getName()));
                    else
                        tkb.addTriple(varobj, dmap.get("hasBindingID"), tkb.createLiteral(""));

                    // end if (var.getDataBinding() != null)
                }
                // end if (var.isDataVariable())
            } else if (var.isParameterVariable()) {
                // If the Variable/Argument is a Parameter
                ValueBinding parambinding = (ValueBinding) var.getBinding();
                if (parambinding != null && parambinding.getValue() != null) {
                    // If the template has any value specified, use that instead
                    //arg_value = tkb.createLiteral(var.getBinding().getValue());
                    KBObject arg_value = tkb.createXSDLiteral(parambinding.getValueAsString(),
                            parambinding.getDatatype());
                    tkb.setPropertyValue(varobj, dmap.get("hasValue"), arg_value);
                }
                if (dmap.containsKey("hasBindingID"))
                    // Set the hasBindingID term
                    tkb.addTriple(varobj, dmap.get("hasBindingID"), tkb.createLiteral("Param" + arg.getName()));
            }

            // Copy argument classes from Catalog as classes for the temporary
            // variable in the temporary kb store
            KBObject argobj = kb.getIndividual(arg.getID());
            this.copyObjectClassesIntoKB(varobj.getID(), argobj, tkb, null, null, true);

            // Set the temporary variable's argumentID so rules can get/set
            // triples based on the argument
            tkb.addTriple(varobj, dmap.get("hasArgumentID"), tkb.createLiteral(rolestr));

            // Set hasInput or hasOutput for the temporary Variable
            if (sInputRoles.containsKey(rolestr)) {
                tkb.addTriple(tcomp, omap.get("hasInput"), varobj);
            } else {
                tkb.addTriple(tcomp, omap.get("hasOutput"), varobj);
            }
            // end of for (String rolestr : sRoleMap.keySet())
        }

        // Add all metrics and datametrics properties to temporary store
        tkb.addTriples(metricTriples);

        // Set current output variable metrics to do a diff with later
        for (String rolestr : sRoleMap.keySet()) {
            Variable var = sRoleMap.get(rolestr);
            if (var.isDataVariable() && !sInputRoles.containsKey(rolestr)) {
                Metrics metrics = new Metrics();
                KBObject varobj = tkb.getResource(variableNameMap.get(var));
                // Create Metrics from PC Properties
                for (KBObject metricProp : metricProps) {
                    KBObject val = tkb.getPropertyValue(varobj, metricProp);
                    if (val == null)
                        continue;
                    // Add value
                    if (val.isLiteral())
                        metrics.addMetric(metricProp.getID(),
                                new Metric(Metric.LITERAL, val.getValue(), val.getDataType()));
                    else
                        metrics.addMetric(metricProp.getID(), new Metric(Metric.URI, val.getID()));
                }
                var.getBinding().setMetrics(metrics);
            }
        }

        KBRuleList rules = this.getCachedComponentRules(comp);
        if (rules.getRules().size() > 0) {
            // Redirect Standard output to a byte stream
            ByteArrayOutputStream bost = new ByteArrayOutputStream();
            PrintStream oldout = System.out;
            System.setOut(new PrintStream(bost, true));

            // *** Run propagation rules on the temporary ontmodel ***
            tkb.setRulePrefixes(this.rulePrefixes);
            tkb.applyRules(rules);
            //tkb.applyRulesFromString(allrules);

            // Add printouts from rules as explanations
            if (!bost.toString().equals("")) {
                for (String exp : bost.toString().split("\\n")) {
                    details.addExplanations(exp);
                }
            }
            // Reset the Standard output
            System.setOut(oldout);
        }

        // Check if the rules marked this component as invalid for
        // the current component details packet
        KBObject invalidProp = this.dataPropMap.get("isInvalid");
        KBObject isInvalid = tkb.getPropertyValue(tcomp, invalidProp);
        if (isInvalid != null && (Boolean) isInvalid.getValue()) {
            details.addExplanations("INFO " + tcomp + " is not valid for its inputs");
            logger.debug(tcomp + " is not valid for its inputs");
            details.setInvalidFlag(true);
            list.add(details);
            return list;
        }

        // Check component dependencies
        // If set, overwrite the component dependencies with these
        ComponentRequirement req = this.getComponentRequirements(tcomp, tkb);
        if (req != null) {
            if (req.getMemoryGB() != 0)
                c.getRequirements().setMemoryGB(req.getMemoryGB());
            if (req.getStorageGB() != 0)
                c.getRequirements().setStorageGB(req.getStorageGB());
        }

        // Set values of variables by looking at values set by rules
        // in temporary kb store
        // - Only set if there isn't already a binding value for the variable
        for (Variable var : sRoleMap.values()) {
            if (var.isParameterVariable() && (noParamBindings.containsKey(var.getID()) || var.getBinding() == null
                    || var.getBinding().getValue() == null)) {
                KBObject varobj = tkb.getResource(variableNameMap.get(var));
                KBObject origvarobj = tkb.getResource(var.getID());
                KBObject val = tkb.getPropertyValue(varobj, dmap.get("hasValue"));
                if (val != null && val.getValue() != null) {
                    tkb.addTriple(origvarobj, tkb.getResource(this.wflowns + "hasParameterValue"), val);
                    var.setBinding(new ValueBinding(val.getValue(), val.getDataType()));
                }
            }
        }

        // To create the output Variable metrics, we go through the metrics
        // property of the output data variables and get their metrics property
        // values
        for (String rolestr : sRoleMap.keySet()) {
            Variable var = sRoleMap.get(rolestr);
            if (var.isDataVariable() && !sInputRoles.containsKey(rolestr)) {
                Metrics curmetrics = var.getBinding().getMetrics();
                Metrics metrics = new Metrics();
                KBObject varobj = tkb.getResource(variableNameMap.get(var));

                // Create Metrics from PC Properties
                for (KBObject metricProp : metricProps) {
                    ArrayList<KBObject> vals = tkb.getPropertyValues(varobj, metricProp);
                    if (vals == null)
                        continue;
                    for (KBObject val : vals) {
                        if (vals.size() > 1) {
                            if (!curmetrics.getMetrics().containsKey(metricProp.getID()))
                                continue;
                            // If multiple values present, ignore value that is equal to current value
                            for (Metric mval : curmetrics.getMetrics().get(metricProp.getID())) {
                                if (!val.isLiteral() && val.getID().equals(mval.getValue()))
                                    continue;
                                else if (val.isLiteral() && val.getValue().equals(mval.getValue()))
                                    continue;
                            }
                        }
                        // Add value
                        if (val.isLiteral())
                            metrics.addMetric(metricProp.getID(),
                                    new Metric(Metric.LITERAL, val.getValue(), val.getDataType()));
                        else
                            metrics.addMetric(metricProp.getID(), new Metric(Metric.URI, val.getID()));
                    }
                }
                ArrayList<KBObject> clses = this.getAllClassesOfInstance(tkb, varobj.getID());
                for (KBObject cls : clses)
                    metrics.addMetric(KBUtils.RDF + "type", new Metric(Metric.URI, cls.getID()));

                // Set metrics for the Binding
                if (var.getBinding() != null)
                    var.getBinding().setMetrics(metrics);

                // -- Dealing with Collections --
                // User other Properties for creating output binding collections
                // and setting the collection item metrics as well
                // PC Properties used:
                // - hasDimensionSizes
                // - hasDimensionIndexProperties
                int dim = 0;
                final int maxdims = 10; // not more than 10 dimensions
                int[] dimSizes = new int[maxdims];
                String[] dimIndexProps = new String[maxdims];
                KBObject dimSizesObj = tkb.getPropertyValue(varobj, dmap.get("hasDimensionSizes"));
                KBObject dimIndexPropsObj = tkb.getPropertyValue(varobj, dmap.get("hasDimensionIndexProperties"));

                // Parse dimension sizes string (can be given as a comma-separated list)
                // Example 2,3
                // - This will create a 2x3 matrix
                if (dimSizesObj != null && dimSizesObj.getValue() != null) {
                    if (dimSizesObj.getValue().getClass().getName().equals("java.lang.Integer")) {
                        dimSizes[0] = (Integer) dimSizesObj.getValue();
                        dim = 1;
                    } else {
                        String dimSizesStr = (String) dimSizesObj.getValue();
                        for (String dimSize : dimSizesStr.split(",")) {
                            try {
                                int size = Integer.parseInt(dimSize);
                                dimSizes[dim] = size;
                                dim++;
                            } catch (Exception e) {
                            }
                        }
                    }
                }

                // Parse dimension index string (can be given as a comma
                // separated list)
                // Example hasXIndex, hasYIndex
                // - This will set each output item's
                // - first dimension index using property hasXIndex
                // - second dimension index using property hasYIndex
                // Example output:
                // - output
                // - output0 (hasXIndex 0)
                // - output00 (hasXIndex 0, hasYIndex 0)
                // - output01 (hasXIndex 0, hasYIndex 1)
                // - output0 (hasXIndex 1)
                // - output10 (hasXIndex 1, hasYIndex 0)
                // - output11 (hasXIndex 1, hasYIndex 1)

                if (dimIndexPropsObj != null && dimIndexPropsObj.getValue() != null) {
                    int xdim = 0;
                    String dimIndexPropsStr = (String) dimIndexPropsObj.getValue();
                    for (String dimIndexProp : dimIndexPropsStr.split(",")) {
                        try {
                            dimIndexProps[xdim] = dimIndexProp;
                            xdim++;
                        } catch (Exception e) {
                        }
                    }
                }

                // If the output is a collection
                // dim = 1 is a List
                // dim = 2 is a Matrix
                // dim = 3 is a Cube
                // .. and so on
                if (dim > 0) {
                    int[] dimCounters = new int[dim];
                    dimCounters[0] = 1;
                    for (int k = 1; k < dim; k++) {
                        int perms = 1;
                        for (int l = k - 1; l >= 0; l--)
                            perms *= dimSizes[l];
                        dimCounters[k] = dimCounters[k - 1] + perms;
                    }

                    Binding b = var.getBinding();
                    ArrayList<Binding> vbs = new ArrayList<Binding>();
                    vbs.add(b);
                    int counter = 0;
                    while (!vbs.isEmpty()) {
                        Binding vb = vbs.remove(0);
                        if (vb.getMetrics() == null)
                            continue;
                        int vdim = 0;
                        for (vdim = 0; vdim < dim; vdim++) {
                            if (counter < dimCounters[vdim])
                                break;
                        }
                        if (vdim < dim) {
                            for (int i = 0; i < dimSizes[vdim]; i++) {
                                Binding cvb = new Binding(b.getNamespace() + UuidGen.generateAUuid("" + i));
                                // Copy over metrics from parent variable binding
                                Metrics tmpMetrics = new Metrics(vb.getMetrics());
                                // Add dimension index (if property set)
                                String prop = dimIndexProps[vdim];
                                if (prop != null && !prop.equals("")) {
                                    Metric nm = new Metric(Metric.LITERAL, i, KBUtils.XSD + "integer");
                                    tmpMetrics.addMetric(this.dcdomns + prop, nm);
                                }
                                cvb.setMetrics(tmpMetrics);
                                vb.add(cvb);
                                vbs.add(cvb);
                            }
                        }
                        counter++;
                    }
                }
                // end if(dim > 0)
            }
        }

        // FIXME: Handle multiple configurations
        list.add(details);
        return list;
    }

    public ComponentInvocation getComponentInvocation(ComponentPacket details) {
        HashMap<String, KBObject> omap = this.objPropMap;
        HashMap<String, KBObject> dmap = this.dataPropMap;

        // Extract info from details object
        ComponentVariable c = details.getComponent();
        Map<Variable, Role> varMap = details.getVariableMap();

        // Get Component
        KBObject comp = this.kb.getResource(c.getBinding().getID());
        String exepath = this.getComponentLocation(comp.getID());
        String exedir = null;
        if (exepath != null) {
            File f = new File(exepath);
            if (f.isDirectory()) {
                exedir = exepath;
                File shexef = new File(exepath + File.separator + "run");
                File winexef = new File(exepath + File.separator + "run.bat");
                if (SystemUtils.IS_OS_WINDOWS && winexef.exists())
                    exepath = winexef.getAbsolutePath();
                else
                    exepath = shexef.getAbsolutePath();
            }
        }

        ComponentInvocation invocation = new ComponentInvocation();
        invocation.setComponentId(comp.getID());
        invocation.setComponentLocation(exepath);
        invocation.setComponentDirectory(exedir);

        ArrayList<KBObject> inputs = this.kb.getPropertyValues(comp, omap.get("hasInput"));
        ArrayList<KBObject> outputs = this.kb.getPropertyValues(comp, omap.get("hasOutput"));
        ArrayList<KBObject> args = new ArrayList<KBObject>(inputs);
        args.addAll(outputs);

        for (KBObject arg : args) {
            KBObject argid = this.kb.getDatatypePropertyValue(arg, dmap.get("hasArgumentID"));
            String role = (String) argid.getValue();
            for (Variable var : varMap.keySet()) {
                Role r = varMap.get(var);
                if (r.getRoleId().equals(role))
                    setInvocationArguments(invocation, arg, var, inputs.contains(arg));
            }
        }

        return invocation;
    }

    private void setInvocationArguments(ComponentInvocation invocation, KBObject arg, Variable var,
            boolean isInput) {

        HashMap<String, KBObject> dmap = this.dataPropMap;
        KBObject defaultValue = this.kb.getPropertyValue(arg, dmap.get("hasValue"));
        KBObject prefixobj = this.kb.getPropertyValue(arg, dmap.get("hasArgumentName"));
        String prefix = (String) prefixobj.getValue();

        ArrayList<Binding> bindings = new ArrayList<Binding>();
        bindings.add(var.getBinding());
        while (!bindings.isEmpty()) {
            Binding b = bindings.remove(0);
            // System.out.println(var.getName() + ":" + b);
            if (b.isSet()) {
                for (WingsSet s : b) {
                    bindings.add((Binding) s);
                }
            } else {
                if (var.isDataVariable()) {
                    invocation.addArgument(prefix, b, var.getID(), isInput);
                } else if (var.isParameterVariable()) {
                    if (var.getBinding() != null) {
                        invocation.addArgument(prefix, ((ValueBinding) var.getBinding()).getValueAsString(),
                                var.getID(), isInput);
                    } else if (defaultValue != null && defaultValue.getValue() != null) {
                        invocation.addArgument(prefix, defaultValue.getValueAsString(), var.getID(), isInput);
                    }
                }
            }
        }
    }
}