Skip to content

Commit 1d48342

Browse files
authored
Merge pull request #608 from bci-oss/554-refactoring-tool-all-aspects-with-references
Refactoring tool all aspects with references
2 parents 33d7436 + a20a570 commit 1d48342

File tree

12 files changed

+727
-0
lines changed

12 files changed

+727
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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.aspectmodel.scanner;
15+
16+
import java.util.List;
17+
18+
import org.eclipse.esmf.aspectmodel.AspectModelFile;
19+
20+
public interface AspectModelScanner {
21+
22+
List<AspectModelFile> find( final String aspectModelFileName );
23+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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.aspectmodel.scanner;
15+
16+
import java.io.File;
17+
import java.nio.file.Path;
18+
import java.nio.file.Paths;
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
import java.util.Optional;
23+
import java.util.Set;
24+
25+
import org.eclipse.esmf.aspectmodel.AspectModelFile;
26+
import org.eclipse.esmf.aspectmodel.RdfUtil;
27+
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
28+
import org.eclipse.esmf.aspectmodel.resolver.AspectModelFileLoader;
29+
import org.eclipse.esmf.aspectmodel.resolver.fs.ModelsRoot;
30+
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
31+
import org.eclipse.esmf.metamodel.AspectModel;
32+
import org.eclipse.esmf.metamodel.ModelElement;
33+
34+
import org.slf4j.Logger;
35+
import org.slf4j.LoggerFactory;
36+
37+
public class FileSystemScanner implements AspectModelScanner {
38+
39+
private static final Logger LOG = LoggerFactory.getLogger( FileSystemScanner.class );
40+
41+
protected final ModelsRoot modelsRoot;
42+
43+
public FileSystemScanner( final ModelsRoot modelsRoot ) {
44+
this.modelsRoot = modelsRoot;
45+
}
46+
47+
@Override
48+
public List<AspectModelFile> find( final String aspectModelFileName ) {
49+
List<AspectModelFile> result = new ArrayList<>();
50+
51+
final AspectModel searchAspectModel = new AspectModelLoader().load( new File( aspectModelFileName ) );
52+
53+
final Path directory = modelsRoot.rootPath();
54+
final List<File> files = Arrays.stream( Optional.ofNullable( directory.toFile().listFiles() ).orElse( new File[] {} ) )
55+
.filter( file -> file.isFile() && file.getName().endsWith( ".ttl" ) && !file.getName()
56+
.equals( Paths.get( aspectModelFileName ).getFileName().toString() ) )
57+
.sorted()
58+
.toList();
59+
60+
if ( files.isEmpty() ) {
61+
LOG.info( "No .ttl files found in the directory '{}'", directory );
62+
return result;
63+
}
64+
65+
final List<AspectModelUrn> modelUrns = searchAspectModel.elements().stream().map( ModelElement::urn ).toList();
66+
67+
for ( final File file : files ) {
68+
result.addAll( processFile( file, modelUrns ) );
69+
}
70+
71+
return result;
72+
}
73+
74+
private List<AspectModelFile> processFile( final File inputFile, final List<AspectModelUrn> modelUrns ) {
75+
final List<AspectModelFile> aspectModelFiles = new ArrayList<>();
76+
final File absoluteFile = inputFile.isAbsolute()
77+
? inputFile
78+
: Path.of( System.getProperty( "user.dir" ) ).resolve( inputFile.toPath() ).toFile().getAbsoluteFile();
79+
80+
final AspectModelFile aspectModelFile = AspectModelFileLoader.load( absoluteFile );
81+
82+
final Set<AspectModelUrn> urnsAspectModelFile = RdfUtil.getAllUrnsInModel( aspectModelFile.sourceModel() );
83+
84+
for ( final AspectModelUrn aspectModelUrn : modelUrns ) {
85+
if ( urnsAspectModelFile.contains( aspectModelUrn ) ) {
86+
aspectModelFiles.add( aspectModelFile );
87+
}
88+
}
89+
90+
return aspectModelFiles;
91+
}
92+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.eclipse.esmf</groupId>
8+
<artifactId>esmf-sdk-parent</artifactId>
9+
<version>DEV-SNAPSHOT</version>
10+
<relativePath>../pom.xml</relativePath>
11+
</parent>
12+
13+
<artifactId>esmf-aspect-model-github-resolver</artifactId>
14+
<name>ESMF Aspect Model GitHub Resolver</name>
15+
16+
<properties>
17+
<maven.compiler.source>17</maven.compiler.source>
18+
<maven.compiler.target>17</maven.compiler.target>
19+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
20+
</properties>
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.eclipse.esmf</groupId>
24+
<artifactId>esmf-aspect-meta-model-java</artifactId>
25+
</dependency>
26+
27+
<!-- Test dependencies -->
28+
<dependency>
29+
<groupId>org.junit.jupiter</groupId>
30+
<artifactId>junit-jupiter</artifactId>
31+
<scope>test</scope>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.assertj</groupId>
35+
<artifactId>assertj-core</artifactId>
36+
<scope>test</scope>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.eclipse.esmf</groupId>
40+
<artifactId>esmf-test-aspect-models</artifactId>
41+
<scope>test</scope>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.eclipse.esmf</groupId>
45+
<artifactId>esmf-aspect-model-validator</artifactId>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.kohsuke</groupId>
49+
<artifactId>github-api</artifactId>
50+
</dependency>
51+
</dependencies>
52+
53+
</project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.eclipse.esmf;
2+
3+
import java.io.IOException;
4+
5+
public class FileNotFoundInRepositoryException extends RuntimeException {
6+
public FileNotFoundInRepositoryException( String message ) {
7+
super( message );
8+
}
9+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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.io.BufferedInputStream;
17+
import java.io.BufferedReader;
18+
import java.io.File;
19+
import java.io.FileInputStream;
20+
import java.io.FileNotFoundException;
21+
import java.io.FileOutputStream;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.io.InputStreamReader;
25+
import java.net.URL;
26+
import java.nio.channels.Channels;
27+
import java.nio.channels.ReadableByteChannel;
28+
import java.nio.charset.StandardCharsets;
29+
import java.nio.file.Files;
30+
import java.util.ArrayList;
31+
import java.util.List;
32+
import java.util.Optional;
33+
import java.util.Set;
34+
import java.util.stream.Collectors;
35+
import java.util.zip.ZipEntry;
36+
import java.util.zip.ZipInputStream;
37+
38+
import org.eclipse.esmf.aspectmodel.AspectModelFile;
39+
import org.eclipse.esmf.aspectmodel.RdfUtil;
40+
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
41+
import org.eclipse.esmf.aspectmodel.resolver.modelfile.RawAspectModelFile;
42+
import org.eclipse.esmf.aspectmodel.resolver.services.TurtleLoader;
43+
import org.eclipse.esmf.aspectmodel.scanner.AspectModelScanner;
44+
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
45+
import org.eclipse.esmf.metamodel.AspectModel;
46+
import org.eclipse.esmf.metamodel.ModelElement;
47+
48+
import io.vavr.control.Try;
49+
import org.apache.jena.rdf.model.Model;
50+
import org.kohsuke.github.GHContent;
51+
import org.kohsuke.github.GHRepository;
52+
import org.kohsuke.github.GitHub;
53+
import org.slf4j.Logger;
54+
import org.slf4j.LoggerFactory;
55+
56+
public class GitHubScanner implements AspectModelScanner {
57+
58+
private static final Logger LOG = LoggerFactory.getLogger( GitHubScanner.class );
59+
60+
private static final String GITHUB_BASE = "https://github.com";
61+
62+
private static final String GITHUB_ZIP_URL = GITHUB_BASE + "/%s/archive/refs/heads/%s.zip";
63+
64+
private static final String DOWNLOADED_ZIP_NAME = "%s/%s.zip";
65+
protected final String repositoryName;
66+
67+
protected final String branchName;
68+
69+
public GitHubScanner( final String repositoryName, final String branchName ) {
70+
this.repositoryName = repositoryName;
71+
this.branchName = branchName;
72+
}
73+
74+
/**
75+
* Finds and returns a list of valid {@link AspectModelFile} objects
76+
* that match the specified aspect model file name.
77+
*
78+
* <ol>
79+
* <li>Connects to GitHub anonymously and retrieves the specified repository.</li>
80+
* <li>Checks if the specified aspect model file exists in the repository.</li>
81+
* <li>If the file exists, it retrieves the file content and loads the aspect model using {@link AspectModelLoader}.</li>
82+
* <li>Downloads the entire repository (or specified branch) as a ZIP file.</li>
83+
* <li>Processes the downloaded package to extract aspect model files.</li>
84+
* <li>Deletes the downloaded package after processing.</li>
85+
* <li>Finds and returns the aspect model files that contain definitions matching the URNs in the search aspect model.</li>
86+
* </ol>
87+
*
88+
* @param aspectModelFileUrl The url of the aspect model file to search for in the repository.
89+
* @return A list of {@link AspectModelFile} objects that match the specified aspect model file name
90+
* by {@link AspectModelUrn}.
91+
* @throws RuntimeException if an I/O error occurs during the process.
92+
*/
93+
@Override
94+
public List<AspectModelFile> find( final String aspectModelFileUrl ) {
95+
List<AspectModelFile> resultAspectModelFiles = new ArrayList<>();
96+
try {
97+
final GitHub github = GitHub.connectAnonymously();
98+
final GHRepository repository = github.getRepository( repositoryName );
99+
100+
if ( checkFileExists( repository, aspectModelFileUrl ) ) {
101+
102+
final GHContent contentOfSearchFile = repository.getFileContent( aspectModelFileUrl, branchName );
103+
104+
final AspectModelLoader aspectModelLoader = new AspectModelLoader();
105+
106+
final AspectModel searchAspectModel = aspectModelLoader.load( new URL( contentOfSearchFile.getDownloadUrl() ).openStream() );
107+
108+
final String githubUrl = String.format( GITHUB_ZIP_URL, repositoryName, branchName );
109+
final String downloadedPackageName = String.format( DOWNLOADED_ZIP_NAME, Files.createTempDirectory( "temporally" ).toString(),
110+
branchName );
111+
final File downloadedPackage = downloadFile( new URL( githubUrl ), downloadedPackageName );
112+
113+
final List<AspectModelFile> filesInPackage = processPackage( new FileInputStream( downloadedPackage ) );
114+
115+
final boolean packageIsDeleted = downloadedPackage.delete();
116+
if ( packageIsDeleted ) {
117+
LOG.debug( String.format( "Package %s was deleted", downloadedPackage.getName() ) );
118+
}
119+
120+
final List<AspectModelUrn> searchAspectUrns = searchAspectModel.elements().stream().map( ModelElement::urn ).toList();
121+
122+
for ( final AspectModelFile aspectModelFile : filesInPackage ) {
123+
final Set<AspectModelUrn> urnsAspectModelFile = RdfUtil.getAllUrnsInModel( aspectModelFile.sourceModel() );
124+
125+
for ( final AspectModelUrn aspectModelUrn : searchAspectUrns ) {
126+
if ( urnsAspectModelFile.contains( aspectModelUrn ) ) {
127+
resultAspectModelFiles.add( aspectModelFile );
128+
}
129+
}
130+
}
131+
}
132+
} catch ( IOException e ) {
133+
throw new RuntimeException( e );
134+
}
135+
136+
return resultAspectModelFiles;
137+
}
138+
139+
private boolean checkFileExists( final GHRepository repository, final String aspectModelFileUrl ) {
140+
try {
141+
final GHContent content = repository.getFileContent( aspectModelFileUrl, branchName );
142+
return content != null;
143+
} catch ( IOException e ) {
144+
throw new FileNotFoundInRepositoryException(
145+
String.format( "File %s can't be found in repository %s.", aspectModelFileUrl, repository.getUrl() ) );
146+
}
147+
}
148+
149+
private File downloadFile( final URL repositoryUrl, final String outputFileName ) throws IOException {
150+
151+
ReadableByteChannel rbc;
152+
File outputFile = new File( outputFileName );
153+
try ( final BufferedInputStream bis = new BufferedInputStream( repositoryUrl.openStream() );
154+
final FileOutputStream fos = new FileOutputStream( outputFileName ) ) {
155+
rbc = Channels.newChannel( bis );
156+
fos.getChannel().transferFrom( rbc, 0, Long.MAX_VALUE );
157+
} catch ( FileNotFoundException e ) {
158+
throw new FileNotFoundException( String.format( "Can't download repository, file %s not found! %s", repositoryName, e ) );
159+
} catch ( IOException e ) {
160+
throw new IOException( String.format( "Can't write zip file %s", outputFileName ) );
161+
}
162+
163+
LOG.info( String.format( "Downloaded %s repository to local.", repositoryUrl.getPath() ) );
164+
165+
return outputFile;
166+
}
167+
168+
/**
169+
* This method provides valid files from package
170+
*
171+
* @param inputStream of repository package
172+
* @return list of valid {@link AspectModelFile} from package
173+
*/
174+
private List<AspectModelFile> processPackage( final InputStream inputStream ) {
175+
List<AspectModelFile> aspectModelFiles = new ArrayList<>();
176+
177+
try ( ZipInputStream zis = new ZipInputStream( inputStream ) ) {
178+
ZipEntry entry;
179+
180+
while ( (entry = zis.getNextEntry()) != null ) {
181+
if ( entry.getName().endsWith( ".ttl" ) ) {
182+
final String content = new BufferedReader( new InputStreamReader( zis, StandardCharsets.UTF_8 ) ).lines()
183+
.collect( Collectors.joining( "\n" ) );
184+
final Try<Model> tryModel = TurtleLoader.loadTurtle( content );
185+
if ( !tryModel.isFailure() ) {
186+
final AspectModelFile aspectModelFile = new RawAspectModelFile( tryModel.get(), new ArrayList<>(), Optional.empty() );
187+
aspectModelFiles.add( aspectModelFile );
188+
}
189+
}
190+
}
191+
192+
zis.closeEntry();
193+
} catch ( IOException e ) {
194+
LOG.error( "Error reading the Package input stream", e );
195+
throw new RuntimeException( new IOException( "Error reading the Package input stream", e ) );
196+
}
197+
198+
return aspectModelFiles;
199+
}
200+
}

0 commit comments

Comments
 (0)