Skip to content

Commit

Permalink
Add a new "mint" command.
Browse files Browse the repository at this point in the history
Add a new command, initially suggested by Jim Balhoff, to rename
entities that were given temporary identifiers (in a dedicated
namespace) with newly minted permanent identifiers.

This command is not tied to KGCL at all and therefore does not really
belong here, but since (1) it reuses some of the logical already used in
the "apply" command, and (2) it is needed to finalize the work started
by the "apply" command if the --auto-id-temp-prefix option has been
used, it is not *completely* out of place here.

We may think later about moving this command to a separate plugin or
maybe even adding it to the ROBOT core.
  • Loading branch information
gouttegd committed Mar 25, 2024
1 parent 7f91921 commit 76fe2e4
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 0 deletions.
226 changes: 226 additions & 0 deletions robot/src/main/java/org/incenp/obofoundry/kgcl/robot/MintCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*
* KGCL-Java - KGCL library for Java
* Copyright © 2024 Damien Goutte-Gattat
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the Gnu General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.incenp.obofoundry.kgcl.robot;

import java.util.HashMap;
import java.util.HashSet;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.compress.utils.Sets;
import org.incenp.obofoundry.kgcl.IAutoIDGenerator;
import org.incenp.obofoundry.kgcl.SequentialIDGenerator;
import org.obolibrary.robot.Command;
import org.obolibrary.robot.CommandLineHelper;
import org.obolibrary.robot.CommandState;
import org.obolibrary.robot.IOHelper;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.util.OWLEntityRenamer;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;

