diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/AnyNode.java b/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/AnyNode.java deleted file mode 100644 index 4c1fc06df..000000000 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/AnyNode.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.apache.jena.graph; - -import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; - -/** - * Because of some internal implementation details in Jena library, it was not possible - * to extend all node types via one wrapper node; as a workaround each node type must have its own extension. - */ -public class AnyNode extends Node_ANY { - private final SmartToken token; - - public AnyNode( final SmartToken token ) { - super(); - this.token = token; - } - - public SmartToken getToken() { - return token; - } -} diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/BlankNode.java b/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/BlankNode.java deleted file mode 100644 index a8eb6d0d4..000000000 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/BlankNode.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.apache.jena.graph; - -import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; - -/** - * Because of some internal implementation details in Jena library, it was not possible - * to extend all node types via one wrapper node; as a workaround each node type must have its own extension. - */ -public class BlankNode extends Node_Blank { - - private final SmartToken token; - - public BlankNode( final Node_Blank original, final SmartToken token ) { - super( original.getBlankNodeId() ); - this.token = token; - } - - public SmartToken getToken() { - return token; - } -} diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/LiteralNode.java b/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/LiteralNode.java deleted file mode 100644 index d05956e27..000000000 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/LiteralNode.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.apache.jena.graph; - -import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; - -/** - * Because of some internal implementation details in Jena library, it was not possible - * to extend all node types via one wrapper node; as a workaround each node type must have its own extension. - */ -public class LiteralNode extends Node_Literal { - - private final SmartToken token; - - public LiteralNode( final Node_Literal node, final SmartToken token ) { - super( node.getLiteral() ); - this.token = token; - } - - public SmartToken getToken() { - return token; - } -} diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/TokenNode.java b/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/TokenNode.java deleted file mode 100644 index 7dd06c9ad..000000000 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/TokenNode.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.apache.jena.graph; - -import java.io.ObjectStreamException; - -import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; - -import org.apache.jena.datatypes.RDFDatatype; -import org.apache.jena.graph.impl.LiteralLabel; -import org.apache.jena.riot.tokens.Token; -import org.apache.jena.shared.PrefixMapping; - -public class TokenNode extends Node { - private final SmartToken token; - private final Node wrappedNode; - - public TokenNode( final Node wrappedNode, final Token token ) { - super( wrappedNode.label ); - this.wrappedNode = wrappedNode; - this.token = new SmartToken( token ); - } - - @Override - public Object visitWith( final NodeVisitor visitor ) { - return wrappedNode.visitWith( visitor ); - } - - @Override - public boolean isConcrete() { - return wrappedNode.isConcrete(); - } - - @Override - public boolean equals( final Object o ) { - return wrappedNode.equals( o instanceof TokenNode ? ((TokenNode) o).wrappedNode : o ); - } - - public SmartToken getToken() { - return token; - } - - @Override - public boolean isLiteral() { - return wrappedNode.isLiteral(); - } - - @Override - public boolean isBlank() { - return wrappedNode.isBlank(); - } - - @Override - public boolean isURI() { - return wrappedNode.isURI(); - } - - @Override - public boolean isVariable() { - return wrappedNode.isVariable(); - } - - @Override - public boolean isNodeTriple() { - return wrappedNode.isNodeTriple(); - } - - @Override - public boolean isNodeGraph() { - return wrappedNode.isNodeGraph(); - } - - @Override - public boolean isExt() { - return wrappedNode.isExt(); - } - - @Override - public BlankNodeId getBlankNodeId() { - return wrappedNode.getBlankNodeId(); - } - - @Override - public String getBlankNodeLabel() { - return wrappedNode.getBlankNodeLabel(); - } - - @Override - public LiteralLabel getLiteral() { - return wrappedNode.getLiteral(); - } - - @Override - public Object getLiteralValue() { - return wrappedNode.getLiteralValue(); - } - - @Override - public String getLiteralLexicalForm() { - return wrappedNode.getLiteralLexicalForm(); - } - - @Override - public String getLiteralLanguage() { - return wrappedNode.getLiteralLanguage(); - } - - @Override - public String getLiteralDatatypeURI() { - return wrappedNode.getLiteralDatatypeURI(); - } - - @Override - public RDFDatatype getLiteralDatatype() { - return wrappedNode.getLiteralDatatype(); - } - - @Override - public boolean getLiteralIsXML() { - return wrappedNode.getLiteralIsXML(); - } - - @Override - public Object getIndexingValue() { - return wrappedNode.getIndexingValue(); - } - - @Override - public String getURI() { - return wrappedNode.getURI(); - } - - @Override - public String getNameSpace() { - return wrappedNode.getNameSpace(); - } - - @Override - public String getLocalName() { - return wrappedNode.getLocalName(); - } - - @Override - public String getName() { - return wrappedNode.getName(); - } - - @Override - public Triple getTriple() { - return wrappedNode.getTriple(); - } - - @Override - public Graph getGraph() { - return wrappedNode.getGraph(); - } - - @Override - public boolean hasURI( final String uri ) { - return wrappedNode.hasURI( uri ); - } - - @Override - public boolean sameValueAs( final Object o ) { - return wrappedNode.sameValueAs( o ); - } - - @Override - public int hashCode() { - return wrappedNode.hashCode(); - } - - @Override - public boolean matches( final Node other ) { - return wrappedNode.matches( other ); - } - - @Override - protected Object writeReplace() throws ObjectStreamException { - return wrappedNode.writeReplace(); - } - - @Override - public String toString() { - return wrappedNode.toString(); - } - - @Override - public String toString( final boolean quoting ) { - return wrappedNode.toString( quoting ); - } - - @Override - public String toString( final PrefixMapping pm ) { - return wrappedNode.toString( pm ); - } - - @Override - public String toString( final PrefixMapping pm, final boolean quoting ) { - return wrappedNode.toString( pm, quoting ); - } -} diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/UriNode.java b/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/UriNode.java deleted file mode 100644 index d19e6df1b..000000000 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/UriNode.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.apache.jena.graph; - -import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; - -/** - * Because of some internal implementation details in Jena library, it was not possible - * to extend all node types via one wrapper node; as a workaround each node type must have its own extension. - */ -public class UriNode extends Node_URI { - - private final SmartToken token; - - public UriNode( final Node_URI nodeUri, final SmartToken token ) { - super( nodeUri.getURI() ); - this.token = token; - } - - public SmartToken getToken() { - return token; - } -} diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/VariableNode.java b/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/VariableNode.java deleted file mode 100644 index 2862f33aa..000000000 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/apache/jena/graph/VariableNode.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for additional - * information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -package org.apache.jena.graph; - -import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; - -/** - * Because of some internal implementation details in Jena library, it was not possible - * to extend all node types via one wrapper node; as a workaround each node type must have its own extension. - */ -public class VariableNode extends Node_Variable { - - private final SmartToken token; - - public VariableNode( final String name, final SmartToken token ) { - super( name ); - this.token = token; - } - - public SmartToken getToken() { - return token; - } -} diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/ReaderRiotTurtle.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/ReaderRiotTurtle.java index c593ef5d8..c7734f1fe 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/ReaderRiotTurtle.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/ReaderRiotTurtle.java @@ -27,7 +27,6 @@ import org.apache.jena.sparql.util.Context; public class ReaderRiotTurtle implements ReaderRIOT { - public static ReaderRIOTFactory factory = ReaderRiotTurtle::new; private final Lang lang; diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/TokenRegistry.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/TokenRegistry.java new file mode 100644 index 000000000..9c16102ad --- /dev/null +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/TokenRegistry.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH + * + * See the AUTHORS file(s) distributed with this work for additional + * information regarding authorship. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +package org.eclipse.esmf.aspectmodel.resolver.parser; + +import java.util.Map; +import java.util.Optional; + +import org.eclipse.esmf.aspectmodel.resolver.services.TurtleLoader; + +import com.google.common.collect.MapMaker; +import org.apache.jena.graph.Node; + +/** + * This map keeps track of location information for nodes, i.e., when an RDF document is parsed using {@link TurtleParserProfile} + * (i.e., also when using {@link TurtleLoader}), the TokenRegistry will know about line/column/token information for each RDF node + * in the document. + */ +public class TokenRegistry { + /** + * The map that holds the node->token relations. It's important that this map shares the properties of both an + * IdentityHashMap (key identity must be determined using == instead of equals(), because Jena's Node_URI will be equal + * to another if the URI matches, but here we need to distinguish between their actual occurences) and a + * WeakHashMap (because we'd cause a memory leak if we keep token information around once a node is GC'ed). + * For this reason, Guava MapMaker with weakKeys() is used for the map implementation, is it defaults to object + * identity for comparison. + */ + private static final Map TOKENS = new MapMaker().weakKeys().makeMap(); + + public static void put( final Node node, final SmartToken token ) { + TOKENS.put( node, token ); + } + + public static Optional getToken( final Node node ) { + return Optional.ofNullable( TOKENS.get( node ) ); + } +} diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/TurtleParserProfile.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/TurtleParserProfile.java index cb6d4c22a..b4844674c 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/TurtleParserProfile.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/parser/TurtleParserProfile.java @@ -14,33 +14,20 @@ package org.eclipse.esmf.aspectmodel.resolver.parser; import org.apache.jena.datatypes.RDFDatatype; -import org.apache.jena.datatypes.xsd.XSDDatatype; -import org.apache.jena.graph.BlankNode; import org.apache.jena.graph.Graph; -import org.apache.jena.graph.LiteralNode; import org.apache.jena.graph.Node; -import org.apache.jena.graph.NodeFactory; -import org.apache.jena.graph.Node_Blank; -import org.apache.jena.graph.Node_Literal; -import org.apache.jena.graph.Node_URI; -import org.apache.jena.graph.TokenNode; import org.apache.jena.graph.Triple; -import org.apache.jena.graph.UriNode; -import org.apache.jena.query.ARQ; -import org.apache.jena.riot.RiotException; import org.apache.jena.riot.system.ErrorHandler; import org.apache.jena.riot.system.FactoryRDF; import org.apache.jena.riot.system.ParserProfile; import org.apache.jena.riot.system.PrefixMap; -import org.apache.jena.riot.system.RiotLib; import org.apache.jena.riot.tokens.Token; import org.apache.jena.riot.tokens.TokenType; import org.apache.jena.sparql.core.Quad; /** - * Customized parser profile that overwrites Jena's built-in Node generation to instead return {@link TokenNode}s that retain a link to - * their - * originating token. + * Customized parser profile that delegates to Jena's built-in Node generation but also registers the nodes in the {@link TokenRegistry}, + * where information about the line/column/token can be retrieved at a later time. */ public class TurtleParserProfile implements ParserProfile { private final ParserProfile parserProfile; @@ -49,65 +36,16 @@ public TurtleParserProfile( final ParserProfile parserProfile ) { this.parserProfile = parserProfile; } + @Override + public String getBaseURI() { + return parserProfile.getBaseURI(); + } + @Override public Node create( final Node currentGraph, final Token token ) { - // Dispatches to the underlying ParserFactory operation - final long line = token.getLine(); - final long col = token.getColumn(); - final String str = token.getImage(); - final SmartToken smartToken = new SmartToken( token ); - switch ( token.getType() ) { - case BNODE: - return new BlankNode( (Node_Blank) createBlankNode( currentGraph, str, line, col ), smartToken ); - case IRI: - return new UriNode( (Node_URI) createURI( str, line, col ), smartToken ); - case PREFIXED_NAME: { - final String suffix = token.getImage2(); - final String expansion = expandPrefixedName( str, suffix, token ); - return new UriNode( (Node_URI) createURI( expansion, line, col ), smartToken ); - } - case DECIMAL: - return new LiteralNode( (Node_Literal) createTypedLiteral( str, XSDDatatype.XSDdecimal, line, col ), smartToken ); - case DOUBLE: - return new LiteralNode( (Node_Literal) createTypedLiteral( str, XSDDatatype.XSDdouble, line, col ), smartToken ); - case INTEGER: - return new LiteralNode( (Node_Literal) createTypedLiteral( str, XSDDatatype.XSDinteger, line, col ), smartToken ); - case LITERAL_DT: { - final Token tokenDt = token.getSubToken2(); - String uriStr; - switch ( tokenDt.getType() ) { - case IRI -> uriStr = tokenDt.getImage(); - case PREFIXED_NAME -> { - final String prefix = tokenDt.getImage(); - final String suffix = tokenDt.getImage2(); - uriStr = expandPrefixedName( prefix, suffix, tokenDt ); - break; - } - default -> throw new RiotException( "Expected IRI for datatype: " + token ); - } - uriStr = resolveIRI( uriStr, tokenDt.getLine(), tokenDt.getColumn() ); - final RDFDatatype dt = NodeFactory.getType( uriStr ); - return new LiteralNode( (Node_Literal) createTypedLiteral( str, dt, line, col ), smartToken ); - } - - case LITERAL_LANG: - return new LiteralNode( (Node_Literal) createLangLiteral( str, token.getImage2(), line, col ), smartToken ); - - case STRING: - return new LiteralNode( (Node_Literal) createStringLiteral( str, line, col ), smartToken ); - - case BOOLEAN: - return new LiteralNode( (Node_Literal) createTypedLiteral( str, XSDDatatype.XSDboolean, line, col ), smartToken ); - - default: { - final Node x = createNodeFromToken( currentGraph, token, line, col ); - if ( x != null ) { - return new TokenNode( x, token ); - } - getErrorHandler().fatal( "Not a valid token for an RDF term: " + token, line, col ); - return null; - } - } + final Node node = parserProfile.create( currentGraph, token ); + TokenRegistry.put( node, new SmartToken( token ) ); + return node; } @Override @@ -180,7 +118,9 @@ public Node createBlankNode( final Node scope, final String label, final long li public Node createBlankNode( final Node scope, final long line, final long col ) { final Token token = new Token( line, col ); token.setType( TokenType.LBRACKET ); - return new BlankNode( (Node_Blank) parserProfile.createBlankNode( scope, line, col ), new SmartToken( token ) ); + final Node node = parserProfile.createBlankNode( scope, line, col ); + TokenRegistry.put( node, new SmartToken( token ) ); + return node; } @Override @@ -202,19 +142,4 @@ public Node createGraphNode( final Graph graph, final long line, final long col public Node createNodeFromToken( final Node scope, final Token token, final long line, final long col ) { return parserProfile.createNodeFromToken( scope, token, line, col ); } - - /* - * (non-javadoc) - * Implementation adapted from {@link ParserProfileStd#expandPrefixedName(String, String, Token)} - */ - private String expandPrefixedName( final String prefix, final String localPart, final Token token ) { - final String expansion = getPrefixMap().expand( prefix, localPart ); - if ( expansion == null ) { - if ( ARQ.isTrue( ARQ.fixupUndefinedPrefixes ) ) { - return RiotLib.fixupPrefixIRI( prefix, localPart ); - } - parserProfile.getErrorHandler().fatal( "Undefined prefix: " + prefix, token.getLine(), token.getColumn() ); - } - return expansion; - } } diff --git a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/services/TurtleLoader.java b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/services/TurtleLoader.java index 1367aa631..572691f39 100644 --- a/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/services/TurtleLoader.java +++ b/core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/services/TurtleLoader.java @@ -29,11 +29,11 @@ import io.vavr.control.Try; import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.riot.Lang; -import org.apache.jena.riot.RDFLanguages; +import org.apache.jena.riot.RDFParser; import org.apache.jena.riot.RDFParserRegistry; import org.apache.jena.riot.RiotException; +import org.apache.jena.riot.system.FactoryRDFStd; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,10 +86,15 @@ public static Try loadTurtle( final URL url ) { public static Try loadTurtle( @Nullable final String modelContent ) { Objects.requireNonNull( modelContent, "Model content must not be null." ); SammXsdType.setupTypeMapping(); - final Model streamModel = ModelFactory.createDefaultModel(); registerTurtle(); try ( final InputStream turtleInputStream = new ByteArrayInputStream( modelContent.getBytes( StandardCharsets.UTF_8 ) ) ) { - streamModel.read( turtleInputStream, "", RDFLanguages.TURTLE.getName() ); + final Model streamModel = RDFParser.create() + // Make sure to NOT use FactoryRDFCaching because it will return the same objects for nodes appearing + // in different places of a source document, which would break functionality of the TokenRegistry. + .factory( new FactoryRDFStd() ) + .source( turtleInputStream ) + .lang( Lang.TURTLE ) + .toModel(); return Try.success( streamModel ); } catch ( final IllegalArgumentException exception ) { LOG.error( "Invalid value encountered in Aspect Model.", exception ); diff --git a/core/esmf-aspect-model-serializer/src/main/java/org/eclipse/esmf/aspectmodel/serializer/PrettyPrinter.java b/core/esmf-aspect-model-serializer/src/main/java/org/eclipse/esmf/aspectmodel/serializer/PrettyPrinter.java index dc037c568..110ca7e1e 100644 --- a/core/esmf-aspect-model-serializer/src/main/java/org/eclipse/esmf/aspectmodel/serializer/PrettyPrinter.java +++ b/core/esmf-aspect-model-serializer/src/main/java/org/eclipse/esmf/aspectmodel/serializer/PrettyPrinter.java @@ -30,13 +30,14 @@ import org.eclipse.esmf.aspectmodel.AspectModelFile; import org.eclipse.esmf.aspectmodel.RdfUtil; +import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; +import org.eclipse.esmf.aspectmodel.resolver.parser.TokenRegistry; import org.eclipse.esmf.metamodel.ModelElement; import org.eclipse.esmf.metamodel.vocabulary.SammNs; import com.google.common.collect.ImmutableList; import org.apache.commons.text.StringEscapeUtils; import org.apache.jena.datatypes.RDFDatatype; -import org.apache.jena.graph.BlankNodeId; import org.apache.jena.graph.Graph; import org.apache.jena.graph.NodeVisitor; import org.apache.jena.graph.Node_ANY; @@ -47,8 +48,6 @@ import org.apache.jena.graph.Node_URI; import org.apache.jena.graph.Node_Variable; import org.apache.jena.graph.Triple; -import org.apache.jena.graph.UriNode; -import org.apache.jena.graph.impl.LiteralLabel; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; @@ -108,17 +107,16 @@ private Comparator createElementDefinitionOrder() { final StmtIterator iterator = resource.getModel().listStatements( resource, RDF.type, (RDFNode) null ); if ( iterator.hasNext() ) { final Statement statement = iterator.next(); - if ( statement.getSubject().asNode() instanceof final UriNode uriNode ) { - return uriNode.getToken().line(); - } else { - // This happens when the model was not loaded using the esmf-sdk customized RDF parser, e.g. - // for programmatically created models. - // Fall back to inherent RDF order (i.e. no order). At least try to keep samm:Aspect on the top. - if ( statement.getObject().isURIResource() && statement.getResource().equals( SammNs.SAMM.Aspect() ) ) { - return 0; - } - return 1; - } + return TokenRegistry.getToken( statement.getSubject().asNode() ) + .map( SmartToken::line ) + .orElseGet( () -> { + // This happens when the model was not loaded using the esmf-sdk customized RDF parser, e.g. + // for programmatically created models. + // Fall back to inherent RDF order (i.e. no order). At least try to keep samm:Aspect on the top. + return statement.getObject().isURIResource() && statement.getResource().equals( SammNs.SAMM.Aspect() ) + ? 0 + : 1; + } ); } return Integer.MAX_VALUE; } ); @@ -464,13 +462,13 @@ public Object visitAny( final Node_ANY it ) { } @Override - public Object visitBlank( final Node_Blank it, final BlankNodeId id ) { + public Object visitBlank( final Node_Blank it, final String id ) { return it.toString(); } @Override - public Object visitLiteral( final Node_Literal it, final LiteralLabel lit ) { - String lf = it.getLiteralLexicalForm(); + public Object visitLiteral( final Node_Literal it, final String lex, final String lang, final RDFDatatype dtype ) { + String lf = lex; final String singleQuote = "'"; if ( lf.contains( singleQuote ) ) { lf = lf.replace( singleQuote, "\\'" ); diff --git a/core/esmf-aspect-model-validator/pom.xml b/core/esmf-aspect-model-validator/pom.xml index 3914209ab..740681c6c 100644 --- a/core/esmf-aspect-model-validator/pom.xml +++ b/core/esmf-aspect-model-validator/pom.xml @@ -43,6 +43,7 @@ org.graalvm.js js + pom org.graalvm.js diff --git a/core/esmf-aspect-model-validator/src/main/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatter.java b/core/esmf-aspect-model-validator/src/main/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatter.java index 95cd23ab0..60e684291 100644 --- a/core/esmf-aspect-model-validator/src/main/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatter.java +++ b/core/esmf-aspect-model-validator/src/main/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatter.java @@ -24,14 +24,9 @@ import org.eclipse.esmf.aspectmodel.resolver.parser.PlainTextFormatter; import org.eclipse.esmf.aspectmodel.resolver.parser.RdfTextFormatter; import org.eclipse.esmf.aspectmodel.resolver.parser.SmartToken; +import org.eclipse.esmf.aspectmodel.resolver.parser.TokenRegistry; import com.google.common.collect.Ordering; -import org.apache.jena.graph.AnyNode; -import org.apache.jena.graph.BlankNode; -import org.apache.jena.graph.LiteralNode; -import org.apache.jena.graph.Node; -import org.apache.jena.graph.UriNode; -import org.apache.jena.graph.VariableNode; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.RDFList; import org.apache.jena.rdf.model.RDFNode; @@ -355,19 +350,6 @@ private void formatText( final String reconstructedText ) { } private static SmartToken extractToken( final RDFNode rdfNode ) { - final Node node = rdfNode.asNode(); - if ( node instanceof final AnyNode an ) { - return an.getToken(); - } else if ( node instanceof final BlankNode bn ) { - return bn.getToken(); - } else if ( node instanceof final LiteralNode ln ) { - return ln.getToken(); - } else if ( node instanceof final UriNode un ) { - return un.getToken(); - } else if ( node instanceof final VariableNode vn ) { - return vn.getToken(); - } else { - return null; - } + return TokenRegistry.getToken( rdfNode.asNode() ).orElse( null ); } } diff --git a/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatterTest.java b/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatterTest.java index 4bc3dd9db..fa2159f7d 100644 --- a/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatterTest.java +++ b/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/shacl/RustLikeFormatterTest.java @@ -31,7 +31,7 @@ public class RustLikeFormatterTest { void testMiddleStatement() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :firstProperty 1 ; :secondProperty 2 . @@ -47,7 +47,7 @@ void testMiddleStatement() { void testLastStatement() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :firstProperty 1 ; :secondProperty 2 . @@ -63,7 +63,7 @@ void testLastStatement() { void testMultipleStatementsSameLine() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :firstProperty 1 ; :secondProperty 2 . """ ); @@ -78,7 +78,7 @@ void testMultipleStatementsSameLine() { void testMultiSubjectSameLine() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :property 1 . :Bar a :TestClass ; :property 2 . """ ); @@ -92,7 +92,7 @@ void testMultiSubjectSameLine() { void testAnonymousNodes() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :testProperty [ a :MyType ] . """ ); @@ -107,7 +107,7 @@ void testAnonymousNodes() { void testMultilineAnonymousNode() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :prop1 [ :prop2 23 ; @@ -124,7 +124,7 @@ void testMultilineAnonymousNode() { void testMultilineAnonymousNodeMiddlePart() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :prop1 [ :prop2 23 ; @@ -141,7 +141,7 @@ void testMultilineAnonymousNodeMiddlePart() { void testEmptyList() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :listProperty () . """ ); @@ -156,7 +156,7 @@ void testEmptyList() { void testList() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :listProperty ( :firstValue :secondValue ) . """ ); @@ -171,7 +171,7 @@ void testList() { void testMultilineListStarted() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :listProperty ( :firstValue :secondValue ) . @@ -187,7 +187,7 @@ void testMultilineListStarted() { void testMultilineListFinished() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :listProperty ( :firstValue :secondValue :thirdValue ) . @@ -203,7 +203,7 @@ void testMultilineListFinished() { void testListWithAnonymousNodes() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass ; :listProperty ( :firstValue [ :property :prop2; :name "givenName"; ] ) . """ ); @@ -218,7 +218,7 @@ void testListWithAnonymousNodes() { void testDenseFormatting() { final Model dataModel = createModel( """ @prefix : . - + :Foo a :TestClass;:property 1.:Bar a :TestClass;:property 2. """ ); diff --git a/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/validation/services/AspectModelValidatorTest.java b/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/validation/services/AspectModelValidatorTest.java index 389df44d1..a0ff8509d 100644 --- a/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/validation/services/AspectModelValidatorTest.java +++ b/core/esmf-aspect-model-validator/src/test/java/org/eclipse/esmf/aspectmodel/validation/services/AspectModelValidatorTest.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Supplier; import org.eclipse.esmf.aspectmodel.resolver.modelfile.MetaModelFile; import org.eclipse.esmf.aspectmodel.shacl.fix.Fix; @@ -33,7 +34,7 @@ import org.eclipse.esmf.test.TestProperty; import org.eclipse.esmf.test.TestResources; -import com.github.jsonldjava.shaded.com.google.common.base.Supplier; +import io.vavr.control.Either; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; @@ -194,6 +195,18 @@ void testCycleDetectionWithCycleBreakers() { assertThat( report ).isEmpty(); } + @Test + void testLoadWithValidation() { + final Supplier versionedModel = () -> TestResources.load( TestAspect.ASPECT_WITH_ENTITY ); + final Either, AspectModel> model = service.loadModel( versionedModel ); + if ( model.isLeft() ) { + final List violations = model.getLeft(); + final String report = new DetailedViolationFormatter().apply( violations ); + System.out.println( report ); + } + assertThat( model.isRight() ).isTrue(); + } + private List cycles( final String... cycles ) { final List errors = new ArrayList<>(); Arrays.stream( cycles ).forEach( diff --git a/pom.xml b/pom.xml index d0bcb70f5..372c29a3b 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ org.eclipse.esmf esmf-parent - 15 + 16 esmf-sdk-parent @@ -181,11 +181,6 @@ pom import - - org.graalvm.js - js - ${graalvm-version} - org.apache.logging.log4j log4j-to-slf4j diff --git a/tools/esmf-aspect-model-maven-plugin/pom.xml b/tools/esmf-aspect-model-maven-plugin/pom.xml index 3f6d0e7d9..69d880115 100644 --- a/tools/esmf-aspect-model-maven-plugin/pom.xml +++ b/tools/esmf-aspect-model-maven-plugin/pom.xml @@ -74,6 +74,12 @@ junit test + + org.junit.vintage + junit-vintage-engine + ${junit-jupiter-version} + test + diff --git a/tools/samm-cli/pom.xml b/tools/samm-cli/pom.xml index a8991da68..9470b5150 100644 --- a/tools/samm-cli/pom.xml +++ b/tools/samm-cli/pom.xml @@ -99,6 +99,7 @@ org.graalvm.js js + pom diff --git a/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/AasReflection.java b/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/AasReflection.java index 40fcb57f7..2d2a16f9e 100644 --- a/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/AasReflection.java +++ b/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/AasReflection.java @@ -18,6 +18,7 @@ import java.util.List; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.internal.util.ReflectionHelper; +import org.eclipse.digitaltwin.aas4j.v3.model.LangStringTextType; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; @@ -35,6 +36,8 @@ public void beforeAnalysis( final BeforeAnalysisAccess access ) { registerClassesInPackage( ReflectionHelper.DEFAULT_IMPLEMENTATION_PACKAGE_NAME ); registerClassesInPackage( ReflectionHelper.JSON_MIXINS_PACKAGE_NAME ); registerClassesInPackage( ReflectionHelper.XML_MIXINS_PACKAGE_NAME ); + + register( LangStringTextType[].class ); } @Override diff --git a/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java b/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java index 94a3fe192..bfac76952 100644 --- a/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java +++ b/tools/samm-cli/src/main/java/org/eclipse/esmf/nativefeatures/LogbackFeature.java @@ -34,5 +34,6 @@ public void beforeAnalysis( final BeforeAnalysisAccess access ) { initializeAtBuildTime( ch.qos.logback.core.util.StatusPrinter2.class ); initializeAtBuildTime( org.slf4j.LoggerFactory.class ); + initializeAtBuildTime( org.slf4j.helpers.Reporter.class ); } }