Skip to content

Commit 24865de

Browse files
committed
Update samm-cli commands to allow URNs and URLs in addition to files
1 parent 7cfef3c commit 24865de

30 files changed

+906
-232
lines changed

core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/resolver/services/TurtleLoader.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.nio.charset.StandardCharsets;
2222
import java.util.Objects;
2323
import java.util.stream.Collectors;
24-
2524
import javax.annotation.Nullable;
2625

2726
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ParserException;

core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/stats/Usage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818

1919
import org.eclipse.esmf.aspectmodel.AspectModelFile;
2020
import org.eclipse.esmf.aspectmodel.RdfUtil;
21-
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
2221
import org.eclipse.esmf.aspectmodel.resolver.ModelSource;
22+
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
2323
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
2424

2525
import com.google.common.collect.Streams;

core/esmf-aspect-meta-model-java/src/main/java/org/eclipse/esmf/aspectmodel/versionupdate/MetaModelVersionMigrator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020

2121
import org.eclipse.esmf.aspectmodel.AspectModelFile;
2222
import org.eclipse.esmf.aspectmodel.VersionNumber;
23-
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
2423
import org.eclipse.esmf.aspectmodel.resolver.exceptions.InvalidVersionException;
24+
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
2525
import org.eclipse.esmf.aspectmodel.resolver.modelfile.RawAspectModelFile;
2626
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
2727
import org.eclipse.esmf.aspectmodel.urn.ElementType;

core/esmf-aspect-model-github-resolver/src/test/java/org/eclipse/esmf/aspectmodel/resolver/github/GitHubStrategyTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ private void inject( final GitHubModelSource gitHubModelSource, final File tempF
142142
}
143143

