nl.uu.cs.ape.domain.APEDomainSetup Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of APE Show documentation
Show all versions of APE Show documentation
APE is a command line tool and an API for the automated exploration of possible computational pipelines (workflows) from large collections of computational tools.
package nl.uu.cs.ape.domain;
import java.io.IOException;
import java.util.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import nl.uu.cs.ape.configuration.APECoreConfig;
import nl.uu.cs.ape.configuration.ToolAnnotationTag;
import nl.uu.cs.ape.constraints.ConstraintFactory;
import nl.uu.cs.ape.constraints.ConstraintFormatException;
import nl.uu.cs.ape.constraints.ConstraintTemplate;
import nl.uu.cs.ape.constraints.ConstraintTemplateParameter;
import nl.uu.cs.ape.utils.APEUtils;
import nl.uu.cs.ape.models.AbstractModule;
import nl.uu.cs.ape.models.AllModules;
import nl.uu.cs.ape.models.AllTypes;
import nl.uu.cs.ape.models.AuxiliaryPredicate;
import nl.uu.cs.ape.models.ConstraintTemplateData;
import nl.uu.cs.ape.models.Module;
import nl.uu.cs.ape.models.Type;
import nl.uu.cs.ape.models.logic.constructs.TaxonomyPredicate;
/**
* The {@code APEDomainSetup} class is used to store the domain information and
* initial constraints that have to be encoded.
*
* @author Vedran Kasalica
*/
@Slf4j
public class APEDomainSetup {
/* Helper objects used to keep track of the domain quality. */
private Set emptyTools = new HashSet<>();
private Set wrongToolIO = new HashSet<>();
private Set wrongToolTax = new HashSet<>();
/**
* All modules/operations used in the domain.
*/
private AllModules allModules;
/**
* All data types defined in the domain.
*/
private AllTypes allTypes;
/**
* Prefix used to define OWL class IDs
*/
private String ontologyPrefixIRI;
/**
* Object used to create temporal constraints.
*/
private ConstraintFactory constraintFactory;
/**
* List of data gathered from the constraint file.
*/
private List unformattedConstr;
private List helperPredicates;
private List constraintsSLTLx;
/**
* Maximum number of inputs that a tool can have.
*/
private int maxNoToolInputs = 0;
/**
* Maximum number of outputs that a tool can have.
*/
private int maxNoToolOutputs = 0;
/**
* Holds information whether the domain was annotated under the strict rules of
* the output dependency.
*/
private boolean useStrictToolAnnotations;
private static final String CONSTR_JSON_TAG = "constraints";
private static final String CONSTR_ID_TAG = "constraintid";
private static final String CONSTR_SLTLx = "formula";
private static final String CONSTR_PARAM_JSON_TAG = "parameters";
private static final String TOOLS_JSON_TAG = "functions";
/**
* Instantiates a new Ape domain setup.
*
* @param config the config
*/
public APEDomainSetup(APECoreConfig config) {
this.unformattedConstr = new ArrayList<>();
this.allModules = new AllModules(config);
this.allTypes = new AllTypes(config);
this.constraintFactory = new ConstraintFactory();
this.helperPredicates = new ArrayList<>();
this.constraintsSLTLx = new ArrayList<>();
this.ontologyPrefixIRI = config.getOntologyPrefixIRI();
this.useStrictToolAnnotations = config.getUseStrictToolAnnotations();
}
/**
* Gets all modules.
*
* @return The field {@link #allModules}.
*/
public AllModules getAllModules() {
return allModules;
}
/**
* Add constraint data.
*
* @param constr Add a constraint to the list of constraints, that should be
* encoded during the execution of the synthesis.
*/
public void addConstraintData(ConstraintTemplateData constr) {
this.unformattedConstr.add(constr);
}
/**
* Add the String that corresponds to an SLTLx formula that should be parsed to
* the list of constraints.
*
* @param formulaSLTLx - String that corresponds to an SLTLx formula that should
* be parsed
*/
public void addSLTLxConstraint(String formulaSLTLx) {
this.constraintsSLTLx.add(formulaSLTLx);
}
/**
* Gets unformatted constraints.
*
* @return the field {@link #unformattedConstr}.
*/
public List getUnformattedConstr() {
return unformattedConstr;
}
/**
* Gets all SLTLx constraints specified by the user in SLTLx as text.
*
* @return Set of string representations of the constraints.
*/
public List getSLTLxConstraints() {
return constraintsSLTLx;
}
/**
* Removes all of the unformatted constraints, in order to start a new synthesis
* run.
*/
public void clearConstraints() {
this.unformattedConstr.clear();
this.constraintsSLTLx.clear();
}
/**
* Gets all types.
*
* @return the field {@link #allTypes}.
*/
public AllTypes getAllTypes() {
return allTypes;
}
/**
* Gets constraint factory.
*
* @return the field {@link #constraintFactory}.
*/
public ConstraintFactory getConstraintFactory() {
return constraintFactory;
}
/**
* Adding each constraint format in the set of all cons. formats. method
* should be called only once all the data types and modules have been
* initialized.
*/
public void initializeConstraints() {
constraintFactory.initializeConstraints(allModules, allTypes);
}
/**
* Trim taxonomy boolean.
*
* @return the boolean
*/
public boolean trimTaxonomy() {
boolean succRun = true;
succRun &= allModules.trimTaxonomy();
succRun &= allTypes.trimTaxonomy();
return succRun;
}
/**
* Return the {@link ConstraintTemplate} that corresponds to the given ID, or
* null if the constraint with the given ID does not exist.
*
* @param constraintID ID of the {@code ConstraintTemplate}.
* @return The {@code ConstraintTemplate} that corresponds to the given ID, or
* null if the ID is not mapped to any constraint.
*/
public ConstraintTemplate getConstraintTemplate(String constraintID) {
return constraintFactory.getConstraintTemplate(constraintID);
}
/**
* Method reads the constraints from a JSON object and updates the
* {@link APEDomainSetup} object accordingly.
*
* @param constraintsJSONArray JSON array containing the constraints
* @throws ConstraintFormatException exception in case of bad constraint json
* formatting
*/
public void updateConstraints(JSONArray constraintsJSONArray) throws ConstraintFormatException {
if (constraintsJSONArray == null) {
return;
}
String constraintID = null;
int currNode = 0;
List constraints = APEUtils.getListFromJsonList(constraintsJSONArray, JSONObject.class);
/* Iterate through each constraint in the list */
for (JSONObject jsonConstraint : APEUtils.safe(constraints)) {
currNode++;
/* READ THE CONSTRAINT */
try {
constraintID = jsonConstraint.getString(CONSTR_ID_TAG);
ConstraintTemplate currConstrTemplate = getConstraintFactory()
.getConstraintTemplate(constraintID);
if (currConstrTemplate == null) {
if (constraintID.equals("SLTLx")) {
String formulaSLTLx = jsonConstraint.getString(CONSTR_SLTLx);
if (formulaSLTLx == null) {
throw ConstraintFormatException.wrongNumberOfParameters(
getConstrErrorMsg(currNode, constraintID));
}
this.addSLTLxConstraint(formulaSLTLx);
continue;
} else {
throw ConstraintFormatException.wrongConstraintID(
getConstrErrorMsg(currNode, constraintID));
}
}
List currTemplateParameters = currConstrTemplate.getParameters();
List jsonConstParam = APEUtils.getListFromJson(jsonConstraint, CONSTR_PARAM_JSON_TAG,
JSONObject.class);
if (currTemplateParameters.size() != jsonConstParam.size()) {
throw ConstraintFormatException.wrongNumberOfParameters(
getConstrErrorMsg(currNode, constraintID));
}
int paramNo = 0;
List constraintParameters = new ArrayList<>();
/* for each constraint parameter */
for (JSONObject jsonParam : jsonConstParam) {
ConstraintTemplateParameter taxInstanceFromJson = currTemplateParameters.get(paramNo++);
TaxonomyPredicate currParameter = taxInstanceFromJson.readConstraintParameterFromJson(jsonParam,
this);
constraintParameters.add(currParameter);
}
ConstraintTemplateData currConstr = getConstraintFactory()
.generateConstraintTemplateData(constraintID, constraintParameters);
if (constraintParameters.stream().anyMatch(Objects::isNull)) {
throw ConstraintFormatException.wrongParameter(
getConstrErrorMsg(currNode, constraintID));
} else {
this.addConstraintData(currConstr);
}
} catch (JSONException e) {
throw ConstraintFormatException.badFormat(
getConstrErrorMsg(currNode, constraintID));
}
}
}
private String getConstrErrorMsg(int currNode, String constraintID) {
return String.format("Error at constraint no: %d, constraint ID: %s", currNode, constraintID);
}
/**
* Updates the list of All Modules by annotating the existing ones (or adding
* non-existing) using the I/O DataInstance from the @file. Returns the list of
* Updated Modules.
*
* @param toolAnnotationsFile JSON file containing tool annotations.
* @return The list of all annotated Modules in the process (possibly empty
* list).
* @throws IOException Error in handling a JSON file containing tool
* annotations.
* @throws JSONException Error if the tool annotation JSON file, bad format
*/
public boolean updateToolAnnotationsFromJson(JSONObject toolAnnotationsFile) throws IOException, JSONException {
int currModule = 0;
for (JSONObject jsonModule : APEUtils
.safe(APEUtils.getListFromJson(toolAnnotationsFile, TOOLS_JSON_TAG, JSONObject.class))) {
currModule++;
updateModuleFromJson(jsonModule);
}
if (currModule == 0) {
log.warn("No tools were annotated in the current domain.");
return false;
}
return true;
}
/**
* Creates/updates a module from a tool annotation instance from a JSON file and
* updates the list of modules ({@link AllModules}) in the domain accordingly.
*
* @param jsonModule JSON representation of a module
* @return {@code true} if the domain was updated, false otherwise.
* @throws JSONException Error if the JSON file was not properly formatted.
*/
private boolean updateModuleFromJson(JSONObject jsonModule)
throws JSONException, APEDimensionsException {
String moduleIRI = APEUtils.createClassIRI(jsonModule.getString(ToolAnnotationTag.ID.toString()),
ontologyPrefixIRI);
if (allModules.get(moduleIRI) != null) {
moduleIRI = moduleIRI + "[tool]";
}
String moduleLabel = jsonModule.getString(ToolAnnotationTag.LABEL.toString());
Set taxonomyModules = new HashSet<>(
APEUtils.getListFromJson(jsonModule, ToolAnnotationTag.TAXONOMY_OPERATIONS.toString(), String.class));
taxonomyModules = APEUtils.createIRIsFromLabels(taxonomyModules, ontologyPrefixIRI);
/* Check if the referenced module taxonomy classes exist. */
List toRemove = new ArrayList<>();
for (String taxonomyModule : taxonomyModules) {
String taxonomyModuleIRI = APEUtils.createClassIRI(taxonomyModule, ontologyPrefixIRI);
if (allModules.get(taxonomyModuleIRI) == null) {
log.warn("Tool '" + moduleIRI + "' annotation issue. "
+ "Referenced '" + ToolAnnotationTag.TAXONOMY_OPERATIONS.toString() + "': '" + taxonomyModuleIRI
+ "' cannot be found in the Tool Taxonomy.");
wrongToolTax.add(moduleLabel);
toRemove.add(taxonomyModuleIRI);
}
}
taxonomyModules.removeAll(toRemove);
/*
* If the taxonomy terms were not properly specified the tool taxonomy root is
* used as superclass of the tool.
*/
if (taxonomyModules.isEmpty()) {
log.warn("Tool '" + moduleIRI + "' annotation issue. "
+ "None of the referenced '" + ToolAnnotationTag.TAXONOMY_OPERATIONS.toString()
+ "' can be found in the Tool Taxonomy.");
taxonomyModules.add(allModules.getRootModuleID());
}
String executionCode = null;
try {
executionCode = jsonModule.getJSONObject(ToolAnnotationTag.IMPLEMENTATION.toString())
.getString(ToolAnnotationTag.CODE.toString());
} catch (JSONException e) {
/* Skip the execution code */
}
List jsonModuleInput = APEUtils.getListFromJson(jsonModule, ToolAnnotationTag.INPUTS.toString(),
JSONObject.class);
updateMaxNoToolInputs(jsonModuleInput.size());
List jsonModuleOutput = APEUtils.getListFromJson(jsonModule, ToolAnnotationTag.OUTPUTS.toString(),
JSONObject.class);
updateMaxNoToolOutputs(jsonModuleOutput.size());
List inputs = new ArrayList<>();
List outputs = new ArrayList<>();
try {
/* For each input and output, allocate the corresponding abstract types. */
for (JSONObject jsonInput : jsonModuleInput) {
if (!jsonInput.isEmpty()) {
inputs.add(Type.taxonomyInstanceFromJson(jsonInput, this, false));
}
}
for (JSONObject jsonOutput : jsonModuleOutput) {
if (!jsonOutput.isEmpty()) {
outputs.add(Type.taxonomyInstanceFromJson(jsonOutput, this, true));
}
}
} catch (APEDimensionsException badDimension) {
wrongToolIO.add(moduleLabel);
log.warn("Operation '" + "' was not included." + badDimension.getMessage());
return false;
}
String moduleExecutionImpl = null;
if (executionCode != null && !executionCode.equals("")) {
moduleExecutionImpl = executionCode;
}
if (inputs.isEmpty() && outputs.isEmpty()) {
emptyTools.add(moduleLabel);
log.debug("Operation '" + "' was not included as it has no (valid) inputs and outputs specified.");
return false;
}
/*
* Add the module and make it sub module of the currSuperModule (if it was not
* previously defined)
*/
Module currModule = (Module) allModules
.addPredicate(new Module(moduleLabel, moduleIRI, allModules.getRootModuleID(), moduleExecutionImpl));
/* For each supermodule add the current module as a subset and vice versa. */
for (String superModuleID : taxonomyModules) {
AbstractModule superModule = allModules.get(superModuleID);
if (superModule != null) {
superModule.addSubPredicate(currModule);
currModule.addSuperPredicate(superModule);
}
}
currModule.setModuleInput(inputs);
currModule.setModuleOutput(outputs);
currModule.setAsRelevantTaxonomyTerm(allModules);
return currModule != null;
}
/**
* Updates the list of All Modules to include the CWL annotations.
*
* @param cwlAnnotations A Map of the content of the CWL annotations file.
* @return Whether the update was successful.
*/
public boolean updateCWLAnnotationsFromYaml(Map cwlAnnotations) {
for (Map.Entry entry : cwlAnnotations.entrySet()) {
Object[] ids = allModules.getModules().stream()
.filter(m -> m.getPredicateID().toLowerCase().contains(entry.getKey().toLowerCase())
&& m.getType().equals("module"))
.toArray();
String id;
if (ids.length > 0) {
TaxonomyPredicate predicate = (TaxonomyPredicate) ids[0];
id = predicate.getPredicateID();
} else {
// Could not find module related to annotation entry, skip the entry.
continue;
}
Module currModule = (Module) allModules.get(id);
Map tool = (Map) cwlAnnotations.get(currModule.getPredicateLabel());
ArrayList> cwlInputs = null;
Map implementation = null;
if (tool != null) {
ArrayList> cwlInp = (ArrayList>) tool
.get("inputs");
Map imp = (Map) tool.get("implementation");
cwlInputs = cwlInp;
implementation = imp;
}
currModule.setCwlInputs(cwlInputs);
currModule.setCwlImplementation(implementation);
}
return true;
}
/**
* Gets ontology prefix IRI.
*
* @return the ontology prefix IRI
*/
public String getOntologyPrefixIRI() {
return ontologyPrefixIRI;
}
/**
* Get the maximum number of inputs that a tool can have.
*
* @return the field {@link #maxNoToolInputs}.
*/
public int getMaxNoToolInputs() {
return maxNoToolInputs;
}
/**
* Update the maximum number of inputs that a tool can have, i.e. increase the
* number if the current max number is smaller than the new number of inputs.
*
* @param currNoInputs the number of inputs that a tool has
*/
public void updateMaxNoToolInputs(int currNoInputs) {
if (this.maxNoToolInputs < currNoInputs) {
this.maxNoToolInputs = currNoInputs;
}
}
/**
* Get the maximum number of outputs that a tool can have.
*
* @return the field {@link #maxNoToolOutputs}.
*/
public int getMaxNoToolOutputs() {
return maxNoToolOutputs;
}
/**
* Update the maximum number of outputs that a tool can have, i.e. increase the
* number if the current max number is smaller than the new number of outputs.
*
* @param currNoOutputs the number of outputs that the current tool has
*/
public void updateMaxNoToolOutputs(int currNoOutputs) {
if (this.maxNoToolOutputs < currNoOutputs) {
this.maxNoToolOutputs = currNoOutputs;
}
}
/**
* Add predicate to the list of auxiliary predicates that should be encoded.
*
* @param helperPredicate
*/
public void addHelperPredicate(AuxiliaryPredicate helperPredicate) {
helperPredicates.add(helperPredicate);
}
/**
* Get information whether the domain was annotated under the strict rules of
* the output dependency.
*
* @return {@code true} if the strict rules apply, {@code false} otherwise.
*/
public boolean getUseStrictToolAnnotations() {
return useStrictToolAnnotations;
}
/**
* Get the list of helper predicates used in the domain.
*
* @return List of the auxiliary helper predicates.
*/
public List getHelperPredicates() {
return helperPredicates;
}
}