- * The CIM version is identified by the namespace prefix URI used in the RDF representation of CIM.
- *
- * See also CIM 16,
- * CIM 17, and
- * CIM 18.
- */
-public enum CimVersion {
- /** No CIM version specified */
- NO_CIM,
- /**
- * CIM version 16.
- * This version is used in CGMES v2.4.15
- */
- CIM_16,
- /**
- * CIM version 17.
- * This version is used in CGMES v3.0
- * */
- CIM_17,
- /**
- * CIM version 18.
- * There is no matching version of CGMES yet.
- */
- CIM_18;
-
- /**
- * Get the CIM version for a given CIM namespace.
- *
- * @param namespace The CIM namespace prefix URI. Must not be {@code null}.
- * @return The corresponding CIM version, or {@link #NO_CIM} if the namespace is not recognized.
- * @throws NullPointerException if {@code namespace} is {@code null}.
- */
- public static CimVersion fromCimNamespace(String namespace) {
- Objects.requireNonNull(namespace, "namespace");
- return switch(namespace) {
- case "http://iec.ch/TC57/2013/CIM-schema-cim16#" -> CimVersion.CIM_16;
- case "http://iec.ch/TC57/CIM100#" -> CimVersion.CIM_17;
- case "https://cim.ucaiug.io/ns#" -> CimVersion.CIM_18;
- default -> CimVersion.NO_CIM;
- };
- }
-}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/CimXmlDocumentContext.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/CimXmlDocumentContext.java
index 3402a495..5dd77246 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/CimXmlDocumentContext.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/CimXmlDocumentContext.java
@@ -22,30 +22,31 @@
import org.apache.jena.sparql.core.Quad;
/**
- * The context of a CIMXML document: full model, difference model, or one of the named graphs
- * in a difference model.
+ * The context of a CIMXML document: full model, difference model, or one of the named graphs in a
+ * difference model.
*/
public enum CimXmlDocumentContext {
- fullModel,
- body,
- differenceModel,
- forwardDifferences,
- reverseDifferences,
- preconditions;
+ fullModel,
+ body,
+ differenceModel,
+ forwardDifferences,
+ reverseDifferences,
+ preconditions;
- /**
- * Get the graph name (Node) for the given context.
- * @param context the context
- * @return the graph name (Node)
- */
- public static Node getGraphName(CimXmlDocumentContext context) {
- return switch (context) {
- case fullModel -> CimHeaderVocabulary.TYPE_FULL_MODEL;
- case body -> Quad.defaultGraphIRI;
- case differenceModel -> CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL;
- case forwardDifferences -> CimHeaderVocabulary.GRAPH_FORWARD_DIFFERENCES;
- case reverseDifferences -> CimHeaderVocabulary.GRAPH_REVERSE_DIFFERENCES;
- case preconditions -> CimHeaderVocabulary.GRAPH_PRECONDITIONS;
- };
- }
+ /**
+ * Get the graph name (Node) for the given context.
+ *
+ * @param context the context
+ * @return the graph name (Node)
+ */
+ public static Node getGraphName(CimXmlDocumentContext context) {
+ return switch (context) {
+ case fullModel -> CimHeaderVocabulary.TYPE_FULL_MODEL;
+ case body -> Quad.defaultGraphIRI;
+ case differenceModel -> CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL;
+ case forwardDifferences -> CimHeaderVocabulary.GRAPH_FORWARD_DIFFERENCES;
+ case reverseDifferences -> CimHeaderVocabulary.GRAPH_REVERSE_DIFFERENCES;
+ case preconditions -> CimHeaderVocabulary.GRAPH_PRECONDITIONS;
+ };
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimGraph.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimGraph.java
index c634c376..c2bde564 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimGraph.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimGraph.java
@@ -18,39 +18,36 @@
package de.soptim.opencgmes.cimxml.graph;
-import de.soptim.opencgmes.cimxml.CimVersion;
-import org.apache.jena.graph.Graph;
-
import java.util.Objects;
+import org.apache.jena.graph.Graph;
/**
- * A specialization of {@link Graph} that provides methods to determine the CIM version
- * of the graph based on its namespace prefixes.
+ * A specialization of {@link Graph} that provides methods to determine the 'cim' namespace of the
+ * graph based on its namespace prefixes.
*/
public interface CimGraph extends Graph {
- /**
- * Determines the CIM version of this graph based on its namespace prefixes.
- * If the graph does not use a CIM namespace, {@link CimVersion#NO_CIM} is returned.
- * @return The CIM version of the graph, or {@link CimVersion#NO_CIM} if no CIM namespace is used.
- */
- default CimVersion getCIMVersion() {
- return getCIMXMLVersion(this);
- }
-
- /**
- * Determines the CIM version of the given graph based on its namespace prefixes.
- * If the graph does not use a CIM namespace, {@link CimVersion#NO_CIM} is returned.
- * @param graph The graph to determine the CIM version for. Must not be null.
- * @return The CIM version of the graph, or {@link CimVersion#NO_CIM} if no CIM namespace is used.
- * @throws NullPointerException if the graph is null.
- */
- static CimVersion getCIMXMLVersion(Graph graph) {
- Objects.requireNonNull(graph, "graph");
- var cimURI = graph.getPrefixMapping().getNsPrefixURI("cim");
- if (cimURI == null)
- return CimVersion.NO_CIM;
+ /**
+ * Get the CIM namespace URI associated with the 'cim' prefix in the given graph's prefix
+ * mapping.
+ *
+ * @param graph the graph from which to retrieve the CIM namespace
+ * @return the CIM namespace URI or null if the 'cim' prefix is not defined in the graph's prefix
+ * mapping
+ * @throws NullPointerException if the graph is null
+ */
+ static String getCimNs(Graph graph) {
+ Objects.requireNonNull(graph, "graph");
+ return graph.getPrefixMapping().getNsPrefixURI("cim");
+ }
- return CimVersion.fromCimNamespace(graph.getPrefixMapping().getNsPrefixURI("cim"));
- }
+ /**
+ * Get the CIM namespace URI associated with the 'cim' prefix in the graph's prefix mapping.
+ *
+ * @return the CIM namespace URI or null if the 'cim' prefix is not defined in the graph's prefix
+ * mapping
+ */
+ default String getCimNamespace() {
+ return getCimNs(this);
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimModelHeader.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimModelHeader.java
index d9871841..1fa87b2e 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimModelHeader.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimModelHeader.java
@@ -19,121 +19,130 @@
package de.soptim.opencgmes.cimxml.graph;
import de.soptim.opencgmes.cimxml.CimHeaderVocabulary;
-import de.soptim.opencgmes.cimxml.CimVersion;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.sparql.graph.GraphWrapper;
import org.apache.jena.vocabulary.RDF;
-import java.util.Set;
-import java.util.stream.Collectors;
-
/**
* A wrapper for a graph that contains the model header information of a CIMXML document.
- *
- * The model header is expected to contain exactly one instance of either md:FullModel or dm:DifferenceModel.
- * It may contain zero or more "Model.profile" references, each referencing one of the registered profile ontologies.
- * It may also contain zero or more Model.Supersedes and Model.DependentOn references to other models.
+ *
+ *
The model header is expected to contain exactly one instance of either md:FullModel or
+ * dm:DifferenceModel. It may contain zero or more "Model.profile" references, each referencing one
+ * of the registered profile ontologies. It may also contain zero or more Model.Supersedes and
+ * Model.DependentOn references to other models.
*/
public interface CimModelHeader extends CimGraph {
- /**
- * Checks if the model is a full model.
- * @return true if the model is a full model, false otherwise.
- */
- default boolean isFullModel() {
- return find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_FULL_MODEL).hasNext();
+ /**
+ * Wraps a given graph as a {@link CimModelHeader}. If the graph is already an instance of
+ * {@link CimModelHeader}, it is returned as is. If the graph is null, null is returned. If the
+ * graph does not appear to be a CIM graph (no 'cim' namespace defined), an
+ * IllegalArgumentException is thrown.
+ *
+ * @param graph The graph to wrap.
+ * @return The wrapped graph as a {@link CimModelHeader}, or null if the input graph is null.
+ * @throws IllegalArgumentException if the graph does not appear to be a CIM graph.
+ */
+ static CimModelHeader wrap(Graph graph) {
+ if (graph == null) {
+ return null;
}
-
- /**
- * Checks if the model is a difference model.
- * @return true if the model is a difference model, false otherwise.
- */
- default boolean isDifferenceModel() {
- return find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL).hasNext();
+ if (graph instanceof CimModelHeader cimModelHeader) {
+ return cimModelHeader;
}
-
- /**
- * Get the node representing the model (either md:FullModel or dm:DifferenceModel).
- * The node is expected to be an IRI.
- * @return The model node.
- * @throws IllegalStateException if neither a FullModel nor a DifferenceModel is found in the header graph.
- */
- default Node getModel() {
- var iter = find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_FULL_MODEL);
- if (iter.hasNext()) {
- return iter.next().getSubject();
- }
- iter = find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL);
- if (iter.hasNext()) {
- return iter.next().getSubject();
- }
- throw new IllegalStateException("Found neither FullModel nor DifferenceModel in the header graph.");
+ if (CimGraph.getCimNs(graph) == null) {
+ throw new IllegalArgumentException(
+ "Graph does not appear to be a CIM graph. No proper 'cim' namespace defined.");
}
+ return new CimModelHeaderGraphWrapper(graph);
+ }
- /**
- * Get the profiles associated with the model.
- * Each profile node in a model header references one owlVersionIRI in {@link CimProfile#getOwlVersionIRIs()}
- * of the matching profile ontology.
- * @return A set of profile nodes (IRIs).
- */
- default Set getProfiles() {
- return stream(getModel(), CimHeaderVocabulary.PREDICATE_PROFILE, Node.ANY)
- .map(Triple::getObject)
- .collect(Collectors.toUnmodifiableSet());
- }
+ /**
+ * Checks if the model is a full model.
+ *
+ * @return true if the model is a full model, false otherwise.
+ */
+ default boolean isFullModel() {
+ return find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_FULL_MODEL).hasNext();
+ }
- /**
- * Get the models that are superseded by this model.
- * Each superseded model is referenced by its IRI.
- * @return A set of model IRIs that are superseded by this model.
- */
- default Set getSupersedes() {
- return stream(getModel(), CimHeaderVocabulary.PREDICATE_SUPERSEDES, Node.ANY)
- .map(Triple::getObject)
- .collect(Collectors.toUnmodifiableSet());
- }
+ /**
+ * Checks if the model is a difference model.
+ *
+ * @return true if the model is a difference model, false otherwise.
+ */
+ default boolean isDifferenceModel() {
+ return find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL).hasNext();
+ }
- /**
- * Gets all models that this model is dependent on.
- * Each dependent model is referenced by its IRI.
- * @return A set of models (IRIs) that this model is dependent on.
- */
- default Set getDependentOn() {
- return stream(getModel(), CimHeaderVocabulary.PREDICATE_DEPENDENT_ON, Node.ANY)
- .map(Triple::getObject)
- .collect(Collectors.toUnmodifiableSet());
+ /**
+ * Get the node representing the model (either md:FullModel or dm:DifferenceModel). The node is
+ * expected to be an IRI.
+ *
+ * @return The model node.
+ * @throws IllegalStateException if neither a FullModel nor a DifferenceModel is found in the
+ * header graph.
+ */
+ default Node getModel() {
+ var iter = find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_FULL_MODEL);
+ if (iter.hasNext()) {
+ return iter.next().getSubject();
}
-
- /**
- * Wraps a given graph as a {@link CimModelHeader}.
- * If the graph is already an instance of {@link CimModelHeader}, it is returned as is.
- * If the graph is null, null is returned.
- * If the graph does not appear to be a CIM graph (no 'cim' namespace defined), an IllegalArgumentException is thrown.
- * @param graph The graph to wrap.
- * @return The wrapped graph as a {@link CimModelHeader}, or null if the input graph is null.
- * @throws IllegalArgumentException if the graph does not appear to be a CIM graph.
- */
- static CimModelHeader wrap(Graph graph) {
- if (graph == null) {
- return null;
- }
- if (graph instanceof CimModelHeader cimModelHeader) {
- return cimModelHeader;
- }
- if (CimGraph.getCIMXMLVersion(graph) == CimVersion.NO_CIM) {
- throw new IllegalArgumentException("Graph does not appear to be a CIM graph. No proper 'cim' namespace defined.");
- }
- return new CimModelHeaderGraphWrapper(graph);
+ iter = find(Node.ANY, RDF.type.asNode(), CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL);
+ if (iter.hasNext()) {
+ return iter.next().getSubject();
}
+ throw new IllegalStateException(
+ "Found neither FullModel nor DifferenceModel in the header graph.");
+ }
+
+ /**
+ * Get the profiles associated with the model. Each profile node in a model header references one
+ * owlVersionIRI in {@link CimProfile#getOwlVersionIris()} of the matching profile ontology.
+ *
+ * @return A set of profile nodes (IRIs).
+ */
+ default Set getProfiles() {
+ return stream(getModel(), CimHeaderVocabulary.PREDICATE_PROFILE, Node.ANY)
+ .map(Triple::getObject)
+ .collect(Collectors.toUnmodifiableSet());
+ }
+
+ /**
+ * Get the models that are superseded by this model. Each superseded model is referenced by its
+ * IRI.
+ *
+ * @return A set of model IRIs that are superseded by this model.
+ */
+ default Set getSupersedes() {
+ return stream(getModel(), CimHeaderVocabulary.PREDICATE_SUPERSEDES, Node.ANY)
+ .map(Triple::getObject)
+ .collect(Collectors.toUnmodifiableSet());
+ }
+
+ /**
+ * Gets all models that this model is dependent on. Each dependent model is referenced by its
+ * IRI.
+ *
+ * @return A set of models (IRIs) that this model is dependent on.
+ */
+ default Set getDependentOn() {
+ return stream(getModel(), CimHeaderVocabulary.PREDICATE_DEPENDENT_ON, Node.ANY)
+ .map(Triple::getObject)
+ .collect(Collectors.toUnmodifiableSet());
+ }
+
+ /**
+ * An implementation of {@link CimModelHeader} that wraps a {@link Graph}.
+ */
+ class CimModelHeaderGraphWrapper extends GraphWrapper implements CimModelHeader {
- /**
- * An implementation of {@link CimModelHeader} that wraps a {@link Graph}.
- */
- class CimModelHeaderGraphWrapper extends GraphWrapper implements CimModelHeader {
- public CimModelHeaderGraphWrapper(Graph graph) {
- super(graph);
- }
+ public CimModelHeaderGraphWrapper(Graph graph) {
+ super(graph);
}
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimNamespaceFactoryRegistry.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimNamespaceFactoryRegistry.java
new file mode 100644
index 00000000..56d3e34c
--- /dev/null
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimNamespaceFactoryRegistry.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 de.soptim.opencgmes.cimxml.graph;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import org.apache.jena.graph.Graph;
+
+/**
+ * A registry for mapping CIM namespaces to factory functions that create {@link CimProfile}
+ * instances for those namespaces. This allows for dynamic registration and retrieval of profile
+ * factories based on the CIM namespace of a graph.
+ */
+public class CimNamespaceFactoryRegistry {
+
+ public static final String NAMESPACE = "namespace";
+ private static final Map> map;
+
+ static {
+ map = new ConcurrentHashMap<>();
+ map.put(CimProfile16.CIM_NAMESPACE, CimProfile16::new);
+ map.put(CimProfile17.CIM_NAMESPACE, CimProfile17::new);
+ map.put(CimProfile18.CIM_NAMESPACE, CimProfile18::new);
+ }
+
+ /**
+ * Registers a factory function for creating {@link CimProfile} instances for the given
+ * CIM namespace.
+ *
+ * @param namespace the CIM namespace for which the factory should be registered
+ * @param factory a function that takes a {@link Graph} and returns a {@link CimProfile}
+ * @throws NullPointerException if either the namespace or the factory is null
+ */
+ public static void registerProfileFactory(String namespace, Function factory) {
+ Objects.requireNonNull(namespace, NAMESPACE);
+ Objects.requireNonNull(factory, "constructor");
+ map.put(namespace, factory);
+ }
+
+ /**
+ * Unregisters the factory function associated with the given CIM namespace.
+ *
+ * @param namespace the CIM namespace for which the factory should be unregistered
+ * @throws NullPointerException if the namespace is null
+ */
+ public static void unregisterProfileFactory(String namespace) {
+ Objects.requireNonNull(namespace, NAMESPACE);
+ map.remove(namespace);
+ }
+
+ /**
+ * Retrieves the factory function associated with the given CIM namespace.
+ *
+ * @param namespace the CIM namespace for which to retrieve the factory
+ * @return a function that takes a {@link Graph} and returns a {@link CimProfile},
+ * or null if no factory is registered for the given namespace
+ * @throws NullPointerException if the namespace is null
+ */
+ public static Function getProfileFactory(String namespace) {
+ Objects.requireNonNull(namespace, NAMESPACE);
+ return map.get(namespace);
+ }
+
+ /**
+ * Checks if a factory function is registered for the given CIM namespace.
+ *
+ * @param namespace the CIM namespace to check
+ * @return true if a factory function is registered for the given namespace, false otherwise
+ * @throws NullPointerException if the namespace is null
+ */
+ public static boolean hasProfileFactory(String namespace) {
+ Objects.requireNonNull(namespace, NAMESPACE);
+ return map.containsKey(namespace);
+ }
+}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile.java
index dcb63ef2..07c77685 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile.java
@@ -18,22 +18,21 @@
package de.soptim.opencgmes.cimxml.graph;
-import de.soptim.opencgmes.cimxml.CimVersion;
import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistry;
+import java.util.Set;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
-import java.util.Set;
-
/**
* Represents a CIM profile ontology (RDFS schema) graph.
*
*
A CIM profile defines a subset of the CIM schema for specific use cases, such as
- * equipment models, topology, or state variables. Profiles are versioned and identified
- * by their version IRIs.
+ * equipment models, topology, or state variables. Profiles are versioned and identified by their
+ * version IRIs.
*
*
Profile Structure:
+ *
*
CIM profiles can be defined in different formats depending on the CIM version:
*
*
CIM 16 (CGMES 2.4.15):
@@ -57,158 +56,126 @@
* CimProfile profile = CimProfile.wrap(profileGraph);
*
* // Query profile metadata
- * CimVersion version = profile.getCIMVersion();
+ * String cimNamespace = profile.getCimNamespace();
* Set versionIris = profile.getOwlVersionIRIs();
* String keyword = profile.getDcatKeyword();
* boolean isHeader = profile.isHeaderProfile();
* }
*
- * @see CimVersion
* @see CimProfileRegistry
* @since Jena 5.6.0
*/
public interface CimProfile extends CimGraph {
- String NS_CIMS = "http://iec.ch/TC57/1999/rdf-schema-extensions-19990926#";
- String CLASS_CLASS_CATEGORY = "ClassCategory";
- String PACKAGE_FILE_HEADER_PROFILE = "#Package_FileHeaderProfile";
+ String NS_CIMS = "http://iec.ch/TC57/1999/rdf-schema-extensions-19990926#";
+ String CLASS_CLASS_CATEGORY = "ClassCategory";
+ String PACKAGE_FILE_HEADER_PROFILE = "#Package_FileHeaderProfile";
- Node TYPE_CLASS_CATEGORY = NodeFactory.createURI(NS_CIMS + CLASS_CLASS_CATEGORY);
+ Node TYPE_CLASS_CATEGORY = NodeFactory.createURI(NS_CIMS + CLASS_CLASS_CATEGORY);
- /**
- * The header profile describes the RDF schema for a CIM model header or document header.
- * These profiles are not references by the model. So in the profile registry header profiles usually are not
- * selected by their versionIRI.
- *
- * @return true if this profile is a header profile, false otherwise.
- */
- boolean isHeaderProfile();
+ /**
+ * Wraps a graph as a CimProfile. If the graph is already a CimProfile, it is returned as is.
+ * Otherwise, a new ProfileOntologyImpl is created wrapping the graph.
+ *
+ *
If the graph does not appear to be a CIM graph (no 'cim' namespace defined), an
+ * IllegalArgumentException is thrown.
+ *
+ * @param graph The graph to wrap.
+ * @return A CimProfile wrapping the given graph.
+ * @throws IllegalArgumentException if the graph does not appear to be a CIM graph.
+ */
+ static CimProfile wrap(Graph graph) throws IllegalArgumentException {
+ if (graph instanceof CimProfile po) {
+ return po;
+ }
+ var cimNamespace = CimGraph.getCimNs(graph);
+ if (cimNamespace == null) {
+ throw new IllegalArgumentException(
+ "Graph does not appear to be a CIM graph. No proper 'cim' namespace defined.");
+ }
+ var factory = CimNamespaceFactoryRegistry.getProfileFactory(cimNamespace);
+ if (factory == null) {
+ throw new IllegalArgumentException(
+ "No profile factory registered for 'cim' namespace URI: " + cimNamespace);
+ }
+ return factory.apply(graph);
+ }
- /**
- * Abbreviation or keyword for the profile.
- * This is usually dcat:keyword.
- * For CGMES 2.4.15 profiles it is "{Profile}Version.shortName cims:isFixed ?keyword".
- *
- * In CGMES 2.4.15 file header profiles do not contain a "shortName" or "keyword". But the new ontology document
- * header typically contain "DH" as keyword. To maintain compatibility, "DH" shall be used for old CGMES 2.4.15
- * file header profiles.
- *
- * @return The keyword for the profile, or null if no keyword is defined.
- */
- String getDcatKeyword();
+ /**
+ * The header profile describes the RDF schema for a CIM model header or document header. These
+ * profiles are not references by the model. So in the profile registry header profiles usually
+ * are not selected by their versionIRI.
+ *
+ * @return true if this profile is a header profile, false otherwise.
+ */
+ boolean isHeaderProfile();
- /**
- * The version IRIs of the profile.
- * This is usually owl:versionIRI.
- * For CGMES 2.4.15 profiles it is
- * "{Profile}Version.baseURI.{*} cims:isFixed ?versionIRI"
- * and
- * "{Profile}Version.entsoeURI{*} cims:isFixed ?versionIRI".
- *
- * One profile can have multiple version IRIs, at least in CGMES 2.4.15 profiles.
- *
- * @return The version IRI of the profile, or null if no version IRI is defined.
- */
- Set getOwlVersionIRIs();
+ /**
+ * Abbreviation or keyword for the profile. This is usually dcat:keyword. For CGMES 2.4.15
+ * profiles it is "{Profile}Version.shortName cims:isFixed ?keyword".
+ *
+ *
In CGMES 2.4.15 file header profiles do not contain a "shortName" or "keyword". But the new
+ * ontology document header typically contain "FH" as keyword. To maintain compatibility, "FH"
+ * shall be used for old CGMES 2.4.15 file header profiles.
+ *
+ * @return The keyword for the profile, or null if no keyword is defined.
+ */
+ String getDcatKeyword();
- /**
- * Return owl:versionInfo of the ontology object of the profile.
- * For CGMES 2.4.15, there is no such version info.
- *
- * @return The version info of the profile, or null if no version info is defined.
- */
- String getOwlVersionInfo();
+ /**
+ * The version IRIs of the profile. This is usually owl:versionIRI. For CGMES 2.4.15 profiles it
+ * is "{Profile}Version.baseURI.{*} cims:isFixed ?versionIRI" and "{Profile}Version.entsoeURI{*}
+ * cims:isFixed ?versionIRI".
+ *
+ *
One profile can have multiple version IRIs, at least in CGMES 2.4.15 profiles.
+ *
+ * @return The version IRI of the profile, or null if no version IRI is defined.
+ */
+ Set getOwlVersionIris();
- /**
- * Checks if this profile is equal to another profile.
- * Two profiles are equal if they have the same CIM version and the same set of version
- * IRIs, or if both are header profiles.
- * @param other The other profile to compare to.
- * @return true if the profiles are equal, false otherwise.
- */
- default boolean equals(CimProfile other) {
- if (other == null) {
- return false;
- }
- if (!this.getCIMVersion().equals(other.getCIMVersion())) {
- return false;
- }
- if (isHeaderProfile()) {
- return other.isHeaderProfile();
- }
- return this.getOwlVersionIRIs().equals(other.getOwlVersionIRIs());
- }
+ /**
+ * Return owl:versionInfo of the ontology object of the profile. For CGMES 2.4.15, there is no
+ * such version info.
+ *
+ * @return The version info of the profile, or null if no version info is defined.
+ */
+ String getOwlVersionInfo();
- /**
- * Calculates the hash code for this profile.
- * If the model is a header profile, the hash code is based on the CIM version and the fact that it is a header
- * profile.
- * If the model is not a header profile, the hash code is based on the CIM version and the set of version IRIs.
- * @return The hash code for this profile.
- */
- default int calculateHashCode() {
- // hash code from isHeader, cimVersion and version IRIs
- int result = Boolean.hashCode(isHeaderProfile());
- result = 31 * result + getCIMVersion().hashCode();
- if (!isHeaderProfile()) {
- result = 31 * result + getOwlVersionIRIs().hashCode();
- }
- return result;
+ /**
+ * Checks if this profile is equal to another profile. Two profiles are equal if they have the
+ * same CIM version and the same set of version IRIs, or if both are header profiles.
+ *
+ * @param other The other profile to compare to.
+ * @return true if the profiles are equal, false otherwise.
+ */
+ default boolean equals(CimProfile other) {
+ if (other == null) {
+ return false;
}
-
- /**
- * Wraps a graph as a CimProfile.
- * If the graph is already a CimProfile, it is returned as is.
- * Otherwise, a new ProfileOntologyImpl is created wrapping the graph.
- *
- * @param graph The graph to wrap.
- * @return A CimProfile wrapping the given graph.
- */
- static CimProfile wrap(Graph graph) throws IllegalArgumentException {
- if (graph instanceof CimProfile po) {
- return po;
- }
- var cimVersion = CimGraph.getCIMXMLVersion(graph);
- return switch (cimVersion) {
- case CIM_16 -> {
- if (CimProfile16.isHeaderProfile(graph)) {
- // If the graph contains header profile, skip the version IRI and keyword check.
- yield new CimProfile16(graph, true);
- }
- if (CimProfile16.hasVersionIRIAndKeyword(graph)) {
- yield new CimProfile16(graph, false);
- }
- throw new IllegalArgumentException("Graph does not contain the required '...Version.shortName' and '...Version.entsoeURI*' or '...Version.baseURI...' for a CGMES 2.4.15 profile.");
- }
- case CIM_17 -> {
- if (CimProfile17.isHeaderProfile(graph)) {
- // If the graph contains header profile --> it is still CIM16 style
- yield new CimProfile17(graph, true);
- }
- if (!CimProfile17.hasOntology(graph)) {
- throw new IllegalArgumentException("Graph does not contain the required ontology subject for a CIM profile.");
- }
- if (!CimProfile17.hasVersionIRIAndKeyword(graph)) {
- throw new IllegalArgumentException("Graphs ontology does not contain the required versionIRI and keyword for a CIM profile.");
- }
- yield new CimProfile17(graph, false);
- }
- case CIM_18 -> {
- if (CimProfile18.isHeaderProfile(graph)) {
- // If the graph contains header profile --> it is still CIM16 style
- yield new CimProfile18(graph, true);
- }
- if (!CimProfile18.hasOntology(graph)) {
- throw new IllegalArgumentException("Graph does not contain the required ontology subject for a CIM profile.");
- }
- if (!CimProfile18.hasVersionIRIAndKeyword(graph)) {
- throw new IllegalArgumentException("Graphs ontology does not contain the required versionIRI and keyword for a CIM profile.");
- }
- yield new CimProfile18(graph, false);
- }
- case NO_CIM -> throw new IllegalArgumentException("Graph does not appear to be a CIM graph. No proper 'cim' namespace defined.");
- };
+ if (!this.getCimNamespace().equals(other.getCimNamespace())) {
+ return false;
}
+ if (isHeaderProfile()) {
+ return other.isHeaderProfile();
+ }
+ return this.getOwlVersionIris().equals(other.getOwlVersionIris());
+ }
+ /**
+ * Calculates the hash code for this profile. If the model is a header profile, the hash code is
+ * based on the CIM version and the fact that it is a header profile. If the model is not a header
+ * profile, the hash code is based on the CIM version and the set of version IRIs.
+ *
+ * @return The hash code for this profile.
+ */
+ default int calculateHashCode() {
+ // hash code from isHeader, cim namespace and version IRIs
+ int result = Boolean.hashCode(isHeaderProfile());
+ result = 31 * result + getCimNamespace().hashCode();
+ if (!isHeaderProfile()) {
+ result = 31 * result + getOwlVersionIris().hashCode();
+ }
+ return result;
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile16.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile16.java
index 7aaf2a10..f23ea84e 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile16.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile16.java
@@ -18,139 +18,151 @@
package de.soptim.opencgmes.cimxml.graph;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.sparql.graph.GraphWrapper;
import org.apache.jena.vocabulary.RDF;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
/**
- * A wrapper for a graph that contains a CIM profile ontology as defined using CIM 16
+ * A wrapper for a graph that contains a CIM profile ontology as defined using CIM 16.
*/
public class CimProfile16 extends GraphWrapper implements CimProfile {
- final static String NS_RDFS = "http://www.w3.org/2000/01/rdf-schema#";
-
- /**
- * This is how the profile IRI ends in CGMES 2.4.15.
- * Example: "http://entsoe.eu/CIM/SchemaExtension/3/1#EquipmentVersion"
- */
- final static String PROFILE_VERSION_POSTFIX = "Version";
-
- final static Node PREDICATE_RDFS_DOMAIN = NodeFactory.createURI(NS_RDFS + "domain");
- final static Node PREDICATE_CIMS_IS_FIXED = NodeFactory.createURI(NS_CIMS + "isFixed");
-
- /**
- * Checks if the given graph contains the required information to be wrapped as a CimProfile.
- * It must contain exactly one fixed text for the property cim:shortName.
- * It may contain zero or more fixed texts for the properties cim:entsoeURI or cim:baseURI.
- * If the graph contains at least one fixed text for cim:entsoeURI or cim:baseURI.
- * @param graph The graph to check.
- * @return true if the graph can be wrapped as a CimProfile, false otherwise.
- */
- public static boolean hasVersionIRIAndKeyword(Graph graph) {
- if (getProfilePropertyFixedTexts(graph, ".shortName").findAny().isEmpty()) {
- return false; //no keyword defined
- }
- if (getProfilePropertyFixedTexts(graph, ".entsoeURI").findAny().isPresent()) {
- return true; //at least one version IRI defined
- }
- if (getProfilePropertyFixedTexts(graph, ".baseURI").findAny().isPresent()) {
- return true; //at least one version IRI defined
- }
- return false; //no version IRI defined
- }
-
- /**
- * Looks for all triples with rdfs:domain pointing to a profile class (i.e. ending with "Version")
- * and with a subject starting with the profile class IRI + the given property name (including the dot).
- * Then looks for all triples with cim:isFixed for the found profile class
- * and returns the literal values of those triples.
- * @param graph the graph to search in
- * @param propertyNameStartWithIncludingDot the property name to look for, including the dot (e.g. ".shortName", ".entsoeURI", ".baseURI")
- * @return a stream of fixed text values for the given property name
- */
- private static Stream getProfilePropertyFixedTexts(Graph graph, String propertyNameStartWithIncludingDot) {
- return graph.stream(Node.ANY, PREDICATE_RDFS_DOMAIN, Node.ANY) //first look for the domain
- .filter(t
- -> t.getObject().isURI()
- && t.getObject().getURI().endsWith(PROFILE_VERSION_POSTFIX)
- && t.getSubject().isURI()
- && t.getSubject().getURI().startsWith(t.getObject().getURI())
- && t.getSubject().getURI().regionMatches(t.getObject().getURI().length(),
- propertyNameStartWithIncludingDot,0, propertyNameStartWithIncludingDot.length()))
- .flatMap(t -> graph
- .stream(t.getSubject(), PREDICATE_CIMS_IS_FIXED, Node.ANY)
- .filter(t2 -> t2.getObject().isLiteral())
- .map(t2 -> t2.getObject().getLiteralLexicalForm()));
- }
-
- private final boolean isHeaderProfile;
-
- /**
- * Wraps the given graph as a CimProfile16.
- * @param graph The graph to wrap.
- * @throws IllegalArgumentException if the graph does not contain the required information to be a CimProfile16.
- */
- public CimProfile16(Graph graph, boolean isHeaderProfile) {
- super(graph);
- this.isHeaderProfile = isHeaderProfile;
+ public static final String CIM_NAMESPACE = "http://iec.ch/TC57/2013/CIM-schema-cim16#";
+
+ static final String NS_RDFS = "http://www.w3.org/2000/01/rdf-schema#";
+
+ /**
+ * This is how the profile IRI ends in CGMES 2.4.15. Example:
+ * {@code http://entsoe.eu/CIM/SchemaExtension/3/1#EquipmentVersion}
+ */
+ static final String PROFILE_VERSION_POSTFIX = "Version";
+
+ static final Node PREDICATE_RDFS_DOMAIN = NodeFactory.createURI(NS_RDFS + "domain");
+ static final Node PREDICATE_CIMS_IS_FIXED = NodeFactory.createURI(NS_CIMS + "isFixed");
+
+ /**
+ * Wraps the given graph as a CimProfile16.
+ *
+ * @param graph The graph to wrap.
+ * @throws IllegalArgumentException if the graph does not contain the required information to be a
+ * CimProfile16.
+ */
+ public CimProfile16(Graph graph) {
+ super(graph);
+ if (isCim16HeaderProfile(graph)) {
+ return;
}
-
- @Override
- public boolean isHeaderProfile() {
- return this.isHeaderProfile;
+ if (!hasVersionIriAndKeyword(graph)) {
+ throw new IllegalArgumentException(
+ "Graph does not contain the required '...Version.shortName' and '...Version.entsoeURI*'"
+ + " or '...Version.baseURI...' for a CGMES 2.4.15 profile.");
}
-
- @Override
- public String getDcatKeyword() {
- if (isHeaderProfile) {
- // CGMES 2.4.15 file header profiles do not have a keyword.
- return "DH"; // Use "DH" for compatibility with old CGMES 2.4.15 file header profiles.
- }
- return getProfilePropertyFixedTexts(get(), ".shortName")
- .findFirst().orElse(null);
+ }
+
+ /**
+ * Checks if the given graph contains the required information to be wrapped as a CimProfile. It
+ * must contain exactly one fixed text for the property cim:shortName. It may contain zero or more
+ * fixed texts for the properties cim:entsoeURI or cim:baseURI. If the graph contains at least one
+ * fixed text for cim:entsoeURI or cim:baseURI.
+ *
+ * @param graph The graph to check.
+ * @return true if the graph can be wrapped as a CimProfile, false otherwise.
+ */
+ public static boolean hasVersionIriAndKeyword(Graph graph) {
+ if (getProfilePropertyFixedTexts(graph, ".shortName")
+ .findAny().isEmpty()) {
+ return false; // no keyword defined
}
-
- @Override
- public Set getOwlVersionIRIs() {
- return Stream.concat(
- getProfilePropertyFixedTexts(get(), ".entsoeURI"),
- getProfilePropertyFixedTexts(get(), ".baseURI"))
- .map(NodeFactory::createURI)
- .collect(Collectors.toUnmodifiableSet());
+ if (getProfilePropertyFixedTexts(graph, ".entsoeURI")
+ .findAny().isPresent()) {
+ return true; // at least one version IRI defined
}
-
- @Override
- public String getOwlVersionInfo() {
- return null;
+ return getProfilePropertyFixedTexts(graph, ".baseURI")
+ .findAny().isPresent(); // at least one version IRI defined or no version IRI defined
+ }
+
+ /**
+ * Looks for all triples with rdfs:domain pointing to a profile class (i.e. ending with "Version")
+ * and with a subject starting with the profile class IRI + the given property name (including the
+ * dot). Then looks for all triples with cim:isFixed for the found profile class and returns the
+ * literal values of those triples.
+ *
+ * @param graph the graph to search in
+ * @param propertyNameStartWithIncludingDot the property name to look for, including the dot (e.g.
+ * ".shortName", ".entsoeURI", ".baseURI")
+ * @return a stream of fixed text values for the given property name
+ */
+ private static Stream getProfilePropertyFixedTexts(Graph graph,
+ String propertyNameStartWithIncludingDot) {
+ return graph.stream(Node.ANY, PREDICATE_RDFS_DOMAIN, Node.ANY) // first look for the domain
+ .filter(t
+ -> t.getObject().isURI()
+ && t.getObject().getURI().endsWith(PROFILE_VERSION_POSTFIX)
+ && t.getSubject().isURI()
+ && t.getSubject().getURI().startsWith(t.getObject().getURI())
+ && t.getSubject().getURI().regionMatches(t.getObject().getURI().length(),
+ propertyNameStartWithIncludingDot, 0, propertyNameStartWithIncludingDot.length()))
+ .flatMap(t -> graph
+ .stream(t.getSubject(), PREDICATE_CIMS_IS_FIXED, Node.ANY)
+ .filter(t2 -> t2.getObject().isLiteral())
+ .map(t2 -> t2.getObject().getLiteralLexicalForm()));
+ }
+
+ @Override
+ public boolean isHeaderProfile() {
+ return isCim16HeaderProfile(this);
+ }
+
+ @Override
+ public String getDcatKeyword() {
+ if (isHeaderProfile()) {
+ // CGMES 2.4.15 file header profiles do not have a keyword.
+ return "FH"; // Use "FH" for compatibility with new CGMES 3.0 file header profiles.
}
-
- @Override
- public final boolean equals(Object other) {
- if (!(other instanceof CimProfile16 that)) return false;
-
- return this.equals(that);
- }
-
- @Override
- public int hashCode() {
- return this.calculateHashCode();
- }
-
- /**
- * Determines if the given graph is a header profile.
- * In CIM 16, header profiles are identified by the presence of a
- * cim:Category with the value "PackageFileHeaderProfile".
- */
- public static boolean isHeaderProfile(Graph graph) {
- return graph.stream(Node.ANY, RDF.type.asNode(), TYPE_CLASS_CATEGORY)
- .anyMatch(t
- -> t.getSubject().isURI()
- && t.getSubject().getURI().endsWith(PACKAGE_FILE_HEADER_PROFILE));
+ return getProfilePropertyFixedTexts(get(), ".shortName")
+ .findFirst().orElse(null);
+ }
+
+ @Override
+ public Set getOwlVersionIris() {
+ return Stream.concat(
+ getProfilePropertyFixedTexts(get(), ".entsoeURI"),
+ getProfilePropertyFixedTexts(get(), ".baseURI"))
+ .map(NodeFactory::createURI)
+ .collect(Collectors.toUnmodifiableSet());
+ }
+
+ @Override
+ public String getOwlVersionInfo() {
+ return null;
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ if (!(other instanceof CimProfile16 that)) {
+ return false;
}
+ return this.equals(that);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.calculateHashCode();
+ }
+
+ /**
+ * Determines if the given graph is a header profile. In CIM 16, header profiles are identified by
+ * the presence of a cim:Category with the value "PackageFileHeaderProfile".
+ */
+ public static boolean isCim16HeaderProfile(Graph graph) {
+ return graph.stream(Node.ANY, RDF.type.asNode(), TYPE_CLASS_CATEGORY)
+ .anyMatch(t
+ -> t.getSubject().isURI()
+ && t.getSubject().getURI().endsWith(PACKAGE_FILE_HEADER_PROFILE));
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile17.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile17.java
index f13cd82a..4bfda886 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile17.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile17.java
@@ -18,6 +18,10 @@
package de.soptim.opencgmes.cimxml.graph;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
@@ -25,110 +29,156 @@
import org.apache.jena.sparql.graph.GraphWrapper;
import org.apache.jena.vocabulary.RDF;
-import java.util.Set;
-import java.util.stream.Collectors;
-
/**
* A wrapper for a graph that contains a CIM profile ontology as defined in CIM 17.
*/
public class CimProfile17 extends GraphWrapper implements CimProfile {
- final static String NS_OWL = "http://www.w3.org/2002/07/owl#";
- final static String NS_DCAT = "http://www.w3.org/ns/dcat#";
- final static Node CLASS_ONTOLOGY = NodeFactory.createURI(NS_OWL + "Ontology");
- final static Node PREDICATE_DCAT_KEYWORD = NodeFactory.createURI(NS_DCAT + "keyword");
- final static Node PREDICATE_OWL_VERSION_IRI = NodeFactory.createURI(NS_OWL + "versionIRI");
- final static Node PREDICATE_OWL_VERSION_INFO = NodeFactory.createURI(NS_OWL + "versionInfo");
-
- /**
- * Find the ontology node in the graph.
- * The ontology node is defined as a subject with type owl:Ontology.
- * If no such node is found, null is returned.
- *
- * @param graph The graph to search in.
- * @return The ontology node or null if not found.
- */
- static boolean hasOntology(Graph graph) {
- return graph.find(Node.ANY, RDF.type.asNode(), CLASS_ONTOLOGY).hasNext();
- }
-
- public static Node getOntology(Graph graph) {
- return graph.stream(Node.ANY, RDF.type.asNode(), CLASS_ONTOLOGY).findAny().map(Triple::getSubject).orElseThrow();
- }
-
- public Node getOntology() {
- return getOntology(this);
- }
-
- public static boolean hasVersionIRIAndKeyword(Graph graph) {
- return graph.find(Node.ANY, PREDICATE_DCAT_KEYWORD, Node.ANY).hasNext()
- && graph.find(Node.ANY, PREDICATE_OWL_VERSION_IRI, Node.ANY).hasNext();
- }
-
- private final boolean isHeaderProfile;
-
- /**
- * Wraps the given graph as a CimProfile17.
- * @param graph The graph to wrap.
- * @throws IllegalArgumentException if the graph does not contain the required information to be a CimProfile17.
- */
- public CimProfile17(Graph graph, boolean isHeaderProfile) {
- super(graph);
- this.isHeaderProfile = isHeaderProfile;
- }
-
- @Override
- public boolean isHeaderProfile() {
- return this.isHeaderProfile;
- }
-
- @Override
- public String getDcatKeyword() {
- if (isHeaderProfile) {
- // CGMES v3.0 file header profiles do not have a keyword.
- return "DH"; // Use "DH" for compatibility with old CGMES 2.4.15 file header profiles.
- }
- var iter = find(getOntology(), PREDICATE_DCAT_KEYWORD, Node.ANY);
- return iter.hasNext() ? iter.next().getObject().getLiteralValue().toString() : null;
+ public static final String CIM_NAMESPACE = "http://iec.ch/TC57/CIM100#";
+
+ protected static final String NS_OWL = "http://www.w3.org/2002/07/owl#";
+ protected static final Node CLASS_ONTOLOGY = NodeFactory.createURI(NS_OWL + "Ontology");
+ protected static final String NS_DCAT = "http://www.w3.org/ns/dcat#";
+ protected static final Node PREDICATE_DCAT_KEYWORD = NodeFactory.createURI(NS_DCAT + "keyword");
+ protected static final Node PREDICATE_OWL_VERSION_IRI = NodeFactory.createURI(
+ NS_OWL + "versionIRI");
+ protected static final Node PREDICATE_OWL_VERSION_INFO = NodeFactory.createURI(
+ NS_OWL + "versionInfo");
+
+ /**
+ * Wraps the given graph as a CimProfile17.
+ *
+ * @param graph The graph to wrap.
+ * @throws IllegalArgumentException if the graph does not contain the required information to be a
+ * CimProfile17.
+ */
+ public CimProfile17(Graph graph) {
+ this(graph, CimProfile17::isCim17HeaderProfile);
+ }
+
+ /**
+ * Wraps the given graph as a CimProfile17, using the provided predicate to determine if the graph
+ * is a header profile.
+ *
+ * @param graph The graph to wrap.
+ * @param isHeaderProfilePredicate A predicate that determines if the graph is a header profile.
+ * @throws IllegalArgumentException if the graph does not contain the required information to be a
+ * CimProfile17.
+ */
+ protected CimProfile17(Graph graph, Predicate isHeaderProfilePredicate) {
+ super(graph);
+ if (isHeaderProfilePredicate.test(graph)) {
+ return;
}
-
- @Override
- public Set getOwlVersionIRIs() {
- return stream(getOntology(), PREDICATE_OWL_VERSION_IRI, Node.ANY)
- .map(Triple::getObject)
- .collect(Collectors.toUnmodifiableSet());
- }
-
- @Override
- public String getOwlVersionInfo() {
- var iter = find(getOntology(), PREDICATE_OWL_VERSION_INFO, Node.ANY);
- return iter.hasNext() ? iter.next().getObject().getLiteralValue().toString() : null;
+ if (!hasOntology(graph)) {
+ throw new IllegalArgumentException(
+ "Graph does not contain the required ontology subject for a CIM profile.");
}
-
- @Override
- public final boolean equals(Object other) {
- if (!(other instanceof CimProfile17 that)) return false;
-
- return this.equals(that);
+ if (!hasVersionIriAndKeyword(graph)) {
+ throw new IllegalArgumentException("Graphs ontology does not contain the required versionIRI"
+ + " and keyword for a CIM profile.");
}
+ }
- @Override
- public int hashCode() {
- return this.calculateHashCode();
+ @Override
+ public String getDcatKeyword() {
+ if (isHeaderProfile()) {
+ // CGMES v3.0 file header profiles do not have a keyword.
+ return "FH"; // Use "FH" for compatibility with old CGMES 2.4.15 file header profiles.
}
-
- /**
- * Checks if the given graph is a header profile.
- * A header profile is defined as a graph that contains a subject with type
- * "cims:ClassCategory" and
- * the subject URI ends with "#Package_FileHeaderProfile".
- * @param graph The graph to check.
- * @return true if the graph is a header profile, false otherwise.
- */
- public static boolean isHeaderProfile(Graph graph) {
- return graph.stream(Node.ANY, RDF.type.asNode(), TYPE_CLASS_CATEGORY)
- .anyMatch(t
- -> t.getSubject().isURI()
- && t.getSubject().getURI().endsWith(PACKAGE_FILE_HEADER_PROFILE));
+ var iter = find(getOntology(), PREDICATE_DCAT_KEYWORD, Node.ANY);
+ return iter.hasNext() ? iter.next().getObject().getLiteralValue().toString() : null;
+ }
+
+ @Override
+ public Set getOwlVersionIris() {
+ return stream(getOntology(), PREDICATE_OWL_VERSION_IRI, Node.ANY)
+ .map(Triple::getObject)
+ .collect(Collectors.toUnmodifiableSet());
+ }
+
+ @Override
+ public String getOwlVersionInfo() {
+ var iter = find(getOntology(), PREDICATE_OWL_VERSION_INFO, Node.ANY);
+ return iter.hasNext() ? iter.next().getObject().getLiteralValue().toString() : null;
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ if (!(other instanceof CimProfile17 that)) {
+ return false;
}
+ return this.equals(that);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.calculateHashCode();
+ }
+
+ /**
+ * Get the ontology node in this graph. The ontology node is defined as a subject with type
+ * owl:Ontology. If no such node is found, an exception is thrown.
+ *
+ * @return The ontology node.
+ * @throws NoSuchElementException if no ontology node is found.
+ */
+ public Node getOntology() {
+ return getOntology(this);
+ }
+
+ /**
+ * Get the ontology node in the graph. The ontology node is defined as a subject with type
+ * owl:Ontology. If no such node is found, an exception is thrown.
+ *
+ * @param graph The graph to search in.
+ * @return The ontology node.
+ * @throws NoSuchElementException if no ontology node is found.
+ */
+ public static Node getOntology(Graph graph) {
+ return graph.stream(Node.ANY, RDF.type.asNode(), CLASS_ONTOLOGY).findAny()
+ .map(Triple::getSubject).orElseThrow();
+ }
+
+ /**
+ * Find the ontology node in the graph. The ontology node is defined as a subject with type
+ * owl:Ontology. If no such node is found, null is returned.
+ *
+ * @param graph The graph to search in.
+ * @return The ontology node or null if not found.
+ */
+ static boolean hasOntology(Graph graph) {
+ return graph.find(Node.ANY, RDF.type.asNode(), CLASS_ONTOLOGY).hasNext();
+ }
+
+ /**
+ * Checks if the given graph contains both a DCAT keyword and an OWL version IRI.
+ *
+ * @param graph The graph to check.
+ * @return true if both a DCAT keyword and an OWL version IRI are present, false otherwise.
+ */
+ public static boolean hasVersionIriAndKeyword(Graph graph) {
+ return graph.find(Node.ANY, PREDICATE_DCAT_KEYWORD, Node.ANY).hasNext()
+ && graph.find(Node.ANY, PREDICATE_OWL_VERSION_IRI, Node.ANY).hasNext();
+ }
+
+ @Override
+ public boolean isHeaderProfile() {
+ return isCim17HeaderProfile(this);
+ }
+
+ /**
+ * Checks if the given graph is a header profile. A header profile is defined as a graph that
+ * contains a subject with type "cims:ClassCategory" and the subject URI ends with
+ * "#Package_FileHeaderProfile".
+ *
+ * @param graph The graph to check.
+ * @return true if the graph is a header profile, false otherwise.
+ */
+ public static boolean isCim17HeaderProfile(Graph graph) {
+ return graph.stream(Node.ANY, RDF.type.asNode(), TYPE_CLASS_CATEGORY)
+ .anyMatch(t
+ -> t.getSubject().isURI()
+ && t.getSubject().getURI().endsWith(PACKAGE_FILE_HEADER_PROFILE));
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile18.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile18.java
index a15b477a..2d4c16d8 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile18.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/CimProfile18.java
@@ -22,48 +22,47 @@
import org.apache.jena.graph.Node;
/**
- * A wrapper for a graph that contains a CIM profile ontology as defined in CIM 18
+ * A wrapper for a graph that contains a CIM profile ontology as defined in CIM 18.
*/
-public class CimProfile18 extends CimProfile17 implements CimProfile {
+public class CimProfile18 extends CimProfile17 {
- final static String DOCUMENT_HEADER_VERSION_IRI_START = "https://ap-voc.cim4.eu/DocumentHeader";
+ public static final String CIM_NAMESPACE = "https://cim.ucaiug.io/ns#";
+ private static final String DOCUMENT_HEADER_VERSION_IRI_START = "https://ap-voc.cim4.eu/DocumentHeader";
- /**
- * Wraps the given graph as a CimProfile18.
- * @param graph The graph to wrap.
- * @throws IllegalArgumentException if the graph does not contain the required information to be a CimProfile18.
- */
- public CimProfile18(Graph graph, boolean isHeaderProfile) {
- super(graph, isHeaderProfile);
- }
-
- /**
- * Checks if the given graph contains both a DCAT keyword and an OWL version IRI.
- * @param graph The graph to check.
- * @return true if both a DCAT keyword and an OWL version IRI are present, false otherwise.
- */
- public static boolean hasVersionIRIAndKeyword(Graph graph) {
- return graph.find(Node.ANY, PREDICATE_DCAT_KEYWORD, Node.ANY).hasNext()
- && graph.find(Node.ANY, PREDICATE_OWL_VERSION_IRI, Node.ANY).hasNext();
- }
+ /**
+ * Wraps the given graph as a CimProfile18.
+ *
+ * @param graph The graph to wrap.
+ * @throws IllegalArgumentException if the graph does not contain the required information to be a
+ * CimProfile18.
+ */
+ public CimProfile18(Graph graph) {
+ super(graph, CimProfile18::isCim18HeaderProfile);
+ }
- /**
- * Checks if the given graph is a header profile.
- * A header profile is identified by having an ontology with a version IRI
- * that starts with "https://ap-voc.cim4.eu/DocumentHeader".
- *
- * @param graph The graph to check.
- * @return true if the graph is a header profile, false otherwise.
- */
- public static boolean isHeaderProfile(Graph graph) {
- if (!hasOntology(graph))
- return false;
- var ontology = getOntology(graph);
+ @Override
+ public boolean isHeaderProfile() {
+ return isCim18HeaderProfile(this);
+ }
- // look for https://ap.cim4.eu/DocumentHeader# without # in version IRIs
- return graph.stream(ontology, PREDICATE_OWL_VERSION_IRI, Node.ANY)
- .anyMatch(t
- -> t.getObject().isURI()
- && t.getObject().getURI().startsWith(DOCUMENT_HEADER_VERSION_IRI_START));
+ /**
+ * Checks if the given graph is a header profile. A header profile is identified by having an
+ * ontology with a version IRI that starts with
+ * {@code https://ap-voc.cim4.eu/DocumentHeader}.
+ *
+ * @param graph The graph to check.
+ * @return true if the graph is a header profile, false otherwise.
+ */
+ public static boolean isCim18HeaderProfile(Graph graph) {
+ if (!hasOntology(graph)) {
+ return false;
}
+ var ontology = getOntology(graph);
+
+ // look for https://ap.cim4.eu/DocumentHeader# without # in version IRIs
+ return graph.stream(ontology, PREDICATE_OWL_VERSION_IRI, Node.ANY)
+ .anyMatch(t
+ -> t.getObject().isURI()
+ && t.getObject().getURI().startsWith(DOCUMENT_HEADER_VERSION_IRI_START));
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/DisjointMultiUnion.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/DisjointMultiUnion.java
index edb21568..393b56c5 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/DisjointMultiUnion.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/DisjointMultiUnion.java
@@ -18,6 +18,8 @@
package de.soptim.opencgmes.cimxml.graph;
+import java.util.Iterator;
+import java.util.stream.Stream;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
@@ -25,74 +27,122 @@
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.util.iterator.NullIterator;
-import java.util.Iterator;
-import java.util.stream.Stream;
-
/**
- * Extends the Apache Jena MultiUnion but find does not elminiate duplicates
- * This is based on from https://github.com/apache/jena/blob/master/jena-core/src/main/java/org/apache/jena/graph/compose/MultiUnion.java
+ * Extends the Apache Jena MultiUnion but find does not elminiate duplicates This is based on from
+ * .
*/
public class DisjointMultiUnion extends MultiUnion {
- public DisjointMultiUnion() {
- super();
- }
+ /**
+ * Create an empty MultiUnion. Graphs can be added to the union using addGraph.
+ */
+ public DisjointMultiUnion() {
+ super();
+ }
- public DisjointMultiUnion(Graph... graphs) {
- super(graphs);
- }
+ /**
+ * Create a MultiUnion with the given graphs as components.
+ *
+ * @param graphs the component graphs of the union
+ */
+ public DisjointMultiUnion(Graph... graphs) {
+ super(graphs);
+ }
- public DisjointMultiUnion(Iterator graphs) {
- super(graphs);
- }
+ /**
+ * Create a MultiUnion with the given graphs as components.
+ *
+ * @param graphs the component graphs of the union
+ */
+ public DisjointMultiUnion(Iterator graphs) {
+ super(graphs);
+ }
- /**
- *
- * Answer an iterator over the triples in the union of the graphs in this composition. Note
- * that the requirement to remove duplicates from the union means that this will be an
- * expensive operation for large (and especially for persistent) graphs.
- *
- *
- * @param t The matcher to match against
- *
- * @return An iterator of all triples matching t in the union of the graphs.
- */
- @Override
- public ExtendedIterator graphBaseFind(Triple t) { // optimise the case where there's only one component graph.
- ExtendedIterator iter = NullIterator.instance();
- for(var g: m_subGraphs) {
- iter = iter.andThen(g.find(t));
- }
- return iter;
+ /**
+ * Answer an iterator over the triples in the union of the graphs in this composition.
+ *
+ *
Note
+ * that this implementation does not eliminate duplicates, so if the same triple appears in
+ * multiple component graphs, it will appear multiple times in the result.
+ * This is a deviation from the standard MultiUnion implementation, which eliminates duplicates.
+ *
+ * @param t The matcher to match against
+ * @return An iterator of all triples matching t in the union of the graphs.
+ */
+ @Override
+ public ExtendedIterator graphBaseFind(
+ Triple t) { // optimise the case where there's only one component graph.
+ ExtendedIterator iter = NullIterator.instance();
+ for (var g : m_subGraphs) {
+ iter = iter.andThen(g.find(t));
}
+ return iter;
+ }
- @Override
- public ExtendedIterator find() {
- ExtendedIterator iter = NullIterator.instance();
- for(var g: m_subGraphs) {
- iter = iter.andThen(g.find());
- }
- return iter;
+ /**
+ * Answer an iterator over all the triples in the union of the graphs in this composition.
+ * This implementation does not eliminate duplicates, so if the same triple appears in multiple
+ * component graphs, it will appear multiple times in the result.
+ * This is a deviation from the standard MultiUnion implementation, which eliminates duplicates.
+ *
+ * @return An iterator of all triples in the union of the graphs.
+ */
+ @Override
+ public ExtendedIterator find() {
+ ExtendedIterator iter = NullIterator.instance();
+ for (var g : m_subGraphs) {
+ iter = iter.andThen(g.find());
}
+ return iter;
+ }
- @Override
- public Stream stream(Node s, Node p, Node o) {
- return m_subGraphs.stream()
- .flatMap(g -> g.stream(s, p, o));
- }
+ /**
+ * Answer a stream over the triples in the union of the graphs in this composition.
+ * This implementation does not eliminate duplicates, so if the same triple appears in multiple
+ * component graphs, it will appear multiple times in the result.
+ * This is a deviation from the standard MultiUnion implementation, which eliminates duplicates.
+ *
+ * @param s the subject node to match, or Node.ANY for a wildcard
+ * @param p the predicate node to match, or Node.ANY for a wildcard
+ * @param o the object node to match, or Node.ANY for a wildcard
+ * @return a stream of all triples matching the given pattern in the union of the graphs.
+ */
+ @Override
+ public Stream stream(Node s, Node p, Node o) {
+ return m_subGraphs.stream()
+ .flatMap(g -> g.stream(s, p, o));
+ }
- @Override
- public Stream stream() {
- return m_subGraphs.stream()
- .flatMap(Graph::stream);
- }
+ /**
+ * Answer a stream over all the triples in the union of the graphs in this composition.
+ * This implementation does not eliminate duplicates, so if the same triple appears in multiple
+ * component graphs, it will appear multiple times in the result.
+ * This is a deviation from the standard MultiUnion implementation, which eliminates duplicates.
+ *
+ * @return a stream of all triples in the union of the graphs.
+ */
+ @Override
+ public Stream stream() {
+ return m_subGraphs.stream()
+ .flatMap(Graph::stream);
+ }
- @Override
- protected int graphBaseSize() {
- var size = 0;
- for(var g: m_subGraphs) {
- size += g.size();
- }
- return size;
+ /**
+ * Answer the number of triples in the union of the graphs in this composition.
+ * This implementation does not eliminate duplicates, so if the same triple appears in multiple
+ * component graphs, it will be counted multiple times in the result.
+ * This is a deviation from the standard MultiUnion implementation, which eliminates duplicates.
+ *
+ * @return The number of triples in the union of the graphs.
+ */
+ @Override
+ protected int graphBaseSize() {
+ var size = 0;
+ for (var g : m_subGraphs) {
+ size += g.size();
}
+ return size;
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/FastDeltaGraph.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/FastDeltaGraph.java
index 376e2ce1..d3285b89 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/FastDeltaGraph.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/graph/FastDeltaGraph.java
@@ -18,142 +18,185 @@
package de.soptim.opencgmes.cimxml.graph;
+import java.util.Iterator;
+import java.util.stream.Stream;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.graph.compose.Delta;
import org.apache.jena.graph.impl.GraphBase;
-import org.apache.jena.mem2.GraphMem2Roaring;
-import org.apache.jena.mem2.IndexingStrategy;
+import org.apache.jena.sparql.graph.GraphFactory;
import org.apache.jena.util.iterator.ExtendedIterator;
-import java.util.Iterator;
-import java.util.stream.Stream;
-
/**
- * Faster alternative to {@link Delta}
+ * Faster alternative to {@link Delta}.
*/
public class FastDeltaGraph extends GraphBase {
- private final Graph base;
- private final Graph additions;
- private final Graph deletions;
-
- public FastDeltaGraph(Graph base) {
- super();
- if (base == null)
- throw new IllegalArgumentException("base graph must not be null");
- this.base = base;
- this.additions = new GraphMem2Roaring(IndexingStrategy.LAZY_PARALLEL);
- this.deletions = new GraphMem2Roaring(IndexingStrategy.LAZY_PARALLEL);
- }
-
- /**
- * Creates a new {@link FastDeltaGraph} that is based on the given {@code newBase} graph.
- * This is used to rebase a {@link FastDeltaGraph} on a new base graph.
- * There are no checks performed to ensure that the new base graph is compatible with the
- * previous base graph.
- * @param newBase the new base graph
- * @param deltaGraphToRebase the delta graph to rebase
- */
- public FastDeltaGraph(Graph newBase, FastDeltaGraph deltaGraphToRebase) {
- super();
- if (newBase == null)
- throw new IllegalArgumentException("base graph must not be null");
- this.base = newBase;
- this.additions = deltaGraphToRebase.additions;
- this.deletions = deltaGraphToRebase.deletions;
- }
-
- public FastDeltaGraph(Graph base, Graph additions, Graph deletions) {
- super();
- if (base == null)
- throw new IllegalArgumentException("base graph must not be null");
- this.base = base;
- this.additions = additions;
- this.deletions = deletions;
- }
-
- public Iterator getAdditions() {
- return additions.find();
- }
-
- public Iterator getDeletions() {
- return deletions.find();
- }
-
- public boolean hasChanges() {
- return !additions.isEmpty() || !deletions.isEmpty();
- }
-
- public Graph getBase() {
- return base;
- }
-
- @Override
- public void performAdd(Triple t) {
- if (!base.contains(t))
- additions.add(t);
- deletions.delete(t);
- }
-
- @Override
- public void performDelete(Triple t) {
- additions.delete(t);
- if (base.contains(t))
- deletions.add(t);
- }
-
- @Override
- protected boolean graphBaseContains(Triple t) {
- if (t.isConcrete()) {
- if (base.contains(t)) {
- return !deletions.contains(t);
- }
- return additions.contains(t);
- } else {
- return graphBaseFind(t).hasNext();
- }
- }
-
- @Override
- protected ExtendedIterator graphBaseFind(Triple triplePattern) {
- return base.find(triplePattern)
- .filterDrop(deletions::contains)
- .andThen(additions.find(triplePattern));
- }
-
- @Override
- public ExtendedIterator find() {
- return base.find()
- .filterDrop(deletions::contains)
- .andThen(additions.find());
- }
-
- @Override
- public Stream stream() {
- return Stream.concat(
- base.stream().filter(t -> !deletions.contains(t)),
- additions.stream());
- }
-
- @Override
- public Stream stream(Node s, Node p, Node o) {
- return Stream.concat(
- base.stream(s, p, o).filter(t -> !deletions.contains(t)),
- additions.stream(s, p, o));
- }
-
- @Override
- public void close() {
- super.close();
- base.close();
- additions.close();
- deletions.close();
- }
-
- @Override
- public int graphBaseSize() {
- return base.size() + additions.size() - deletions.size();
- }
+ private final Graph base;
+ private final Graph additions;
+ private final Graph deletions;
+
+ /**
+ * Creates a new {@link FastDeltaGraph} that is based on the given {@code base} graph. The delta
+ * graph will initially be empty, i.e. it will not contain any additions or deletions.
+ *
+ * @param base the base graph on which this delta graph is based
+ */
+ public FastDeltaGraph(Graph base) {
+ super();
+ if (base == null) {
+ throw new IllegalArgumentException("base graph must not be null");
+ }
+ this.base = base;
+ this.additions = GraphFactory.createGraphMem();
+ this.deletions = GraphFactory.createGraphMem();
+ }
+
+ /**
+ * Creates a new {@link FastDeltaGraph} that is based on the given {@code newBase} graph. This is
+ * used to rebase a {@link FastDeltaGraph} on a new base graph. There are no checks performed to
+ * ensure that the new base graph is compatible with the previous base graph.
+ *
+ * @param newBase the new base graph
+ * @param deltaGraphToRebase the delta graph to rebase
+ */
+ public FastDeltaGraph(Graph newBase, FastDeltaGraph deltaGraphToRebase) {
+ super();
+ if (newBase == null) {
+ throw new IllegalArgumentException("base graph must not be null");
+ }
+ this.base = newBase;
+ this.additions = deltaGraphToRebase.additions;
+ this.deletions = deltaGraphToRebase.deletions;
+ }
+
+ /**
+ * Creates a new {@link FastDeltaGraph} that is based on the given {@code base} graph and contains
+ * the given additions and deletions. This constructor is used to create a delta graph with
+ * predefined additions and deletions, for example when rebasing a delta graph on a new base
+ * graph.
+ *
+ * @param base the base graph on which this delta graph is based
+ * @param additions the graph containing the triples that are added in this delta graph
+ * @param deletions the graph containing the triples that are deleted in this delta graph
+ */
+ public FastDeltaGraph(Graph base, Graph additions, Graph deletions) {
+ super();
+ if (base == null) {
+ throw new IllegalArgumentException("base graph must not be null");
+ }
+ this.base = base;
+ this.additions = additions;
+ this.deletions = deletions;
+ }
+
+ /**
+ * Returns an iterator over the triples that are added in this delta graph.
+ * These are the triples that are present in the delta graph but not in the base graph.
+ *
+ * @return an iterator over the added triples in this delta graph
+ */
+ public Iterator getAdditions() {
+ return additions.find();
+ }
+
+ /**
+ * Returns an iterator over the triples that are deleted in this delta graph.
+ * These are the triples that are present in the base graph but not in the delta graph.
+ *
+ * @return an iterator over the deleted triples in this delta graph
+ */
+ public Iterator getDeletions() {
+ return deletions.find();
+ }
+
+ /**
+ * Checks if this delta graph has any changes compared to the base graph. A delta graph is
+ * considered to have changes if it contains any additions or deletions.
+ *
+ * @return true if this delta graph has any changes, false otherwise
+ */
+ public boolean hasChanges() {
+ return !additions.isEmpty() || !deletions.isEmpty();
+ }
+
+ /**
+ * Returns the base graph on which this delta graph is based. The base graph represents the state
+ * of the graph before any additions or deletions are applied.
+ *
+ * @return the base graph of this delta graph
+ */
+ public Graph getBase() {
+ return base;
+ }
+
+ @Override
+ public void performAdd(Triple t) {
+ if (!base.contains(t)) {
+ additions.add(t);
+ }
+ deletions.delete(t);
+ }
+
+ @Override
+ public void performDelete(Triple t) {
+ additions.delete(t);
+ if (base.contains(t)) {
+ deletions.add(t);
+ }
+ }
+
+ @Override
+ protected boolean graphBaseContains(Triple t) {
+ if (t.isConcrete()) {
+ if (base.contains(t)) {
+ return !deletions.contains(t);
+ }
+ return additions.contains(t);
+ } else {
+ return graphBaseFind(t).hasNext();
+ }
+ }
+
+ @Override
+ protected ExtendedIterator graphBaseFind(Triple triplePattern) {
+ return base.find(triplePattern)
+ .filterDrop(deletions::contains)
+ .andThen(additions.find(triplePattern));
+ }
+
+ @Override
+ public ExtendedIterator find() {
+ return base.find()
+ .filterDrop(deletions::contains)
+ .andThen(additions.find());
+ }
+
+ @Override
+ public Stream stream() {
+ return Stream.concat(
+ base.stream().filter(t -> !deletions.contains(t)),
+ additions.stream());
+ }
+
+ @Override
+ public Stream stream(Node s, Node p, Node o) {
+ return Stream.concat(
+ base.stream(s, p, o).filter(t -> !deletions.contains(t)),
+ additions.stream(s, p, o));
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ base.close();
+ additions.close();
+ deletions.close();
+ }
+
+ @Override
+ public int graphBaseSize() {
+ return base.size() + additions.size() - deletions.size();
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/CimXmlParser.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/CimXmlParser.java
index 5acf256d..4a3d56f4 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/CimXmlParser.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/CimXmlParser.java
@@ -19,27 +19,26 @@
package de.soptim.opencgmes.cimxml.parser;
import de.soptim.opencgmes.cimxml.graph.CimProfile;
-import de.soptim.opencgmes.cimxml.parser.system.StreamCIMXMLToDatasetGraph;
+import de.soptim.opencgmes.cimxml.parser.system.StreamCimXmlToDatasetGraph;
import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistry;
import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistryStd;
import de.soptim.opencgmes.cimxml.sparql.core.CimDatasetGraph;
-import org.apache.commons.io.input.BufferedFileChannelInputStream;
-import org.apache.jena.riot.system.ErrorHandler;
-import org.apache.jena.riot.system.ErrorHandlerFactory;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
+import org.apache.commons.io.input.BufferedFileChannelInputStream;
+import org.apache.jena.riot.system.ErrorHandler;
+import org.apache.jena.riot.system.ErrorHandlerFactory;
/**
* IEC 61970-552 CIMXML parser for OpenCGMES.
*
*
This parser provides specialized handling for Common Information Model (CIM) XML files
- * as defined by the IEC 61970-552 standard. It extends standard RDF/XML parsing with
- * CIM-specific features including:
+ * as defined by the IEC 61970-552 standard. It extends standard RDF/XML parsing with CIM-specific
+ * features including:
*
*
*
Automatic CIM version detection from namespace declarations
@@ -68,6 +67,7 @@
* }
*
*
Thread Safety:
+ *
*
This class is thread-safe for parsing operations. The internal profile registry
* is synchronized and can be safely accessed from multiple threads.
*
@@ -77,93 +77,102 @@
*/
public class CimXmlParser {
- private final ReaderCIMXML_StAX_SR reader;
- private final CimProfileRegistry cimProfileRegistry;
- private final RdfXmlParser rdfXmlParser;
+ private final ReaderCIMXML_StAX_SR reader;
+ private final CimProfileRegistry cimProfileRegistry;
+ private final RdfXmlParser rdfXmlParser;
- /**
- * Gets the error handler used by this parser.
- * @return the error handler
- */
- public ErrorHandler getErrorHandler() {
- return reader.errorHandler;
- }
+ /**
+ * Creates a new CIMXML parser with the standard error handler.
+ */
+ public CimXmlParser() {
+ this(ErrorHandlerFactory.errorHandlerStd);
+ }
- /**
- * Gets the CIM profile registry used by this parser.
- * @return the CIM profile registry
- */
- public CimProfileRegistry getCimProfileRegistry() {
- return cimProfileRegistry;
- }
+ /**
+ * Creates a new CIMXML parser with the given error handler.
+ *
+ * @param errorHandler the error handler
+ */
+ public CimXmlParser(final ErrorHandler errorHandler) {
+ this.reader = new ReaderCIMXML_StAX_SR(errorHandler);
+ this.rdfXmlParser = new RdfXmlParser(this.reader);
+ this.cimProfileRegistry = new CimProfileRegistryStd();
+ }
- /**
- * Creates a new CIMXML parser with the standard error handler.
- */
- public CimXmlParser() {
- this(ErrorHandlerFactory.errorHandlerStd);
- }
+ /**
+ * Gets the error handler used by this parser.
+ *
+ * @return the error handler
+ */
+ public ErrorHandler getErrorHandler() {
+ return reader.errorHandler;
+ }
- /**
- * Creates a new CIMXML parser with the given error handler.
- * @param errorHandler the error handler
- */
- public CimXmlParser(final ErrorHandler errorHandler) {
- this.reader = new ReaderCIMXML_StAX_SR(errorHandler);
- this.rdfXmlParser = new RdfXmlParser(this.reader);
- this.cimProfileRegistry = new CimProfileRegistryStd();
- }
+ /**
+ * Gets the CIM profile registry used by this parser.
+ *
+ * @return the CIM profile registry
+ */
+ public CimProfileRegistry getCimProfileRegistry() {
+ return cimProfileRegistry;
+ }
- /**
- * Parses the CIM profile from the given path and registers it in the internal CIM profile registry.
- * @param pathToCimProfile the path to the CIM profile
- * @return the parsed CIM profile
- * @throws IOException if an I/O error occurs
- */
- public CimProfile parseAndRegisterCimProfile(final Path pathToCimProfile) throws IOException {
- final var profile = rdfXmlParser.parseCimProfile(pathToCimProfile);
- cimProfileRegistry.register(profile);
- return profile;
- }
+ /**
+ * Parses the CIM profile from the given path and registers it in the internal CIM profile
+ * registry.
+ *
+ * @param pathToCimProfile the path to the CIM profile
+ * @return the parsed CIM profile
+ * @throws IOException if an I/O error occurs
+ */
+ public CimProfile parseAndRegisterCimProfile(final Path pathToCimProfile) throws IOException {
+ final var profile = rdfXmlParser.parseCimProfile(pathToCimProfile);
+ cimProfileRegistry.register(profile);
+ return profile;
+ }
- /**
- * Parses the CIMXML from the given reader and returns the resulting CIM dataset graph.
- * @param reader the reader containing the CIMXML
- * @return the resulting CIM dataset graph
- */
- public CimDatasetGraph parseCimModel(final Reader reader) {
- final var streamRDFProfile = new StreamCIMXMLToDatasetGraph();
- this.reader.read(reader, cimProfileRegistry, streamRDFProfile);
- return streamRDFProfile.getCIMDatasetGraph();
- }
+ /**
+ * Parses the CIMXML from the given reader and returns the resulting CIM dataset graph.
+ *
+ * @param reader the reader containing the CIMXML
+ * @return the resulting CIM dataset graph
+ */
+ public CimDatasetGraph parseCimModel(final Reader reader) {
+ final var streamRdfProfile = new StreamCimXmlToDatasetGraph();
+ this.reader.read(reader, cimProfileRegistry, streamRdfProfile);
+ return streamRdfProfile.getCimDatasetGraph();
+ }
- /**
- * Parses the CIMXML from the given input stream and returns the resulting CIM dataset graph.
- * @param inputStream the input stream containing the CIMXML
- * @return the resulting CIM dataset graph
- */
- public CimDatasetGraph parseCimModel(final InputStream inputStream) {
- final var streamRDFProfile = new StreamCIMXMLToDatasetGraph();
- this.reader.read(inputStream, cimProfileRegistry, streamRDFProfile);
- return streamRDFProfile.getCIMDatasetGraph();
- }
+ /**
+ * Parses the CIMXML from the given input stream and returns the resulting CIM dataset graph.
+ *
+ * @param inputStream the input stream containing the CIMXML
+ * @return the resulting CIM dataset graph
+ */
+ public CimDatasetGraph parseCimModel(final InputStream inputStream) {
+ final var streamRdfProfile = new StreamCimXmlToDatasetGraph();
+ this.reader.read(inputStream, cimProfileRegistry, streamRdfProfile);
+ return streamRdfProfile.getCimDatasetGraph();
+ }
- /**
- * Parses the CIMXML file at the given path and returns the resulting CIM dataset graph.
- * @param pathToCimModel the path to the CIMXML file
- * @return the resulting CIM dataset graph
- * @throws IOException if an I/O error occurs
- */
- public CimDatasetGraph parseCimModel(final Path pathToCimModel) throws IOException {
- final var fileSize = Files.size(pathToCimModel);
- final var streamRDFProfile = new StreamCIMXMLToDatasetGraph();
- try(final var is = new BufferedFileChannelInputStream.Builder()
- .setPath(pathToCimModel)
- .setOpenOptions(StandardOpenOption.READ)
- .setBufferSize((fileSize > RdfXmlParser.MAX_BUFFER_SIZE) ? RdfXmlParser.MAX_BUFFER_SIZE : (int) fileSize)
- .get()) {
- this.reader.read(is, cimProfileRegistry, streamRDFProfile);
- }
- return streamRDFProfile.getCIMDatasetGraph();
+ /**
+ * Parses the CIMXML file at the given path and returns the resulting CIM dataset graph.
+ *
+ * @param pathToCimModel the path to the CIMXML file
+ * @return the resulting CIM dataset graph
+ * @throws IOException if an I/O error occurs
+ */
+ public CimDatasetGraph parseCimModel(final Path pathToCimModel) throws IOException {
+ final var fileSize = Files.size(pathToCimModel);
+ final var streamRdfProfile = new StreamCimXmlToDatasetGraph();
+ try (var is = new BufferedFileChannelInputStream.Builder()
+ .setPath(pathToCimModel)
+ .setOpenOptions(StandardOpenOption.READ)
+ .setBufferSize((fileSize > RdfXmlParser.MAX_BUFFER_SIZE) ? RdfXmlParser.MAX_BUFFER_SIZE
+ : (int) fileSize)
+ .get()) {
+ this.reader.read(is, cimProfileRegistry, streamRdfProfile);
}
+ return streamRdfProfile.getCimDatasetGraph();
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ParserCIMXML_StAX_SR.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ParserCIMXML_StAX_SR.java
index b570b401..329666d4 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ParserCIMXML_StAX_SR.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ParserCIMXML_StAX_SR.java
@@ -19,9 +19,9 @@
package de.soptim.opencgmes.cimxml.parser;
import de.soptim.opencgmes.cimxml.CimHeaderVocabulary;
-import de.soptim.opencgmes.cimxml.CimVersion;
import de.soptim.opencgmes.cimxml.CimXmlDocumentContext;
-import de.soptim.opencgmes.cimxml.parser.system.StreamCIMXML;
+import de.soptim.opencgmes.cimxml.graph.CimNamespaceFactoryRegistry;
+import de.soptim.opencgmes.cimxml.parser.system.StreamCimXml;
import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistry;
import org.apache.commons.lang3.StringUtils;
import org.apache.jena.atlas.io.IndentedWriter;
@@ -68,6 +68,7 @@
* This implementation is based on the RDF/XML parser ParserRRX_StAX_SR in Apache Jena, originally.
* It has been adapted to handle CIMXML specifics.
*/
+@SuppressWarnings("PMD")
public class ParserCIMXML_StAX_SR {
static {
@@ -78,7 +79,7 @@ public class ParserCIMXML_StAX_SR {
}
}
- private final static int IRI_CACHE_SIZE = 8192;
+ private static final int IRI_CACHE_SIZE = 8192;
private final IndentedWriter trace;
private final XMLStreamReader2 xmlSource;
@@ -99,12 +100,12 @@ public class ParserCIMXML_StAX_SR {
private boolean hasRDF = false;
private boolean hasCimXmlNamespace = false;
private boolean isCimXmlModel = false;
- private CimVersion versionOfCIMXML = CimVersion.NO_CIM;
+ private String versionOfCIMXML = null;
private final ErrorHandler errorHandler;
- private final StreamCIMXML destination;
+ private final StreamCimXml destination;
private final CimProfileRegistry cimProfileRegistry;
- private final static String PROCESSING_INSTRUCTION_IEC61970_552 = "iec61970-552";
+ private static final String PROCESSING_INSTRUCTION_IEC61970_552 = "iec61970-552";
private void updateCurrentIriCacheForCurrentBase() {
if (currentBase != null) {
@@ -216,7 +217,7 @@ private Location previousUseOfID(String idStr, Location location) {
private static class Counter { int value = 1; }
public ParserCIMXML_StAX_SR(XMLStreamReader2 reader, CimProfileRegistry cimProfileRegistry, String xmlBase,
- StreamCIMXML destination, ErrorHandler errorHandler) {
+ StreamCimXml destination, ErrorHandler errorHandler) {
// Debug
IndentedWriter out = IndentedWriter.stdout.clone();
out.setFlushOnNewline(true);
@@ -405,7 +406,7 @@ void parse() {
// Not necessary to track this element when parsing.
hasFrame = startElement();
// Only the XML base and namespaces that apply throughout rdf:RDF are parser output.
- emitInitialBaseAndNamespacesDetermineCIMVersionAndSetBaseIfNeeded();
+ emitInitialBaseAndNamespacesDetermineCimNamespaceAndSetBaseIfNeeded();
hasRDF = true;
eventType = nextEventTag();
}
@@ -1399,7 +1400,7 @@ private int nextEventAny() {
if ( lookingAt(ev, PROCESSING_INSTRUCTION) ) {
if (PROCESSING_INSTRUCTION_IEC61970_552.equals(xmlSource.getPITarget())) {
final var versionOfIEC61970_552 = xmlSource.getPIData();
- destination.setVersionOfIEC61970_552(versionOfIEC61970_552);
+ destination.setVersionOfIec61970_552(versionOfIEC61970_552);
} else {
RDFXMLparseWarning("XML Processing instruction - ignored");
}
@@ -1482,7 +1483,7 @@ private void endElement(boolean hasFrame) {
// ---- Parser output
- private void emitInitialBaseAndNamespacesDetermineCIMVersionAndSetBaseIfNeeded() {
+ private void emitInitialBaseAndNamespacesDetermineCimNamespaceAndSetBaseIfNeeded() {
int numNS = xmlSource.getNamespaceCount();
for ( int i = 0 ; i < numNS ; i++ ) {
@@ -1492,9 +1493,9 @@ private void emitInitialBaseAndNamespacesDetermineCIMVersionAndSetBaseIfNeeded()
prefix = "";
} else if ("cim".equals(prefix)) {
// Determine CIM version.
- versionOfCIMXML = CimVersion.fromCimNamespace(prefixURI);
- if (versionOfCIMXML == CimVersion.NO_CIM) {
- RDFXMLparseWarning("Unrecognized 'cim' namespace: " + prefixURI, location());
+ versionOfCIMXML = prefixURI;
+ if (!CimNamespaceFactoryRegistry.hasProfileFactory(prefixURI)) {
+ RDFXMLparseWarning("The provided 'cim' namespace: " + prefixURI + " is not registered in CimNamespaceMapper.", location());
}
if (ReaderCIMXML_StAX_SR.TRACE) {
trace.printf("CIM version of CIMXML: %s\n", versionOfCIMXML);
@@ -1504,9 +1505,9 @@ private void emitInitialBaseAndNamespacesDetermineCIMVersionAndSetBaseIfNeeded()
}
String xmlBase = attribute(xmlQNameBase);
- if (versionOfCIMXML != CimVersion.NO_CIM) {
+ if (versionOfCIMXML != null) {
hasCimXmlNamespace = true;
- destination.setVersionOfCIMXML(versionOfCIMXML);
+ destination.setCimNamespace(versionOfCIMXML);
if (xmlBase == null) {
xmlBase = xmlBaseForCIMXML;
currentBase = IRIx.create(xmlBase);
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/RdfXmlParser.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/RdfXmlParser.java
index a8f2f2f2..2a68ee4f 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/RdfXmlParser.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/RdfXmlParser.java
@@ -19,135 +19,140 @@
package de.soptim.opencgmes.cimxml.parser;
import de.soptim.opencgmes.cimxml.graph.CimProfile;
-import de.soptim.opencgmes.cimxml.parser.system.StreamCIMXMLToDatasetGraph;
-import org.apache.commons.io.input.BufferedFileChannelInputStream;
-import org.apache.jena.graph.Graph;
-import org.apache.jena.riot.system.ErrorHandler;
-import org.apache.jena.riot.system.ErrorHandlerFactory;
-
+import de.soptim.opencgmes.cimxml.parser.system.StreamCimXmlToDatasetGraph;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
+import org.apache.commons.io.input.BufferedFileChannelInputStream;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.riot.system.ErrorHandler;
+import org.apache.jena.riot.system.ErrorHandlerFactory;
/**
- * RDF/XML parser.
- * This implementation uses ReaderCIMXML_StAX_SR, which is based on the RDF/XML reader ReaderRDFXML_StAX_SR
- * in Apache Jena, originally.
- * It has been adapted to the CIMXML needs.
- *
- * This implementation uses StAX via {@link javax.xml.stream.XMLStreamReader}.
+ * RDF/XML parser. This implementation uses ReaderCIMXML_StAX_SR, which is based on the RDF/XML
+ * reader ReaderRDFXML_StAX_SR in Apache Jena, originally. It has been adapted to the CIMXML needs.
+ *
+ *
This implementation uses StAX via {@link javax.xml.stream.XMLStreamReader}.
*
- * @see https://webstore.iec.ch/en/publication/25939
+ * @see https://webstore.iec.ch/en/publication/25939
*/
public class RdfXmlParser {
- final static int MAX_BUFFER_SIZE = 64*4096;
-
- private final ReaderCIMXML_StAX_SR reader;
-
- /**
- * Gets the error handler used by this parser.
- * @return the error handler
- */
- public ErrorHandler getErrorHandler() {
- return reader.errorHandler;
- }
-
- /**
- * Creates a new RDF/XML parser with the standard error handler.
- */
- public RdfXmlParser() {
- this(ErrorHandlerFactory.errorHandlerStd);
- }
-
- /**
- * Creates a new RDF/XML parser with the given reader.
- * @param reader the reader to use
- */
- RdfXmlParser(final ReaderCIMXML_StAX_SR reader) {
- this.reader = reader;
+ static final int MAX_BUFFER_SIZE = 64 * 4096;
+
+ private final ReaderCIMXML_StAX_SR reader;
+
+ /**
+ * Creates a new RDF/XML parser with the standard error handler.
+ */
+ public RdfXmlParser() {
+ this(ErrorHandlerFactory.errorHandlerStd);
+ }
+
+ /**
+ * Creates a new RDF/XML parser with the given reader.
+ *
+ * @param reader the reader to use
+ */
+ RdfXmlParser(final ReaderCIMXML_StAX_SR reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Creates a new RDF/XML parser with the given error handler.
+ *
+ * @param errorHandler the error handler to use
+ */
+ public RdfXmlParser(final ErrorHandler errorHandler) {
+ this(new ReaderCIMXML_StAX_SR(errorHandler));
+ }
+
+ /**
+ * Gets the error handler used by this parser.
+ *
+ * @return the error handler
+ */
+ public ErrorHandler getErrorHandler() {
+ return reader.errorHandler;
+ }
+
+ /**
+ * Parses the RDF/XML from the given reader and returns the resulting CIM profile.
+ *
+ * @param reader the reader containing RDF/XML data
+ * @return the resulting CIM profile
+ */
+ public CimProfile parseCimProfile(final Reader reader) {
+ return CimProfile.wrap(parseGraph(reader));
+ }
+
+ /**
+ * Parses the RDF/XML from the given input stream and returns the resulting CIM profile.
+ *
+ * @param inputStream the input stream containing RDF/XML data
+ * @return the resulting CIM profile
+ */
+ public CimProfile parseCimProfile(final InputStream inputStream) {
+ return CimProfile.wrap(parseGraph(inputStream));
+ }
+
+ /**
+ * Parses the RDF/XML file at the given path and returns the resulting CIM profile.
+ *
+ * @param pathToCimProfile the path to the RDF/XML file
+ * @return the resulting CIM profile
+ * @throws IOException if an I/O error occurs
+ */
+ public CimProfile parseCimProfile(final Path pathToCimProfile) throws IOException {
+ return CimProfile.wrap(parseGraph(pathToCimProfile));
+ }
+
+ /**
+ * Parses the RDF/XML from the given reader and returns the resulting graph.
+ *
+ * @param reader the reader containing RDF/XML data
+ * @return the resulting graph
+ */
+ public Graph parseGraph(final Reader reader) {
+ final var streamRdfProfile = new StreamCimXmlToDatasetGraph();
+ this.reader.read(reader, streamRdfProfile);
+ return streamRdfProfile.getCimDatasetGraph().getDefaultGraph();
+ }
+
+ /**
+ * Parses the RDF/XML from the given input stream and returns the resulting graph.
+ *
+ * @param inputStream the input stream containing RDF/XML data
+ * @return the resulting graph
+ */
+ public Graph parseGraph(final InputStream inputStream) {
+ final var streamRdfProfile = new StreamCimXmlToDatasetGraph();
+ this.reader.read(inputStream, streamRdfProfile);
+ return streamRdfProfile.getCimDatasetGraph().getDefaultGraph();
+ }
+
+ /**
+ * Parses the RDF/XML file at the given path and returns the resulting graph.
+ *
+ * @param rdfxmlFilePath the path to the RDF/XML file
+ * @return the resulting graph
+ * @throws IOException if an I/O error occurs
+ */
+ public Graph parseGraph(final Path rdfxmlFilePath) throws IOException {
+ final var fileSize = Files.size(rdfxmlFilePath);
+ final var streamRdfProfile = new StreamCimXmlToDatasetGraph();
+ try (var is = new BufferedFileChannelInputStream.Builder()
+ .setPath(rdfxmlFilePath)
+ .setOpenOptions(StandardOpenOption.READ)
+ .setBufferSize((fileSize > MAX_BUFFER_SIZE) ? MAX_BUFFER_SIZE : (int) fileSize)
+ .get()) {
+ reader.read(is, streamRdfProfile);
}
-
- /**
- * Creates a new RDF/XML parser with the given error handler.
- * @param errorHandler the error handler to use
- */
- public RdfXmlParser(final ErrorHandler errorHandler) {
- this(new ReaderCIMXML_StAX_SR(errorHandler));
- }
-
- /**
- * Parses the RDF/XML from the given reader and returns the resulting CIM profile.
- * @param reader the reader containing RDF/XML data
- * @return the resulting CIM profile
- */
- public CimProfile parseCimProfile(final Reader reader) {
- return CimProfile.wrap(parseGraph(reader));
- }
-
- /**
- * Parses the RDF/XML from the given input stream and returns the resulting CIM profile.
- * @param inputStream the input stream containing RDF/XML data
- * @return the resulting CIM profile
- */
- public CimProfile parseCimProfile(final InputStream inputStream) {
- return CimProfile.wrap(parseGraph(inputStream));
- }
-
- /**
- * Parses the RDF/XML file at the given path and returns the resulting CIM profile.
- * @param pathToCimProfile the path to the RDF/XML file
- * @return the resulting CIM profile
- * @throws IOException if an I/O error occurs
- */
- public CimProfile parseCimProfile(final Path pathToCimProfile) throws IOException {
- return CimProfile.wrap(parseGraph(pathToCimProfile));
- }
-
- /**
- * Parses the RDF/XML from the given reader and returns the resulting graph.
- * @param reader the reader containing RDF/XML data
- * @return the resulting graph
- */
- public Graph parseGraph(final Reader reader) {
- final var streamRDFProfile = new StreamCIMXMLToDatasetGraph();
- this.reader.read(reader, streamRDFProfile);
- return streamRDFProfile.getCIMDatasetGraph().getDefaultGraph();
- }
-
- /**
- * Parses the RDF/XML from the given input stream and returns the resulting graph.
- * @param inputStream the input stream containing RDF/XML data
- * @return the resulting graph
- */
- public Graph parseGraph(final InputStream inputStream) {
- final var streamRDFProfile = new StreamCIMXMLToDatasetGraph();
- this.reader.read(inputStream, streamRDFProfile);
- return streamRDFProfile.getCIMDatasetGraph().getDefaultGraph();
- }
-
- /**
- * Parses the RDF/XML file at the given path and returns the resulting graph.
- * @param rdfxmlFilePath the path to the RDF/XML file
- * @return the resulting graph
- * @throws IOException if an I/O error occurs
- */
- public Graph parseGraph(final Path rdfxmlFilePath) throws IOException {
- final var fileSize = Files.size(rdfxmlFilePath);
- final var streamRDFProfile = new StreamCIMXMLToDatasetGraph();
- try(final var is = new BufferedFileChannelInputStream.Builder()
- .setPath(rdfxmlFilePath)
- .setOpenOptions(StandardOpenOption.READ)
- .setBufferSize((fileSize > MAX_BUFFER_SIZE) ? MAX_BUFFER_SIZE : (int) fileSize)
- .get()) {
- reader.read(is, streamRDFProfile);
- }
- return streamRDFProfile.getCIMDatasetGraph().getDefaultGraph();
- }
-
-
+ return streamRdfProfile.getCimDatasetGraph().getDefaultGraph();
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ReaderCIMXML_StAX_SR.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ReaderCIMXML_StAX_SR.java
index d56ca5a1..f74eb882 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ReaderCIMXML_StAX_SR.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/ReaderCIMXML_StAX_SR.java
@@ -18,7 +18,7 @@
package de.soptim.opencgmes.cimxml.parser;
-import de.soptim.opencgmes.cimxml.parser.system.StreamCIMXML;
+import de.soptim.opencgmes.cimxml.parser.system.StreamCimXml;
import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistry;
import org.apache.jena.riot.RiotException;
import org.apache.jena.riot.lang.rdfxml.SysRRX;
@@ -41,6 +41,7 @@
*
* @see https://webstore.iec.ch/en/publication/25939
*/
+@SuppressWarnings("PMD")
public class ReaderCIMXML_StAX_SR
{
public static XMLInputFactory2 createXMLInputFactory() {
@@ -63,19 +64,19 @@ public ReaderCIMXML_StAX_SR(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
- public void read(InputStream input, StreamCIMXML output) {
+ public void read(InputStream input, StreamCimXml output) {
read(input, null, null, output);
}
- public void read(InputStream input, CimProfileRegistry cimProfileRegistry, StreamCIMXML output) {
+ public void read(InputStream input, CimProfileRegistry cimProfileRegistry, StreamCimXml output) {
read(input, cimProfileRegistry, null, output);
}
- public void read(InputStream input, String xmlBase, StreamCIMXML output) {
+ public void read(InputStream input, String xmlBase, StreamCimXml output) {
read(input, null, xmlBase, output);
}
- public void read(InputStream input, CimProfileRegistry cimProfileRegistry, String xmlBase, StreamCIMXML output) {
+ public void read(InputStream input, CimProfileRegistry cimProfileRegistry, String xmlBase, StreamCimXml output) {
try {
var xmlStreamReader = (XMLStreamReader2) xmlInputFactory.createXMLStreamReader(input);
parse(xmlStreamReader, cimProfileRegistry, xmlBase, output);
@@ -84,19 +85,19 @@ public void read(InputStream input, CimProfileRegistry cimProfileRegistry, Strin
}
}
- public void read(Reader reader, StreamCIMXML output) {
+ public void read(Reader reader, StreamCimXml output) {
read(reader, null, null, output);
}
- public void read(Reader reader, String xmlBase, StreamCIMXML output) {
+ public void read(Reader reader, String xmlBase, StreamCimXml output) {
read(reader, null, xmlBase, output);
}
- public void read(Reader reader, CimProfileRegistry cimProfileRegistry, StreamCIMXML output) {
+ public void read(Reader reader, CimProfileRegistry cimProfileRegistry, StreamCimXml output) {
read(reader, cimProfileRegistry, null, output);
}
- public void read(Reader reader, CimProfileRegistry cimProfileRegistry, String xmlBase, StreamCIMXML output) {
+ public void read(Reader reader, CimProfileRegistry cimProfileRegistry, String xmlBase, StreamCimXml output) {
try {
var xmlStreamReader = (XMLStreamReader2) xmlInputFactory.createXMLStreamReader(reader);
parse(xmlStreamReader, cimProfileRegistry, xmlBase, output);
@@ -106,7 +107,7 @@ public void read(Reader reader, CimProfileRegistry cimProfileRegistry, String xm
}
private void parse(XMLStreamReader2 xmlStreamReader, CimProfileRegistry cimProfileRegistry, String xmlBase,
- StreamCIMXML destination) {
+ StreamCimXml destination) {
var parser = new ParserCIMXML_StAX_SR(xmlStreamReader, cimProfileRegistry, xmlBase, destination, errorHandler);
destination.start();
try {
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCIMXML.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCIMXML.java
deleted file mode 100644
index 59167a9f..00000000
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCIMXML.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 de.soptim.opencgmes.cimxml.parser.system;
-
-import de.soptim.opencgmes.cimxml.CimVersion;
-import de.soptim.opencgmes.cimxml.CimXmlDocumentContext;
-import de.soptim.opencgmes.cimxml.graph.CimModelHeader;
-import de.soptim.opencgmes.cimxml.sparql.core.CimDatasetGraph;
-import org.apache.jena.riot.system.StreamRDF;
-
-/**
- * An extension of {@link StreamRDF} to provide access to the underlying {@link CimDatasetGraph}
- * and to store additional information about the CIMXML file being processed.
- */
-public interface StreamCIMXML extends StreamRDF {
-
- /**
- * Gets the underlying {@link CimDatasetGraph} that is being populated
- */
- CimDatasetGraph getCIMDatasetGraph();
-
- /**
- * Gets the model header information as found in the CIMXML file
- */
- CimModelHeader getModelHeader();
-
- /**
- * Sets the preprocessing instruction version of IEC 61970-552
- * @param versionOfCIMXML the version string as found in the CIMXML file
- */
- void setVersionOfIEC61970_552(String versionOfCIMXML);
-
- /**
- * Gets the preprocessing instruction version of IEC 61970-552
- * @return the version string as found in the CIMXML file
- */
- String getVersionOfIEC61970_552();
-
- /**
- * Gets the CIMXMLVersion enum value for the given version string
- * @return the CIMXMLVersion enum value
- */
- CimVersion getVersionOfCIMXML();
-
- /**
- * Sets the CIMXMLVersion enum value for the given version string
- * @param versionOfCIMXML the CIMXMLVersion enum value
- */
- void setVersionOfCIMXML(CimVersion versionOfCIMXML);
-
- /**
- * Gets the current document context
- */
- CimXmlDocumentContext getCurrentContext();
-
- /**
- * Sets the current document context
- * @param context the current document context
- */
- void setCurrentContext(CimXmlDocumentContext context);
-}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCIMXMLToDatasetGraph.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCIMXMLToDatasetGraph.java
deleted file mode 100644
index 38503142..00000000
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCIMXMLToDatasetGraph.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 de.soptim.opencgmes.cimxml.parser.system;
-
-import de.soptim.opencgmes.cimxml.CimVersion;
-import de.soptim.opencgmes.cimxml.CimXmlDocumentContext;
-import de.soptim.opencgmes.cimxml.graph.CimModelHeader;
-import de.soptim.opencgmes.cimxml.sparql.core.CimDatasetGraph;
-import de.soptim.opencgmes.cimxml.sparql.core.LinkedCimDatasetGraph;
-import org.apache.jena.graph.Graph;
-import org.apache.jena.graph.Node;
-import org.apache.jena.graph.Triple;
-import org.apache.jena.mem2.GraphMem2Roaring;
-import org.apache.jena.mem2.IndexingStrategy;
-import org.apache.jena.sparql.core.Quad;
-
-/**
- * An implementation of {@link StreamCIMXML} that populates a {@link LinkedCimDatasetGraph}
- * with the triples from the CIMXML file being processed.
- *
- * This class manages multiple named graphs within the dataset, switching the current graph
- * context based on the {@link CimXmlDocumentContext}. It uses different indexing strategies
- * for different contexts to optimize performance.
- */
-public class StreamCIMXMLToDatasetGraph implements StreamCIMXML {
-
- private final LinkedCimDatasetGraph linkedCIMDatasetGraph;
- private String versionOfIEC61970_552 = null;
- private Graph currentGraph;
- private CimXmlDocumentContext currentContext;
- private CimVersion versionOfCIMXML = CimVersion.NO_CIM;
-
- public StreamCIMXMLToDatasetGraph() {
- // init default graph for body context
- currentContext = CimXmlDocumentContext.body;
- currentGraph = new GraphMem2Roaring(IndexingStrategy.LAZY_PARALLEL);
- linkedCIMDatasetGraph = new LinkedCimDatasetGraph(currentGraph);
- }
-
- @Override
- public String getVersionOfIEC61970_552() {
- return versionOfIEC61970_552;
- }
-
- @Override
- public CimVersion getVersionOfCIMXML() {
- return versionOfCIMXML;
- }
-
- @Override
- public void setVersionOfCIMXML(CimVersion versionOfCIMXML) {
- this.versionOfCIMXML = versionOfCIMXML;
- }
-
- @Override
- public CimDatasetGraph getCIMDatasetGraph() {
- return linkedCIMDatasetGraph;
- }
-
- private void setCurrentGraphAndCreateIfNecessary(Node graphName, IndexingStrategy indexingStrategy) {
- if (linkedCIMDatasetGraph.containsGraph(graphName)) {
- currentGraph = linkedCIMDatasetGraph.getGraph(graphName);
- } else {
- final var newGraph = new GraphMem2Roaring(indexingStrategy);
- newGraph.getPrefixMapping().setNsPrefixes(currentGraph.getPrefixMapping());
- currentGraph = newGraph;
- linkedCIMDatasetGraph.addGraph(graphName, currentGraph);
- }
- }
-
- @Override
- public void start() {
- // Nothing to do
- }
-
- @Override
- public void triple(Triple triple) {
- currentGraph.add(triple);
- }
-
- @Override
- public void quad(Quad quad) {
- throw new UnsupportedOperationException("Quads are not supported in this context.");
- }
-
- @Override
- public void base(String base) {
- // Nothing to do
- }
-
- @Override
- public void prefix(String prefix, String iri) {
- linkedCIMDatasetGraph.prefixes().add(prefix, iri);
- currentGraph.getPrefixMapping().setNsPrefix(prefix, iri);
- }
-
- @Override
- public void finish() {
- // Initialize indexes in parallel for all graphs that use LAZY_PARALLEL indexing strategy.
- linkedCIMDatasetGraph.getGraphs().parallelStream().forEach(graph -> {
- if (graph instanceof GraphMem2Roaring roaring && !roaring.isIndexInitialized()) {
- roaring.initializeIndexParallel();
- }
- });
- }
-
- @Override
- public CimModelHeader getModelHeader() {
- return linkedCIMDatasetGraph.getModelHeader();
- }
-
- @Override
- public void setVersionOfIEC61970_552(String versionOfIEC61970_552) {
- this.versionOfIEC61970_552 = versionOfIEC61970_552;
- }
-
- @Override
- public CimXmlDocumentContext getCurrentContext() {
- return currentContext;
- }
-
- @Override
- public void setCurrentContext(CimXmlDocumentContext context) {
- switchContext(context);
- }
-
- /**
- * Switches the current graph context based on the provided {@link CimXmlDocumentContext}.
- * This method updates the current graph to the appropriate named graph in the dataset,
- * creating it if it does not already exist. The indexing strategy is chosen based on the
- * context to optimize performance for different types of data.
- * @param cimDocumentContext the new document context to switch to
- */
- private void switchContext(CimXmlDocumentContext cimDocumentContext) {
- var indexingStrategy = switch (cimDocumentContext) {
- // The metadata is usually very small, so we use a minimal indexing strategy.
- case fullModel, differenceModel -> IndexingStrategy.MINIMAL;
- // The data parts can be large, so we use a lazy parallel indexing strategy.
- default -> IndexingStrategy.LAZY_PARALLEL;
- };
- var graphName = CimXmlDocumentContext.getGraphName(cimDocumentContext);
- setCurrentGraphAndCreateIfNecessary(graphName, indexingStrategy);
- currentContext = cimDocumentContext;
- }
-}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCimXml.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCimXml.java
new file mode 100644
index 00000000..bf48adc4
--- /dev/null
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCimXml.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 de.soptim.opencgmes.cimxml.parser.system;
+
+import de.soptim.opencgmes.cimxml.CimXmlDocumentContext;
+import de.soptim.opencgmes.cimxml.graph.CimModelHeader;
+import de.soptim.opencgmes.cimxml.sparql.core.CimDatasetGraph;
+import org.apache.jena.riot.system.StreamRDF;
+
+/**
+ * An extension of {@link StreamRDF} to provide access to the underlying {@link CimDatasetGraph} and
+ * to store additional information about the CIMXML file being processed.
+ */
+public interface StreamCimXml extends StreamRDF {
+
+ /**
+ * Gets the underlying {@link CimDatasetGraph} that is being populated.
+ */
+ CimDatasetGraph getCimDatasetGraph();
+
+ /**
+ * Gets the model header information as found in the CIMXML file.
+ */
+ CimModelHeader getModelHeader();
+
+ /**
+ * Gets the preprocessing instruction version of IEC 61970-552.
+ *
+ * @return the version string as found in the CIMXML file
+ */
+ String getVersionOfIec61970_552();
+
+ /**
+ * Sets the preprocessing instruction version of IEC 61970-552.
+ *
+ * @param versionOfCimXml the version string as found in the CIMXML file
+ */
+ void setVersionOfIec61970_552(String versionOfCimXml);
+
+ /**
+ * Gets the 'cim' namespace URI as found in the CIMXML file.
+ *
+ * @return the 'cim' namespace as found in the CIMXML file
+ */
+ String getCimNamespace();
+
+ /**
+ * Sets the 'cim' namespace URI as found in the CIMXML file.
+ *
+ * @param cimNamespace the 'cim' namespace URI as found in the CIMXML file
+ */
+ void setCimNamespace(String cimNamespace);
+
+ /**
+ * Gets the current document context.
+ */
+ CimXmlDocumentContext getCurrentContext();
+
+ /**
+ * Sets the current document context.
+ *
+ * @param context the current document context
+ */
+ void setCurrentContext(CimXmlDocumentContext context);
+}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCimXmlToDatasetGraph.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCimXmlToDatasetGraph.java
new file mode 100644
index 00000000..77d88310
--- /dev/null
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/parser/system/StreamCimXmlToDatasetGraph.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 de.soptim.opencgmes.cimxml.parser.system;
+
+import de.soptim.opencgmes.cimxml.CimXmlDocumentContext;
+import de.soptim.opencgmes.cimxml.graph.CimModelHeader;
+import de.soptim.opencgmes.cimxml.sparql.core.CimDatasetGraph;
+import de.soptim.opencgmes.cimxml.sparql.core.LinkedCimDatasetGraph;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.graph.GraphFactory;
+
+/**
+ * An implementation of {@link StreamCimXml} that populates a {@link LinkedCimDatasetGraph} with the
+ * triples from the CIMXML file being processed.
+ *
+ *
This class manages multiple named graphs within the dataset, switching the current graph
+ * context based on the {@link CimXmlDocumentContext}. It uses different indexing strategies for
+ * different contexts to optimize performance.
+ */
+public class StreamCimXmlToDatasetGraph implements StreamCimXml {
+
+ private final LinkedCimDatasetGraph linkedCimDatasetGraph;
+ private String versionOfIec61970_552 = null;
+ private Graph currentGraph;
+ private CimXmlDocumentContext currentContext;
+ private String cimNamespace = null;
+
+ /**
+ * Creates a new instance of {@link StreamCimXmlToDatasetGraph} and initializes the default graph
+ * for the body context. The default graph is created as an in-memory graph and is associated with
+ * the body context in the linked dataset graph.
+ */
+ public StreamCimXmlToDatasetGraph() {
+ // init default graph for body context
+ currentContext = CimXmlDocumentContext.body;
+ currentGraph = GraphFactory.createGraphMem();
+ linkedCimDatasetGraph = new LinkedCimDatasetGraph(currentGraph);
+ }
+
+ @Override
+ public String getVersionOfIec61970_552() {
+ return versionOfIec61970_552;
+ }
+
+ @Override
+ public void setVersionOfIec61970_552(String versionOfCimXml) {
+ this.versionOfIec61970_552 = versionOfCimXml;
+ }
+
+ @Override
+ public String getCimNamespace() {
+ return cimNamespace;
+ }
+
+ @Override
+ public void setCimNamespace(String cimNamespace) {
+ this.cimNamespace = cimNamespace;
+ }
+
+ @Override
+ public CimDatasetGraph getCimDatasetGraph() {
+ return linkedCimDatasetGraph;
+ }
+
+ private void setCurrentGraphAndCreateIfNecessary(Node graphName) {
+ if (linkedCimDatasetGraph.containsGraph(graphName)) {
+ currentGraph = linkedCimDatasetGraph.getGraph(graphName);
+ } else {
+ final Graph newGraph = GraphFactory.createGraphMem();
+ newGraph.getPrefixMapping().setNsPrefixes(currentGraph.getPrefixMapping());
+ currentGraph = newGraph;
+ linkedCimDatasetGraph.addGraph(graphName, currentGraph);
+ }
+ }
+
+ @Override
+ public void start() {
+ // Nothing to do
+ }
+
+ @Override
+ public void triple(Triple triple) {
+ currentGraph.add(triple);
+ }
+
+ @Override
+ public void quad(Quad quad) {
+ throw new UnsupportedOperationException("Quads are not supported in this context.");
+ }
+
+ @Override
+ public void base(String base) {
+ // Nothing to do
+ }
+
+ @Override
+ public void prefix(String prefix, String iri) {
+ linkedCimDatasetGraph.prefixes().add(prefix, iri);
+ currentGraph.getPrefixMapping().setNsPrefix(prefix, iri);
+ }
+
+ @Override
+ public void finish() {
+ // Nothing to do
+ }
+
+ @Override
+ public CimModelHeader getModelHeader() {
+ return linkedCimDatasetGraph.getModelHeader();
+ }
+
+ @Override
+ public CimXmlDocumentContext getCurrentContext() {
+ return currentContext;
+ }
+
+ @Override
+ public void setCurrentContext(CimXmlDocumentContext context) {
+ switchContext(context);
+ }
+
+ /**
+ * Switches the current graph context based on the provided {@link CimXmlDocumentContext}. This
+ * method updates the current graph to the appropriate named graph in the dataset, creating it if
+ * it does not already exist.
+ *
+ * @param cimDocumentContext the new document context to switch to
+ */
+ private void switchContext(CimXmlDocumentContext cimDocumentContext) {
+ var graphName = CimXmlDocumentContext.getGraphName(cimDocumentContext);
+ setCurrentGraphAndCreateIfNecessary(graphName);
+ currentContext = cimDocumentContext;
+ }
+}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistry.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistry.java
index 3eec9ae0..94c191ce 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistry.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistry.java
@@ -18,13 +18,11 @@
package de.soptim.opencgmes.cimxml.rdfs;
-import de.soptim.opencgmes.cimxml.CimVersion;
import de.soptim.opencgmes.cimxml.graph.CimProfile;
-import org.apache.jena.datatypes.RDFDatatype;
-import org.apache.jena.graph.Node;
-
import java.util.Map;
import java.util.Set;
+import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.graph.Node;
/**
* Registry for managing CIM profile ontologies and their associated datatypes.
@@ -67,6 +65,7 @@
* }
*
*
Thread Safety:
+ *
*
Implementations must be thread-safe for all read operations. Registration operations
* may require external synchronization.
*
@@ -75,96 +74,118 @@
*/
public interface CimProfileRegistry {
- /**
- * Information about a CIM property including its domain, range, and datatype.
- *
- *
This record encapsulates all metadata needed to properly parse and validate
- * a CIM property value:
- *
- *
- *
rdfType: The class (domain) this property belongs to
- *
property: The property URI
- *
cimDatatype: The CIM datatype definition
- *
primitiveType: RDF datatype for primitive properties (e.g., xsd:float)
- *
referenceType: Target class for object properties
- *
- *
- *
Either primitiveType or referenceType will be non-null, but not both:
- *
- *
If primitiveType is non-null, this is a datatype property
- *
If referenceType is non-null, this is an object property
- *
- *
- * @param rdfType The domain class of this property
- * @param property The property URI
- * @param cimDatatype The CIM datatype definition (may be null)
- * @param primitiveType The RDF datatype for primitive values (may be null)
- * @param referenceType The range class for object properties (may be null)
- */
- record PropertyInfo(Node rdfType, Node property, Node cimDatatype, RDFDatatype primitiveType, Node referenceType) {}
+ /**
+ * Registers an ontology graph for profiles in the registry. During registration, the data types
+ * of all properties in the graph are extracted and stored in a map for fast lookup. Throws an
+ * IllegalArgumentException if one of the profiles owlVersionIRIs is already registered or in case
+ * of a header profile, if one has already been registered for the same CIM version.
+ *
+ * @param cimProfile The profile ontology to register.
+ */
+ void register(CimProfile cimProfile);
+
+ /**
+ * Checks if the registry contains all profile IRIs in the given set.
+ *
+ * @param owlVersionIris A set of profile IRIs as found in the model header.
+ * @return true if all profile IRIs are registered, false otherwise.
+ */
+ boolean containsProfile(Set owlVersionIris);
+
+ /**
+ * Get all registered profiles that match the given set of profile IRIs.
+ *
+ * @param owlVersionIris A set of profile IRIs as found in the model header.
+ * @return A set of matching profiles. If no profiles match, an empty set is returned.
+ */
+ Set getMatchingProfiles(Set owlVersionIris);
- /**
- * Registers an ontology graph for profiles in the registry.
- * During registration, the data types of all properties in the graph are extracted and stored in a map for fast lookup.
- * Throws an IllegalArgumentException if one of the profiles owlVersionIRIs is already registered
- * or in case of a header profile, if one has already been registered for the same CIM version.
- * @param cimProfile The profile ontology to register.
- */
- void register(CimProfile cimProfile);
+ /**
+ * Checks if the registry contains a header profile for the given CIM namespace.
+ *
+ * @param cimNamespace The 'cim' namespace to check.
+ * @return true if a header profile for the given 'cim' namespace is registered, false otherwise.
+ */
+ boolean containsHeaderProfile(String cimNamespace);
- /**
- * Checks if the registry contains all profile IRIs in the given set.
- * @param owlVersionIRIs A set of profile IRIs as found in the model header.
- * @return true if all profile IRIs are registered, false otherwise.
- */
- boolean containsProfile(Set owlVersionIRIs);
+ /**
+ * Get all registered ontologies in the registry.
+ *
+ * @return A collection of all registered ontologies.
+ */
+ Set getRegisteredProfiles();
- /**
- * Checks if the registry contains a header profile for the given CIM version.
- * @param version The CIM version to check.
- * @return true if a header profile for the given CIM version is registered, false otherwise.
- */
- boolean containsHeaderProfile(CimVersion version);
+ /**
+ * Get all properties and their associated RDF datatypes for the given set of profile IRIs. The
+ * set may contain profile IRIs for multiple ontologies. Throws an IllegalArgumentException if one
+ * of the profile IRIs is not registered.
+ *
+ * @param owlVersionIris A set of profile IRIs as found in the model header.
+ * @return A map of properties and their associated RDF datatypes. The map is thread-safe for
+ * reading. Returns an empty map if one of the profile IRIs is not registered.
+ */
+ Map getPropertiesAndDatatypes(Set owlVersionIris);
- /**
- * Get all registered ontologies in the registry.
- * @return A collection of all registered ontologies.
- */
- Set getRegisteredProfiles();
+ /**
+ * Get all properties and their associated RDF datatypes for the header profile of the given CIM
+ * version. Throws an IllegalArgumentException if no header profile has been registered for the
+ * given CIM version.
+ *
+ * @param cimNamespace The 'cim' namespace for which the header profile should be used.
+ * @return A map of properties and their associated RDF datatypes. The map is thread-safe for
+ * reading. Returns an empty map if no header profile is registered for the given 'cim'
+ * namespace.
+ */
+ Map getHeaderPropertiesAndDatatypes(String cimNamespace);
- /**
- * Get all properties and their associated RDF datatypes for the given set of profile IRIs.
- * The set may contain profile IRIs for multiple ontologies.
- * Throws an IllegalArgumentException if one of the profile IRIs is not registered.
- * @param owlVersionIRIs A set of profile IRIs as found in the model header.
- * @return A map of properties and their associated RDF datatypes. The map is thread-safe for reading.
- * Returns null if one of the profile IRIs is not registered.
- */
- Map getPropertiesAndDatatypes(Set owlVersionIRIs);
+ /**
+ * Get a mapping of primitive type names to RDF datatypes for all registered profiles. This
+ * includes primitive types from all registered ontologies.
+ *
+ * @return A map of primitive type names to RDF datatypes. The map is thread-safe for reading.
+ */
+ Map getPrimitiveToRdfDatatypeMapping();
- /**
- * Get all properties and their associated RDF datatypes for the header profile of the given CIM version.
- * Throws an IllegalArgumentException if no header profile has been registered for the given CIM version.
- * @param version The CIM version for which the header profile should be used.
- * @return A map of properties and their associated RDF datatypes. The map is thread-safe for reading.
- * Returns null if no header profile is registered for the given CIM version.
- */
- Map getHeaderPropertiesAndDatatypes(CimVersion version);
+ /**
+ * Registers a custom primitive type with the given CIM primitive type name and RDF datatype. If
+ * the primitive type name is already registered, it will be overwritten with the new RDF
+ * datatype. This method can be used to register custom primitive types that are not part of the
+ * standard CIM profiles. The rdfDatatype must also be registered with Jena's TypeMapper.
+ *
+ * @param cimPrimitiveTypeName The CIM primitive type name, e.g. "string", "int", "float", etc.
+ * @param rdfDatatype The RDF datatype to associate with the given CIM primitive type
+ * name.
+ */
+ void registerPrimitiveType(String cimPrimitiveTypeName, RDFDatatype rdfDatatype);
- /**
- * Get a mapping of primitive type names to RDF datatypes for all registered profiles.
- * This includes primitive types from all registered ontologies.
- * @return A map of primitive type names to RDF datatypes. The map is thread-safe for reading.
- */
- Map getPrimitiveToRDFDatatypeMapping();
+ /**
+ * Information about a CIM property including its domain, range, and datatype.
+ *
+ *
This record encapsulates all metadata needed to properly parse and validate
+ * a CIM property value:
+ *
+ *
+ *
rdfType: The class (domain) this property belongs to
+ *
property: The property URI
+ *
cimDatatype: The CIM datatype definition
+ *
primitiveType: RDF datatype for primitive properties (e.g., xsd:float)
+ *
referenceType: Target class for object properties
+ *
+ *
+ *
Either primitiveType or referenceType will be non-null, but not both:
+ *
+ *
If primitiveType is non-null, this is a datatype property
+ *
If referenceType is non-null, this is an object property
+ *
+ *
+ * @param rdfType The domain class of this property
+ * @param property The property URI
+ * @param cimDatatype The CIM datatype definition (may be null)
+ * @param primitiveType The RDF datatype for primitive values (may be null)
+ * @param referenceType The range class for object properties (may be null)
+ */
+ record PropertyInfo(Node rdfType, Node property, Node cimDatatype, RDFDatatype primitiveType,
+ Node referenceType) {
- /**
- * Registers a custom primitive type with the given CIM primitive type name and RDF datatype.
- * If the primitive type name is already registered, it will be overwritten with the new RDF datatype.
- * This method can be used to register custom primitive types that are not part of the standard CIM profiles.
- * The rdfDatatype must also be registered with Jena's TypeMapper.
- * @param cimPrimitiveTypeName The CIM primitive type name, e.g. "string", "int", "float", etc.
- * @param rdfDatatype The RDF datatype to associate with the given CIM primitive type name.
- */
- void registerPrimitiveType(String cimPrimitiveTypeName, RDFDatatype rdfDatatype);
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistryStd.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistryStd.java
index 866873f2..d1fc8290 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistryStd.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/rdfs/CimProfileRegistryStd.java
@@ -18,9 +18,16 @@
package de.soptim.opencgmes.cimxml.rdfs;
-import de.soptim.opencgmes.cimxml.CimVersion;
import de.soptim.opencgmes.cimxml.graph.CimProfile;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.datatypes.TypeMapper;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.datatypes.xsd.impl.RDFLangString;
import org.apache.jena.graph.Graph;
@@ -31,288 +38,315 @@
import org.apache.jena.riot.system.ErrorHandlerFactory;
import org.apache.jena.sparql.exec.QueryExec;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
/**
- * Standard implementation of the {@link CimProfileRegistry}.
- * This implementation is thread-safe.
- * Registration of custom primitive type mappings should be done before any other operations on the registry.
- * The primitive type mapping is static for all instances of the registry.
+ * Standard implementation of the {@link CimProfileRegistry}. This implementation is thread-safe.
+ * Registration of custom primitive type mappings should be done before any other operations on the
+ * registry. The primitive type mapping is static for all instances of the registry.
*/
public class CimProfileRegistryStd implements CimProfileRegistry {
+ private static final Query typedPropertiesQuery = QueryFactory.create(
+ """
+ PREFIX cims:
+ PREFIX rdf:
+ PREFIX rdfs:
+ PREFIX xsd:
+
+ SELECT ?rdfType ?property ?cimDatatype ?primitiveType ?referenceType
+ WHERE
+ {
+ ?rdfType rdf:type rdfs:Class .
+ ?property rdf:type rdf:Property ;
+ rdfs:domain ?rdfType .
+ {
+ ?property rdfs:range ?referenceType.
+ FILTER NOT EXISTS { ?property cims:AssociationUsed "No" } # Filter out associations that are not used as properties
+ }
+ UNION
+ {
+ ?property cims:dataType ?cimDatatype.
+ {
+ ?cimDatatype cims:stereotype "Primitive";
+ rdfs:label ?primitiveType.
+ }
+ UNION
+ {
+ ?cimDatatype cims:stereotype "CIMDatatype".
+ BIND(IRI(CONCAT(STR(?cimDatatype), ".value")) AS ?parentTypeValue)
+ ?parentTypeValue rdfs:domain ?cimDatatype;
+ cims:dataType/cims:stereotype "Primitive";
+ cims:dataType/rdfs:label ?primitiveType.
+ }
+ }
+ }
+ """);
+ private final ErrorHandler errorHandler;
+ private final Map, CimProfile> multiVersionIriProfiles = new ConcurrentHashMap<>();
+ private final Map singleVersionIriProfiles = new ConcurrentHashMap<>();
+ private final Map headerProfilesByCimNamespace = new ConcurrentHashMap<>();
+ private final Map> profilePropertiesCache
+ = new ConcurrentHashMap<>();
+ private final Map, Map> profileSetPropertiesCache
+ = new ConcurrentHashMap<>();
+ private final Map primitiveToRdfDatatypeMap;
- private static Map initPrimitiveToRDFDatatypeMapUsingXSDDatatypesOnly() {
- var map = new HashMap();
- map.put("Base64Binary", XSDDatatype.XSDbase64Binary);
- map.put("Boolean", XSDDatatype.XSDboolean);
- map.put("Byte", XSDDatatype.XSDbyte);
- map.put("Date", XSDDatatype.XSDdate);
- map.put("DateTime", XSDDatatype.XSDdateTime);
- map.put("DateTimeStamp", XSDDatatype.XSDdateTimeStamp);
- map.put("Day", XSDDatatype.XSDgDay);
- map.put("DayTimeDuration", XSDDatatype.XSDdayTimeDuration);
- map.put("Decimal", XSDDatatype.XSDdecimal);
- map.put("Double", XSDDatatype.XSDdouble);
- map.put("Duration", XSDDatatype.XSDduration);
- map.put("Float", XSDDatatype.XSDfloat);
- map.put("HexBinary", XSDDatatype.XSDhexBinary);
- map.put("Int", XSDDatatype.XSDint);
- map.put("Integer", XSDDatatype.XSDinteger);
- map.put("IRI", XSDDatatype.XSDstring);
- map.put("LangString", RDFLangString.rdfLangString);
- map.put("Long", XSDDatatype.XSDlong);
- map.put("Month", XSDDatatype.XSDgMonth);
- map.put("MonthDay", XSDDatatype.XSDgMonthDay);
- map.put("NegativeInteger", XSDDatatype.XSDnegativeInteger);
- map.put("NonNegativeInteger", XSDDatatype.XSDnonNegativeInteger);
- map.put("NonPositiveInteger", XSDDatatype.XSDnonPositiveInteger);
- map.put("PositiveInteger", XSDDatatype.XSDpositiveInteger);
- map.put("String", XSDDatatype.XSDstring);
- map.put("StringFixedLanguage", XSDDatatype.XSDstring);
- map.put("StringIRI", XSDDatatype.XSDstring);
- map.put("Time", XSDDatatype.XSDtime);
- map.put("UnsignedByte", XSDDatatype.XSDunsignedByte);
- map.put("UnsignedInt", XSDDatatype.XSDunsignedInt);
- map.put("UnsignedLong", XSDDatatype.XSDunsignedLong);
- map.put("UnsignedShort", XSDDatatype.XSDunsignedShort);
- map.put("URI", XSDDatatype.XSDanyURI);
- map.put("UUID", XSDDatatype.XSDstring);
- map.put("Version", XSDDatatype.XSDstring);
- map.put("Year", XSDDatatype.XSDgYear);
- map.put("YearMonth", XSDDatatype.XSDgYearMonth);
- map.put("YearMonthDuration", XSDDatatype.XSDyearMonthDuration);
- return map;
- }
+ /**
+ * Creates a new instance of the registry using the standard Jena error handler.
+ */
+ public CimProfileRegistryStd() {
+ this(ErrorHandlerFactory.errorHandlerStd);
+ }
- private final Map, CimProfile> multiVersionIriProfiles = new ConcurrentHashMap<>();
- private final Map singleVersionIriProfiles = new ConcurrentHashMap<>();
- private final Map headerProfiles = new ConcurrentHashMap<>();
- private final Map> profilePropertiesCache = new ConcurrentHashMap<>();
- private final Map, Map> profileSetPropertiesCache = new ConcurrentHashMap<>();
- private final Map primitiveToRDFDatatypeMap;
+ /**
+ * Creates a new instance of the registry using the given error handler.
+ *
+ * @param errorHandler The error handler to use for logging warnings and errors.
+ */
+ public CimProfileRegistryStd(ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ this.primitiveToRdfDatatypeMap = initPrimitiveToRdfDatatypeMapUsingXsdDatatypesOnly();
+ }
- public final ErrorHandler errorHandler;
+ private static Map initPrimitiveToRdfDatatypeMapUsingXsdDatatypesOnly() {
+ var map = new ConcurrentHashMap();
+ map.put("Base64Binary", XSDDatatype.XSDbase64Binary);
+ map.put("Boolean", XSDDatatype.XSDboolean);
+ map.put("Byte", XSDDatatype.XSDbyte);
+ map.put("Date", XSDDatatype.XSDdate);
+ map.put("DateTime", XSDDatatype.XSDdateTime);
+ map.put("DateTimeStamp", XSDDatatype.XSDdateTimeStamp);
+ map.put("Day", XSDDatatype.XSDgDay);
+ map.put("DayTimeDuration", XSDDatatype.XSDdayTimeDuration);
+ map.put("Decimal", XSDDatatype.XSDdecimal);
+ map.put("Double", XSDDatatype.XSDdouble);
+ map.put("Duration", XSDDatatype.XSDduration);
+ map.put("Float", XSDDatatype.XSDfloat);
+ map.put("HexBinary", XSDDatatype.XSDhexBinary);
+ map.put("Int", XSDDatatype.XSDint);
+ map.put("Integer", XSDDatatype.XSDinteger);
+ map.put("IRI", XSDDatatype.XSDstring);
+ map.put("LangString", RDFLangString.rdfLangString);
+ map.put("Long", XSDDatatype.XSDlong);
+ map.put("Month", XSDDatatype.XSDgMonth);
+ map.put("MonthDay", XSDDatatype.XSDgMonthDay);
+ map.put("NegativeInteger", XSDDatatype.XSDnegativeInteger);
+ map.put("NonNegativeInteger", XSDDatatype.XSDnonNegativeInteger);
+ map.put("NonPositiveInteger", XSDDatatype.XSDnonPositiveInteger);
+ map.put("PositiveInteger", XSDDatatype.XSDpositiveInteger);
+ map.put("String", XSDDatatype.XSDstring);
+ map.put("StringFixedLanguage", XSDDatatype.XSDstring);
+ map.put("StringIRI", XSDDatatype.XSDstring);
+ map.put("Time", XSDDatatype.XSDtime);
+ map.put("UnsignedByte", XSDDatatype.XSDunsignedByte);
+ map.put("UnsignedInt", XSDDatatype.XSDunsignedInt);
+ map.put("UnsignedLong", XSDDatatype.XSDunsignedLong);
+ map.put("UnsignedShort", XSDDatatype.XSDunsignedShort);
+ map.put("URI", XSDDatatype.XSDanyURI);
+ map.put("UUID", XSDDatatype.XSDstring);
+ map.put("Version", XSDDatatype.XSDstring);
+ map.put("Year", XSDDatatype.XSDgYear);
+ map.put("YearMonth", XSDDatatype.XSDgYearMonth);
+ map.put("YearMonthDuration", XSDDatatype.XSDyearMonthDuration);
+ return map;
+ }
- /**
- * Creates a new instance of the registry using the standard Jena error handler.
- */
- public CimProfileRegistryStd() {
- this(ErrorHandlerFactory.errorHandlerStd);
+ @Override
+ public void register(CimProfile cimProfile) {
+ if (cimProfile.isHeaderProfile()) {
+ final var cimNamespace = cimProfile.getCimNamespace();
+ if (cimNamespace == null) {
+ throw new IllegalArgumentException(
+ "Header profile must have the 'cim' prefix and namespace URI defined.");
+ }
+ if (headerProfilesByCimNamespace.containsKey(cimNamespace)) {
+ throw new IllegalArgumentException(
+ "Header profile for 'cim' namespace URI '" + cimNamespace + "' is already registered.");
+ }
+ headerProfilesByCimNamespace.put(cimNamespace, cimProfile);
+ profilePropertiesCache.put(cimProfile, getTypedProperties(cimProfile));
+ return;
}
- /**
- * Creates a new instance of the registry using the given error handler.
- * @param errorHandler The error handler to use for logging warnings and errors.
- */
- public CimProfileRegistryStd(ErrorHandler errorHandler) {
- this.errorHandler = errorHandler;
- this.primitiveToRDFDatatypeMap = initPrimitiveToRDFDatatypeMapUsingXSDDatatypesOnly();
+ var owlVersionIris = cimProfile.getOwlVersionIris();
+ if (owlVersionIris == null || owlVersionIris.isEmpty()) {
+ throw new IllegalArgumentException("Profile ontology must have at least one owlVersionIRI.");
}
- private final static Query typedPropertiesQuery = QueryFactory.create("""
- PREFIX rdf:
- PREFIX rdfs:
- PREFIX cims:
-
- SELECT ?rdfType ?property ?cimDatatype ?primitiveType ?referenceType
- WHERE
- {
- {
- ?property rdfs:domain ?rdfType;
- rdfs:range ?referenceType;
- OPTIONAL {
- ?property cims:AssociationUsed ?associationUsed
- }
- FILTER(!BOUND(?associationUsed) || ?associationUsed = "Yes")
- }
- UNION
- {
- ?property rdfs:domain ?rdfType;
- cims:dataType ?cimDatatype.
- {
- ?cimDatatype cims:stereotype "CIMDatatype".
- [] rdfs:domain ?cimDatatype;
- rdfs:label ?label;
- #rdfs:label "value";
- cims:dataType/cims:stereotype "Primitive";
- cims:dataType/rdfs:label ?primitiveType.
- FILTER (!bound(?label) || str(?label) = "value")
- }
- UNION
- {
- ?cimDatatype cims:stereotype "Primitive";
- rdfs:label ?primitiveType.
- }
- }
- }
- """);
+ if (owlVersionIris.size() == 1) {
+ var iri = owlVersionIris.iterator().next();
+ if (singleVersionIriProfiles.containsKey(iri)) {
+ throw new IllegalArgumentException(
+ "Profile ontology with owlVersionIRI " + iri + " is already registered.");
+ }
+ singleVersionIriProfiles.put(iri, cimProfile);
+ } else {
+ if (multiVersionIriProfiles.containsKey(owlVersionIris)) {
+ throw new IllegalArgumentException(
+ "Profile ontology with owlVersionIris " + owlVersionIris + " is already registered.");
+ }
+ multiVersionIriProfiles.put(owlVersionIris, cimProfile);
+ }
+ profilePropertiesCache.put(cimProfile, getTypedProperties(cimProfile));
+ }
- @Override
- public void register(CimProfile cimProfile) {
- if (cimProfile.isHeaderProfile()) {
- final var cimVersion = cimProfile.getCIMVersion();
- if (cimVersion == CimVersion.NO_CIM)
- throw new IllegalArgumentException("Header profile must have a valid CIM version.");
- if (headerProfiles.containsKey(cimVersion))
- throw new IllegalArgumentException("Header profile for CIM version " + cimVersion + " is already registered.");
- headerProfiles.put(cimVersion, cimProfile);
- profilePropertiesCache.put(cimProfile, getTypedProperties(cimProfile));
- return;
+ @Override
+ public boolean containsProfile(Set owlVersionIris) {
+ if (owlVersionIris == null || owlVersionIris.isEmpty()) {
+ throw new IllegalArgumentException("At least one profile owlVersionIRI must be provided.");
+ }
+ for (var iri : owlVersionIris) {
+ if (!singleVersionIriProfiles.containsKey(iri)) {
+ var foundInMulti = false;
+ for (var registeredVersionIris : multiVersionIriProfiles.keySet()) {
+ if (registeredVersionIris.contains(iri)) {
+ foundInMulti = true;
+ break;
+ }
}
-
- var owlVersionIRIs = cimProfile.getOwlVersionIRIs();
- if (owlVersionIRIs == null || owlVersionIRIs.isEmpty())
- throw new IllegalArgumentException("Profile ontology must have at least one owlVersionIRI.");
-
- if (owlVersionIRIs.size() == 1) {
- var iri = owlVersionIRIs.iterator().next();
- if (singleVersionIriProfiles.containsKey(iri))
- throw new IllegalArgumentException("Profile ontology with owlVersionIRI " + iri + " is already registered.");
- singleVersionIriProfiles.put(iri, cimProfile);
- } else {
- if (multiVersionIriProfiles.containsKey(owlVersionIRIs))
- throw new IllegalArgumentException("Profile ontology with owlVersionIRIs " + owlVersionIRIs + " is already registered.");
- multiVersionIriProfiles.put(owlVersionIRIs, cimProfile);
+ if (!foundInMulti) {
+ return false;
}
- profilePropertiesCache.put(cimProfile, getTypedProperties(cimProfile));
+ }
}
+ return true;
+ }
- @Override
- public boolean containsProfile(Set owlVersionIRIs) {
- if (owlVersionIRIs == null || owlVersionIRIs.isEmpty())
- throw new IllegalArgumentException("At least one profile owlVersionIRI must be provided.");
- for(var iri : owlVersionIRIs) {
- if (!singleVersionIriProfiles.containsKey(iri)) {
- var foundInMulti = false;
- for(var registeredVersionIRIs: multiVersionIriProfiles.keySet()) {
- if (registeredVersionIRIs.contains(iri)) {
- foundInMulti = true;
- break;
- }
- }
- if (!foundInMulti)
- return false;
- }
- }
- return true;
+ @Override
+ public Set getMatchingProfiles(Set owlVersionIris) {
+ if (owlVersionIris == null || owlVersionIris.isEmpty()) {
+ throw new IllegalArgumentException("At least one profile owlVersionIRI must be provided.");
}
- @Override
- public boolean containsHeaderProfile(CimVersion version) {
- if (version == CimVersion.NO_CIM)
- throw new IllegalArgumentException("CIM version must be valid.");
- return headerProfiles.containsKey(version);
+ if (owlVersionIris.size() == 1) {
+ var versionIri = owlVersionIris.iterator().next();
+ if (singleVersionIriProfiles.containsKey(versionIri)) {
+ return Set.of(singleVersionIriProfiles.get(versionIri));
+ }
}
- @Override
- public Set getRegisteredProfiles() {
- return profilePropertiesCache.keySet();
+ var profile = multiVersionIriProfiles.get(owlVersionIris);
+ if (profile != null) {
+ return Set.of(profile);
}
- @Override
- public Map getPropertiesAndDatatypes(Set owlVersionIRIs) {
- if (owlVersionIRIs == null || owlVersionIRIs.isEmpty())
- throw new IllegalArgumentException("At least one profile owlVersionIRI must be provided.");
-
- if (owlVersionIRIs.size() == 1) {
- var versionIRI = owlVersionIRIs.iterator().next();
- if (singleVersionIriProfiles.containsKey(versionIRI)) {
- var profile = singleVersionIriProfiles.get(versionIRI);
- return profilePropertiesCache.get(profile);
- }
+ final var set = new HashSet();
+ for (var owlVersionIri : owlVersionIris) {
+ final var p = singleVersionIriProfiles.get(owlVersionIri);
+ if (p == null) {
+ var foundInMulti = false;
+ for (var entrySet : multiVersionIriProfiles.entrySet()) {
+ if (entrySet.getKey().contains(owlVersionIri)) {
+ foundInMulti = true;
+ set.add(entrySet.getValue());
+ break;
+ }
}
-
- var profile = multiVersionIriProfiles.get(owlVersionIRIs);
- if (profile != null)
- return profilePropertiesCache.get(profile);
-
- var set = new HashSet();
- for(var owlVersionIRI : owlVersionIRIs) {
- final var p = singleVersionIriProfiles.get(owlVersionIRI);
- if (p == null) {
- var foundInMulti = false;
- for (var entrySet : multiVersionIriProfiles.entrySet()) {
- if (entrySet.getKey().contains(owlVersionIRI)) {
- foundInMulti = true;
- set.add(entrySet.getValue());
- break;
- }
- }
- if (!foundInMulti)
- return null;
- } else {
- set.add(p);
- }
+ if (!foundInMulti) {
+ return Collections.emptySet();
}
- if (set.size() == 1)
- return profilePropertiesCache.get(set.iterator().next());
+ } else {
+ set.add(p);
+ }
+ }
+ return set;
+ }
- Map properties = profileSetPropertiesCache.get(set);
- if (properties != null)
- return properties;
+ @Override
+ public boolean containsHeaderProfile(String cimNamespace) {
+ Objects.requireNonNull(cimNamespace, "cimNamespace");
+ return headerProfilesByCimNamespace.containsKey(cimNamespace);
+ }
- properties = new HashMap<>(1024);
- for(var p : set) {
- properties.putAll(profilePropertiesCache.get(p));
- }
- properties = Collections.unmodifiableMap(properties);
- profileSetPropertiesCache.put(set, properties);
- return properties;
+ @Override
+ public Set getRegisteredProfiles() {
+ return profilePropertiesCache.keySet();
+ }
+
+ @Override
+ public Map getPropertiesAndDatatypes(Set owlVersionIris) {
+ if (owlVersionIris == null || owlVersionIris.isEmpty()) {
+ throw new IllegalArgumentException("At least one profile owlVersionIRI must be provided.");
}
- @Override
- public Map getHeaderPropertiesAndDatatypes(CimVersion version) {
- Objects.requireNonNull(version, "version");
- if (version == CimVersion.NO_CIM)
- throw new IllegalArgumentException("CIM version must be valid.");
- final var profile = headerProfiles.get(version);
- if (profile == null)
- return null;
- return profilePropertiesCache.get(profile);
+ final var set = getMatchingProfiles(owlVersionIris);
+
+ if (set.size() == 1) {
+ return profilePropertiesCache.get(set.iterator().next());
}
- @Override
- public Map getPrimitiveToRDFDatatypeMapping() {
- return Collections.unmodifiableMap(primitiveToRDFDatatypeMap);
+ Map properties = profileSetPropertiesCache.get(set);
+ if (properties != null) {
+ return properties;
}
- @Override
- public void registerPrimitiveType(String cimPrimitiveTypeName, RDFDatatype rdfDatatype) {
- Objects.requireNonNull(cimPrimitiveTypeName, "cimPrimitiveTypeName");
- Objects.requireNonNull(rdfDatatype, "rdfDatatype");
- primitiveToRDFDatatypeMap.put(cimPrimitiveTypeName, rdfDatatype);
+ properties = new HashMap<>(1024);
+ for (var p : set) {
+ properties.putAll(profilePropertiesCache.get(p));
}
+ properties = Collections.unmodifiableMap(properties);
+ profileSetPropertiesCache.put(set, properties);
+ return properties;
+ }
- private Map getTypedProperties(Graph g) {
- final var map = new HashMap(1024);
- QueryExec.graph(g)
- .query(typedPropertiesQuery)
- .select()
- .forEachRemaining(vars -> { //?class ?property ?primitiveType ?referenceType
- final var rdfType = vars.get("rdfType");
- final var property = vars.get("property");
- final var cimDatatype = vars.get("cimDatatype");
- final var primitiveType = vars.get("primitiveType");
- final var referenceType = vars.get("referenceType");
- map.put(property, new PropertyInfo(
- rdfType,
- property,
- cimDatatype,
- primitiveType != null
- ? getXsdDatatype(primitiveType.getLiteralLexicalForm())
- : null,
- referenceType));
- });
- return Collections.unmodifiableMap(map);
+ @Override
+ public Map getHeaderPropertiesAndDatatypes(String cimNamespace) {
+ Objects.requireNonNull(cimNamespace, "cimNamespace");
+ final var profile = headerProfilesByCimNamespace.get(cimNamespace);
+ if (profile == null) {
+ return Collections.emptyMap();
}
+ return profilePropertiesCache.get(profile);
+ }
+
+ @Override
+ public Map getPrimitiveToRdfDatatypeMapping() {
+ return Collections.unmodifiableMap(primitiveToRdfDatatypeMap);
+ }
+
+ @Override
+ public void registerPrimitiveType(String cimPrimitiveTypeName, RDFDatatype rdfDatatype) {
+ Objects.requireNonNull(cimPrimitiveTypeName, "cimPrimitiveTypeName");
+ Objects.requireNonNull(rdfDatatype, "rdfDatatype");
+ primitiveToRdfDatatypeMap.put(cimPrimitiveTypeName, rdfDatatype);
+ }
+
+ private Map getTypedProperties(Graph g) {
+ final var map = new HashMap(1024);
+ QueryExec.graph(g)
+ .query(typedPropertiesQuery)
+ .select()
+ .forEachRemaining(vars -> { // ?class ?property ?primitiveType ?referenceType
+ final var rdfType = vars.get("rdfType");
+ final var property = vars.get("property");
+ final var cimDatatype = vars.get("cimDatatype");
+ final var primitiveType = vars.get("primitiveType");
+ final var referenceType = vars.get("referenceType");
+ final var rdfDataType = primitiveType != null
+ // retrieve CIM specific primitive type mapping
+ ? getXsdDatatype(primitiveType.getLiteralLexicalForm())
+ // if no cimDatatype is given, but a referenceType is given
+ : cimDatatype == null && referenceType != null
+ // then this may be an XSD datatype defined as rdf:range
+ // if it cannot be mapped, we treat it as a reference type
+ ? TypeMapper.getInstance().getTypeByName(referenceType.getURI())
+ : null;
+ map.put(property, new PropertyInfo(
+ rdfType,
+ property,
+ cimDatatype,
+ rdfDataType,
+ rdfDataType != null ? null : referenceType));
+ });
+ return Collections.unmodifiableMap(map);
+ }
- private RDFDatatype getXsdDatatype(String primitiveType) {
- var dt = primitiveToRDFDatatypeMap.get(primitiveType);
- if (dt != null)
- return dt;
- errorHandler.warning("Unknown mapping from CIM primitive'" + primitiveType + "' to XSD datatype. Using xsd:string as fallback.", -1,-1);
- return XSDDatatype.XSDstring;
+ private RDFDatatype getXsdDatatype(String primitiveType) {
+ var dt = primitiveToRdfDatatypeMap.get(primitiveType);
+ if (dt != null) {
+ return dt;
}
+ errorHandler.warning("Unknown mapping from CIM primitive'" + primitiveType
+ + "' to XSD datatype. Using xsd:string as fallback.", -1, -1);
+ return XSDDatatype.XSDstring;
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/CimDatasetGraph.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/CimDatasetGraph.java
index f1d7a213..74b4a4b8 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/CimDatasetGraph.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/CimDatasetGraph.java
@@ -22,18 +22,17 @@
import de.soptim.opencgmes.cimxml.graph.CimModelHeader;
import de.soptim.opencgmes.cimxml.graph.DisjointMultiUnion;
import de.soptim.opencgmes.cimxml.graph.FastDeltaGraph;
+import java.util.ArrayList;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Triple;
import org.apache.jena.sparql.core.DatasetGraph;
-import java.util.ArrayList;
-
/**
* A specialized {@link DatasetGraph} for IEC 61970-552 CIM models.
*
*
This interface extends the standard Jena DatasetGraph with CIM-specific operations
- * for handling the structure defined in IEC 61970-552. A CIM dataset can contain either
- * a FullModel or a DifferenceModel:
+ * for handling the structure defined in IEC 61970-552. A CIM dataset can contain either a FullModel
+ * or a DifferenceModel:
*
*
FullModel Structure:
*
@@ -77,141 +76,180 @@
*/
public interface CimDatasetGraph extends DatasetGraph {
- /**
- * Checks if this dataset contains a FullModel.
- * @return true if this dataset contains a FullModel, false otherwise
- */
- default boolean isFullModel() {
- return containsGraph(CimHeaderVocabulary.TYPE_FULL_MODEL);
+ /**
+ * Checks if this dataset contains a FullModel.
+ *
+ * @return true if this dataset contains a FullModel, false otherwise
+ */
+ default boolean isFullModel() {
+ return containsGraph(CimHeaderVocabulary.TYPE_FULL_MODEL);
+ }
+
+ /**
+ * Checks if this dataset contains a DifferenceModel.
+ *
+ * @return true if this dataset contains a DifferenceModel, false otherwise
+ */
+ default boolean isDifferenceModel() {
+ return containsGraph(CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL);
+ }
+
+ /**
+ * Gets the forward differences graph of this DifferenceModel.
+ *
+ * @return the forward differences graph
+ * @throws IllegalStateException if this dataset is not a DifferenceModel
+ */
+ default Graph getForwardDifferences() {
+ if (!this.isDifferenceModel()) {
+ throw new IllegalStateException(
+ "Forward differences are only available for DifferenceModels. Use isDifferenceModel()"
+ + " to check.");
}
-
- /**
- * Checks if this dataset contains a DifferenceModel.
- * @return true if this dataset contains a DifferenceModel, false otherwise
- */
- default boolean isDifferenceModel() {
- return containsGraph(CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL);
+ return getGraph(CimHeaderVocabulary.GRAPH_FORWARD_DIFFERENCES);
+ }
+
+ /**
+ * Gets the reverse differences graph of this DifferenceModel.
+ *
+ * @return the reverse differences graph
+ * @throws IllegalStateException if this dataset is not a DifferenceModel
+ */
+ default Graph getReverseDifferences() {
+ if (!this.isDifferenceModel()) {
+ throw new IllegalStateException(
+ "Reverse differences are only available for DifferenceModels. Use isDifferenceModel()"
+ + " to check.");
}
-
- /**
- * Gets the forward differences graph of this DifferenceModel.
- * @return the forward differences graph
- * @throws IllegalStateException if this dataset is not a DifferenceModel
- */
- default Graph getForwardDifferences() {
- if (!this.isDifferenceModel())
- throw new IllegalStateException("Forward differences are only available for DifferenceModels. Use isDifferenceModel() to check.");
- return getGraph(CimHeaderVocabulary.GRAPH_FORWARD_DIFFERENCES);
+ return getGraph(CimHeaderVocabulary.GRAPH_REVERSE_DIFFERENCES);
+ }
+
+ /**
+ * Gets the preconditions graph of this DifferenceModel.
+ *
+ * @return the preconditions graph
+ * @throws IllegalStateException if this dataset is not a DifferenceModel
+ */
+ default Graph getPreconditions() {
+ if (!this.isDifferenceModel()) {
+ throw new IllegalStateException(
+ "Preconditions are only available for DifferenceModels. Use isDifferenceModel()"
+ + " to check.");
}
-
- /**
- * Gets the reverse differences graph of this DifferenceModel.
- * @return the reverse differences graph
- * @throws IllegalStateException if this dataset is not a DifferenceModel
- */
- default Graph getReverseDifferences() {
- if (!this.isDifferenceModel())
- throw new IllegalStateException("Reverse differences are only available for DifferenceModels. Use isDifferenceModel() to check.");
- return getGraph(CimHeaderVocabulary.GRAPH_REVERSE_DIFFERENCES);
+ return getGraph(CimHeaderVocabulary.GRAPH_PRECONDITIONS);
+ }
+
+ /**
+ * Gets the model header of this FullModel or DifferenceModel.
+ *
+ * @return the model header
+ * @throws IllegalStateException if this dataset is not a FullModel or DifferenceModel
+ */
+ default CimModelHeader getModelHeader() {
+ var graphName = isFullModel()
+ ? CimHeaderVocabulary.TYPE_FULL_MODEL
+ : isDifferenceModel()
+ ? CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL
+ : null;
+
+ if (graphName == null) {
+ throw new IllegalStateException(
+ "Model header is only available for FullModels or DifferenceModels. Use isFullModel()"
+ + " or isDifferenceModel() to check.");
}
- /**
- * Gets the preconditions graph of this DifferenceModel.
- * @return the preconditions graph
- * @throws IllegalStateException if this dataset is not a DifferenceModel
- */
- default Graph getPreconditions() {
- if (!this.isDifferenceModel())
- throw new IllegalStateException("Preconditions are only available for DifferenceModels. Use isDifferenceModel() to check.");
- return getGraph(CimHeaderVocabulary.GRAPH_PRECONDITIONS);
+ return CimModelHeader.wrap(getGraph(graphName));
+ }
+
+ /**
+ * Gets the body graph of this FullModel.
+ *
+ * @return the body graph
+ * @throws IllegalStateException if this dataset is not a FullModel
+ */
+ default Graph getBody() {
+ if (!this.isFullModel()) {
+ throw new IllegalStateException(
+ "Body graph is only available for FullModels. Use isFullModel() to check.");
}
-
- /**
- * Gets the model header of this FullModel or DifferenceModel.
- * @return the model header
- * @throws IllegalStateException if this dataset is not a FullModel or DifferenceModel
- */
- default CimModelHeader getModelHeader() {
- var graphName = isFullModel()
- ? CimHeaderVocabulary.TYPE_FULL_MODEL
- : isDifferenceModel()
- ? CimHeaderVocabulary.TYPE_DIFFERENCE_MODEL
- : null;
-
- if (graphName == null)
- throw new IllegalStateException("Model header is only available for FullModels or DifferenceModels. Use isFullModel() or isDifferenceModel() to check.");
-
- return CimModelHeader.wrap(getGraph(graphName));
+ return getDefaultGraph();
+ }
+
+ /**
+ * Converts this FullModel to a single graph by combining the model header and the body graph.
+ *
+ * @return a new Graph representing the FullModel - containing both the Model header and the body
+ * graph
+ * @throws IllegalStateException if this dataset is not a FullModel
+ */
+ default Graph fullModelToSingleGraph() {
+ if (!this.isFullModel()) {
+ throw new IllegalStateException(
+ "Full model graph is only available for FullModels. Use isFullModel() to check.");
}
- /**
- * Gets the body graph of this FullModel.
- * @return the body graph
- * @throws IllegalStateException if this dataset is not a FullModel
- */
- default Graph getBody() {
- if (!this.isFullModel())
- throw new IllegalStateException("Body graph is only available for FullModels. Use isFullModel() to check.");
- return getDefaultGraph();
+ var header = getModelHeader();
+ var body = getBody();
+
+ var union = new DisjointMultiUnion(header, body);
+ union.getPrefixMapping().setNsPrefixes(header.getPrefixMapping());
+
+ return union;
+ }
+
+ /**
+ * Converts this DifferenceModel to a FullModel by applying the forward and reverse differences to
+ * the provided predecessor FullModel.
+ *
+ * @param predecessorFullModel the predecessor FullModel to which the differences will be applied
+ * @return a new Graph representing the resulting FullModel - containing only the body graph
+ * @throws IllegalStateException if this dataset is not a DifferenceModel
+ * @throws IllegalArgumentException if the provided predecessorFullModel is not a FullModel, if
+ * its Model is not in the current Model.Supersedes, or if it
+ * does not contain all required preconditions
+ */
+ default Graph differenceModelToFullModel(CimDatasetGraph predecessorFullModel) {
+ if (!this.isDifferenceModel()) {
+ throw new IllegalStateException(
+ "Conversion to full model is only available for DifferenceModels. Use isDifferenceModel()"
+ + " to check.");
+ }
+ if (!predecessorFullModel.isFullModel()) {
+ throw new IllegalArgumentException(
+ "The provided predecessorFullModel dataset must be a FullModel. Use isFullModel()"
+ + " to check.");
}
- /**
- * Converts this FullModel to a single graph by combining the model header and the body graph.
- * @return a new Graph representing the FullModel - containing both the Model header and the body graph
- * @throws IllegalStateException if this dataset is not a FullModel
- */
- default Graph fullModelToSingleGraph() {
- if (!this.isFullModel())
- throw new IllegalStateException("Full model graph is only available for FullModels. Use isFullModel() to check.");
-
- var header = getModelHeader();
- var body = getBody();
-
- var union = new DisjointMultiUnion(header, body);
- union.getPrefixMapping().setNsPrefixes(header.getPrefixMapping());
-
- return union;
+ if (this.getModelHeader().getSupersedes()
+ .contains(predecessorFullModel.getModelHeader().getModel())) {
+ throw new IllegalArgumentException(
+ "The provided predecessorFullModel dataset Model must be in current Model.Supersedes.");
}
- /**
- * Converts this DifferenceModel to a FullModel by applying the forward and reverse differences
- * to the provided predecessor FullModel.
- * @param predecessorFullModel the predecessor FullModel to which the differences will be applied
- * @return a new Graph representing the resulting FullModel - containing only the body graph
- * @throws IllegalStateException if this dataset is not a DifferenceModel
- * @throws IllegalArgumentException if the provided predecessorFullModel is not a FullModel,
- * if its Model is not in the current Model.Supersedes,
- * or if it does not contain all required preconditions
- */
- default Graph differenceModelToFullModel(CimDatasetGraph predecessorFullModel) {
- if (!this.isDifferenceModel())
- throw new IllegalStateException("Conversion to full model is only available for DifferenceModels. Use isDifferenceModel() to check.");
- if (!predecessorFullModel.isFullModel())
- throw new IllegalArgumentException("The provided predecessorFullModel dataset must be a FullModel. Use isFullModel() to check.");
-
- if (this.getModelHeader().getSupersedes().contains(predecessorFullModel.getModelHeader().getModel()))
- throw new IllegalArgumentException("The provided predecessorFullModel dataset Model must be in current Model.Supersedes.");
-
- var predecessorBody = predecessorFullModel.getBody();
-
- var preconditions = getPreconditions();
- if (preconditions != null && !preconditions.isEmpty()) {
- var missingPreconditions = new ArrayList();
- preconditions.find().forEachRemaining(t -> {
- if (!predecessorBody.contains(t))
- missingPreconditions.add(t);
- });
- if (!missingPreconditions.isEmpty()) {
- throw new IllegalArgumentException("The provided predecessorFullModel dataset does not contain all required preconditions. Missing preconditions: " + missingPreconditions);
- }
+ var predecessorBody = predecessorFullModel.getBody();
+
+ var preconditions = getPreconditions();
+ if (preconditions != null && !preconditions.isEmpty()) {
+ var missingPreconditions = new ArrayList();
+ preconditions.find().forEachRemaining(t -> {
+ if (!predecessorBody.contains(t)) {
+ missingPreconditions.add(t);
}
+ });
+ if (!missingPreconditions.isEmpty()) {
+ throw new IllegalArgumentException(
+ "The provided predecessorFullModel dataset does not contain all required preconditions."
+ + " Missing preconditions: "
+ + missingPreconditions);
+ }
+ }
- var forwardDifferences = getForwardDifferences();
- var reverseDifferences = getReverseDifferences();
+ var forwardDifferences = getForwardDifferences();
+ var reverseDifferences = getReverseDifferences();
- var deltaGraph = new FastDeltaGraph(predecessorBody, forwardDifferences, reverseDifferences);
- deltaGraph.getPrefixMapping().setNsPrefixes(this.getModelHeader().getPrefixMapping());
+ var deltaGraph = new FastDeltaGraph(predecessorBody, forwardDifferences, reverseDifferences);
+ deltaGraph.getPrefixMapping().setNsPrefixes(this.getModelHeader().getPrefixMapping());
- return deltaGraph;
- }
+ return deltaGraph;
+ }
}
diff --git a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/LinkedCimDatasetGraph.java b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/LinkedCimDatasetGraph.java
index e0eaad21..73c4796e 100644
--- a/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/LinkedCimDatasetGraph.java
+++ b/cimxml/src/main/java/de/soptim/opencgmes/cimxml/sparql/core/LinkedCimDatasetGraph.java
@@ -18,190 +18,233 @@
package de.soptim.opencgmes.cimxml.sparql.core;
+import java.io.Serial;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.query.ReadWrite;
import org.apache.jena.query.TxnType;
import org.apache.jena.riot.system.PrefixMap;
import org.apache.jena.riot.system.PrefixMapStd;
-import org.apache.jena.sparql.core.*;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphCollection;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.core.Transactional;
+import org.apache.jena.sparql.core.TransactionalLock;
/**
* A {@link DatasetGraph} that holds a set of named graphs and an optional default graph.
- *
- * This implementation provides "best effort" transactions; it only provides MRPlusSW locking.
- * So all graphs that are transactional must support MRPlusSW locking.
- *
- * Only the transactional graphs are included in the transaction.
+ *
+ *
This implementation provides "best effort" transactions; it only provides MRPlusSW locking. So
+ * all graphs that are transactional must support MRPlusSW locking.
+ *
+ *
Only the transactional graphs are included in the transaction.
*/
public class LinkedCimDatasetGraph extends DatasetGraphCollection implements CimDatasetGraph {
- public LinkedCimDatasetGraph() {
- super();
- }
-
- public LinkedCimDatasetGraph(Graph defaultGraph) {
- this();
- addGraph(Quad.defaultGraphIRI, defaultGraph);
- }
-
- public static class LinkedDatasetTransactionException extends RuntimeException {
-
- private final Collection exceptions;
-
- public Collection getExceptions() {
- return exceptions;
- }
-
- public LinkedDatasetTransactionException(String message, Collection exceptions) {
- super(message);
- this.exceptions = exceptions;
- }
- }
-
- private final ConcurrentMap graphs = new ConcurrentHashMap<>();
- private final ConcurrentMap transactionalGraphs = new ConcurrentHashMap<>();
-
- private final TransactionalLock txn = TransactionalLock.createMRPlusSW();
-
- public Collection getGraphs() {
- return graphs.values();
- }
-
- @Override
- public Iterator listGraphNodes() {
- return graphs.keySet().iterator();
- }
-
- protected final PrefixMap prefixes = new PrefixMapStd();
-
- @Override
- public PrefixMap prefixes() {
- return prefixes;
- }
-
- @Override
- public boolean supportsTransactions() {
- return true;
- }
-
- @Override
- public Graph getDefaultGraph() {
- return graphs.getOrDefault(Quad.defaultGraphIRI, Graph.emptyGraph);
- }
-
- @Override
- public Graph getGraph(Node graphNode) {
- return graphs.get(graphNode);
- }
-
- @Override
- public void addGraph(Node graphName, Graph graph) {
- graphs.put(graphName, graph);
- if (graph instanceof Transactional transactional) {
- transactionalGraphs.put(graphName, transactional);
- }
- }
-
- @Override
- public void removeGraph(Node graphName) {
- graphs.remove(graphName);
- transactionalGraphs.remove(graphName);
- }
-
- @Override
- public void begin(TxnType type) {
- txn.begin(type);
- var openedTransactions = new ArrayList();
- try {
- for (Transactional transactional : transactionalGraphs.values()) {
- transactional.begin(type);
- openedTransactions.add(transactional);
- }
- } catch (Exception e) {
- txn.abort();
- for (Transactional transactional : openedTransactions) {
- transactional.abort();
- }
- throw e;
- }
- }
-
- @Override
- public boolean promote(Promote mode) {
- return false;
- }
-
- @Override
- public void commit() {
- txn.commit();
- var failedCommits = new ArrayList();
- for (Transactional transactional : transactionalGraphs.values()) {
- try {
- transactional.commit();
- } catch (Exception e) {
- failedCommits.add(e);
- }
- }
- if (!failedCommits.isEmpty()) {
- //Exception with message "Failed to commit transactions on x graphs"
- throw new LinkedDatasetTransactionException("Failed to commit transactions on " + failedCommits.size() + " graphs", failedCommits);
- }
- }
-
- @Override
- public void abort() {
- txn.abort();
- var failedAborts = new ArrayList();
- for (Transactional transactional : transactionalGraphs.values()) {
- try {
- transactional.abort();
- } catch (Exception e) {
- failedAborts.add(e);
- }
- }
- if (!failedAborts.isEmpty()) {
- //Exception with message "Failed to abort transactions on x graphs"
- throw new LinkedDatasetTransactionException("Failed to abort transactions on " + failedAborts.size() + " graphs", failedAborts);
- }
- }
-
- @Override
- public void end() {
- txn.end();
- var failedEnds = new ArrayList();
- for (Transactional transactional : transactionalGraphs.values()) {
- try {
- transactional.end();
- } catch (Exception e) {
- failedEnds.add(e);
- }
- }
- if (!failedEnds.isEmpty()) {
- //Exception with message "Failed to end transaction on x graphs"
- throw new LinkedDatasetTransactionException("Failed to end transaction on " + failedEnds.size() + " graphs", failedEnds);
- }
- }
-
- @Override
- public ReadWrite transactionMode() {
- return txn.transactionMode();
- }
-
- @Override
- public TxnType transactionType() {
- return txn.transactionType();
- }
-
- @Override
- public boolean isInTransaction() {
- return txn.isInTransaction();
- }
+ protected final PrefixMap prefixes = new PrefixMapStd();
+ private final ConcurrentMap graphs = new ConcurrentHashMap<>();
+ private final ConcurrentMap transactionalGraphs = new ConcurrentHashMap<>();
+ private final TransactionalLock txn = TransactionalLock.createMRPlusSW();
+
+ /**
+ * Creates a new empty LinkedCimDatasetGraph with no default graph and no named graphs.
+ */
+ public LinkedCimDatasetGraph() {
+ super();
+ }
+
+ /**
+ * Creates a new LinkedCimDatasetGraph with the given default graph and no named graphs.
+ * The defaultgraph is associated with the default graph name (Quad.defaultGraphIRI).
+ *
+ * @param defaultGraph the default graph to be used in this dataset
+ */
+ public LinkedCimDatasetGraph(Graph defaultGraph) {
+ this();
+ addGraph(graphs, transactionalGraphs, Quad.defaultGraphIRI, defaultGraph);
+ }
+
+ public Collection getGraphs() {
+ return graphs.values();
+ }
+
+ @Override
+ public Iterator listGraphNodes() {
+ return graphs.keySet().iterator();
+ }
+
+ @Override
+ public PrefixMap prefixes() {
+ return prefixes;
+ }
+
+ @Override
+ public boolean supportsTransactions() {
+ return true;
+ }
+
+ @Override
+ public Graph getDefaultGraph() {
+ return graphs.getOrDefault(Quad.defaultGraphIRI, Graph.emptyGraph);
+ }
+
+ @Override
+ public Graph getGraph(Node graphNode) {
+ return graphs.get(graphNode);
+ }
+
+ private static void addGraph(Map namedGraphs,
+ Map namedTransactionals,
+ Node graphName, Graph graph) {
+ namedGraphs.put(graphName, graph);
+ if (graph instanceof Transactional transactional) {
+ namedTransactionals.put(graphName, transactional);
+ }
+ }
+
+ @Override
+ public void addGraph(Node graphName, Graph graph) {
+ addGraph(graphs, transactionalGraphs, graphName, graph);
+ }
+
+ @Override
+ public void removeGraph(Node graphName) {
+ graphs.remove(graphName);
+ transactionalGraphs.remove(graphName);
+ }
+
+ @Override
+ public void begin(TxnType type) {
+ txn.begin(type);
+ var openedTransactions = new ArrayList();
+ try {
+ for (Transactional transactional : transactionalGraphs.values()) {
+ transactional.begin(type);
+ openedTransactions.add(transactional);
+ }
+ } catch (Exception e) {
+ txn.abort();
+ for (Transactional transactional : openedTransactions) {
+ transactional.abort();
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public boolean promote(Promote mode) {
+ return false;
+ }
+
+ @Override
+ public void commit() {
+ txn.commit();
+ var failedCommits = new ArrayList();
+ for (Transactional transactional : transactionalGraphs.values()) {
+ try {
+ transactional.commit();
+ } catch (Exception e) {
+ failedCommits.add(e);
+ }
+ }
+ if (!failedCommits.isEmpty()) {
+ // Exception with message "Failed to commit transactions on x graphs"
+ throw new LinkedDatasetTransactionException(
+ "Failed to commit transactions on " + failedCommits.size() + " graphs", failedCommits);
+ }
+ }
+
+ @Override
+ public void abort() {
+ txn.abort();
+ var failedAborts = new ArrayList();
+ for (Transactional transactional : transactionalGraphs.values()) {
+ try {
+ transactional.abort();
+ } catch (Exception e) {
+ failedAborts.add(e);
+ }
+ }
+ if (!failedAborts.isEmpty()) {
+ // Exception with message "Failed to abort transactions on x graphs"
+ throw new LinkedDatasetTransactionException(
+ "Failed to abort transactions on " + failedAborts.size() + " graphs", failedAborts);
+ }
+ }
+
+ @Override
+ public void end() {
+ txn.end();
+ var failedEnds = new ArrayList();
+ for (Transactional transactional : transactionalGraphs.values()) {
+ try {
+ transactional.end();
+ } catch (Exception e) {
+ failedEnds.add(e);
+ }
+ }
+ if (!failedEnds.isEmpty()) {
+ // Exception with message "Failed to end transaction on x graphs"
+ throw new LinkedDatasetTransactionException(
+ "Failed to end transaction on " + failedEnds.size() + " graphs", failedEnds);
+ }
+ }
+
+ @Override
+ public ReadWrite transactionMode() {
+ return txn.transactionMode();
+ }
+
+ @Override
+ public TxnType transactionType() {
+ return txn.transactionType();
+ }
+
+ @Override
+ public boolean isInTransaction() {
+ return txn.isInTransaction();
+ }
+
+ /**
+ * Exception thrown when one or more transactional graphs fail to commit, abort,
+ * or end a transaction.
+ * This exception contains a collection of the individual exceptions that occurred
+ * during the transaction operation.
+ */
+ public static class LinkedDatasetTransactionException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ private final ArrayList exceptions;
+
+ /**
+ * Creates a new LinkedDatasetTransactionException with the given message
+ * and collection of exceptions.
+ *
+ * @param message the detail message for this exception
+ * @param exceptions the collection of exceptions that occurred during the transaction operation
+ */
+ public LinkedDatasetTransactionException(String message, Collection exceptions) {
+ super(message);
+ this.exceptions = new ArrayList<>(exceptions);
+ }
+
+ /**
+ * Returns the collection of exceptions that occurred during the transaction operation.
+ *
+ * @return the collection of exceptions
+ */
+ public List getExceptions() {
+ return exceptions;
+ }
+ }
}
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimModelHeader.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimModelHeader.java
index 78fe7e19..5da8d2ac 100644
--- a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimModelHeader.java
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimModelHeader.java
@@ -19,8 +19,9 @@
package de.soptim.opencgmes.cimxml.graph;
import de.soptim.opencgmes.cimxml.parser.ReaderCIMXML_StAX_SR;
-import de.soptim.opencgmes.cimxml.parser.system.StreamCIMXMLToDatasetGraph;
+import de.soptim.opencgmes.cimxml.parser.system.StreamCimXmlToDatasetGraph;
import org.apache.jena.graph.Node;
+import org.apache.jena.riot.system.ErrorHandlerFactory;
import org.junit.Test;
import java.io.StringReader;
@@ -52,14 +53,14 @@ public void parseFullModelHeader() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertTrue(streamRDF.getCIMDatasetGraph().isFullModel());
- assertNotNull(streamRDF.getCIMDatasetGraph().getModelHeader());
- var modelHeader = streamRDF.getCIMDatasetGraph().getModelHeader();
+ assertTrue(streamRDF.getCimDatasetGraph().isFullModel());
+ assertNotNull(streamRDF.getCimDatasetGraph().getModelHeader());
+ var modelHeader = streamRDF.getCimDatasetGraph().getModelHeader();
assertEquals("urn:uuid:08984e27-811f-4042-9125-1531ae0de0f6", modelHeader.getModel().toString());
@@ -102,14 +103,14 @@ public void parseDifferenceModelHeader() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertTrue(streamRDF.getCIMDatasetGraph().isDifferenceModel());
- assertNotNull(streamRDF.getCIMDatasetGraph().getModelHeader());
- var modelHeader = streamRDF.getCIMDatasetGraph().getModelHeader();
+ assertTrue(streamRDF.getCimDatasetGraph().isDifferenceModel());
+ assertNotNull(streamRDF.getCimDatasetGraph().getModelHeader());
+ var modelHeader = streamRDF.getCimDatasetGraph().getModelHeader();
assertEquals("urn:uuid:08984e27-811f-4042-9125-1531ae0de0f6", modelHeader.getModel().toString());
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile16.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile16.java
index cfa3d3c4..6fc463fe 100644
--- a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile16.java
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile16.java
@@ -18,9 +18,8 @@
package de.soptim.opencgmes.cimxml.graph;
-import de.soptim.opencgmes.cimxml.CimVersion;
-import org.apache.jena.mem2.GraphMem2Roaring;
import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.graph.GraphFactory;
import org.junit.Test;
import java.io.StringReader;
@@ -118,7 +117,7 @@ public void parseProfileOntologyHeader() {
""";
- var graph = new GraphMem2Roaring();
+ var graph = GraphFactory.createGraphMem();
RDFParser.create()
.source(new StringReader(rdfxml))
@@ -129,20 +128,20 @@ public void parseProfileOntologyHeader() {
var ontology = CimProfile.wrap(graph);
assertFalse(ontology.isHeaderProfile());
- assertEquals(CimVersion.CIM_16, ontology.getCIMVersion());
+ assertEquals(CimProfile16.CIM_NAMESPACE, ontology.getCimNamespace());
- assertEquals(6, ontology.getOwlVersionIRIs().size());
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertEquals(6, ontology.getOwlVersionIris().size());
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://example.org/MyCustom/Core/1/1")));
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://example.org/MyCustom/Operation/1/1")));
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://example.org/MyCustom/ShortCircuit/1/1")));
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://entsoe.eu/CIM/MyCustomCore/2/2")));
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://entsoe.eu/CIM/MyCustomOperation/2/2")));
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://entsoe.eu/CIM/MyCustomShortCircuit/2/2")));
assertNull(ontology.getOwlVersionInfo());
@@ -164,7 +163,7 @@ public void parseProfileFileHeaderProfile() {
""";
- var graph = new GraphMem2Roaring();
+ var graph = GraphFactory.createGraphMem();
RDFParser.create()
.source(new StringReader(rdfxml))
@@ -175,6 +174,6 @@ public void parseProfileFileHeaderProfile() {
var ontology = CimProfile.wrap(graph);
assertTrue(ontology.isHeaderProfile());
- assertEquals(CimVersion.CIM_16, ontology.getCIMVersion());
+ assertEquals(CimProfile16.CIM_NAMESPACE, ontology.getCimNamespace());
}
}
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile17.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile17.java
index 92175f89..3fd78467 100644
--- a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile17.java
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile17.java
@@ -18,13 +18,11 @@
package de.soptim.opencgmes.cimxml.graph;
-import de.soptim.opencgmes.cimxml.CimVersion;
-import org.apache.jena.mem2.GraphMem2Roaring;
import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.graph.GraphFactory;
import org.junit.Test;
import java.io.StringReader;
-import java.util.Set;
import static org.junit.Assert.*;
@@ -56,7 +54,7 @@ public void parseProfileOntologyHeader() {
""";
- var graph = new GraphMem2Roaring();
+ var graph = GraphFactory.createGraphMem();
RDFParser.create()
.source(new StringReader(rdfxml))
@@ -67,12 +65,12 @@ public void parseProfileOntologyHeader() {
var ontology = CimProfile.wrap(graph);
assertFalse(ontology.isHeaderProfile());
- assertTrue(Set.of(CimVersion.CIM_17, CimVersion.CIM_18).contains(ontology.getCIMVersion()));
+ assertEquals(CimProfile17.CIM_NAMESPACE, ontology.getCimNamespace());
- assertEquals(2, ontology.getOwlVersionIRIs().size());
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertEquals(2, ontology.getOwlVersionIris().size());
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://example.org/MyCustom/Core/1/1")));
- assertTrue(ontology.getOwlVersionIRIs().stream()
+ assertTrue(ontology.getOwlVersionIris().stream()
.anyMatch(n -> n.getURI().equals("http://example.org/MyCustom/Operation/1/1")));
assertEquals("1.1.0", ontology.getOwlVersionInfo());
assertEquals("MYCUST", ontology.getDcatKeyword());
@@ -95,7 +93,7 @@ public void parseProfileFileHeaderProfile() {
""";
- var graph = new GraphMem2Roaring();
+ var graph = GraphFactory.createGraphMem();
RDFParser.create()
.source(new StringReader(rdfxml))
@@ -106,7 +104,7 @@ public void parseProfileFileHeaderProfile() {
var ontology = CimProfile.wrap(graph);
assertTrue(ontology.isHeaderProfile());
- assertEquals(CimVersion.CIM_17, ontology.getCIMVersion());
+ assertEquals(CimProfile17.CIM_NAMESPACE, ontology.getCimNamespace());
}
}
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile18.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile18.java
index a77ea7a0..2bf7c2e6 100644
--- a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile18.java
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/graph/TestCimProfile18.java
@@ -18,9 +18,8 @@
package de.soptim.opencgmes.cimxml.graph;
-import de.soptim.opencgmes.cimxml.CimVersion;
-import org.apache.jena.mem2.GraphMem2Roaring;
import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.graph.GraphFactory;
import org.junit.Test;
import java.io.StringReader;
@@ -47,7 +46,7 @@ public void parseProfileFileHeaderProfile() {
""";
- var graph = new GraphMem2Roaring();
+ var graph = GraphFactory.createGraphMem();
RDFParser.create()
.source(new StringReader(rdfxml))
@@ -58,7 +57,7 @@ public void parseProfileFileHeaderProfile() {
var ontology = CimProfile.wrap(graph);
assertTrue(ontology.isHeaderProfile());
- assertEquals(CimVersion.CIM_18, ontology.getCIMVersion());
+ assertEquals(CimProfile18.CIM_NAMESPACE, ontology.getCimNamespace());
}
}
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestConvertCimXmlToJsonLd.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestConvertCimXmlToJsonLd.java
new file mode 100644
index 00000000..3ca9e612
--- /dev/null
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestConvertCimXmlToJsonLd.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 de.soptim.opencgmes.cimxml.parser;
+
+import de.soptim.opencgmes.cimxml.graph.CimProfile;
+import de.soptim.opencgmes.cimxml.sparql.core.LinkedCimDatasetGraph;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFDataMgr;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class TestConvertCimXmlToJsonLd {
+
+ private static final String CIM_SCHEMA_IRI = "cgmes:schema:";
+ private static final String CIM_MODEL_IRI = "cgmes:model:";
+ private static final String CIM_HEADER_IRI = "cgmes:header:";
+ //private static final String CIM_SHACL_IRI = "cgmes:shacl:";
+ //private static final String CIM_VALIDATION_REPORT_IRI = "cgmes:validationReport:";
+
+ public void convertCimXmlToJsonLd(String rdfsSchemaFolder, String cimXmlFolder,
+ String jsonldOutputFile) {
+ convertCimXmlToJsonLd(Path.of(rdfsSchemaFolder), Path.of(cimXmlFolder),
+ Path.of(jsonldOutputFile));
+ }
+
+ public void convertCimXmlToJsonLd(Path rdfsSchemaFolder, Path cimXmlFolder,
+ Path jsonldOutputFile) {
+ final var dataset = new LinkedCimDatasetGraph();
+ final var parser = new CimXmlParser();
+ try (var rdfFiles = Files.list(rdfsSchemaFolder)) {
+ rdfFiles.parallel()
+ .filter(f -> f.toString().endsWith(".rdf"))
+ .forEach(rdfFile -> {
+ try {
+ final var schema = parser.parseAndRegisterCimProfile(rdfFile);
+ // add only header profiles to the dataset.
+ // the other profiles are only added when referenced in the dataset.
+ if (schema.isHeaderProfile()) {
+ dataset.addGraph(NodeFactory.createURI(CIM_SCHEMA_IRI + schema.getDcatKeyword()),
+ schema);
+ dataset.prefixes().putAll(schema.getPrefixMapping());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ try (var cimXmlFiles = Files.list(cimXmlFolder)) {
+ cimXmlFiles.parallel()
+ .filter(f -> f.toString().endsWith(".xml"))
+ .forEach(cimXmlFile -> {
+ try {
+ final var cimDataset = parser.parseCimModel(cimXmlFile);
+ if (!cimDataset.isFullModel()) {
+ System.out.println(
+ "File " + cimXmlFile.getFileName() + " is not a FullModel. Skipping.");
+ return;
+ }
+ final var keywords = parser.getCimProfileRegistry()
+ .getMatchingProfiles(cimDataset.getModelHeader().getProfiles())
+ .stream()
+ .peek(schema -> {
+ final var schemaGraphName = NodeFactory.createURI(
+ CIM_SCHEMA_IRI + schema.getDcatKeyword());
+ if (!dataset.containsGraph(schemaGraphName)) {
+ dataset.addGraph(schemaGraphName, schema);
+ dataset.prefixes().putAll(schema.getPrefixMapping());
+ }
+ })
+ .map(CimProfile::getDcatKeyword)
+ .distinct()
+ .sorted()
+ .toList();
+ if (keywords.isEmpty()) {
+ System.out.println(
+ "File " + cimXmlFile.getFileName() + " does not match any profile. Skipping.");
+ return;
+ }
+ var keyword = keywords.getFirst();
+ if (keywords.size() > 1) {
+ System.out.println(
+ "File " + cimXmlFile.getFileName() + " matches multiple profiles: " + keywords
+ + ". Using first one: " + keyword);
+ }
+ dataset.addGraph(NodeFactory.createURI(CIM_HEADER_IRI + keyword),
+ cimDataset.getModelHeader());
+ dataset.addGraph(NodeFactory.createURI(CIM_MODEL_IRI + keyword),
+ cimDataset.getBody());
+ dataset.prefixes().putAll(cimDataset.prefixes());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ // create java nio file and output stream
+ try (var rdfFiles = new BufferedOutputStream(Files.newOutputStream(jsonldOutputFile))) {
+ RDFDataMgr.write(rdfFiles, dataset, Lang.JSONLD11);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Ignore
+ @Test
+ public void convertFullGridMerged() {
+ convertCimXmlToJsonLd(
+ "../data/ApplicationProfilesLibrary-release1-1-0/CGMES/RDFS/",
+ "../data/CGMES_ConformityAssessmentScheme_r3-0-2/CGMES_ConformityAssessmentScheme_TestConfigurations_v3-0-3/v3.0/FullGrid/FullGrid-Merged/",
+ "../output/FullGrid-Merged.jsonld");
+ }
+
+ @Ignore
+ @Test
+ public void convertRealGridMerged() {
+ convertCimXmlToJsonLd(
+ "../data/ApplicationProfilesLibrary-release1-1-0/CGMES/RDFS/",
+ "../data/CGMES_ConformityAssessmentScheme_r3-0-2/CGMES_ConformityAssessmentScheme_TestConfigurations_v3-0-3/v3.0/RealGrid/RealGrid-Merged/",
+ "../output/RealGrid-Merged.jsonld");
+ }
+}
\ No newline at end of file
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/parser/TestParserCIMXMLConformity.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestParserCIMXMLConformity.java
similarity index 88%
rename from cimxml/src/test/java/de/soptim/opencgmes/parser/TestParserCIMXMLConformity.java
rename to cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestParserCIMXMLConformity.java
index e5b5cc0b..5897fd90 100644
--- a/cimxml/src/test/java/de/soptim/opencgmes/parser/TestParserCIMXMLConformity.java
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestParserCIMXMLConformity.java
@@ -16,16 +16,17 @@
* limitations under the License.
*/
-package de.soptim.opencgmes.parser;
+package de.soptim.opencgmes.cimxml.parser;
-import de.soptim.opencgmes.cimxml.CimVersion;
import de.soptim.opencgmes.cimxml.graph.CimProfile;
-import de.soptim.opencgmes.cimxml.parser.ReaderCIMXML_StAX_SR;
-import de.soptim.opencgmes.cimxml.parser.system.StreamCIMXMLToDatasetGraph;
+import de.soptim.opencgmes.cimxml.graph.CimProfile17;
+import de.soptim.opencgmes.cimxml.graph.CimProfile18;
+import de.soptim.opencgmes.cimxml.parser.system.StreamCimXmlToDatasetGraph;
import de.soptim.opencgmes.cimxml.rdfs.CimProfileRegistryStd;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.riot.system.ErrorHandlerFactory;
import org.junit.Test;
import java.io.StringReader;
@@ -34,7 +35,6 @@
public class TestParserCIMXMLConformity {
-
/**
* Test that the parser can parse a CIMXML document with a version declaration.
* And that the version is correctly parsed.
@@ -48,12 +48,12 @@ public void parseIEC61970_552Version() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertEquals("version=\"2.0\"", streamRDF.getVersionOfIEC61970_552());
+ assertEquals("version=\"2.0\"", streamRDF.getVersionOfIec61970_552());
}
/**
@@ -67,16 +67,16 @@ public void parseWithoutIEC61970_552Version() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertNull(streamRDF.getVersionOfIEC61970_552());
+ assertNull(streamRDF.getVersionOfIec61970_552());
}
@Test
- public void parseCIMVersion17() {
+ public void parseCimNamespace17() {
final var rdfxml = """
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertEquals(CimVersion.CIM_17, streamRDF.getVersionOfCIMXML());
+ assertEquals(CimProfile17.CIM_NAMESPACE, streamRDF.getCimNamespace());
}
@Test
- public void parseCIMVersion18() {
+ public void parseCimNamespace18() {
final var rdfxml = """
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertEquals(CimVersion.CIM_18, streamRDF.getVersionOfCIMXML());
+ assertEquals(CimProfile18.CIM_NAMESPACE, streamRDF.getCimNamespace());
}
@Test
@@ -125,20 +125,20 @@ public void parseFullModelHeaderAndContentInDifferentGraphs() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertTrue(streamRDF.getCIMDatasetGraph().isFullModel());
+ assertTrue(streamRDF.getCimDatasetGraph().isFullModel());
- assertNotNull(streamRDF.getCIMDatasetGraph().getModelHeader());
- var modelHeader = streamRDF.getCIMDatasetGraph().getModelHeader();
+ assertNotNull(streamRDF.getCimDatasetGraph().getModelHeader());
+ var modelHeader = streamRDF.getCimDatasetGraph().getModelHeader();
assertEquals("urn:uuid:08984e27-811f-4042-9125-1531ae0de0f6", modelHeader.getModel().toString());
assertEquals(1, modelHeader.getProfiles().size());
assertTrue(modelHeader.getProfiles().stream().map(Node::getLiteralLexicalForm).toList()
.contains("http://iec.ch/TC57/ns/CIM/CoreEquipment-EU/3.0"));
- var graph = streamRDF.getCIMDatasetGraph().getDefaultGraph();
+ var graph = streamRDF.getCimDatasetGraph().getDefaultGraph();
assertTrue(graph.contains(
NodeFactory.createURI("urn:uuid:f67fc354-9e39-4191-a456-67537399bc48"),
NodeFactory.createURI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
@@ -206,18 +206,18 @@ public void parseDifferenceModelHeaderAndContentInDifferentGraphs() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertTrue(streamRDF.getCIMDatasetGraph().isDifferenceModel());
- assertNotNull(streamRDF.getCIMDatasetGraph().getModelHeader());
- var modelHeader = streamRDF.getCIMDatasetGraph().getModelHeader();
+ assertTrue(streamRDF.getCimDatasetGraph().isDifferenceModel());
+ assertNotNull(streamRDF.getCimDatasetGraph().getModelHeader());
+ var modelHeader = streamRDF.getCimDatasetGraph().getModelHeader();
assertEquals("urn:uuid:08984e27-811f-4042-9125-1531ae0de0f6", modelHeader.getModel().toString());
- var preconditions = streamRDF.getCIMDatasetGraph().getPreconditions();
+ var preconditions = streamRDF.getCimDatasetGraph().getPreconditions();
assertNotNull(preconditions);
assertEquals(1, preconditions.size());
assertTrue(preconditions.contains(
@@ -226,7 +226,7 @@ public void parseDifferenceModelHeaderAndContentInDifferentGraphs() {
NodeFactory.createLiteralString("Name of my element")
));
- var forwardDifferences = streamRDF.getCIMDatasetGraph().getForwardDifferences();
+ var forwardDifferences = streamRDF.getCimDatasetGraph().getForwardDifferences();
assertNotNull(forwardDifferences);
assertEquals(4, forwardDifferences.size());
assertTrue(forwardDifferences.contains(
@@ -250,7 +250,7 @@ public void parseDifferenceModelHeaderAndContentInDifferentGraphs() {
NodeFactory.createLiteralString("property of new element")
));
- var reverseDifferences = streamRDF.getCIMDatasetGraph().getReverseDifferences();
+ var reverseDifferences = streamRDF.getCimDatasetGraph().getReverseDifferences();
assertNotNull(reverseDifferences);
assertEquals(4, reverseDifferences.size());
assertTrue(reverseDifferences.contains(
@@ -299,29 +299,29 @@ public void writePrefixesToAllGraphInDifferenceModel() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- assertTrue(streamRDF.getCIMDatasetGraph().isDifferenceModel());
- assertNotNull(streamRDF.getCIMDatasetGraph().getModelHeader());
- var modelHeader = streamRDF.getCIMDatasetGraph().getModelHeader();
+ assertTrue(streamRDF.getCimDatasetGraph().isDifferenceModel());
+ assertNotNull(streamRDF.getCimDatasetGraph().getModelHeader());
+ var modelHeader = streamRDF.getCimDatasetGraph().getModelHeader();
assertEquals(4, modelHeader.getPrefixMapping().numPrefixes());
assertEquals("urn:uuid:08984e27-811f-4042-9125-1531ae0de0f6", modelHeader.getModel().toString());
- var preconditions = streamRDF.getCIMDatasetGraph().getPreconditions();
+ var preconditions = streamRDF.getCimDatasetGraph().getPreconditions();
assertNotNull(preconditions);
assertTrue(preconditions.getPrefixMapping().samePrefixMappingAs(modelHeader.getPrefixMapping()));
assertEquals(0, preconditions.size());
- var forwardDifferences = streamRDF.getCIMDatasetGraph().getForwardDifferences();
+ var forwardDifferences = streamRDF.getCimDatasetGraph().getForwardDifferences();
assertNotNull(forwardDifferences);
assertTrue(forwardDifferences.getPrefixMapping().samePrefixMappingAs(modelHeader.getPrefixMapping()));
assertEquals(0, forwardDifferences.size());
- var reverseDifferences = streamRDF.getCIMDatasetGraph().getReverseDifferences();
+ var reverseDifferences = streamRDF.getCimDatasetGraph().getReverseDifferences();
assertNotNull(reverseDifferences);
assertTrue(reverseDifferences.getPrefixMapping().samePrefixMappingAs(modelHeader.getPrefixMapping()));
assertEquals(0, reverseDifferences.size());
@@ -342,11 +342,11 @@ public void replaceUnderscoresInRdfAboutAndRdfId() {
""";
final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- var graph = streamRDF.getCIMDatasetGraph().getDefaultGraph();
+ var graph = streamRDF.getCimDatasetGraph().getDefaultGraph();
assertTrue(graph.contains(
NodeFactory.createURI("urn:uuid:f67fc354-9e39-4191-a456-67537399bc48"),
NodeFactory.createURI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
@@ -378,12 +378,12 @@ public void replaceUnderscoresInRdfAboutAndRdfIdFixingMissingDashesInUuids() {
""";
- final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var parser = new ReaderCIMXML_StAX_SR(ErrorHandlerFactory.errorHandlerNoWarnings);
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxml), streamRDF);
- var graph = streamRDF.getCIMDatasetGraph().getDefaultGraph();
+ var graph = streamRDF.getCimDatasetGraph().getDefaultGraph();
assertTrue(graph.contains(
NodeFactory.createURI("urn:uuid:f67fc354-9e39-4191-a456-67537399bc48"),
NodeFactory.createURI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
@@ -507,14 +507,14 @@ public void fullModelWithProfilesAndDatatypes() {
final var parser = new ReaderCIMXML_StAX_SR();
- final var streamFileHeaderHeaderProfile = new StreamCIMXMLToDatasetGraph();
+ final var streamFileHeaderHeaderProfile = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxmlFileHeaderProfile), streamFileHeaderHeaderProfile);
- var fileHeaderGraph = streamFileHeaderHeaderProfile.getCIMDatasetGraph().getDefaultGraph();
+ var fileHeaderGraph = streamFileHeaderHeaderProfile.getCimDatasetGraph().getDefaultGraph();
var fileHeaderProfile = CimProfile.wrap(fileHeaderGraph);
- final var streamRDFProfile = new StreamCIMXMLToDatasetGraph();
+ final var streamRDFProfile = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(rdfxmlCimProfile), streamRDFProfile);
- var profileGraph = streamRDFProfile.getCIMDatasetGraph().getDefaultGraph();
+ var profileGraph = streamRDFProfile.getCimDatasetGraph().getDefaultGraph();
var profile = CimProfile.wrap(profileGraph);
@@ -523,10 +523,10 @@ public void fullModelWithProfilesAndDatatypes() {
registry.register(profile);
- final var streamInstanceData = new StreamCIMXMLToDatasetGraph();
+ final var streamInstanceData = new StreamCimXmlToDatasetGraph();
parser.read(new StringReader(cimxmlInstanceData), registry, streamInstanceData);
- var instanceGraph = streamInstanceData.getCIMDatasetGraph().getBody();
+ var instanceGraph = streamInstanceData.getCimDatasetGraph().getBody();
assertNotNull(instanceGraph);
assertEquals(6, instanceGraph.size());
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/parser/TestParserRDFXMLConformity.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestParserRDFXMLConformity.java
similarity index 93%
rename from cimxml/src/test/java/de/soptim/opencgmes/parser/TestParserRDFXMLConformity.java
rename to cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestParserRDFXMLConformity.java
index 80711d1e..7c24ad36 100644
--- a/cimxml/src/test/java/de/soptim/opencgmes/parser/TestParserRDFXMLConformity.java
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestParserRDFXMLConformity.java
@@ -16,16 +16,14 @@
* limitations under the License.
*/
-package de.soptim.opencgmes.parser;
+package de.soptim.opencgmes.cimxml.parser;
-import de.soptim.opencgmes.cimxml.parser.ReaderCIMXML_StAX_SR;
-import de.soptim.opencgmes.cimxml.parser.system.StreamCIMXMLToDatasetGraph;
+import de.soptim.opencgmes.cimxml.parser.system.StreamCimXmlToDatasetGraph;
import org.apache.jena.graph.Graph;
-import org.apache.jena.mem2.GraphMem2Roaring;
-import org.apache.jena.mem2.IndexingStrategy;
import org.apache.jena.riot.RDFParser;
import org.apache.jena.riot.system.PrefixMap;
import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.sparql.graph.GraphFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -149,14 +147,14 @@ public void testW3cRdfXmlExample() throws Exception {
public void parseAndCompare(Path rdfxml, Path nTriples) throws Exception {
Objects.requireNonNull(rdfxml);
- final var expectedGraph = new GraphMem2Roaring(IndexingStrategy.LAZY);
+ final var expectedGraph = GraphFactory.createGraphMem();
final var parser = new ReaderCIMXML_StAX_SR();
- final var streamRDF = new StreamCIMXMLToDatasetGraph();
+ final var streamRDF = new StreamCimXmlToDatasetGraph();
try(var fileReader = new FileReader(rdfxml.toFile())) {
parser.read(fileReader, streamRDF);
}
- var graph = streamRDF.getCIMDatasetGraph().getDefaultGraph();
+ var graph = streamRDF.getCimDatasetGraph().getDefaultGraph();
RDFParser.create()
.source(rdfxml)
@@ -165,10 +163,10 @@ public void parseAndCompare(Path rdfxml, Path nTriples) throws Exception {
.parse(expectedGraph);
assertGraphEquals(expectedGraph, graph);
- assertPrefixMappingEquals(expectedGraph.getPrefixMapping(), streamRDF.getCIMDatasetGraph().prefixes());
+ assertPrefixMappingEquals(expectedGraph.getPrefixMapping(), streamRDF.getCimDatasetGraph().prefixes());
if (nTriples != null) {
- final var nTriplesGraph = new GraphMem2Roaring(IndexingStrategy.LAZY);
+ final var nTriplesGraph = GraphFactory.createGraphMem();
RDFParser.create()
.source(nTriples)
.lang(org.apache.jena.riot.Lang.NTRIPLES)
diff --git a/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestReliCapGrid.java b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestReliCapGrid.java
new file mode 100644
index 00000000..a368253a
--- /dev/null
+++ b/cimxml/src/test/java/de/soptim/opencgmes/cimxml/parser/TestReliCapGrid.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 de.soptim.opencgmes.cimxml.parser;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import de.soptim.opencgmes.cimxml.sparql.core.CimDatasetGraph;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests that verify CIMXML grid data files from the ENTSO-E ReliCapGrid can be parsed
+ * using CIM RDFS profiles from the application-profiles-library.
+ *
+ *
The ReliCapGrid is included as a Git submodule at
+ * {@code cimxml/testing/relicapgrid} and is licensed under CC-BY-SA-4.0.
+ * See
+ * https://github.com/entsoe/relicapgrid
+ *
+ *
Each test case corresponds to one directory containing {@code .xml} CIMXML files.
+ * A {@link CimXmlParser} instance is created per directory with the appropriate RDFS profiles
+ * loaded, and all {@code .xml} files in that directory are parsed as CIM models.
+ */
+@RunWith(Parameterized.class)
+public class TestReliCapGrid {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TestReliCapGrid.class);
+
+ private static final Path RELICAPGRID_ROOT =
+ Paths.get("testing", "relicapgrid");
+
+ private static final Path GRID_ROOT =
+ RELICAPGRID_ROOT.resolve("Instance/Grid");
+
+ private static final Path PROFILES_ROOT =
+ Paths.get("testing", "application-profiles-library");
+
+ private static final Path CGMES_30_RDFS =
+ PROFILES_ROOT.resolve("CGMES/CurrentRelease/RDFS");
+
+ private static final Path CGMES_24_RDFS =
+ PROFILES_ROOT.resolve("CGMES/PastReleases/v2-4/Original/RDFS");
+
+ private static final Path CGMES_24_GRID_DIR =
+ GRID_ROOT.resolve("CommonAndBoundaryData/CGMES_2-4");
+
+ /**
+ * CGMES 2.4 "Augmented" equipment profiles to skip when loading RDFS, matching the exclusions
+ * in {@code TestCrossVersionProfileCompatibility#cgmes24And30CoexistInSameRegistry}.
+ */
+ private static final List CGMES_24_SKIPPED_RDFS = List.of(
+ "EquipmentProfileCoreOperationRDFSAugmented-v2_4_15-4Sep2020.rdf",
+ "EquipmentProfileCoreRDFSAugmented-v2_4_15-4Sep2020.rdf",
+ "EquipmentProfileCoreShortCircuitRDFSAugmented-v2_4_15-4Sep2020.rdf"
+ );
+
+ private final String testName;
+ private final Path gridDirectory;
+ private final boolean isCgmes24;
+
+ public TestReliCapGrid(String testName, Path gridDirectory, boolean isCgmes24) {
+ this.testName = testName;
+ this.gridDirectory = gridDirectory;
+ this.isCgmes24 = isCgmes24;
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection