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
2 changes: 1 addition & 1 deletion build/interlis/JvmBuildInputs.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
public final class JvmBuildInputs {
public static final List<String> MODULES = List.of("interlis-model", "interlis-compiler",
"interlis-workspace", "interlis-repository", "interlis-projection", "interlis-documentation",
"interlis-transfer", "interlis-wasm-bridge", "interlis-compiler-cli", "interlis-lsp",
"interlis-transfer", "interlis-xtf23", "interlis-wasm-bridge", "interlis-compiler-cli", "interlis-lsp",
"interlis-compiler-gui", "interlis-verification");

public static void requireProject(Path root) throws Exception {
Expand Down
15 changes: 12 additions & 3 deletions build/interlis/ModulePathContract.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ public static String name(String project) {
public static void requireOwnDescriptor(String project, ModuleDescriptor descriptor) {
if(descriptor.isAutomatic()||descriptor.isOpen()||!descriptor.name().equals(name(project)))
throw new IllegalStateException("Own runtime module must be explicit and encapsulated: "+project);
if(project.equals("interlis-xtf23")) {
var required=descriptor.requires().stream().map(ModuleDescriptor.Requires::name).collect(java.util.stream.Collectors.toSet());
if(!required.equals(Set.of("java.base","java.xml","guru.interlis.model","guru.interlis.transfer"))
||descriptor.exports().size()!=1||descriptor.exports().stream().anyMatch(export->
export.isQualified()||!export.source().equals("guru.interlis.xtf23"))||!descriptor.opens().isEmpty())
throw new IllegalStateException("Unexpected XTF 2.3 module boundary: "+descriptor);
}
}
public static void run(Path root)throws Exception {
for(String project:JvmBuildInputs.MODULES)if(!Files.isRegularFile(root.resolve(project+"/src/main/java/module-info.java")))throw new IllegalStateException("Missing own module descriptor: "+project);
Expand All @@ -26,11 +33,12 @@ public static void run(Path root)throws Exception {
if(!result.explicitDescriptor()||!result.moduleName().orElseThrow().equals(name(project)))throw new IllegalStateException("Wrong own module: "+project);
own.add(name(project));
var runtime=ModuleArtifacts.runtime(root,project,outcome);
if(project.equals("interlis-transfer")) {
if(project.equals("interlis-transfer")||project.equals("interlis-xtf23")) {
String prefix="maven/guru.interlis/", version="/"+JvmBuildInputs.projectVersion(root);
var expected=Set.of(prefix+"interlis-transfer"+version,prefix+"interlis-model"+version);
var expected=new HashSet<>(Set.of(prefix+"interlis-transfer"+version,prefix+"interlis-model"+version));
if(project.equals("interlis-xtf23"))expected.add(prefix+project+version);
var actual=runtime.stream().map(ModuleArtifacts.Artifact::coordinates).collect(java.util.stream.Collectors.toSet());
if(!actual.equals(expected))throw new IllegalStateException("Unexpected transfer runtime closure: "+actual);
if(!actual.equals(expected))throw new IllegalStateException("Unexpected runtime closure for "+project+": "+actual);
}
for(var artifact:runtime) {
Path prior=files.putIfAbsent(artifact.fileName(),artifact.file());
Expand Down Expand Up @@ -71,6 +79,7 @@ public static void run(Path root)throws Exception {
"guru.interlis.projection","guru.interlis.projection.DiagramProjector",
"guru.interlis.documentation","guru.interlis.documentation.DocumentationProjector",
"guru.interlis.transfer","guru.interlis.transfer.TransferPlanner",
"guru.interlis.xtf23","guru.interlis.xtf23.Xtf23Reader",
"guru.interlis.wasm.bridge","guru.interlis.bridge.BridgeDispatcher");
String extra=String.join("\n",own.stream().filter(entrypoints::containsKey).map(name->
"if (named && !"+entrypoints.get(name)+".class.getModule().getName().equals(\""+name+"\")) throw new AssertionError(\"Wrong public entrypoint module\");").toList());
Expand Down
3 changes: 2 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ Die zugehörigen POMs beschreiben die konkreten Abhängigkeiten und Scopes.
| `interlis-projection` | compiler, model | Diagramm-DTOs und reine Projektion |
| `interlis-documentation` | model, projection | Lesende Dokumentationsprojektion und Rendering |
| `interlis-transfer` | model | Formatunabhängige Transferpläne, Objektwerte und Streaming-Protokoll |
| `interlis-xtf23` | transfer, model | XML-Adapter für INTERLIS 2.3 |
| `interlis-compiler-cli` | compiler, model, repository, workspace, projection | CLI, Prozessgrenzen, Präsentation und Ausgabe |
| `interlis-compiler-gui` | model, compiler, workspace, repository | Desktop-Modellprüfung, Diagnosen, Abhängigkeiten und Repositoryexplorer |
| `interlis-lsp` | workspace, compiler, projection, model, repository, documentation | LSP-/Editorhost, Scheduling und Ausgaben |
| `interlis-wasm-bridge` | workspace, compiler, projection, model | Gemeinsames JSON-Protokoll und Sitzungen |
| `interlis-verification` | compiler, model, workspace, repository, wasm-bridge | Prüfwerkzeuge ausserhalb der SDK-Auslieferung |

Alle zwölf JVM-Module werden über denselben Builder erzeugt. Die
Alle dreizehn JVM-Module werden über denselben Builder erzeugt. Die
jeweils ausgewählten JPMS-Closures stehen im
[Buildvertrag](development/build.md). TeaVM und Web Image konsumieren
denselben expliziten JVM-Snapshot. Browser und Editor teilen den neutralen
Expand Down
8 changes: 7 additions & 1 deletion docs/design/transfer-validation-database.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Entwurf für Transfer, Validierung und Datenbanken

Der formatunabhängige Transferkern steht in `interlis-transfer` für INTERLIS 2.3 und 2.4 zur Verfügung. Formatadapter, Datenvalidierung und Datenbankabbildung sind geplant. Die API-Skizzen beschreiben den vorgesehenen Ausbau.
Der formatunabhängige Transferkern steht in `interlis-transfer` für INTERLIS 2.3 und 2.4 zur Verfügung. `interlis-xtf23` liest und schreibt XTF-2.3-FULL-Transfers objektweise. Weitere Formatadapter, Datenvalidierung und Datenbankabbildung sind geplant. Die API-Skizzen beschreiben den vorgesehenen Ausbau.

Die Verantwortung wird entlang fachlicher Produkte geschnitten:

Expand Down Expand Up @@ -103,6 +103,12 @@ Ein `TransferObject` hält Identität, Typbezug und Attribut-/Rollenwerte eines

Der Kern vergleicht die Zeichenwerte innerhalb dieser Namensräume unabhängig von der Herkunft `SOURCE` oder `SYNTHETIC`. Die OID-Regeln für INITIAL und UPDATE gehören zum jeweiligen Transferprofil. Der XTF-2.3-Adapter muss für FULL zusätzlich BID und TID gemeinsam prüfen, auch wenn die gleichlautende Kennung zuerst als TID erscheint. In XTF 2.4 gilt die transferweite Eindeutigkeit der FULL-TIDs und die allgemeine Objektstromregel für Behälter- und Objektidentifikationen. Die Akzeptanz gleicher Zeichenwerte in den getrennten Namensräumen des Kerns trifft keine Aussage über die Gültigkeit einer konkreten XTF-Codierung. [Gepinnte Normbelege und P4.0-Befunde](../reference/iox-legacy.md#normbelege)

`Xtf23Reader` und `Xtf23Writer` verwenden vom Aufrufer verwaltete Streams. OIDSPACE-Deklarationen bleiben in Quellreihenfolge am Adapter verfügbar. Der FULL-Adapter liest und schreibt BLACKBOX XML und BINARY, OID-Attributwerte sowie CLASS-, STRUCTURE- und ATTRIBUTE-Pfadwerte. Modellgebundene Linienattribute bleiben an jeder einzelnen Randlinie von SURFACE und AREA erhalten. Nichtleere ALIAS-Tabellen, inkrementelle Transfers, CLIPPED-Geometrien und benutzerdefinierte Linienformen meldet der Adapter als `UNSUPPORTED`. Er prüft die XML- und Wire-Struktur. Datenconstraints und Geometrietopologie gehören zur späteren Validierung.

Die LINEATTR-Abnahme prüft den Reader-/Writer-Roundtrip gemäss INTERLIS 2.3 §§3.3.11.12–13. In ilivalidator 1.15.0 meldet der Standardreader von IOX 1.24.4 bei dieser Kodierung `expected segment type`. Mit `ILI_XTF23_READER=ch.interlis.iom_j.xtf.Xtf23Reader0` gelangt die Datei bis zur Polygonprüfung, die `Lineattributes not supported` meldet. Beide Validatorläufe liefern Exit 1.

Der XTF-2.3-Header führt mindestens ein Modell. Ein leerer Datenbereich ist zulässig. Ein vorhandener BAG- oder LIST-Wrapper enthält mindestens eine Struktur. XTF 2.3 lässt undefinierte Attribute aus und erhält leeren TEXT als eigenen Wert.

Das Datenmodell unterscheidet mindestens:

- nicht vorhandenen Slot beziehungsweise Wert;
Expand Down
2 changes: 1 addition & 1 deletion docs/development/build.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build und Toolchains

Jenesis 0.18.1 erzeugt die zwölf JVM-Module mit Java 25 ohne Preview. Die POMs
Jenesis 0.18.1 erzeugt die dreizehn JVM-Module mit Java 25 ohne Preview. Die POMs
definieren Group, Artifact, Version, Scopes und Pins. Der Upstreampin gehört
zu den [Jenesis-Quellen](../../build/JENESIS-UPSTREAM.json). Der gepinnte
Gradle-Wrapper baut ausschliesslich TeaVM und GraalVM Web Image.
Expand Down
15 changes: 15 additions & 0 deletions docs/reference/iox-legacy.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,21 @@ abgeschalteten Regeln.
[Shapefile #45][wkf-45], [Shapefile #46][wkf-46], [GeoPackage-Tests][wkf-gpkg],
[Db2Ili #27][wkf-27].

### Linienattribute in XTF 2.3

Das Referenzhandbuch 2.3 beschreibt `LINEATTR` vor dem Startsegment
(§ 3.3.11.12) und verlangt das Strukturelement in jeder Randlinie einer
entsprechend modellierten SURFACE oder AREA (§ 3.3.11.13). Mit ilivalidator
1.15.0 und iox-ili 1.24.4 endet ein so kodierter Transfer im Standardreader
mit Exit 1 und `expected segment type`. Mit
`ILI_XTF23_READER=ch.interlis.iom_j.xtf.Xtf23Reader0` erreicht derselbe
Transfer die Geometriekonvertierung und endet mit Exit 1 und
`Lineattributes not supported`. Die Diagnosen belegen Grenzen der
Referenzwerkzeuge. Der Adapter prüft die Kodierung und die Werterhaltung
durch Roundtrips.
[Standardreader](https://github.com/claeis/iox-ili/blob/8e966a086a72e7e2990a312b1229ee5878fd5994/src/main/java/ch/interlis/iom_j/xtf/Xtf23Reader.java#L1200),
[Geometriekonvertierung](https://github.com/claeis/iox-ili/blob/8e966a086a72e7e2990a312b1229ee5878fd5994/src/main/java/ch/interlis/iox_j/jts/Iox2jtsext.java#L192).

## Validierungsphasen und Rollen

ilivalidator liest die Eingaben und gegebenenfalls Referenzdaten, steuert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,22 @@ record MultiCoordinate(List<Coordinate> coordinates) implements TransferGeometry
/**
* A polyline with its parts in source order. All parts have the same dimension.
* An empty list represents a supplied line with no parts, as in an empty extract.
*
* @param parts nonnull immutable sequence of curve parts
* @param lineAttributes nonnull optional model-bound structure for this line
*/
record Polyline(List<CurvePart> parts) implements TransferGeometry {
record Polyline(List<CurvePart> parts, Optional<TransferValue.Structured> lineAttributes) implements TransferGeometry {
public Polyline {
parts = List.copyOf(parts);
Objects.requireNonNull(lineAttributes, "lineAttributes");
requireHomogeneousDimensions(parts, CurvePart::dimensions);
}

/** Creates a line without a line-attribute structure. */
public Polyline(List<CurvePart> parts) {
this(parts, Optional.empty());
}

private int dimensions() {
return parts.isEmpty() ? 0 : parts.getFirst().dimensions();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public Optional<TransferValue> value(SymbolRef slot) {
private static void requireSnapshot(TransferValue.Structured data) {
var snapshot = data.type().snapshot();
var seen = Collections.newSetFromMap(new IdentityHashMap<TransferValue, Boolean>());
var pending = new ArrayDeque<Iterator<TransferValue>>();
var pending = new ArrayDeque<Iterator<? extends TransferValue>>();
pending.push(data.values().values().iterator());
while (!pending.isEmpty()) {
var fields = pending.peek();
Expand All @@ -61,6 +61,27 @@ private static void requireSnapshot(TransferValue.Structured data) {
pending.push(repeated.values().iterator());
}
}
case TransferGeometry.Polyline line -> {
if (seen.add(line)) {
pending.push(line.lineAttributes().stream().iterator());
}
}
case TransferGeometry.MultiPolyline multi -> {
if (seen.add(multi)) {
pending.push(multi.lines().iterator());
}
}
case TransferGeometry.Surface surface -> {
if (seen.add(surface)) {
pending.push(surface.shell().lines().iterator());
pending.push(surface.holes().stream().flatMap(ring -> ring.lines().stream()).iterator());
}
}
case TransferGeometry.MultiSurface multi -> {
if (seen.add(multi)) {
pending.push(multi.surfaces().iterator());
}
}
default -> { }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ private void projectObject(SymbolRef model, Optional<SymbolRef> topic, Symbol de
private void checkType(TypeDescriptor type, QualifiedName path) {
switch (type.kind()) {
case TEXT, BOOLEAN, NUMERIC, ENUMERATION, ALIGNMENT, FORMATTED, OID,
REFERENCE, STRUCTURE_VALUE, GENERIC, DEFERRED -> { }
BLACKBOX, CLASS_VALUE, ATTRIBUTE_PATH, REFERENCE, STRUCTURE_VALUE, GENERIC, DEFERRED -> { }
case COLLECTION -> checkType(known(queries.type(typeRef(((CollectionType) type).elementType())), path), path);
case DOMAIN_REFERENCE -> checkType(typeOf(ref(((DomainReferenceType) type).targetDomain()), path), path);
case COORDINATE -> {
Expand All @@ -247,9 +247,12 @@ private void checkGeometry(GeometryType geometry, QualifiedName path) {
issues.add(new Issue(path, Feature.CUSTOM_LINE_FORM));
}
}
if (geometry.lineAttributes().isPresent()) {
issues.add(new Issue(path, Feature.LINE_ATTRIBUTES));
}
geometry.lineAttributes().ifPresent(id -> {
// The structure's effective fields are checked by projectObject, as for other structures.
if (!(known(queries.symbol(ref(id)), path) instanceof StructureSymbol)) {
issues.add(new Issue(path, Feature.LINE_ATTRIBUTES));
}
});
geometry.vertexDomain().ifPresent(vertex -> checkType(typeOf(ref(vertex), path), path));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package guru.interlis.transfer;

import guru.interlis.model.CollectionKind;
import guru.interlis.model.QualifiedName;
import guru.interlis.model.SnapshotMismatchException;
import guru.interlis.model.SymbolRef;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
Expand All @@ -17,7 +19,56 @@
* omitting its slot. Values and collection entries are nonnull. */
public sealed interface TransferValue permits TransferValue.Text, TransferValue.BooleanValue,
TransferValue.Enumeration, TransferValue.NumberValue, TransferValue.Structured,
TransferValue.Repeated, TransferValue.Reference, TransferValue.Undefined, TransferGeometry {
TransferValue.Repeated, TransferValue.Reference, TransferValue.Binary, TransferValue.XmlFragment,
TransferValue.Oid, TransferValue.ClassName, TransferValue.AttributePath,
TransferValue.Undefined, TransferGeometry {

/** Binary payload with content equality. The constructor and accessor copy the bytes. */
record Binary(byte[] bytes) implements TransferValue {
public Binary {
bytes = bytes.clone();
}

@Override public byte[] bytes() { return bytes.clone(); }
/** Returns the payload length in bytes without copying it. */
public int size() { return bytes.length; }
@Override public boolean equals(Object other) {
return other instanceof Binary binary && Arrays.equals(bytes, binary.bytes);
}
@Override public int hashCode() { return Arrays.hashCode(bytes); }
}

/** Serialized XML fragment, possibly empty, with any necessary namespace declarations.
* Consumers must parse and validate it before emitting XML. */
record XmlFragment(String xml) implements TransferValue {
public XmlFragment {
Objects.requireNonNull(xml, "xml");
}
}

/** Exact identifier spelling of an attribute value, including any identifier space prefix. */
record Oid(String lexeme) implements TransferValue {
public Oid {
if (Objects.requireNonNull(lexeme, "lexeme").isBlank()) {
throw new IllegalArgumentException("An OID needs its lexical spelling");
}
}
}

/** Qualified declaration name in a CLASS or STRUCTURE slot.
* The slot's type determines the declaration family and any restrictions. */
record ClassName(QualifiedName name) implements TransferValue {
public ClassName {
Objects.requireNonNull(name, "name");
}
}

/** Qualified owner name followed by its attribute name. */
record AttributePath(QualifiedName name) implements TransferValue {
public AttributePath {
Objects.requireNonNull(name, "name");
}
}

/** Exact text, including the empty string. */
record Text(String value) implements TransferValue {
Expand Down

This file was deleted.

Loading
Loading