Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
</scm>

<dependencies>
<dependency>
<groupId>org.incenp</groupId>
<artifactId>linkml-core</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
Expand Down
59 changes: 59 additions & 0 deletions core/src/main/java/org/incenp/obofoundry/kgcl/KGCLHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
import java.io.IOException;
import java.io.StringReader;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.incenp.linkml.core.ConverterContext;
import org.incenp.linkml.core.LinkMLRuntimeException;
import org.incenp.obofoundry.kgcl.model.Change;
import org.incenp.obofoundry.kgcl.owl.OntologyPatcher;
import org.incenp.obofoundry.kgcl.owl.ProvisionalOWLTranslator;
Expand All @@ -33,6 +36,9 @@
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.util.DefaultPrefixManager;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

/**
* A class providing static helper methods to work with KGCL.
*/
Expand Down Expand Up @@ -210,6 +216,59 @@ private static List<Change> doParse(KGCLReader reader, PrefixManager prefixManag
return reader.getChangeSet();
}

/**
* Parses a changeset serialised in YAML form.
*
* @param kgcl The YAML file from which to read the changeset. That file is
* expected to contain a <em>list</em> of KGCL change objects.
* @param prefixMap A map of prefix names to prefix IRIs. May be {@code null}.
* @return A KGCL changeset.
* @throws IOException If any error occurs. Note that, contrary to the methods
* that read from the KGCL controlled natural language, here
* this includes post-I/O errors caused by invalid KGCL
* contents within the source file.
*/
public static List<Change> parseYAML(File kgcl, Map<String, String> prefixMap) throws IOException {
List<Change> changeset = new ArrayList<>();

ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
@SuppressWarnings("unchecked")
List<Object> rawChanges = mapper.readValue(kgcl, List.class);

ConverterContext ctx = new ConverterContext();
if ( prefixMap != null ) {
prefixMap.forEach(ctx::addPrefix);
}
try {
for ( Object rawChange : rawChanges ) {
changeset.add((Change) ctx.getConverter(Change.class).convert(rawChange, ctx));
}
ctx.finalizeAssignments();
} catch ( LinkMLRuntimeException e ) {
throw new IOException("Cannot read KGCL file: invalid content", e);
}

return changeset;
}

/**
* Parses a changeset serialised in YAML form.
*
* @param kgcl The YAML file from which to read the changeset. That
* file is expected to contain a <em>list</em> of KGCL
* change objects.
* @param prefixManager A prefix manager to expand CURIEs into IRIs. May be
* {@code null}.
* @return A KGCL changeset.
* @throws IOException If any error occurs. Note that, contrary to the methods
* that read from the KGCL controlled natural language, here
* this includes post-I/O errors caused by invalid KGCL
* contents within the source file.
*/
public static List<Change> parseYAML(File kgcl, PrefixManager prefixManager) throws IOException {
return parseYAML(kgcl, prefixManager != null ? prefixManager.getPrefixName2PrefixMap() : null);
}

