SchemaImportMojo.java
/*
* Copyright © 2026 IKE Network (support@ike.network)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package network.ike.plugin;
import network.ike.plugin.schema.LedgerSectionWriter;
import network.ike.plugin.schema.SchemaSignature;
import network.ike.plugin.schema.XmlSchemaReader;
import org.apache.maven.api.di.Inject;
import org.apache.maven.api.plugin.MojoException;
import org.apache.maven.api.plugin.annotations.Mojo;
import org.apache.maven.api.plugin.annotations.Parameter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Imports an XML Schema as a ledger section, a node catalog (IKE-Network/ike-issues#1104): reads
* the schema files, one catalog across all of them, and writes a KnowledgeSet section
* class in which every named type, argument position, schema primitive, and enumerated
* value is a concept, with the schema's own documentation as each definition and the
* extension base as the is-a. The first use is HL7's ELM, the Expression Logical Model of
* the CQL specification, whose catalog is five schemas.
*
* <p>The goal runs on demand, not in a lifecycle phase: the section it writes is source to
* check in and regenerate when the pinned schema release changes, never to edit. It needs
* no store and no fork; the JDK's XML support is enough.
*/
@Mojo(name = IkeGoal.NAME_SCHEMA_IMPORT)
public class SchemaImportMojo implements org.apache.maven.api.plugin.Mojo {
/** Creates the goal. */
public SchemaImportMojo() {
}
@Inject
private org.apache.maven.api.plugin.Log log;
/** The schema files, comma-separated paths, read in the order given. */
@Parameter(property = "ike.schemaImport.schemas", required = true)
String schemas;
/** The source root the section is written under, by default the project's main sources. */
@Parameter(property = "ike.schemaImport.outputDirectory",
defaultValue = "${project.basedir}/src/main/java")
String outputDirectory;
/** The generated class's package. */
@Parameter(property = "ike.schemaImport.packageName", required = true)
String packageName;
/** The generated class's simple name, for example {@code ElmSignatureSet}. */
@Parameter(property = "ike.schemaImport.className", required = true)
String className;
/** The fully-qualified-name tag written in parentheses after every label, for example {@code ELM}. */
@Parameter(property = "ike.schemaImport.tag", required = true)
String tag;
/** The prefix every label carries; by default the tag followed by a space. */
@Parameter(property = "ike.schemaImport.labelPrefix")
String labelPrefix;
/**
* The label prefix per namespace, as {@code namespace=prefix} pairs separated by
* semicolons, for example {@code urn:hl7-org:elm-types:r1=ELM System }; a namespace
* not listed uses the default label prefix.
*/
@Parameter(property = "ike.schemaImport.namespacePrefixes")
String namespacePrefixes;
/** Whose definitions these are, for example {@code the ELM specification}. */
@Parameter(property = "ike.schemaImport.attribution", required = true)
String attribution;
/** The release the schemas were taken from, for example a repository tag. */
@Parameter(property = "ike.schemaImport.pin", required = true)
String pin;
/** The Java expression of the inception stamp the section declares at. */
@Parameter(property = "ike.schemaImport.stampExpression", defaultValue = "Ike.INCEPTION")
String stampExpression;
/** The class holding the field data-type constants the pattern uses. */
@Parameter(property = "ike.schemaImport.termsClass", defaultValue = "IkeTerm")
String termsClass;
/** The Java expression of the family root's parent concept. */
@Parameter(property = "ike.schemaImport.rootParentExpression", defaultValue = "IkeTerm.MODEL_CONCEPT")
String rootParentExpression;
/** Skips the goal. */
@Parameter(property = "ike.schemaImport.skip", defaultValue = "false")
boolean skip;
@Override
public void execute() {
if (skip) {
log.info("ike:schema-import skipped (ike.schemaImport.skip=true)");
return;
}
List<Path> files = new ArrayList<>();
for (String schema : schemas.split(",")) {
String trimmed = schema.trim();
if (!trimmed.isEmpty()) {
Path path = Path.of(trimmed);
if (!Files.isRegularFile(path)) {
throw new MojoException("Schema file not found: " + path);
}
files.add(path);
}
}
if (files.isEmpty()) {
throw new MojoException("ike.schemaImport.schemas names no schema file");
}
String prefix = labelPrefix == null || labelPrefix.isEmpty() ? tag + " " : labelPrefix;
SchemaSignature signature;
try {
signature = new XmlSchemaReader().read(files);
} catch (IOException e) {
throw new MojoException("Cannot read the schemas: " + e.getMessage(), e);
}
Map<String, String> prefixes = new LinkedHashMap<>();
if (namespacePrefixes != null && !namespacePrefixes.isBlank()) {
for (String pair : namespacePrefixes.split(";")) {
int equals = pair.indexOf('=');
if (equals <= 0) {
throw new MojoException("ike.schemaImport.namespacePrefixes entry is not namespace=prefix: " + pair);
}
prefixes.put(pair.substring(0, equals).trim(), pair.substring(equals + 1));
}
}
String source = LedgerSectionWriter.write(signature, new LedgerSectionWriter.Options(
packageName, className, tag, prefix, prefixes, attribution, pin, stampExpression, termsClass,
rootParentExpression));
Path target = Path.of(outputDirectory).resolve(packageName.replace('.', '/')).resolve(className + ".java");
try {
Files.createDirectories(target.getParent());
Files.writeString(target, source, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new MojoException("Cannot write " + target + ": " + e.getMessage(), e);
}
log.info("Wrote " + target + ": " + signature.types().size() + " types and "
+ signature.enumerations().size() + " enumerations from " + files.size()
+ " schema file(s), pinned to " + pin);
}
}