Skip to content

Commit a0b47b0

Browse files
committed
Preocess prefix declarations early on.
For now, we only use the prefixes declared in a schema to resolve the imports, which can be done after a SchemaDefinition has been completed converted. But at some point, we will likely need to have those prefix declarations while we are still in the process of converting the SchemaDefinition (e.g. to resolve the values of the `class_uri` or `slot_uri` slots, which we currently do not support but that we will have to support soon). So instead of using the generic ObjectConverter to convert SchemaDefinition, we use a specialised derived class in which we proactively look for the `prefixes` slot and process (convert) it immediately at the beginning of the conversion, before processing any other slot. This will ensure that all prefix declarations are known to the ConverterContext when all the other slots are processed in turn.
1 parent 5a93863 commit a0b47b0

4 files changed

Lines changed: 77 additions & 5 deletions

File tree

core/src/main/java/org/incenp/linkml/core/ConverterContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ public void finalizeAssignments() throws LinkMLRuntimeException {
282282
*/
283283
public static ConverterContext getLinkMLContext() {
284284
ConverterContext ctx = new ConverterContext();
285-
ctx.addConverter(SchemaDefinition.class);
285+
ctx.addConverter(SchemaDefinition.class, new SchemaDefinitionConverter());
286286
ctx.addConverter(TypeDefinition.class);
287287
ctx.addConverter(EnumDefinition.class);
288288
ctx.addConverter(SlotDefinition.class);

core/src/main/java/org/incenp/linkml/core/ObjectConverter.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ public ObjectConverter(Class<?> klass) {
6363
}
6464
}
6565

66+
/**
67+
* Gets the specified slot.
68+
* <p>
69+
* This is mostly intended for the benefit of derived classes, so that they do
70+
* not have to call {@link Slot#getSlot(Class, String)} themselves (which would
71+
* be a duplication of the job done in the constructor of this class).
72+
*
73+
* @param name The name of the slot.
74+
* @return The corresponding slot, or <code>null</code> if the class for which
75+
* this object is a converter does not have any slot with that name.
76+
*/
77+
protected Slot getSlot(String name) {
78+
return slots.get(name);
79+
}
80+
6681
/**
6782
* Indicates whether this converter converts raw objects into “identifiable
6883
* objects”.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* LinkML-Java - LinkML library for Java
3+
* Copyright © 2026 Damien Goutte-Gattat
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
package org.incenp.linkml.core;
20+
21+
import java.util.Map;
22+
23+
import org.incenp.linkml.model.SchemaDefinition;
24+
25+
/**
26+
* A converter object specifically intended to convert {@link SchemaDefinition}
27+
* objects.
28+
* <p>
29+
* We need a subclass for {@link SchemaDefinition} objects because we want to
30+
* ensure that prefix declarations are processed early on when converting the
31+
* schema object, so that they can be made available to the converter context as
32+
* soon as possible.
33+
* <p>
34+
* We cannot rely on the fact that the <code>prefixes</code> block is typically
35+
* somewhere near the beginning of a LinkML schema, because (1) there is no
36+
* guarantee of that (the LinkML spec does not mandate an order for any of the
37+
* keys that make up the SchemaDefinition class), and (2) in any case since the
38+
* YAML object has been transformed into a Map, we have no guarantee that we
39+
* iterate over the keys in the same order than they appear in the source file.
40+
*/
41+
public class SchemaDefinitionConverter extends ObjectConverter {
42+
43+
private final static String PREFIXES_KEY = "prefixes";
44+
45+
public SchemaDefinitionConverter() {
46+
super(SchemaDefinition.class);
47+
}
48+
49+
@Override
50+
public void convertTo(Map<String, Object> rawMap, Object dest, ConverterContext ctx) throws LinkMLRuntimeException {
51+
// Pre-process the prefixes slot before everything else
52+
Object prefixes = rawMap.remove(PREFIXES_KEY);
53+
if ( prefixes != null ) {
54+
Slot slot = getSlot(PREFIXES_KEY);
55+
convertSlot(prefixes, slot, dest, ctx);
56+
ctx.addPrefixes(((SchemaDefinition) dest).getPrefixes());
57+
}
58+
59+
super.convertTo(rawMap, dest, ctx);
60+
}
61+
}

core/src/main/java/org/incenp/linkml/core/SchemaDocument.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,6 @@ private SchemaDefinition parseSchema(ISchemaSource source, ObjectMapper mapper,
115115
throw new InvalidSchemaException(INVALID_LINKML, e);
116116
}
117117

118-
if ( schema.getPrefixes() != null ) {
119-
ctx.addPrefixes(schema.getPrefixes());
120-
}
121-
122118
importedSources.add(source);
123119
if ( schema.getImports() != null ) {
124120
for ( String importName : schema.getImports() ) {

0 commit comments

Comments
 (0)