/**
* Gets the "pending" (provisional) changes that are stored as KGCL annotations
* in the ontology,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,22 @@ public String visit(NodeDeletion v) {

@Override
public String visit(NodeCreation v) {
return String.format("create %s %s %s", v.getAboutNode().getOwlType(), renderNode(v.getAboutNode()),
String nodeType = null;
switch ( v.getAboutNode().getOwlType() ) {
case ANNOTATION_PROPERTY:
nodeType = "annotation property";
break;
case CLASS:
nodeType = "class";
break;
case NAMED_INDIVIDUAL:
nodeType = "instance";
break;
case OBJECT_PROPERTY:
nodeType = "relation";
break;
}
return String.format("create %s %s %s", nodeType, renderNode(v.getAboutNode()),
renderNewValue(v));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* KGCL-Java - KGCL library for Java
* Copyright © 2026 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;

import java.util.Map;

import org.incenp.linkml.core.ConverterContext;
import org.incenp.linkml.core.LinkMLRuntimeException;
import org.incenp.linkml.core.ObjectConverter;
import org.incenp.obofoundry.kgcl.model.SimpleChange;

/**
* A custom LinkML converter for SimpleChange objects.
* <p>
* A custom converter is needed here because, even though the
* <code>new_value</code> and <code>old_value</code> slots of the
* {@link SimpleChange} class are typed as strings, they are sometimes expected
* to contain CURIEs, when the value that is being affected by the change is
* itself an IRI (for example, when the change is a
* <code>PredicateChange</code>).
* <p>
* When that happens, the fact that <code>old_value</code> (resp.
* <code>new_value</code>) is a CURIE is indicated by the
* <code>old_value_type</code> (resp. <code>new_value_type</code>) slot, which
* is set to <code>curie</code>. This is a KGCL-specific mechanism that cannot
* be handled generically at the level of the LinkML runtime, so we must deal
* with it here.
* <p>
* An alternative option would be to deal with those values in a post-parsing
* step, in which we iterate over SimpleChange-typed changes and expand their
* <code>old_value</code> / <code>new_value</code> slots as needed. But custom
* converters offer a nice way of ensuring that expansion is done for us during
* the parsing phase.
*/
public class SimpleChangeConverter extends ObjectConverter {

public SimpleChangeConverter(Class<SimpleChange> type) {
super(type);
}

@Override
public void convertTo(Map<String, Object> raw, Object dest, ConverterContext ctx) throws LinkMLRuntimeException {
// Let the default converter do most of the job
super.convertTo(raw, dest, ctx);

// Then expand the old_value/new_value slots if needed
SimpleChange c = (SimpleChange) dest;
if ( c.getOldValueType() != null && c.getOldValueType().equals("curie") && c.getOldValue() != null ) {
c.setOldValue(ctx.resolve(c.getOldValue()));
}
if ( c.getNewValueType() != null && c.getNewValueType().equals("curie") && c.getNewValue() != null ) {
c.setNewValue(ctx.resolve(c.getNewValue()));
}
}

@Override
public Map<String, Object> serialise(Object object, boolean withIdentifier, ConverterContext ctx)
throws LinkMLRuntimeException {
// Same approach as above: we let the default converter do its job
Map<String, Object> serialised = super.serialise(object, withIdentifier, ctx);

// Then we overwrite the new/old_value slots if needed
SimpleChange c = (SimpleChange) object;
if ( c.getOldValueType() != null && c.getOldValueType().equals("curie") && c.getOldValue() != null ) {
serialised.put("old_value", ctx.compact(c.getOldValue()));
}
if ( c.getNewValueType() != null && c.getNewValueType().equals("curie") && c.getNewValue() != null ) {
serialised.put("new_value", ctx.compact(c.getNewValue()));
}

return serialised;
}
}
67 changes: 0 additions & 67 deletions core/src/main/java/org/incenp/obofoundry/kgcl/model/OwlType.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ public List<OWLOntologyChange> visit(NodeCreation v) {
addedClasses.add(nodeIRI);
}
break;
case NAMED_INVIDIDUAL:
case NAMED_INDIVIDUAL:
if ( ontology.containsIndividualInSignature(nodeIRI) ) {
onReject(v, "Invididual <%s> already exists", nodeIRI.toString());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,23 @@ public Void visitChangeDefinition(KGCLParser.ChangeDefinitionContext ctx) {
@Override
public Void visitNewNode(KGCLParser.NewNodeContext ctx) {
NodeCreation change = null;
OwlType type = OwlType.fromString(ctx.nodeType().getText());
OwlType type = null;
switch (ctx.nodeType().getText()) {
case "class":
type = OwlType.CLASS;
break;
case "relation":
type = OwlType.OBJECT_PROPERTY;
break;

case "instance":
type = OwlType.NAMED_INDIVIDUAL;
break;

case "annotation property":
type = OwlType.ANNOTATION_PROPERTY;
break;
}

switch ( type ) {
case CLASS:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
package org.incenp.obofoundry.kgcl.model;

import java.time.ZonedDateTime;
import java.util.List;

import lombok.Data;
import lombok.EqualsAndHashCode;

import org.incenp.linkml.core.annotations.Converter;
import org.incenp.linkml.core.annotations.Identifier;
import org.incenp.linkml.core.annotations.Inlined;
import org.incenp.linkml.core.annotations.SlotName;
import org.incenp.linkml.core.annotations.TypeDesignator;
import org.incenp.linkml.core.CurieConverter;

/**
* a provence-generating activity
*/
@Data
@EqualsAndHashCode(callSuper=false)
public class Activity extends ProvElement {
@Identifier
@Converter(CurieConverter.class)
private String id;
@SlotName("started_at_time")
private String startedAtTime;
@SlotName("ended_at_time")
private String endedAtTime;
@SlotName("was_informed_by")
private Activity wasInformedBy;
@SlotName("was_associated_with")
private Agent wasAssociatedWith;
private String used;
private String description;}
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
package org.incenp.obofoundry.kgcl.model;

import java.time.ZonedDateTime;
import java.util.List;

import lombok.Data;
import lombok.EqualsAndHashCode;

import org.incenp.linkml.core.annotations.Converter;
import org.incenp.linkml.core.annotations.Identifier;
import org.incenp.linkml.core.annotations.Inlined;
import org.incenp.linkml.core.annotations.SlotName;
import org.incenp.linkml.core.annotations.TypeDesignator;
import org.incenp.linkml.core.CurieConverter;

/**
* Places a node inside a subset, by annotating that node
*/
@Data
@EqualsAndHashCode(callSuper=true)
public class AddNodeToSubset extends NodeChange {
@SlotName("in_subset")
private OntologySubset inSubset;
private OntologyElement about;
public <T> T accept(IChangeVisitor<T> v) {
return v.visit(this);
}
Expand Down

This file was deleted.

Loading