org.obolibrary.robot.IOHelper Maven / Gradle / Ivy
Show all versions of robot-core Show documentation
package org.obolibrary.robot;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.github.jsonldjava.core.Context;
import com.github.jsonldjava.core.JsonLdApi;
import com.github.jsonldjava.core.JsonLdError;
import com.github.jsonldjava.core.JsonLdOptions;
import com.github.jsonldjava.core.JsonLdProcessor;
import com.github.jsonldjava.utils.JsonUtils;
import com.google.common.collect.Sets;
import com.opencsv.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Pattern;
import java.util.zip.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.ReadWrite;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.shared.JenaException;
import org.apache.jena.tdb.TDBFactory;
import org.geneontology.obographs.core.io.OgJsonGenerator;
import org.geneontology.obographs.core.model.GraphDocument;
import org.geneontology.obographs.owlapi.FromOwl;
import org.geneontology.obographs.owlapi.OboGraphJsonDocumentFormat;
import org.obolibrary.obo2owl.OWLAPIOwl2Obo;
import org.obolibrary.oboformat.model.FrameStructureException;
import org.obolibrary.oboformat.model.OBODoc;
import org.obolibrary.oboformat.writer.OBOFormatWriter;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.formats.*;
import org.semanticweb.owlapi.io.*;
import org.semanticweb.owlapi.io.XMLUtils;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.rdf.rdfxml.renderer.IllegalElementNameException;
import org.semanticweb.owlapi.rdf.rdfxml.renderer.XMLWriterPreferences;
import org.semanticweb.owlapi.util.DefaultPrefixManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
/**
* Provides convenience methods for working with ontology and term files.
*
* @author James A. Overton
*/
public class IOHelper {
/** Logger. */
private static final Logger logger = LoggerFactory.getLogger(IOHelper.class);
/** Namespace for error messages. */
private static final String NS = "errors#";
/** Error message when the specified file does not exist. Expects file name. */
static final String fileDoesNotExistError =
NS + "FILE DOES NOT EXIST ERROR File does not exist: %s";
/** Error message when an invalid extension is provided (file format). Expects the file format. */
static final String invalidFormatError = NS + "INVALID FORMAT ERROR unknown format: %s";
/** Error message when writing output fails due to bad element name. */
private static final String invalidElementError =
NS + "INVALID ELEMENT ERROR \"%s\" contains invalid characters";
/** Error message when the specified file cannot be loaded. Expects the file name. */
private static final String invalidOntologyFileError =
NS + "INVALID ONTOLOGY FILE ERROR Could not load a valid ontology from file: %s";
/** Error message when the specified IRI cannot be loaded. Expects the IRI string. */
private static final String invalidOntologyIRIError =
NS + "INVALID ONTOLOGY IRI ERROR Could not load a valid ontology from IRI: %s";
/** Error message when the specified input stream cannot be loaded. */
private static final String invalidOntologyStreamError =
NS + "INVALID ONTOLOGY STREAM ERROR Could not load a valid ontology from InputStream.";
/** Error message when an invalid prefix is provided. Expects the combined prefix. */
static final String invalidPrefixError = NS + "INVALID PREFIX ERROR Invalid prefix string: %s";
/** Error message when a JSON-LD context cannot be created, for any reason. */
private static final String jsonldContextCreationError =
NS + "JSON-LD CONTEXT CREATION ERROR Could not create the JSON-LD context.";
/** Error message when a JSON-LD context cannot be read, for any reason. */
private static final String jsonldContextParseError =
NS + "JSON-LD CONTEXT PARSE ERROR Could not parse the JSON-LD context.";
/** Error message when a graph object cannot be created from the ontology using obographs. */
private static final String oboGraphError =
NS
+ "OBO GRAPH ERROR Could not convert ontology to OBO Graph (see https://github.com/geneontology/obographs)";
/** Error message when OBO cannot be saved. */
private static final String oboStructureError =
NS + "OBO STRUCTURE ERROR Ontology does not conform to OBO structure rules:\n%s";
/** Error message when the ontology cannot be saved. Expects the IRI string. */
private static final String ontologyStorageError =
NS + "ONTOLOGY STORAGE ERROR Could not save ontology to IRI: %s";
/** Error message when a prefix cannot be loaded. Expects the prefix and target. */
private static final String prefixLoadError =
NS + "PREFIX LOAD ERROR Could not load prefix '%s' for '%s'";
/**
* Error message when Jena cannot load a file to a dataset, probably not RDF/XML (including OWL)
* or TTL.
*/
private static final String syntaxError =
NS
+ "SYNTAX ERROR unable to load '%s' with Jena - "
+ "check that this file is in RDF/XML or TTL syntax and try again.";
/** Error message when an invalid prefix is provided. Expects the combined prefix. */
static final String undefinedPrefixError =
NS + "UNDEFINED PREFIX ERROR \"%s\" has unknown prefix; make sure prefix \"%s\" is defined";
/** Error message when loader contains unparsed triples. */
private static final String unparsedTriplesError =
NS + "UNPARSED TRIPLES ERROR input ontology contains %d triple(s) that could not be parsed:";
/** Optional base namespaces. */
private Set baseNamespaces = new HashSet<>();
/** Path to default context as a resource. */
private static String defaultContextPath = "/obo_context.jsonld";
/** Store the current JSON-LD context. */
private Context context = new Context();
/** Strict parsing; fail on unparsed triples. */
private Boolean strict = false;
/** Store xml entities flag. */
private Boolean useXMLEntities = false;
/**
* Create a new IOHelper with the default prefixes.
*
* @throws IOException on problem getting default context
*/
public IOHelper() throws IOException {
setContext(getDefaultContext());
}
/**
* Create a new IOHelper with or without the default prefixes.
*
* @param defaults false if defaults should not be used
* @throws IOException on problem getting default context
*/
public IOHelper(boolean defaults) throws IOException {
if (defaults) {
setContext(getDefaultContext());
} else {
setContext();
}
}
/**
* Create a new IOHelper with the specified prefixes.
*
* @param map the prefixes to use
* @throws IOException on issue parsing map
*/
public IOHelper(Map map) throws IOException {
setContext(map);
}
/**
* Create a new IOHelper with prefixes from a file path.
*
* @param path to a JSON-LD file with a @context
* @throws IOException on issue parsing JSON-LD file as context
*/
public IOHelper(String path) throws IOException {
String jsonString = FileUtils.readFileToString(new File(path), Charset.defaultCharset());
setContext(jsonString);
}
/**
* Create a new IOHelper with prefixes from a file.
*
* @param file a JSON-LD file with a @context
* @throws IOException on issue reading file or setting context from file
*/
public IOHelper(File file) throws IOException {
String jsonString = FileUtils.readFileToString(file, Charset.defaultCharset());
setContext(jsonString);
}
/**
* Set "strict" value. If true, any loadOntology method will fail on unparsed triples or OWLAPI
* "strict" parsing issues.
*
* @param strict boolean value
*/
public void setStrict(Boolean strict) {
this.strict = strict;
}
/**
* Given an ontology, a file, and a list of prefixes, save the ontology to the file and include
* the prefixes in the header.
*
* @deprecated replaced by {@link #saveOntology(OWLOntology, OWLDocumentFormat, IRI, Map,
* boolean)}
* @param ontology OWLOntology to save
* @param outputFile File to save ontology to
* @param addPrefixes List of prefixes to add ("foo: http://foo.bar/")
* @throws IOException On issue parsing list of prefixes or saving file
*/
@Deprecated
public void addPrefixesAndSave(OWLOntology ontology, File outputFile, List addPrefixes)
throws IOException {
OWLDocumentFormat df = getFormat(FilenameUtils.getExtension(outputFile.getPath()));
// If prefixes are not supported, just save the ontology without adding prefixes
if (!df.isPrefixOWLOntologyFormat()) {
logger.error("Prefixes are not supported in " + df.toString() + " (saving without prefixes)");
saveOntology(ontology, df, IRI.create(outputFile));
return;
}
// Convert prefixes to map
Map prefixMap = new HashMap<>();
for (String pref : addPrefixes) {
String[] split = pref.split(": ");
if (split.length != 2) {
throw new IOException(String.format(invalidPrefixError, pref));
}
prefixMap.put(split[0], split[1]);
}
addPrefixes(df, prefixMap);
saveOntology(ontology, df, IRI.create(outputFile));
}
/**
* Given a directory containing TDB mappings, remove the files and directory. If successful,
* return true.
*
* @param tdbDir directory to remove
* @return boolean indicating success
*/
protected static boolean cleanTDB(String tdbDir) {
File dir = new File(tdbDir);
boolean success = true;
if (dir.exists()) {
String[] files = dir.list();
if (files != null) {
for (String file : files) {
File f = new File(dir.getPath(), file);
success = f.delete();
}
}
// Only delete if all the files in dir were deleted
if (success) {
success = dir.delete();
}
}
return success;
}
/**
* Try to guess the location of the catalog.xml file. Looks in the directory of the given ontology
* file for a catalog file.
*
* @param ontologyFile the
* @return the guessed catalog File; may not exist!
*/
public File guessCatalogFile(File ontologyFile) {
String path = ontologyFile.getParent();
String catalogPath = "catalog-v001.xml";
if (path != null) {
catalogPath = path + "/catalog-v001.xml";
}
return new File(catalogPath);
}
/**
* Load an ontology from a String path, using a catalog file if available.
*
* @param ontologyPath the path to the ontology file
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(String ontologyPath) throws IOException {
File ontologyFile = new File(ontologyPath);
File catalogFile = guessCatalogFile(ontologyFile);
if (!catalogFile.isFile()) {
// If the catalog file does not exist, do not use catalog
catalogFile = null;
}
return loadOntology(ontologyFile, catalogFile);
}
/**
* Load an ontology from a String path, with option to use catalog file.
*
* @param ontologyPath the path to the ontology file
* @param useCatalog when true, a catalog file will be used if one is found
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(String ontologyPath, boolean useCatalog) throws IOException {
File ontologyFile = new File(ontologyPath);
File catalogFile = null;
if (useCatalog) {
catalogFile = guessCatalogFile(ontologyFile);
}
return loadOntology(ontologyFile, catalogFile);
}
/**
* Load an ontology from a String path, with optional catalog file.
*
* @param ontologyPath the path to the ontology file
* @param catalogPath the path to the catalog file
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(String ontologyPath, String catalogPath) throws IOException {
File ontologyFile = new File(ontologyPath);
File catalogFile = new File(catalogPath);
return loadOntology(ontologyFile, catalogFile);
}
/**
* Load an ontology from a File, using a catalog file if available.
*
* @param ontologyFile the ontology file to load
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(File ontologyFile) throws IOException {
File catalogFile = guessCatalogFile(ontologyFile);
if (!catalogFile.isFile()) {
// If the catalog file does not exist, do not use catalog
catalogFile = null;
}
return loadOntology(ontologyFile, catalogFile);
}
/**
* Load an ontology from a File, with option to use a catalog file.
*
* @param ontologyFile the ontology file to load
* @param useCatalog when true, a catalog file will be used if one is found
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(File ontologyFile, boolean useCatalog) throws IOException {
File catalogFile = null;
if (useCatalog) {
catalogFile = guessCatalogFile(ontologyFile);
}
return loadOntology(ontologyFile, catalogFile);
}
/**
* Load an ontology from a File, with optional catalog File.
*
* @param ontologyFile the ontology file to load
* @param catalogFile the catalog file to use
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(File ontologyFile, File catalogFile) throws IOException {
logger.debug("Loading ontology {} with catalog file {}", ontologyFile, catalogFile);
Object jsonObject = null;
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
try {
String extension = FilenameUtils.getExtension(ontologyFile.getName());
extension = extension.trim().toLowerCase();
if (extension.equals("yml") || extension.equals("yaml")) {
logger.debug("Converting from YAML to JSON");
String yamlString = FileUtils.readFileToString(ontologyFile, Charset.defaultCharset());
jsonObject = new Yaml().load(yamlString);
} else if (extension.equals("js") || extension.equals("json") || extension.equals("jsonld")) {
String jsonString = FileUtils.readFileToString(ontologyFile, Charset.defaultCharset());
jsonObject = JsonUtils.fromString(jsonString);
}
// Use Jena to convert a JSON-LD string to RDFXML, then load it
if (jsonObject != null) {
logger.debug("Converting from JSON to RDF");
jsonObject = new JsonLdApi().expand(getContext(), jsonObject);
String jsonString = JsonUtils.toString(jsonObject);
Model model = ModelFactory.createDefaultModel();
model.read(IOUtils.toInputStream(jsonString, Charset.defaultCharset()), null, "JSON-LD");
ByteArrayOutputStream output = new ByteArrayOutputStream();
// model.write(System.out);
model.write(output);
byte[] data = output.toByteArray();
ByteArrayInputStream input = new ByteArrayInputStream(data);
return loadOntology(input);
}
// Handle catalog file
if (catalogFile != null && catalogFile.isFile()) {
manager.setIRIMappers(Sets.newHashSet(new CatalogXmlIRIMapper(catalogFile)));
}
// Maybe unzip
if (ontologyFile.getPath().endsWith(".gz")) {
if (catalogFile == null) {
return loadCompressedOntology(ontologyFile, null);
} else {
return loadCompressedOntology(ontologyFile, catalogFile.getAbsolutePath());
}
}
// Otherwise load from file using default method
return loadOntology(manager, new FileDocumentSource(ontologyFile));
} catch (JsonLdError | OWLOntologyCreationException e) {
throw new IOException(String.format(invalidOntologyFileError, ontologyFile.getName()), e);
}
}
/**
* Load an ontology from an InputStream, without a catalog file.
*
* @param ontologyStream the ontology stream to load
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(InputStream ontologyStream) throws IOException {
return loadOntology(ontologyStream, null);
}
/**
* Load an ontology from an InputStream with a catalog file.
*
* @param ontologyStream the ontology stream to load
* @param catalogPath the catalog file to use or null
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(InputStream ontologyStream, String catalogPath)
throws IOException {
OWLOntology ontology;
// Maybe load a catalog file
File catalogFile = null;
if (catalogPath != null) {
catalogFile = new File(catalogPath);
if (!catalogFile.isFile()) {
throw new IOException(String.format(fileDoesNotExistError, catalogPath));
}
}
try {
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
if (catalogFile != null) {
manager.setIRIMappers(Sets.newHashSet(new CatalogXmlIRIMapper(catalogFile)));
}
ontology = loadOntology(manager, new StreamDocumentSource(ontologyStream));
} catch (OWLOntologyCreationException e) {
throw new IOException(invalidOntologyStreamError, e);
}
return ontology;
}
/**
* Given an ontology IRI, load the ontology from the IRI.
*
* @param ontologyIRI the ontology IRI to load
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(IRI ontologyIRI) throws IOException {
return loadOntology(ontologyIRI, null);
}
/**
* Given an IRI and a path to a catalog file, load the ontology from the IRI with the catalog.
*
* @param ontologyIRI the ontology IRI to load
* @param catalogPath the catalog file to use or null
* @return a new ontology object, with a new OWLManager
* @throws IOException on any problem
*/
public OWLOntology loadOntology(IRI ontologyIRI, String catalogPath) throws IOException {
OWLOntology ontology;
// Maybe load a catalog file
File catalogFile = null;
if (catalogPath != null) {
catalogFile = new File(catalogPath);
if (!catalogFile.isFile()) {
throw new IOException(String.format(fileDoesNotExistError, catalogPath));
}
}
try {
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
// If a catalog file was loaded, set IRI mappers
if (catalogFile != null) {
manager.setIRIMappers(Sets.newHashSet(new CatalogXmlIRIMapper(catalogFile)));
}
// Maybe load a zipped ontology
if (ontologyIRI.toString().endsWith(".gz")) {
ontology = loadCompressedOntology(new URL(ontologyIRI.toString()), catalogPath);
} else {
// Otherwise load ontology as normal
IRI documentIRI = getDocumentIRIFromMappers(manager, ontologyIRI);
ontology = loadOntology(manager, new IRIDocumentSource(documentIRI));
}
} catch (OWLOntologyCreationException e) {
throw new IOException(e);
}
return ontology;
}
/**
* Given an ontology manager and a document source, load the ontology from the source using the
* manager. Log unparsed triples, or throw exception if strict=true.
*
* @param manager OWLOntologyManager with IRI mappers to use
* @param source OWLOntologyDocumentSource to load from
* @return a new ontology object, with a new OWLManager
* @throws IOException on problem with unparsed triples if strict=true
* @throws OWLOntologyCreationException on problem loading ontology document
*/
public OWLOntology loadOntology(OWLOntologyManager manager, OWLOntologyDocumentSource source)
throws IOException, OWLOntologyCreationException {
OWLOntologyLoaderConfiguration config = new OWLOntologyLoaderConfiguration();
if (strict) {
// Set strict OWLAPI parsing
config = config.setStrict(true);
}
// Load the ontology
OWLOntology loadedOntology = manager.loadOntologyFromOntologyDocument(source, config);
// Check for unparsed triples - get the document format and then the loader metadata
OWLDocumentFormat f = manager.getOntologyFormat(loadedOntology);
if (f == null) {
// This should never happen
throw new IOException("Unable to get an OWLDocumentFormat from loaded ontology");
}
RDFParserMetaData metaData = (RDFParserMetaData) f.getOntologyLoaderMetaData();
Set unparsed = metaData.getUnparsedTriples();
Set parsed = loadedOntology.getAxioms();
if (unparsed.size() > 0) {
boolean rdfReification = false;
StringBuilder sb = new StringBuilder();
for (RDFTriple t : unparsed) {
// Check object to see if it's rdfs:Statement used in RDF reification
String objectIRI;
try {
objectIRI = t.getObject().getIRI().toString();
} catch (UnsupportedOperationException e) {
// RDF Literals do not have IRIs
objectIRI = "";
}
if (objectIRI.equals("http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement")) {
rdfReification = true;
}
// Add triple to error lines
sb.append("\n - ").append(t.toString().trim());
}
Set undeclaredPredicates = getUndeclaredPredicates(parsed, unparsed);
if (rdfReification) {
// Add hint for fixing RDF reification
sb.append(
"\n\nHint: you may be using RDF reification - try replacing 'rdf:Statement' with 'owl:Axiom'");
}
if (undeclaredPredicates.size() > 0) {
sb.append(
"\n\nHint: you have undeclared predicates - try adding 'rdf:type' declarations to the following:");
for (IRI p : undeclaredPredicates) {
sb.append("\n - ").append(p.toString());
}
}
sb.append("\n");
if (strict) {
// Fail on unparsed triples
throw new IOException(String.format(unparsedTriplesError, unparsed.size()) + sb.toString());
} else {
// Log unparsed triples as errors
logger.error(
String.format(
"Input ontology contains %d triple(s) that could not be parsed:",
unparsed.size())
+ sb.toString());
}
}
// No issues, return ontology
return loadedOntology;
}
private IRI getDocumentIRIFromMappers(OWLOntologyManager m, IRI iri) {
if (iri == null) {
return null;
}
final Iterator var4 = m.getIRIMappers().iterator();
IRI documentIRI;
do {
if (!var4.hasNext()) {
return iri;
}
OWLOntologyIRIMapper mapper = var4.next();
documentIRI = mapper.getDocumentIRI(iri);
} while (documentIRI == null);
return documentIRI;
}
/**
* Given a path to an RDF/XML or TTL file and a RDF language, load the file as the default model
* of a TDB dataset backed by a directory to improve processing time. Return the new dataset.
*
* WARNING - this creates a directory at given tdbDir location!
*
* @param inputPath input path of RDF/XML or TTL file
* @param tdbDir location to put TDB mappings
* @return Dataset instantiated with triples
* @throws JenaException if TDB directory can't be written to
*/
public static Dataset loadToTDBDataset(String inputPath, String tdbDir) throws JenaException {
// First try opening existing dataset
Dataset dataset = openTDBDataset(tdbDir);
if (dataset != null) {
return dataset;
}
dataset = TDBFactory.createDataset(tdbDir);
logger.debug(String.format("Parsing input '%s' to dataset", inputPath));
// Track parsing time
long start = System.nanoTime();
Model m;
dataset.begin(ReadWrite.WRITE);
try {
m = dataset.getDefaultModel();
RDFDataMgr.read(m, inputPath);
dataset.commit();
} catch (JenaException e) {
dataset.abort();
dataset.end();
dataset.close();
throw new JenaException(String.format(syntaxError, inputPath));
} finally {
dataset.end();
}
long time = (System.nanoTime() - start) / 1000000000;
logger.debug(String.format("Parsing complete - took %s seconds", String.valueOf(time)));
return dataset;
}
/**
* Given a path to a TDB directory, load the TDB as a Dataset.
*
* @param tdbDir path to existing TDB directory
* @return Dataset or null
*/
public static Dataset openTDBDataset(String tdbDir) {
Dataset dataset;
if (new File(tdbDir).isDirectory()) {
dataset = TDBFactory.createDataset(tdbDir);
if (!dataset.isEmpty()) {
return dataset;
}
}
return null;
}
/**
* Given the name of a file format, return an instance of it.
*
*
Suported formats:
*
*
* - OBO as 'obo'
*
- RDFXML as 'owl'
*
- Turtle as 'ttl'
*
- OWLXML as 'owx'
*
- Manchester as 'omn'
*
- OWL Functional as 'ofn'
*
*
* @param formatName the name of the format
* @return an instance of the format
* @throws IllegalArgumentException if format name is not recognized
*/
public static OWLDocumentFormat getFormat(String formatName) throws IllegalArgumentException {
formatName = formatName.trim().toLowerCase();
switch (formatName) {
case "obo":
return new OBODocumentFormat();
case "owl":
return new RDFXMLDocumentFormat();
case "ttl":
return new TurtleDocumentFormat();
case "owx":
return new OWLXMLDocumentFormat();
case "omn":
return new ManchesterSyntaxDocumentFormat();
case "ofn":
return new FunctionalSyntaxDocumentFormat();
case "json":
return new OboGraphJsonDocumentFormat();
default:
throw new IllegalArgumentException(String.format(invalidFormatError, formatName));
}
}
/**
* Save an ontology to a String path.
*
* @param ontology the ontology to save
* @param ontologyPath the path to save the ontology to
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(OWLOntology ontology, String ontologyPath) throws IOException {
return saveOntology(ontology, new File(ontologyPath));
}
/**
* Save an ontology to a File.
*
* @param ontology the ontology to save
* @param ontologyFile the file to save the ontology to
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(OWLOntology ontology, File ontologyFile) throws IOException {
return saveOntology(ontology, IRI.create(ontologyFile));
}
/**
* Save an ontology to an IRI, using the file extension to determine the format.
*
* @param ontology the ontology to save
* @param ontologyIRI the IRI to save the ontology to
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(final OWLOntology ontology, IRI ontologyIRI) throws IOException {
String path = ontologyIRI.toString();
if (path.endsWith(".gz")) {
path = path.substring(0, path.lastIndexOf("."));
}
String formatName = FilenameUtils.getExtension(path);
OWLDocumentFormat format = getFormat(formatName);
return saveOntology(ontology, format, ontologyIRI, true);
}
/**
* Save an ontology in the given format to a file.
*
* @param ontology the ontology to save
* @param format the ontology format to use
* @param ontologyFile the file to save the ontology to
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(
final OWLOntology ontology, OWLDocumentFormat format, File ontologyFile) throws IOException {
return saveOntology(ontology, format, ontologyFile, true);
}
/**
* Save an ontology in the given format to a file, with the option to ignore OBO document checks.
*
* @param ontology the ontology to save
* @param format the ontology format to use
* @param ontologyFile the file to save the ontology to
* @param checkOBO if false, ignore OBO document checks
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(
final OWLOntology ontology, OWLDocumentFormat format, File ontologyFile, boolean checkOBO)
throws IOException {
return saveOntology(ontology, format, IRI.create(ontologyFile), checkOBO);
}
/**
* Save an ontology in the given format to an IRI.
*
* @param ontology the ontology to save
* @param format the ontology format to use
* @param ontologyIRI the IRI to save the ontology to
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(
final OWLOntology ontology, OWLDocumentFormat format, IRI ontologyIRI) throws IOException {
return saveOntology(ontology, format, ontologyIRI, true);
}
/**
* Save an ontology in the given format to a path, with the option to ignore OBO document checks.
*
* @param ontology the ontology to save
* @param format the ontology format to use
* @param ontologyPath the path to save the ontology to
* @param checkOBO if false, ignore OBO document checks
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(
final OWLOntology ontology, OWLDocumentFormat format, String ontologyPath, boolean checkOBO)
throws IOException {
return saveOntology(ontology, format, IRI.create(new File(ontologyPath)), checkOBO);
}
/**
* Save an ontology in the given format to an IRI, with the option to ignore OBO document checks.
*
* @param ontology the ontology to save
* @param format the ontology format to use
* @param ontologyIRI the IRI to save the ontology to
* @param checkOBO if false, ignore OBO document checks
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(
final OWLOntology ontology, OWLDocumentFormat format, IRI ontologyIRI, boolean checkOBO)
throws IOException {
return saveOntology(ontology, format, ontologyIRI, null, checkOBO);
}
/**
* Save an ontology in the given format to an IRI, with option to add prefixes and option to
* ignore OBO document checks.
*
* @param ontology the ontology to save
* @param format the ontology format to use
* @param ontologyIRI the IRI to save the ontology to
* @param addPrefixes map of prefixes to add to header
* @param checkOBO if false, ignore OBO document checks
* @return the saved ontology
* @throws IOException on any problem
*/
public OWLOntology saveOntology(
final OWLOntology ontology,
OWLDocumentFormat format,
IRI ontologyIRI,
Map addPrefixes,
boolean checkOBO)
throws IOException {
// Determine the format if not provided
logger.debug("Saving ontology as {} with to IRI {}", format, ontologyIRI);
XMLWriterPreferences.getInstance().setUseNamespaceEntities(getXMLEntityFlag());
// If saving in compressed format, get byte data then save to gzip
if (ontologyIRI.toString().endsWith(".gz")) {
byte[] data = getOntologyFileData(ontology, format, checkOBO);
saveCompressedOntology(data, ontologyIRI);
return ontology;
}
OWLDocumentFormat previousFormat = ontology.getOWLOntologyManager().getOntologyFormat(ontology);
if (format.isPrefixOWLOntologyFormat()
&& previousFormat != null
&& previousFormat.isPrefixOWLOntologyFormat()) {
String defaultNamespace = format.asPrefixOWLOntologyFormat().getDefaultPrefix();
format
.asPrefixOWLOntologyFormat()
.copyPrefixesFrom(previousFormat.asPrefixOWLOntologyFormat());
format.asPrefixOWLOntologyFormat().setDefaultPrefix(defaultNamespace);
}
// If not compressed, just save the file as-is
if (addPrefixes != null && !addPrefixes.isEmpty()) {
addPrefixes(format, addPrefixes);
}
saveOntologyFile(ontology, format, ontologyIRI, checkOBO);
return ontology;
}
/**
* Extract a set of term identifiers from an input string by removing comments, trimming lines,
* and removing empty lines. A comment is a space or newline followed by a '#', to the end of the
* line. This excludes '#' characters in IRIs.
*
* @param input the String containing the term identifiers
* @return a set of term identifier strings
*/
public Set extractTerms(String input) {
Set results = new HashSet<>();
String[] lines = input.replaceAll("\\r", "").split("\\n");
for (String line : lines) {
if (line.trim().startsWith("#")) {
continue;
}
String result = line.replaceFirst("($|\\s)#.*$", "").trim();
if (!result.isEmpty()) {
results.add(result);
}
}
return results;
}
/**
* Convert a row index and column index for a cell to A1 notation.
*
* @param rowNum row index
* @param colNum column index
* @return A1 notation for cell location
*/
public static String cellToA1(int rowNum, int colNum) {
// To store result (Excel column name)
StringBuilder colLabel = new StringBuilder();
while (colNum > 0) {
// Find remainder
int rem = colNum % 26;
// If remainder is 0, then a
// 'Z' must be there in output
if (rem == 0) {
colLabel.append("Z");
colNum = (colNum / 26) - 1;
} else {
colLabel.append((char) ((rem - 1) + 'A'));
colNum = colNum / 26;
}
}
// Reverse the string and print result
return colLabel.reverse().toString() + rowNum;
}
/**
* Given a term string, use the current prefixes to create an IRI.
*
* @param term the term to convert to an IRI
* @return the new IRI or null
*/
@SuppressWarnings("unchecked")
public IRI createIRI(String term) {
if (term == null) {
return null;
}
IRI iri;
try {
// This is stupid, because better methods aren't public.
// We create a new JSON map and add one entry
// with the term as the key and some string as the value.
// Then we run the JsonLdApi to expand the JSON map
// in the current context, and just grab the first key.
// If everything worked, that key will be our expanded iri.
Map jsonMap = new HashMap<>();
jsonMap.put(term, "ignore this string");
Object expanded = new JsonLdApi().expand(context, jsonMap);
String result = ((Map) expanded).keySet().iterator().next();
if (result != null) {
iri = IRI.create(result);
} else {
iri = IRI.create(term);
}
} catch (Exception e) {
logger.warn("Could not create IRI for {}", term);
if (e.getMessage() != null) {
logger.warn(e.getMessage());
}
return null;
}
if (iri.toString().startsWith("urn:")) {
// A URN is not a valid URL, so we check it with regex
Pattern urnPattern =
Pattern.compile(
"^urn:[a-z0-9][a-z0-9-]{0,31}:([a-z0-9()+,\\-.:=@;$_!*']|%[0-9a-f]{2})+$",
Pattern.CASE_INSENSITIVE);
if (!urnPattern.matcher(iri.toString()).matches()) {
// Not a valid URN (RFC2141)
return null;
}
} else {
try {
// Check for malformed URL, e.g., a CURIE was returned or the value passed has spaces
new URL(iri.toString());
} catch (MalformedURLException e) {
// Invalid IRI
return null;
}
}
return iri;
}
/**
* Given a term string, use the current prefixes to create an IRI.
*
* @deprecated replaced by {@link #createIRI(String)}
* @param term the term to convert to an IRI
* @param qName if true, validate that the IRI expands to a QName
* @return the new IRI or null
*/
@Deprecated
public IRI createIRI(String term, boolean qName) {
IRI iri = createIRI(term);
// Check that this is a valid QName
if (qName && !iri.getRemainder().isPresent()) {
return null;
}
return iri;
}
/**
* Given a set of term identifier strings, return a set of IRIs.
*
* @param terms the set of term identifier strings
* @return the set of IRIs
* @throws IllegalArgumentException if term identifier is not a valid IRI
*/
public Set createIRIs(Set terms) throws IllegalArgumentException {
Set iris = new HashSet<>();
for (String term : terms) {
IRI iri = createIRI(term);
if (iri != null) {
iris.add(iri);
} else {
// Warn and continue
logger.warn("{} is not a valid IRI.", term);
}
}
return iris;
}
/**
* Create an OWLLiteral.
*
* @param value the lexical value
* @return a literal
*/
public static OWLLiteral createLiteral(String value) {
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLDataFactory df = manager.getOWLDataFactory();
return df.getOWLLiteral(value);
}
/**
* Create an OWLLiteral with a language tag.
*
* @param value the lexical value
* @param lang the language tag
* @return a literal
*/
public static OWLLiteral createTaggedLiteral(String value, String lang) {
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLDataFactory df = manager.getOWLDataFactory();
return df.getOWLLiteral(value, lang);
}
/**
* Create a typed OWLLiteral.
*
* @param value the lexical value
* @param type the type IRI string
* @return a literal
*/
public OWLLiteral createTypedLiteral(String value, String type) {
IRI iri = createIRI(type);
return createTypedLiteral(value, iri);
}
/**
* Create a typed OWLLiteral.
*
* @param value the lexical value
* @param type the type IRI
* @return a literal
*/
public OWLLiteral createTypedLiteral(String value, IRI type) {
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLDataFactory df = manager.getOWLDataFactory();
OWLDatatype datatype = df.getOWLDatatype(type);
return df.getOWLLiteral(value, datatype);
}
/**
* Parse a set of IRIs from a space-separated string, ignoring '#' comments.
*
* @param input the string containing the IRI strings
* @return the set of IRIs
* @throws IllegalArgumentException if term identifier is not a valid IRI
*/
public Set parseTerms(String input) throws IllegalArgumentException {
return createIRIs(extractTerms(input));
}
/**
* Load a map of prefixes from the "@context" of a JSON-LD string.
*
* @param jsonString the JSON-LD string
* @return a map from prefix name strings to prefix IRI strings
* @throws IOException on any problem
*/
@SuppressWarnings("unchecked")
public static Context parseContext(String jsonString) throws IOException {
try {
Object jsonObject = JsonUtils.fromString(jsonString);
if (!(jsonObject instanceof Map)) {
throw new IOException(jsonldContextParseError);
}
Map jsonMap = (Map) jsonObject;
if (!jsonMap.containsKey("@context")) {
throw new IOException(jsonldContextParseError);
}
Object jsonContext = jsonMap.get("@context");
return new Context().parse(jsonContext);
} catch (Exception e) {
throw new IOException(jsonldContextParseError, e);
}
}
/**
* Add a base namespace to the IOHelper.
*
* @param baseNamespace namespace to add to bases.
*/
public void addBaseNamespace(String baseNamespace) {
baseNamespaces.add(baseNamespace);
}
/**
* Add a set of base namespaces to the IOHelper from file. Each base namespace should be on its
* own line.
*
* @param baseNamespacePath path to base namespace file
* @throws IOException if file does not exist
*/
public void addBaseNamespaces(String baseNamespacePath) throws IOException {
File prefixFile = new File(baseNamespacePath);
if (!prefixFile.exists()) {
throw new IOException(String.format(fileDoesNotExistError, baseNamespacePath));
}
List lines = FileUtils.readLines(new File(baseNamespacePath), Charset.defaultCharset());
for (String l : lines) {
baseNamespaces.add(l.trim());
}
}
/**
* Get the base namespaces.
*
* @return set of base namespaces
*/
public Set getBaseNamespaces() {
return baseNamespaces;
}
/**
* Get a copy of the default context.
*
* @return a copy of the current context
* @throws IOException if default context file cannot be read
*/
public Context getDefaultContext() throws IOException {
InputStream stream = IOHelper.class.getResourceAsStream(defaultContextPath);
String jsonString = IOUtils.toString(stream, Charset.defaultCharset());
return parseContext(jsonString);
}
/**
* Get a copy of the current context.
*
* @return a copy of the current context
*/
public Context getContext() {
return this.context.clone();
}
/** Set an empty context. */
public void setContext() {
this.context = new Context();
}
/**
* Set the current JSON-LD context to the given context.
*
* @param context the new JSON-LD context
*/
public void setContext(Context context) {
if (context == null) {
setContext();
} else {
this.context = context;
}
}
/**
* Set the current JSON-LD context to the given context.
*
* @param jsonString the new JSON-LD context as a JSON string
* @throws IOException on issue parsing JSON
*/
public void setContext(String jsonString) throws IOException {
this.context = parseContext(jsonString);
}
/**
* Set the current JSON-LD context to the given map.
*
* @param map a map of strings for the new JSON-LD context
* @throws IOException on issue parsing JSON
*/
public void setContext(Map map) throws IOException {
try {
this.context = new Context().parse(map);
} catch (JsonLdError e) {
throw new IOException(jsonldContextParseError, e);
}
}
/**
* Set whether or not XML entities will be swapped into URIs in saveOntology XML output formats.
*
* @param entityFlag value to set
*/
public void setXMLEntityFlag(Boolean entityFlag) {
try {
this.useXMLEntities = entityFlag;
} catch (Exception e) {
logger.warn("Could not set useXMLEntities {}", entityFlag);
logger.warn(e.getMessage());
}
}
/**
* Get the useXMLEntities flag.
*
* @return boolean useXMLEntities flag
*/
public Boolean getXMLEntityFlag() {
return this.useXMLEntities;
}
/**
* Make an OWLAPI DefaultPrefixManager from a map of prefixes.
*
* @param prefixes a map from prefix name strings to prefix IRI strings
* @return a new DefaultPrefixManager
*/
public static DefaultPrefixManager makePrefixManager(Map prefixes) {
DefaultPrefixManager pm = new DefaultPrefixManager();
for (Map.Entry entry : prefixes.entrySet()) {
pm.setPrefix(entry.getKey() + ":", entry.getValue());
}
return pm;
}
/**
* Get a prefix manager with the current prefixes.
*
* @return a new DefaultPrefixManager
*/
public DefaultPrefixManager getPrefixManager() {
return makePrefixManager(context.getPrefixes(false));
}
/**
* Add a prefix mapping as a single string "foo: http://example.com#".
*
* @param combined both prefix and target
* @throws IllegalArgumentException on malformed input
* @throws IOException if prefix cannot be parsed
*/
public void addPrefix(String combined) throws IllegalArgumentException, IOException {
String[] results = combined.split(":", 2);
if (results.length < 2) {
throw new IllegalArgumentException(String.format(invalidPrefixError, combined));
}
addPrefix(results[0], results[1]);
}
/**
* Add a prefix mapping to the current JSON-LD context, as a prefix string and target string.
* Rebuilds the context.
*
* @param prefix the short prefix to add; should not include ":"
* @param target the IRI string that is the target of the prefix
* @throws IOException if prefix cannot be parsed
*/
public void addPrefix(String prefix, String target) throws IOException {
try {
context.put(prefix.trim(), target.trim());
context.remove("@base");
setContext((Map) context);
} catch (Exception e) {
throw new IOException(String.format(prefixLoadError, prefix, target), e);
}
}
/**
* Given a path to a JSON-LD prefix file, add the prefix mappings in the file to the current
* JSON-LD context.
*
* @param prefixPath path to JSON-LD prefix file to add
* @throws IOException if the file does not exist or cannot be read
*/
public void addPrefixes(String prefixPath) throws IOException {
File prefixFile = new File(prefixPath);
if (!prefixFile.exists()) {
throw new IOException(String.format(fileDoesNotExistError, prefixPath));
}
Context context1 =
parseContext(FileUtils.readFileToString(prefixFile, Charset.defaultCharset()));
addPrefixes(context1);
}
/**
* Given a Context, add the prefix mappings to the current JSON-LD context.
*
* @param context1 Context to add
* @throws IOException if the Context cannot be set
*/
public void addPrefixes(Context context1) throws IOException {
context.putAll(context1.getPrefixes(false));
context.remove("@base");
setContext((Map) context);
}
/**
* Get a copy of the current prefix map.
*
* @return a copy of the current prefix map
*/
public Map getPrefixes() {
return this.context.getPrefixes(false);
}
/**
* Set the current prefix map.
*
* @param map the new map of prefixes to use
* @throws IOException on issue parsing map to context
*/
public void setPrefixes(Map map) throws IOException {
setContext(map);
}
/**
* Return the current prefixes as a JSON-LD string.
*
* @return the current prefixes as a JSON-LD string
* @throws IOException on any error
*/
public String getContextString() throws IOException {
try {
Object compact =
JsonLdProcessor.compact(
JsonUtils.fromString("{}"), context.getPrefixes(false), new JsonLdOptions());
return JsonUtils.toPrettyString(compact);
} catch (Exception e) {
throw new IOException(jsonldContextCreationError, e);
}
}
/**
* Write the current context as a JSON-LD file.
*
* @param path the path to write the context
* @throws IOException on any error
*/
public void saveContext(String path) throws IOException {
saveContext(new File(path));
}
/**
* Write the current context as a JSON-LD file.
*
* @param file the file to write the context
* @throws IOException on any error
*/
public void saveContext(File file) throws IOException {
FileWriter writer = new FileWriter(file);
writer.write(getContextString());
writer.close();
}
/**
* Determine if a string is a CURIE. Note that a valid CURIE is not always a valid QName. Adapted
* from:
*
* @see org.semanticweb.owlapi.io.XMLUtils#isQName(CharSequence)
* @param s Character sequence to check
* @return true if valid CURIE
*/
public static boolean isValidCURIE(CharSequence s) {
if (s == null || 0 >= s.length()) {
// string is null or empty
return false;
}
boolean inLocal = false;
for (int i = 0; i < s.length(); ) {
int codePoint = Character.codePointAt(s, i);
if (codePoint == ':') {
if (inLocal) {
// Second colon - illegal
return false;
}
inLocal = true;
} else {
if (!inLocal) {
// Check for valid NS characters
if (!XMLUtils.isXMLNameStartCharacter(codePoint)) {
return false;
}
} else {
// Check for valid local characters
if (!XMLUtils.isXMLNameChar(codePoint)) {
return false;
}
}
}
i += Character.charCount(codePoint);
}
return true;
}
/**
* Read comma-separated values from a path to a list of lists of strings.
*
* @param path file path to the CSV file
* @return a list of lists of strings
* @throws IOException on file or reading problems
*/
public static List> readCSV(String path) throws IOException {
return TemplateHelper.readCSV(path);
}
/**
* Read comma-separated values from a stream to a list of lists of strings.
*
* @param stream the stream to read from
* @return a list of lists of strings
* @throws IOException on file or reading problems
*/
public static List> readCSV(InputStream stream) throws IOException {
return TemplateHelper.readCSV(stream);
}
/**
* Read comma-separated values from a reader to a list of lists of strings.
*
* @param reader a reader to read data from
* @return a list of lists of strings
* @throws IOException on file or reading problems
*/
public static List> readCSV(Reader reader) throws IOException {
return TemplateHelper.readCSV(reader);
}
/**
* Read tab-separated values from a path to a list of lists of strings.
*
* @param path file path to the CSV file
* @return a list of lists of strings
* @throws IOException on file or reading problems
*/
public static List> readTSV(String path) throws IOException {
return TemplateHelper.readTSV(path);
}
/**
* Read tab-separated values from a stream to a list of lists of strings.
*
* @param stream the stream to read from
* @return a list of lists of strings
* @throws IOException on file or reading problems
*/
public static List> readTSV(InputStream stream) throws IOException {
return TemplateHelper.readTSV(stream);
}
/**
* Read tab-separated values from a reader to a list of lists of strings.
*
* @param reader a reader to read data from
* @return a list of lists of strings
* @throws IOException on file or reading problems
*/
public static List> readTSV(Reader reader) throws IOException {
return TemplateHelper.readTSV(reader);
}
/**
* Read a table from a path to a list of lists of strings.
*
* @param path file path to the CSV file
* @return a list of lists of strings
* @throws IOException on file or reading problems
*/
public static List> readTable(String path) throws IOException {
return TemplateHelper.readTable(path);
}
/**
* Write a table from a list of arrays.
*
* @param table List of arrays to write
* @param path path to write to
* @throws IOException on file or writing problems
*/
public static void writeTable(List table, String path) throws IOException {
char separator = '\t';
if (path.endsWith(".csv")) {
separator = ',';
}
writeTable(table, new File(path), separator);
}
/**
* Write a table from a list of arrays.
*
* @param file File to write to
* @param table List of arrays to write
* @param separator table separator
* @throws IOException on problem making Writer object or auto-closing CSVWriter
*/
public static void writeTable(List table, File file, char separator)
throws IOException {
try (Writer w = new FileWriter(file)) {
writeTable(table, w, separator);
}
}
/**
* Write a table from a list of arrays.
*
* @param writer Writer object to write to
* @param table List of arrays to write
* @param separator table separator
* @throws IOException on problem auto-closing writer
*/
public static void writeTable(List table, Writer writer, char separator)
throws IOException {
try (CSVWriter csv =
new CSVWriter(
writer,
separator,
CSVWriter.DEFAULT_QUOTE_CHARACTER,
CSVWriter.DEFAULT_ESCAPE_CHARACTER,
CSVWriter.DEFAULT_LINE_END)) {
csv.writeAll(table, false);
}
}
/**
* Given a document format and a map of prefixes to add, add the prefixes to the document.
*
* @param df OWLDocumentFormat
* @param addPrefixes map of prefix to namespace to add
*/
private void addPrefixes(OWLDocumentFormat df, Map addPrefixes) {
if (!df.isPrefixOWLOntologyFormat()) {
// Warn on non-prefix document format (i.e. OBO)
logger.warn(
String.format(
"Unable to add prefixes to %s document - saving without prefixes", df.toString()));
return;
}
PrefixDocumentFormat pf = df.asPrefixOWLOntologyFormat();
for (Map.Entry pref : addPrefixes.entrySet()) {
pf.setPrefix(pref.getKey(), pref.getValue());
}
}
/**
* Given a URL, check if the URL returns a redirect and return that new URL. Continue following
* redirects until there are no more redirects.
*
* @param url URL to follow redirects
* @return URL after all redirects
* @throws IOException on issue making URL connection
*/
private URL followRedirects(URL url) throws IOException {
// Check if the URL redirects
if (url.toString().startsWith("ftp")) {
// Trying to open HttpURLConnection on FTP will throw exception
return url;
}
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int status = conn.getResponseCode();
boolean redirect = false;
if (status != HttpURLConnection.HTTP_OK) {
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|| status == HttpURLConnection.HTTP_MOVED_PERM
|| status == HttpURLConnection.HTTP_SEE_OTHER) {
redirect = true;
}
}
if (redirect) {
// Get the new URL and then check that for redirect
String newURL = conn.getHeaderField("Location");
logger.info(String.format("<%s> redirecting to <%s>...", url.toString(), newURL));
if (newURL.startsWith("ftp")) {
// No more redirects
return new URL(newURL);
} else {
// Check again if there is another redirect
return followRedirects(new URL(newURL));
}
} else {
// Otherwise just return the URL
return url;
}
}
/**
* Given an ontology, a document format, and a boolean indicating to check OBO formatting, return
* the ontology file in the OWLDocumentFormat as a byte array.
*
* @param ontology OWLOntology to save
* @param format OWLDocumentFormat to save in
* @param checkOBO boolean indiciating to check OBO formatting
* @return byte array of formatted ontology data
* @throws IOException on any problem
*/
private byte[] getOntologyFileData(
final OWLOntology ontology, OWLDocumentFormat format, boolean checkOBO) throws IOException {
byte[] data;
// first handle any non-official output formats.
// currently this is just OboGraphs JSON format
if (format instanceof OboGraphJsonDocumentFormat) {
FromOwl fromOwl = new FromOwl();
GraphDocument gd = fromOwl.generateGraphDocument(ontology);
String doc = OgJsonGenerator.render(gd);
data = doc.getBytes();
} else if (format instanceof OBODocumentFormat && !checkOBO) {
OWLAPIOwl2Obo bridge = new OWLAPIOwl2Obo(ontology.getOWLOntologyManager());
OBODoc oboOntology = bridge.convert(ontology);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(baos))) {
OBOFormatWriter oboWriter = new OBOFormatWriter();
oboWriter.setCheckStructure(checkOBO);
oboWriter.write(oboOntology, bw);
}
data = baos.toByteArray();
} else {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ontology.getOWLOntologyManager().saveOntology(ontology, format, baos);
data = baos.toByteArray();
} catch (IOException | OWLOntologyStorageException e) {
// TODO
throw new IOException(e);
}
}
return data;
}
/**
* Given a set of parsed OWLAxioms and a set of unparsed RDF triples, get any predicates used in
* the unparsed set that are not builtins (OWL, RDF, RDFS) and do not have declarations in the
* parsed axioms.
*
* @param parsedAxioms Set of parsed OWLAxioms from loaded ontology
* @param unparsedTriples Set of unparsed RDF triples from loaded ontology
* @return set of IRIs of any undeclared predicates
*/
private static Set getUndeclaredPredicates(
Set parsedAxioms, Set unparsedTriples) {
Set checkPredicates = new HashSet<>();
for (RDFTriple t : unparsedTriples) {
IRI pIRI = t.getPredicate().getIRI();
if (pIRI.toString().startsWith("http://www.w3.org/2002/07/owl#")
|| pIRI.toString().startsWith("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
|| pIRI.toString().startsWith("http://www.w3.org/2000/01/rdf-schema#")) {
// Skip OWL, RDF, RDFS ...
continue;
}
checkPredicates.add(t.getPredicate().getIRI());
}
// Look for types
for (OWLAxiom a : parsedAxioms) {
if (!a.getAxiomType().equals(AxiomType.DECLARATION)) {
continue;
}
OWLDeclarationAxiom dec = (OWLDeclarationAxiom) a;
IRI eIRI = dec.getEntity().getIRI();
checkPredicates.remove(eIRI);
}
return checkPredicates;
}
/**
* Given a gzipped ontology file and a catalog path, load the ontology from a zip input stream.
*
* @param gzipFile compressed File to load ontology from
* @param catalogPath the path to the catalog file or null
* @return a new ontology object with a new OWLManager
* @throws IOException on any problem
*/
private OWLOntology loadCompressedOntology(File gzipFile, String catalogPath) throws IOException {
FileInputStream fis = new FileInputStream(gzipFile);
GZIPInputStream gis = new GZIPInputStream(fis);
return loadOntology(gis, catalogPath);
}
/**
* Given the URL to a gzipped ontology and a catalog path, load the ontology from a zip input
* stream.
*
* @param url URL to load from
* @param catalogPath the path to the catalog file or null
* @return a new ontology object with a new OWLManager
* @throws IOException on any problem
*/
private OWLOntology loadCompressedOntology(URL url, String catalogPath) throws IOException {
// Check for redirects
url = followRedirects(url);
// Open an input stream
InputStream is;
try {
is = new BufferedInputStream(url.openStream(), 1024);
} catch (FileNotFoundException e) {
throw new IOException(String.format(invalidOntologyIRIError, url));
}
GZIPInputStream gis = new GZIPInputStream(is);
return loadOntology(gis, catalogPath);
}
/**
* Given an ontology, a format, an IRI to save to, and a boolean indiciating to check OBO
* formatting, save the ontology in the given format to a file at the IRI.
*
* @param ontology OWLOntology to save
* @param format OWLDocumentFormat to save in
* @param ontologyIRI IRI to save to
* @param checkOBO boolean indicating to check OBO formatting
* @throws IOException on any problem
*/
private void saveOntologyFile(
final OWLOntology ontology, OWLDocumentFormat format, IRI ontologyIRI, boolean checkOBO)
throws IOException {
// first handle any non-official output formats.
// currently this is just OboGraphs JSON format
format.setParameter(OBODocumentFormat.VALIDATION, checkOBO);
if (format instanceof OboGraphJsonDocumentFormat) {
FromOwl fromOwl = new FromOwl();
GraphDocument gd;
try {
gd = fromOwl.generateGraphDocument(ontology);
} catch (Exception e) {
throw new IOException(oboGraphError);
}
File outfile = new File(ontologyIRI.toURI());
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();
writer.writeValue(new FileOutputStream(outfile), gd);
} else {
// use native save functionality
try {
ontology.getOWLOntologyManager().saveOntology(ontology, format, ontologyIRI);
} catch (OWLOntologyStorageException e) {
// Determine if its caused by an OBO Format error
if (format instanceof OBODocumentFormat
&& e.getCause() instanceof FrameStructureException) {
throw new IOException(
String.format(oboStructureError, e.getCause().getMessage()), e.getCause());
}
if (e.getCause() instanceof IllegalElementNameException) {
IllegalElementNameException e2 = (IllegalElementNameException) e.getCause();
String element = e2.getElementName();
if (isValidCURIE(element)) {
String prefix = element.split(":")[0];
throw new IOException(String.format(undefinedPrefixError, e2.getElementName(), prefix));
} else {
throw new IOException(String.format(invalidElementError, element));
}
}
throw new IOException(String.format(ontologyStorageError, ontologyIRI), e);
} catch (NullPointerException e) {
if (format instanceof OBODocumentFormat && !checkOBO) {
// Critical OBO structure error
throw new IOException(
"OBO STRUCTURE ERROR ontology cannot be saved in OBO format. Please use '--check true' to see cause.");
} else {
// Unknown null pointer exception
throw new IOException(String.format(ontologyStorageError, ontologyIRI), e);
}
}
}
}
/**
* Given a formatted ontology as a byte array and an IRI, save the data to the IRI as a gzipped
* file.
*
* @param data byte array of ontology
* @param ontologyIRI IRI to save to
* @throws IOException on any problem
*/
private void saveCompressedOntology(byte[] data, IRI ontologyIRI) throws IOException {
File f = new File(ontologyIRI.toURI());
boolean newFile = f.createNewFile();
FileOutputStream fos = new FileOutputStream(f);
BufferedOutputStream bos = new BufferedOutputStream(fos);
try (GZIPOutputStream gos = new GZIPOutputStream(bos)) {
gos.write(data, 0, data.length);
} catch (IOException e) {
if (!newFile) {
f.delete();
}
}
}
}