public class MintCommand implements Command {

private static final String DEFAULT_TEMP_PREFIX = "http://purl.obolibrary.org/temp#";
private static final String REPLACED_BY = "http://purl.obolibrary.org/obo/IAO_0100001";

private Options options;

public MintCommand() {
options = CommandLineHelper.getCommonOptions();
options.addOption("i", "input", true, "load ontology from file");
options.addOption("o", "output", true, "save ontology to file");

options.addOption(null, "temp-id-prefix", true, "prefix of temporary identifiers to replace");

options.addOption(null, "keep-deprecated", false, "keep temporary terms as deprecated entities");
options.addOption(null, "minted-from-property", true,
"property used to link minted identifiers to temporary identifiers");

options.addOption(null, "minted-id-prefix", true, "prefix of newly minted identifiers");
options.addOption(null, "pad-width", true, "pad the numerical portion of minted identifiers up to this width");
options.addOption(null, "min-id", true, "lower bound of the range for newly minted identifiers");
options.addOption(null, "max-id", true, "upper bound of the range for newly minted identifiers");

options.addOption(null, "id-range-file", true, "Use the specified ID range file");
options.addOption(null, "id-range-name", true, "Use the specified ID range name");
}

@Override
public String getName() {
return "mint";
}

@Override
public String getDescription() {
return "replace temporary identifiers by newly minted permanent identifiers";
}

@Override
public String getUsage() {
return "robot mint --input <file> --temp-id-prefix <prefix> --keep-deprecated\n"
+ " --minted-from-property <property>] --minted-id-prefix <prefix>\n"
+ " --pad-width <integer> --min-id <integer> --max-id <integer>\n"
+ " --id-range-file <file> --id-range-name <name>\n";
}

@Override
public Options getOptions() {
return options;
}

@Override
public void main(String[] args) {
try {
execute(null, args);
} catch ( Exception e ) {
CommandLineHelper.handleException(e);
}
}

@Override
public CommandState execute(CommandState state, String[] args) throws Exception {
CommandLine line = CommandLineHelper.getCommandLine(getUsage(), options, args);
if ( line == null ) {
return null;
}

IOHelper ioHelper = CommandLineHelper.getIOHelper(line);
state = CommandLineHelper.updateInputOntology(ioHelper, state, line);

OWLOntology ontology = state.getOntology();
OWLOntologyManager manager = ontology.getOWLOntologyManager();
OWLDataFactory factory = manager.getOWLDataFactory();
OWLEntityRenamer renamer = new OWLEntityRenamer(ontology.getOWLOntologyManager(), Sets.newHashSet(ontology));

// Annotation properties we need for --minted-from and --keep-deprecated
OWLAnnotationProperty labelProp = factory.getOWLAnnotationProperty(OWLRDFVocabulary.RDFS_LABEL.getIRI());
OWLAnnotationProperty replacedByProp = factory.getOWLAnnotationProperty(IRI.create(REPLACED_BY));
OWLAnnotationProperty deprecatedProp = factory
.getOWLAnnotationProperty(OWLRDFVocabulary.OWL_DEPRECATED.getIRI());
OWLAnnotationProperty mintedFromProp = null;
if ( line.hasOption("minted-from-property") ) {
mintedFromProp = factory.getOWLAnnotationProperty(IRI.create(line.getOptionValue("minted-from-property")));
}

String tempPrefix = line.getOptionValue("temp-id-prefix", DEFAULT_TEMP_PREFIX);
IAutoIDGenerator generator = getAutoIDGenerator(line, ontology);
if ( generator == null ) {
throw new Exception("No ID generator set, cannot mint new IDs");
}
boolean keepDeprecated = line.hasOption("keep-deprecated");

// Collect IDs that needs to be replaced.
HashMap<String, String> ids = new HashMap<String, String>();
HashMap<String, OWLLiteral> savedLabels = new HashMap<String, OWLLiteral>();
HashSet<OWLAxiom> savedAxioms = new HashSet<OWLAxiom>();
for ( OWLEntity entity : ontology.getSignature() ) {
String origIRI = entity.getIRI().toString();
if ( origIRI.startsWith(tempPrefix) ) {
String newIRI = generator.nextID();
if ( newIRI == null ) {
throw new Exception("Cannot mint new ID");
}

ids.put(origIRI, newIRI);

if ( keepDeprecated ) {
// Keep aside the original declaration axioms and labels
savedAxioms.addAll(ontology.getDeclarationAxioms(entity));
for ( OWLAnnotationAssertionAxiom ax : ontology.getAnnotationAssertionAxioms(entity.getIRI()) ) {
if ( ax.getProperty().isLabel() && ax.getValue().isLiteral() ) {
savedLabels.put(origIRI, ax.getValue().asLiteral().get());
}
}
}
}
}

// Proceed with replacement for the IDs we found
for ( String oldID : ids.keySet() ) {
IRI oldIRI = IRI.create(oldID);
IRI newIRI = IRI.create(ids.get(oldID));

// Actual renaming
manager.applyChanges(renamer.changeIRI(oldIRI, newIRI));

// Add a "minted-from" annotation?
if ( mintedFromProp != null ) {
manager.addAxiom(ontology, factory.getOWLAnnotationAssertionAxiom(mintedFromProp, newIRI, oldIRI));
}

// Keep the original entities as deprecated entities?
if ( keepDeprecated ) {
manager.addAxiom(ontology,
factory.getOWLAnnotationAssertionAxiom(deprecatedProp, oldIRI, factory.getOWLLiteral(true)));
manager.addAxiom(ontology, factory.getOWLAnnotationAssertionAxiom(replacedByProp, oldIRI, newIRI));
OWLLiteral label = savedLabels.get(oldID);
if ( label != null ) {
// Prepend "obsolete " if the label is English or language-neutral
if ( label.getLang().isEmpty() || label.getLang().equalsIgnoreCase("en") ) {
label = factory.getOWLLiteral("obsolete " + label.getLiteral(), label.getLang());
}
manager.addAxiom(ontology, factory.getOWLAnnotationAssertionAxiom(labelProp, oldIRI, label));
}
}
}

// --keep-deprecated: restore the declaration axioms for the deprecated entities
if ( !savedAxioms.isEmpty() ) {
manager.addAxioms(ontology, savedAxioms);
}

CommandLineHelper.maybeSaveOutput(line, ontology);

return state;
}

private IAutoIDGenerator getAutoIDGenerator(CommandLine line, OWLOntology ontology) throws Exception {
if ( line.hasOption("minted-id-prefix") ) {
/*
* Manual mode; generate IDs in a range that is explicitly specified on the
* command line.
*/
if ( !line.hasOption("min-id") ) {
throw new Exception("Missing --min-id option for auto-assigned IDs");
}
int lower = Integer.parseInt(line.getOptionValue("min-id"));
int upper = line.hasOption("max-id") ? Integer.parseInt(line.getOptionValue("max-id")) : lower + 1000;
int width = line.hasOption("pad-width") ? Integer.parseInt(line.getOptionValue("pad-width")) : 7;
String format = String.format("%s%%0%dd", line.getOptionValue("minted-id-prefix"), width);
return new SequentialIDGenerator(ontology, format, lower, upper);
} else {
/*
* Range-file mode; similar, but the range is obtained from a file of ID ranges.
*/
String rangeFile = line.getOptionValue("id-range-file");
String requestedName = line.getOptionValue("id-range-name");
String[] defaultNames = new String[] { "auto-minter" };
return IDRangeHelper.maybeGetIDGenerator(ontology, rangeFile, requestedName, defaultNames, false);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public static void main(String[] args) {
m.addCommand("verify", new VerifyCommand());

m.addCommand("kgcl-apply", new ApplyCommand());
m.addCommand("kgcl-mint", new MintCommand());

new PluginManager().addPluggableCommands(m);

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
org.incenp.obofoundry.kgcl.robot.ApplyCommand
org.incenp.obofoundry.kgcl.robot.MintCommand

0 comments on commit 76fe2e4

Please sign in to comment.