144144
@Test
145-
void testLoadCatenaXBatteryPass() {
145+
void testLoadCatenaxBatteryPass() {
146146
final AspectModelUrn batteryPassUrn = AspectModelUrn.fromUrn( "urn:samm:io.catenax.battery.battery_pass:6.0.0#BatteryPass" );
147147
final GithubRepository sldt = new GithubRepository( "eclipse-tractusx", "sldt-semantic-models",
148148
new GithubRepository.Branch( "main" ) );

tools/samm-cli/src/main/java/org/eclipse/esmf/AbstractCommand.java

Lines changed: 27 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.util.Optional;
2929
import java.util.Set;
3030
import java.util.function.Consumer;
31-
import java.util.stream.Collectors;
3231

3332
import org.eclipse.esmf.aspectmodel.edit.AspectChangeManager;
3433
import org.eclipse.esmf.aspectmodel.edit.AspectChangeManagerConfig;
@@ -37,113 +36,52 @@
3736
import org.eclipse.esmf.aspectmodel.edit.ChangeReportFormatter;
3837
import org.eclipse.esmf.aspectmodel.generator.LanguageCollector;
3938
import org.eclipse.esmf.aspectmodel.generator.diagram.AspectModelDiagramGenerator;
40-
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
41-
import org.eclipse.esmf.aspectmodel.resolver.ExternalResolverStrategy;
42-
import org.eclipse.esmf.aspectmodel.resolver.FileSystemStrategy;
43-
import org.eclipse.esmf.aspectmodel.resolver.ModelResolutionException;
44-
import org.eclipse.esmf.aspectmodel.resolver.ResolutionStrategy;
45-
import org.eclipse.esmf.aspectmodel.resolver.fs.StructuredModelsRoot;
46-
import org.eclipse.esmf.aspectmodel.resolver.github.GitHubStrategy;
4739
import org.eclipse.esmf.aspectmodel.serializer.AspectSerializer;
48-
import org.eclipse.esmf.aspectmodel.shacl.violation.Violation;
49-
import org.eclipse.esmf.aspectmodel.validation.services.AspectModelValidator;
50-
import org.eclipse.esmf.aspectmodel.validation.services.DetailedViolationFormatter;
51-
import org.eclipse.esmf.aspectmodel.validation.services.ViolationFormatter;
5240
import org.eclipse.esmf.exception.CommandException;
5341
import org.eclipse.esmf.metamodel.Aspect;
5442
import org.eclipse.esmf.metamodel.AspectModel;
5543

56-
import io.vavr.control.Either;
57-
import org.apache.commons.io.FilenameUtils;
58-
5944
@SuppressWarnings( "UseOfSystemOutOrSystemErr" )
6045
public abstract class AbstractCommand implements Runnable {
61-
protected Path modelsRootForFile( final File file ) {
62-
return file.toPath().getParent().getParent().getParent();
63-
}
46+
private boolean details;
47+
private ResolverConfigurationMixin resolverConfig;
6448

65-
protected AspectModel loadAspectModelOrFail( final String input, final ResolverConfigurationMixin resolverConfig ) {
66-
return loadAspectModelOrFail( input, resolverConfig, false );
49+
protected void setDetails( final boolean details ) {
50+
this.details = details;
6751
}
6852

69-
protected File getInputFile( final String modelFileName ) {
70-
final File inputFile = new File( modelFileName );
71-
return inputFile.isAbsolute()
72-
? inputFile
73-
: Path.of( System.getProperty( "user.dir" ) ).resolve( inputFile.toPath() ).toFile().getAbsoluteFile();
53+
protected void setResolverConfig( final ResolverConfigurationMixin resolverConfig ) {
54+
this.resolverConfig = resolverConfig;
7455
}
7556

76-
protected AspectModelLoader getAspectModelLoader( final Optional<File> modelFile, final ResolverConfigurationMixin resolverConfig ) {
77-
final List<ResolutionStrategy> strategies = new ArrayList<>();
78-
if ( modelFile.isPresent() ) {
79-
strategies.add( new FileSystemStrategy( modelsRootForFile( modelFile.get().getAbsoluteFile() ) ) );
80-
} else {
81-
strategies.add( AspectModelLoader.DEFAULT_STRATEGY.get() );
82-
}
83-
for ( final String modelsRoot : resolverConfig.modelsRoots ) {
84-
strategies.add( new FileSystemStrategy( new StructuredModelsRoot( Path.of( modelsRoot ) ) ) );
85-
}
86-
if ( !resolverConfig.commandLine.isBlank() ) {
87-
strategies.add( new ExternalResolverStrategy( resolverConfig.commandLine ) );
88-
}
89-
if ( resolverConfig.gitHubResolutionOptions != null && resolverConfig.gitHubResolutionOptions.enableGitHubResolution ) {
90-
strategies.add( new GitHubStrategy(
91-
resolverConfig.gitHubResolutionOptions.gitHubName,
92-
resolverConfig.gitHubResolutionOptions.gitHubBranch,
93-
resolverConfig.gitHubResolutionOptions.gitHubDirectory ) );
94-
}
95-
return new AspectModelLoader( strategies );
57+
protected boolean inputIsFile( final String input ) {
58+
return new File( input ).exists();
9659
}
9760

98-
protected AspectModel loadAspectModelOrFail( final File modelFile, final ResolverConfigurationMixin resolverConfig,
99-
final boolean details ) {
100-
final File absoluteFile = modelFile.getAbsoluteFile();
101-
102-
final Either<List<Violation>, AspectModel> validModelOrViolations = new AspectModelValidator().loadModel( () ->
103-
getAspectModelLoader( Optional.of( modelFile ), resolverConfig ).load( absoluteFile ) );
104-
if ( validModelOrViolations.isLeft() ) {
105-
final List<Violation> violations = validModelOrViolations.getLeft();
106-
if ( details ) {
107-
System.out.println( new DetailedViolationFormatter().apply( violations ) );
108-
} else {
109-
System.out.println( new ViolationFormatter().apply( violations ) );
110-
}
111-
System.exit( 1 );
112-
return null;
113-
}
114-
115-
return validModelOrViolations.get();
61+
protected File absoluteFile( final File inputFile ) {
62+
return inputFile.isAbsolute()
63+
? inputFile
64+
: Path.of( System.getProperty( "user.dir" ) ).resolve( inputFile.toPath() ).toFile().getAbsoluteFile();
11665
}
11766

118-
protected AspectModel loadAspectModelOrFail( final String modelFileName, final ResolverConfigurationMixin resolverConfig,
119-
final boolean details ) {
120-
return loadAspectModelOrFail( getInputFile( modelFileName ), resolverConfig, details );
67+
protected InputHandler getInputHandler( final File input ) {
68+
return new FileInputHandler( input.getAbsolutePath(), resolverConfig, details );
12169
}
12270

123-
protected Aspect loadAspectOrFail( final String modelFileName, final ResolverConfigurationMixin resolverConfig ) {
124-
final File inputFile = new File( modelFileName );
125-
final AspectModel aspectModel = loadAspectModelOrFail( modelFileName, resolverConfig );
126-
final List<Aspect> aspects = aspectModel.aspects();
127-
if ( aspects.isEmpty() ) {
128-
throw new CommandException( new ModelResolutionException( "No Aspects were found in the model" ) );
129-
}
130-
if ( aspects.size() == 1 ) {
131-
return aspectModel.aspect();
71+
protected InputHandler getInputHandler( final String input ) {
72+
if ( FileInputHandler.appliesToInput( input ) ) {
73+
return new FileInputHandler( input, resolverConfig, details );
74+
} else if ( AspectModelUrnInputHandler.appliesToInput( input ) ) {
75+
return new AspectModelUrnInputHandler( input, resolverConfig, details );
76+
} else if ( GitHubUrlInputHandler.appliesToInput( input ) ) {
77+
return new GitHubUrlInputHandler( input, resolverConfig, details );
13278
}
133-
final String expectedAspectName = FilenameUtils.removeExtension( inputFile.getName() );
134-
return aspectModel.aspects().stream()
135-
.filter( aspect -> aspect.getName().equals( expectedAspectName ) )
136-
.findFirst()
137-
.orElseThrow( () -> new ModelResolutionException(
138-
"Found multiple Aspects in the file " + inputFile.getAbsolutePath() + ", but none is called '"
139-
+ expectedAspectName + "': " + aspects.stream().map( Aspect::getName )
140-
.collect( Collectors.joining( ", " ) ) ) );
79+
throw new CommandException( "Can not find file: " + input );
14180
}
14281

143-
protected void generateDiagram( final String inputFileName, final AspectModelDiagramGenerator.Format targetFormat,
144-
final String outputFileName,
145-
final String languageTag, final ResolverConfigurationMixin resolverConfig ) throws IOException {
146-
final Aspect aspect = loadAspectOrFail( inputFileName, resolverConfig );
82+
protected void generateDiagram( final String input, final AspectModelDiagramGenerator.Format targetFormat,
83+
final String outputFileName, final String languageTag ) throws IOException {
84+
final Aspect aspect = getInputHandler( input ).loadAspect();
14785
final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( aspect );
14886
final Set<AspectModelDiagramGenerator.Format> targetFormats = new HashSet<>();
14987
targetFormats.add( targetFormat );
@@ -173,8 +111,8 @@ protected OutputStream getStreamForFile( final String outputFileName ) {
173111
ensureDirectoryExists( outputFileName );
174112
try {
175113
return new FileOutputStream( outputFileName );
176-
} catch ( final FileNotFoundException e ) {
177-
throw new CommandException( e );
114+
} catch ( final FileNotFoundException exception ) {
115+
throw new CommandException( exception );
178116
}
179117
} else {
180118
return new ProtectedOutputStream( System.out );
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH
3+
*
4+
* See the AUTHORS file(s) distributed with this work for additional
5+
* information regarding authorship.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
10+
*
11+
* SPDX-License-Identifier: MPL-2.0
12+
*/
13+
14+
package org.eclipse.esmf;
15+
16+
import java.nio.file.Path;
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import java.util.function.Function;
20+
import java.util.stream.Collectors;
21+
22+
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
23+
import org.eclipse.esmf.aspectmodel.resolver.ExternalResolverStrategy;
24+
import org.eclipse.esmf.aspectmodel.resolver.FileSystemStrategy;
25+
import org.eclipse.esmf.aspectmodel.resolver.GithubRepository;
26+
import org.eclipse.esmf.aspectmodel.resolver.ResolutionStrategy;
27+
import org.eclipse.esmf.aspectmodel.resolver.exceptions.ModelResolutionException;
28+
import org.eclipse.esmf.aspectmodel.resolver.fs.StructuredModelsRoot;
29+
import org.eclipse.esmf.aspectmodel.resolver.github.GitHubStrategy;
30+
import org.eclipse.esmf.aspectmodel.shacl.violation.Violation;
31+
import org.eclipse.esmf.aspectmodel.validation.services.AspectModelValidator;
32+
import org.eclipse.esmf.aspectmodel.validation.services.DetailedViolationFormatter;
33+
import org.eclipse.esmf.aspectmodel.validation.services.ViolationFormatter;
34+
import org.eclipse.esmf.exception.CommandException;
35+
import org.eclipse.esmf.metamodel.Aspect;
36+
import org.eclipse.esmf.metamodel.AspectModel;
37+
38+
import io.vavr.control.Either;
39+
40+
/**
41+
* Base functionality for all InputHandler implementations
42+
*/
43+
@SuppressWarnings( "UseOfSystemOutOrSystemErr" )
44+
public abstract class AbstractInputHandler implements InputHandler {
45+
protected final String input;
46+
protected final ResolverConfigurationMixin resolverConfig;
47+
protected final boolean details;
48+
49+
public AbstractInputHandler( final String input, final ResolverConfigurationMixin resolverConfig, final boolean details ) {
50+
this.input = input;
51+
this.resolverConfig = resolverConfig;
52+
this.details = details;
53+
}
54+
55+
/**
56+
* Returns the list of resolution strategies specific to this input type
57+
*
58+
* @return the resolution strategies
59+
*/
60+
protected abstract List<ResolutionStrategy> resolutionStrategies();
61+
62+
/**
63+
* Returns the expected Aspect name depending on the input, e.g. the file base name.
64+
*
65+
* @return the expected Aspect name
66+
*/
67+
protected abstract String expectedAspectName();
68+
69+
protected List<ResolutionStrategy> configuredStrategies() {
70+
final List<ResolutionStrategy> strategies = new ArrayList<>();
71+
if ( resolverConfig == null ) {
72+
return strategies;
73+
}
74+
final List<String> modelsRoots = resolverConfig.modelsRoots == null
75+
? List.of()
76+
: resolverConfig.modelsRoots;
77+
for ( final String modelsRoot : modelsRoots ) {
78+
strategies.add( new FileSystemStrategy( new StructuredModelsRoot( Path.of( modelsRoot ) ) ) );
79+
}
80+
if ( resolverConfig.commandLine != null && !resolverConfig.commandLine.isBlank() ) {
81+
strategies.add( new ExternalResolverStrategy( resolverConfig.commandLine ) );
82+
}
83+
if ( resolverConfig.gitHubResolutionOptions != null && resolverConfig.gitHubResolutionOptions.gitHubName != null ) {
84+
final String[] parts = resolverConfig.gitHubResolutionOptions.gitHubName.split( "/" );
85+
final String owner = parts[0];
86+
final String repositoryName = parts[1];
87+
final GithubRepository.Ref branchOrTag = resolverConfig.gitHubResolutionOptions.gitHubTag != null
88+
? new GithubRepository.Tag( resolverConfig.gitHubResolutionOptions.gitHubTag )
89+
: new GithubRepository.Branch( resolverConfig.gitHubResolutionOptions.gitHubBranch );
90+
final GithubRepository repository = new GithubRepository( owner, repositoryName, branchOrTag );
91+
strategies.add( new GitHubStrategy( repository, resolverConfig.gitHubResolutionOptions.gitHubDirectory ) );
92+
}
93+
return strategies;
94+
}
95+
96+
@Override
97+
public AspectModelLoader aspectModelLoader() {
98+
return new AspectModelLoader( resolutionStrategies() );
99+
}
100+
101+
protected AspectModel applyAspectModelLoader( final Function<AspectModelLoader, AspectModel> loader ) {
102+
final Either<List<Violation>, AspectModel> validModelOrViolations = new AspectModelValidator().loadModel( () ->
103+
loader.apply( aspectModelLoader() ) );
104+
if ( validModelOrViolations.isLeft() ) {
105+
final List<Violation> violations = validModelOrViolations.getLeft();
106+
if ( details ) {
107+
System.out.println( new DetailedViolationFormatter().apply( violations ) );
108+
} else {
109+
System.out.println( new ViolationFormatter().apply( violations ) );
110+
}
111+
System.exit( 1 );
112+
return null;
113+
}
114+
return validModelOrViolations.get();
115+
}
116+
117+
@Override
118+
public Aspect loadAspect() {
119+
final AspectModel aspectModel = loadAspectModel();
120+
121+
final String expectedAspectName = expectedAspectName();
122+
if ( aspectModel.aspects().isEmpty() ) {
123+
throw new CommandException( new ModelResolutionException( "No Aspects were found in the model" ) );
124+
}
125+
if ( aspectModel.aspects().size() == 1 ) {
126+
return aspectModel.aspect();
127+
}
128+
return aspectModel.aspects().stream()
129+
.filter( aspect -> aspect.getName().equals( expectedAspectName ) )
130+
.findFirst()
131+
.orElseThrow( () -> new ModelResolutionException(
132+
"Found multiple Aspects in the input " + input + ", but none is called '"
133+
+ expectedAspectName + "': " + aspectModel.aspects().stream().map( Aspect::getName )
134+
.collect( Collectors.joining( ", " ) ) ) );
135+
}
136+
}

0 commit comments

Comments
 (0)