org.obolibrary.robot.ExtractCommand Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of robot-command Show documentation
Show all versions of robot-command Show documentation
Command-line interface for ROBOT: Commands for working with OWL ontologies, especially Open Biological and Biomedical Ontologes (OBO).
package org.obolibrary.robot;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import java.io.*;
import java.util.*;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.model.parameters.Imports;
import org.semanticweb.owlapi.util.DefaultPrefixManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.manchester.cs.owlapi.modularity.ModuleType;
/**
* Handles inputs and outputs for the {@link ExtractOperation}.
*
* @author James A. Overton
*/
public class ExtractCommand implements Command {
/** Logger. */
private static final Logger logger = LoggerFactory.getLogger(ExtractCommand.class);
/** Namespace for error messages. */
private static final String NS = "extract#";
/** Error message when lower or branch terms are not specified with MIREOT. */
private static final String missingMireotTermsError =
NS
+ "MISSING MIREOT TERMS ERROR "
+ "either lower term(s) or branch term(s) must be specified for MIREOT";
/** Error message when only upper terms are specified with MIREOT. */
private static final String missingLowerTermError =
NS
+ "MISSING LOWER TERMS ERROR "
+ "lower term(s) must be specified with upper term(s) for MIREOT";
/** Error message when user provides invalid imports option. */
private static final String invalidImportsError =
NS + "INVALID IMPORTS ERROR --imports must be 'include' or 'exclude'";
/** Error message when user provides invalid extraction method. */
private static final String invalidMethodError =
NS + "INVALID METHOD ERROR method must be: MIREOT, STAR, TOP, BOT";
/** Error message when a MIREOT option is used for SLME. */
private static final String invalidOptionError =
NS
+ "INVALID OPTION ERROR "
+ "only --term or --term-file can be used to specify extract term(s) "
+ "for STAR, TOP, or BOT";
/** Error message when the source map is not TSV or CSV. */
private static final String invalidSourceMapError =
NS + "INVALID SOURCE MAP ERROR --sources input must be .tsv or .csv";
/** Store the command-line options for the command. */
private Options options;
/** Initialze the command. */
public ExtractCommand() {
Options o = CommandLineHelper.getCommonOptions();
o.addOption("i", "input", true, "load ontology from a file");
o.addOption("I", "input-iri", true, "load ontology from an IRI");
o.addOption("o", "output", true, "save ontology to a file");
o.addOption("O", "output-iri", true, "set OntologyIRI for output");
o.addOption("m", "method", true, "extract method to use");
o.addOption("t", "term", true, "term to extract");
o.addOption("T", "term-file", true, "load terms from a file");
o.addOption("u", "upper-term", true, "upper level term to extract");
o.addOption("U", "upper-terms", true, "upper level terms to extract");
o.addOption("l", "lower-term", true, "lower level term to extract");
o.addOption("L", "lower-terms", true, "lower level terms to extract");
o.addOption("b", "branch-from-term", true, "root term of branch to extract");
o.addOption("B", "branch-from-terms", true, "root terms of branches to extract");
o.addOption("c", "copy-ontology-annotations", true, "if true, include ontology annotations");
o.addOption("f", "force", true, "if true, warn on empty input terms instead of fail");
o.addOption("a", "annotate-with-source", true, "if true, annotate terms with rdfs:isDefinedBy");
o.addOption("s", "sources", true, "specify a mapping file of term to source ontology");
o.addOption("n", "individuals", true, "handle individuals (default: include)");
o.addOption("M", "imports", true, "handle imports (default: include)");
o.addOption("N", "intermediates", true, "specify how to handle intermediate entities");
options = o;
}
/**
* Name of the command.
*
* @return name
*/
public String getName() {
return "extract";
}
/**
* Brief description of the command.
*
* @return description
*/
public String getDescription() {
return "extract terms from an ontology";
}
/**
* Command-line usage for the command.
*
* @return usage
*/
public String getUsage() {
return "robot extract --input "
+ "--term-file "
+ "--output "
+ "--output-iri ";
}
/**
* Command-line options for the command.
*
* @return options
*/
public Options getOptions() {
return options;
}
/**
* Handle the command-line and file operations for the ExtractOperation.
*
* @param args strings to use as arguments
*/
public void main(String[] args) {
try {
execute(null, args);
} catch (Exception e) {
CommandLineHelper.handleException(e);
}
}
/**
* Given an input state and command line arguments, extract a new ontology and return an new
* state. The input ontology is not changed.
*
* @param state the state from the previous command, or null
* @param args the command-line arguments
* @return a new state with the extracted ontology
* @throws Exception on any problem
*/
public CommandState execute(CommandState state, String[] args) throws Exception {
OWLOntology outputOntology;
CommandLine line = CommandLineHelper.getCommandLine(getUsage(), getOptions(), args);
if (line == null) {
return null;
}
IOHelper ioHelper = CommandLineHelper.getIOHelper(line);
state = CommandLineHelper.updateInputOntology(ioHelper, state, line);
OWLOntology inputOntology = state.getOntology();
// Override default reasoner options with command-line options
Map extractOptions = ExtractOperation.getDefaultOptions();
for (String option : extractOptions.keySet()) {
if (line.hasOption(option)) {
extractOptions.put(option, line.getOptionValue(option));
}
}
// Get method, make sure it has been specified
String method =
CommandLineHelper.getRequiredValue(line, "method", "method of extraction must be specified")
.trim()
.toLowerCase();
ModuleType moduleType = null;
switch (method) {
case "star":
moduleType = ModuleType.STAR;
break;
case "top":
moduleType = ModuleType.TOP;
break;
case "bot":
moduleType = ModuleType.BOT;
break;
}
if (method.equals("mireot")) {
outputOntology = mireotExtract(ioHelper, inputOntology, line, extractOptions);
} else if (moduleType != null) {
outputOntology = slmeExtract(ioHelper, inputOntology, moduleType, line, extractOptions);
} else {
throw new Exception(invalidMethodError);
}
// Maybe copy ontology annotations
boolean copyOntologyAnnotations =
CommandLineHelper.getBooleanValue(line, "copy-ontology-annotations", false);
if (copyOntologyAnnotations) {
for (OWLAnnotation annotation : inputOntology.getAnnotations()) {
OntologyHelper.addOntologyAnnotation(outputOntology, annotation);
}
}
CommandLineHelper.maybeSaveOutput(line, outputOntology);
state.setOntology(outputOntology);
return state;
}
/**
* Perform a MIREOT extraction on an ontology after validating command line options.
*
* @param ioHelper IOHelper to use
* @param inputOntology OWLOntology to extract from
* @param line CommandLine with options
* @param extractOptions Map of extract options
* @return a new ontology containing extracted subset
* @throws IOException on problem parsing terms
* @throws OWLOntologyCreationException on OWLAPI issue
*/
private static OWLOntology mireotExtract(
IOHelper ioHelper,
OWLOntology inputOntology,
CommandLine line,
Map extractOptions)
throws Exception {
Imports imports = getImportsOption(extractOptions);
List outputOntologies = new ArrayList<>();
// Get terms from input (ensuring that they are in the input ontology)
// It's okay for any of these to return empty (allowEmpty = true)
// Checks for empty sets later
Set upperIRIs =
OntologyHelper.filterExistingTerms(
inputOntology,
CommandLineHelper.getTerms(ioHelper, line, "upper-term", "upper-terms"),
true,
imports);
if (upperIRIs.size() == 0) {
upperIRIs = null;
}
Set lowerIRIs =
OntologyHelper.filterExistingTerms(
inputOntology,
CommandLineHelper.getTerms(ioHelper, line, "lower-term", "lower-terms"),
true,
imports);
if (lowerIRIs.size() == 0) {
lowerIRIs = null;
}
Set branchIRIs =
OntologyHelper.filterExistingTerms(
inputOntology,
CommandLineHelper.getTerms(ioHelper, line, "branch-from-term", "branch-from-terms"),
true,
imports);
if (branchIRIs.size() == 0) {
branchIRIs = null;
}
// Need branch IRIs or lower IRIs to proceed
if (branchIRIs == null && lowerIRIs == null) {
throw new IllegalArgumentException(missingMireotTermsError);
} else {
Map sourceMap =
getSourceMap(ioHelper, CommandLineHelper.getOptionalValue(line, "sources"));
// First check for lower IRIs, upper IRIs can be null or not
if (lowerIRIs != null) {
outputOntologies.add(
MireotOperation.getAncestors(
inputOntology, upperIRIs, lowerIRIs, null, extractOptions, sourceMap));
// If there are no lower IRIs, there shouldn't be any upper IRIs
} else if (upperIRIs != null) {
throw new IllegalArgumentException(missingLowerTermError);
}
// Check for branch IRIs
if (branchIRIs != null) {
outputOntologies.add(
MireotOperation.getDescendants(
inputOntology, branchIRIs, null, extractOptions, sourceMap));
}
}
// Get the output IRI and create the output ontology
IRI outputIRI = CommandLineHelper.getOutputIRI(line);
if (outputIRI == null) {
outputIRI = inputOntology.getOntologyID().getOntologyIRI().orNull();
}
OWLOntology outputOntology = MergeOperation.merge(outputOntologies);
if (outputIRI != null) {
outputOntology.getOWLOntologyManager().setOntologyDocumentIRI(outputOntology, outputIRI);
}
return outputOntology;
}
/**
* Perform a SLME extraction after validating command line options.
*
* @param inputOntology OWLOntology to extract from
* @param moduleType type of extraction
* @param line CommandLine with options
* @param extractOptions Map of extract options
* @return a new ontology containing extracted subset
* @throws IOException on issue parsing terms
* @throws OWLOntologyCreationException on OWLAPI issue
*/
private static OWLOntology slmeExtract(
IOHelper ioHelper,
OWLOntology inputOntology,
ModuleType moduleType,
CommandLine line,
Map extractOptions)
throws Exception {
Imports imports = getImportsOption(extractOptions);
// upper-term, lower-term, and branch-from term should not be used
List mireotTerms =
Arrays.asList(
CommandLineHelper.getOptionalValue(line, "upper-term"),
CommandLineHelper.getOptionalValue(line, "upper-terms"),
CommandLineHelper.getOptionalValue(line, "lower-term"),
CommandLineHelper.getOptionalValue(line, "lower-terms"),
CommandLineHelper.getOptionalValue(line, "branch-from-term"),
CommandLineHelper.getOptionalValue(line, "branch-from-terms"));
for (String mt : mireotTerms) {
if (mt != null) {
throw new IllegalArgumentException(invalidOptionError);
}
}
// Make sure the terms exist in the input ontology
Set terms =
OntologyHelper.filterExistingTerms(
inputOntology,
CommandLineHelper.getTerms(ioHelper, line),
OptionsHelper.optionIsTrue(extractOptions, "force"),
imports);
// Determine what to do with sources
Map sourceMap =
getSourceMap(ioHelper, CommandLineHelper.getOptionalValue(line, "sources"));
// Get the output IRI
IRI outputIRI = CommandLineHelper.getOutputIRI(line);
if (outputIRI == null) {
outputIRI = inputOntology.getOntologyID().getOntologyIRI().orNull();
}
return ExtractOperation.extract(
inputOntology, terms, outputIRI, moduleType, extractOptions, sourceMap);
}
/**
* Given a map of options, return the imports option as Imports.
*
* @param extractOptions map of options
* @return Imports INCLUDED or EXCLUDED
* @throws Exception if option is not 'include' or 'exclude'
*/
private static Imports getImportsOption(Map extractOptions) throws Exception {
String importsOption = OptionsHelper.getOption(extractOptions, "imports", "include");
if (importsOption.equalsIgnoreCase("include")) {
return Imports.INCLUDED;
} else if (importsOption.equalsIgnoreCase("exclude")) {
return Imports.EXCLUDED;
} else {
throw new Exception(invalidImportsError);
}
}
/**
* Given an IOHelper and the path to a term-to-source map, return a map of term IRI to source IRI.
*
* @param ioHelper IOHelper to handle prefixes
* @param sourceMapPath path of the term-to-source map
* @return map of term IRI to source IRI
* @throws Exception on file reading issue
*/
private static Map getSourceMap(IOHelper ioHelper, String sourceMapPath)
throws Exception {
// If no source map path is specified, just return null
if (sourceMapPath == null) {
return null;
}
// Otherwise, use the path to get a file containing the mappings
File sourceMapFile = new File(sourceMapPath);
if (!sourceMapFile.exists()) {
throw new Exception(String.format(missingFileError, sourceMapPath, "--sources"));
}
char separator;
if (sourceMapPath.endsWith(".tsv")) {
separator = '\t';
} else if (sourceMapPath.endsWith(".csv")) {
separator = ',';
} else {
throw new Exception(invalidSourceMapError);
}
DefaultPrefixManager pm = ioHelper.getPrefixManager();
Reader reader = new FileReader(sourceMapFile);
CSVReader csv =
new CSVReaderBuilder(reader)
.withCSVParser(new CSVParserBuilder().withSeparator(separator).build())
.build();
// Skip first line
csv.skip(1);
Map sourceMap = new HashMap<>();
for (String[] line : csv) {
IRI entity = ioHelper.createIRI(line[0]);
// Maybe create a source IRI from a prefix
// Otherwise the full IRI should be provided
IRI source;
String sourceStr = line[1];
String namespace = pm.getPrefix(sourceStr + ":");
if (namespace != null) {
if (namespace.endsWith("_") || namespace.endsWith("#") || namespace.endsWith("/")) {
namespace = namespace.substring(0, namespace.length() - 1);
}
source = IRI.create(namespace.toLowerCase() + ".owl");
} else {
source = IRI.create(sourceStr);
}
sourceMap.put(entity, source);
}
return sourceMap;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy