Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ suitable for inclusion in the SPDX Java Library

To run the utility as a command line interface, execute the main method `ShaclToJavaCli` with 2 parameters:

- model file in turtle format
- input directory of model files in turtle format - there should be one model file per version to be generated
- output directory

## Usage Library

To use the code as a library, the main entry point is the `ShaclToJava` class which takes a single parameter of the SPDX Ontology model.
To use the code as a library, the main entry point is the `ShaclToJava` class which takes two parameters:

- SPDX Ontology model for the code to be generated
- SPDX Ontology model for the previous version of the model - this will provide the proper class hierarchy for compatibility

The `generate(dir)` method will generate the Java files in the `dir` directory.

Expand Down
2 changes: 1 addition & 1 deletion resources/javaTemplates/ModelInfoTemplate.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class SpdxModelInfoV3_0 implements ISpdxModelInfo {

@Override
public List<String> getSpecVersions() {
return Arrays.asList(new String[] {"SPDX-3.0", "{{{versionSemVer}}}"});
return Arrays.asList(new String[] {"{{{versionSuffix}}}"});
}

@Override
Expand Down
34 changes: 17 additions & 17 deletions resources/javaTemplates/TestValuesGeneratorTemplate.txt
Original file line number Diff line number Diff line change
Expand Up @@ -329,23 +329,23 @@ public class TestValuesGenerator {
{{/enumerationProperties}}
{{#stringCollection}}
{{^pattern}}
public static final String {{{propertyNameUpper}}}_TEST_VALUE1 = "test 1 {{{propertyName}}}";
public static final String {{{propertyNameUpper}}}_TEST_VALUE2 = "test 2 {{{propertyName}}}";
public static final String {{{propertyNameUpper}}}_TEST_VALUE3 = "test 3 {{{propertyName}}}";
public static final String {{{propertyNameUpper}}}_COL_TEST_VALUE1 = "test 1 {{{propertyName}}}";
public static final String {{{propertyNameUpper}}}_COL_TEST_VALUE2 = "test 2 {{{propertyName}}}";
public static final String {{{propertyNameUpper}}}_COL_TEST_VALUE3 = "test 3 {{{propertyName}}}";
{{/pattern}}
{{#pattern}}
public static final String {{{propertyNameUpper}}}_TEST_VALUE1 = UnitTestHelper.genRandPattern("{{{pattern}}}");
public static final String {{{propertyNameUpper}}}_TEST_VALUE2 = UnitTestHelper.genRandPattern("{{{pattern}}}");
public static final String {{{propertyNameUpper}}}_TEST_VALUE3 = UnitTestHelper.genRandPattern("{{{pattern}}}");
public static final String {{{propertyNameUpper}}}_COL_TEST_VALUE1 = UnitTestHelper.genRandPattern("{{{pattern}}}");
public static final String {{{propertyNameUpper}}}_COL_TEST_VALUE2 = UnitTestHelper.genRandPattern("{{{pattern}}}");
public static final String {{{propertyNameUpper}}}_COL_TEST_VALUE3 = UnitTestHelper.genRandPattern("{{{pattern}}}");
{{/pattern}}
public static final List<String> {{{propertyNameUpper}}}_TEST_LIST1 = Arrays.asList(new String[] { {{{propertyNameUpper}}}_TEST_VALUE1, {{{propertyNameUpper}}}_TEST_VALUE2 });
public static final List<String> {{{propertyNameUpper}}}_TEST_LIST2 = Arrays.asList(new String[] { {{{propertyNameUpper}}}_TEST_VALUE3 });
public static final List<String> {{{propertyNameUpper}}}_TEST_LIST1 = Arrays.asList(new String[] { {{{propertyNameUpper}}}_COL_TEST_VALUE1, {{{propertyNameUpper}}}_COL_TEST_VALUE2 });
public static final List<String> {{{propertyNameUpper}}}_TEST_LIST2 = Arrays.asList(new String[] { {{{propertyNameUpper}}}_COL_TEST_VALUE3 });
{{/stringCollection}}
{{#enumPropertyValueCollection}}
public static final {{{type}}} {{{propertyNameUpper}}}_TEST_VALUE1 = {{{type}}}.values()[0];
public static final {{{type}}} {{{propertyNameUpper}}}_TEST_VALUE2 = {{{type}}}.values()[1];
public static final List<{{{type}}}> {{{propertyNameUpper}}}_TEST_LIST1 = Arrays.asList(new {{{type}}}[] { {{{propertyNameUpper}}}_TEST_VALUE1, {{{propertyNameUpper}}}_TEST_VALUE2 });
public static final List<{{{type}}}> {{{propertyNameUpper}}}_TEST_LIST2 = Arrays.asList(new {{{type}}}[] { {{{propertyNameUpper}}}_TEST_VALUE1 });
public static final {{{type}}} {{{propertyNameUpper}}}_COL_TEST_VALUE1 = {{{type}}}.values()[0];
public static final {{{type}}} {{{propertyNameUpper}}}_COL_TEST_VALUE2 = {{{type}}}.values()[1];
public static final List<{{{type}}}> {{{propertyNameUpper}}}_TEST_LIST1 = Arrays.asList(new {{{type}}}[] { {{{propertyNameUpper}}}_COL_TEST_VALUE1, {{{propertyNameUpper}}}_COL_TEST_VALUE2 });
public static final List<{{{type}}}> {{{propertyNameUpper}}}_TEST_LIST2 = Arrays.asList(new {{{type}}}[] { {{{propertyNameUpper}}}_COL_TEST_VALUE1 });
{{/enumPropertyValueCollection}}

private IModelStore modelStore;
Expand All @@ -355,7 +355,7 @@ public class TestValuesGenerator {
public TestValuesGenerator(IModelStore modelStore, IModelCopyManager copyManager) throws InvalidSPDXAnalysisException {
this.modelStore = modelStore;
this.copyManager = copyManager;
if (!ModelRegistry.getModelRegistry().containsSpecVersion("{{{versionSemVer}}}")) {
if (!ModelRegistry.getModelRegistry().containsSpecVersion("{{{versionSuffix}}}")) {
ModelRegistry.getModelRegistry().registerModel(new TestSpdxModelInfo());
}
creationInfo = new CreationInfo.CreationInfoBuilder(modelStore, modelStore.getNextId(IdType.Anonymous), copyManager)
Expand Down Expand Up @@ -389,15 +389,15 @@ public class TestValuesGenerator {
.{{{setter}}}({{{propertyNameUpper}}}_TEST_VALUE)
{{/stringProperties}}
{{#stringCollection}}
.{{{adder}}}({{{propertyNameUpper}}}_TEST_VALUE1)
.{{{adder}}}({{{propertyNameUpper}}}_TEST_VALUE2)
.{{{adder}}}({{{propertyNameUpper}}}_COL_TEST_VALUE1)
.{{{adder}}}({{{propertyNameUpper}}}_COL_TEST_VALUE2)
{{/stringCollection}}
{{#enumerationProperties}}
.{{{setter}}}({{{propertyNameUpper}}}_TEST_VALUE1)
{{/enumerationProperties}}
{{#enumPropertyValueCollection}}
.{{{adder}}}({{{propertyNameUpper}}}_TEST_VALUE1)
.{{{adder}}}({{{propertyNameUpper}}}_TEST_VALUE2)
.{{{adder}}}({{{propertyNameUpper}}}_COL_TEST_VALUE1)
.{{{adder}}}({{{propertyNameUpper}}}_COL_TEST_VALUE2)
{{/enumPropertyValueCollection}}
{{#elementProperties}}
.{{{setter}}}(builderFor{{{type}}}Tests(modelStore.getNextId(IdType.SpdxId)).build())
Expand Down
20 changes: 15 additions & 5 deletions src/main/java/org/spdx/tools/model2java/ShaclToJava.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,14 @@ public enum SuperclassRequired {

/**
* @param model model to use to generate the java files
* @param packageVersion version string to use in package names
*/
public ShaclToJava(OntModel model) {
public ShaclToJava(OntModel model, String packageVersion) {
this.model = model;
String spdxUri = model.getNsPrefixURI("spdx");
versionSemVer = spdxUri.substring("https://spdx.org/rdf/".length());
versionSemVer = versionSemVer.substring(0, versionSemVer.indexOf('/'));
versionSuffix = "v" + versionSemVer.replaceAll("\\.", "_");
versionSuffix = packageVersion;
shapes = Shapes.parse(model);
shapeMap = shapes.getShapeMap();
allIndividuals = model.listIndividuals().toList();
Expand Down Expand Up @@ -1568,16 +1569,25 @@ private Map<String, Object> propertyToMustachMap(PropertyShape propertyShape,
}
}
String propertySuffix = propertyUri.substring(propertyUri.lastIndexOf("/terms/"));
PropertyType propertyType = ShaclToJavaConstants.SET_PROPERTY_SUFFIXES.contains(propertySuffix) ? PropertyType.OBJECT_SET : determinePropertyType(classRestriction, dataTypeRestriction,
minCardinality, maxCardinality);
PropertyType propertyType;
if (ShaclToJavaConstants.SET_PROPERTY_SUFFIXES.contains(propertySuffix)) {
propertyType = PropertyType.OBJECT_SET;
} else if ("extension".equals(name)) {
propertyType = PropertyType.OBJECT; // workaround for https://github.com/spdx/spec-parser/issues/207
} else {
propertyType = determinePropertyType(classRestriction, dataTypeRestriction,
minCardinality, maxCardinality);
}
if (PropertyType.OBJECT_COLLECTION.equals(propertyType) || PropertyType.STRING_COLLECTION.equals(propertyType) ||
PropertyType.ENUM_COLLECTION.equals(propertyType)) {
requiredImports.add("import java.util.Collection;");
requiredImports.add("import java.util.Collections;");
requiredImports.add("import java.util.Objects;");
}
retval.put("propertyType", propertyType);
String typeUri = getTypeUri(classRestriction, dataTypeRestriction);
String typeUri = "extension".equals(name) ?
String.format("https://spdx.org/rdf/%s/terms/Extension/Extension", this.versionSemVer) : // workaround for https://github.com/spdx/spec-parser/issues/207
getTypeUri(classRestriction, dataTypeRestriction);
String type;
if (ShaclToJavaConstants.BOOLEAN_TYPE.equals(typeUri)) {
type = "Boolean";
Expand Down
145 changes: 108 additions & 37 deletions src/main/java/org/spdx/tools/model2java/ShaclToJavaCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,72 +9,143 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

import org.apache.jena.ontology.OntModel;
import org.apache.jena.ontology.OntModelSpec;
import org.apache.jena.rdf.model.ModelFactory;

/**
* Command Line Interface for the ShaclToJava utility
*
* <p/>
* Generates Java code from a SHACL file specifically for SPDX version 3+
*
* Usage: ShaclToJavaCli spdx-model.ttl outputdirectory
*
* <p/>
* Usage: ShaclToJavaCli inputdirectory outputdirectory
* <p/>
* The input directory contains model files in TTL format for all versions to be generated
* <p/>
* The output directory will contain generated Java code to implement the model
* <p/>
* @author Gary O'Neall
*/
public class ShaclToJavaCli {

/**
* @param args
* @param args args[0] input directory args[1] output directory
*/
public static void main(String[] args) {
System.exit(run(args));
}
/**
* @param args args[0] input directory args[1] output directory
*/
public static int run(String[] args) {
if (args.length != 2) {
System.out.println(String.format("Invalid arguments. Expected 2 arguments, found %d arguments.", args.length));
System.out.printf("Invalid arguments. Expected 2 arguments, found %d arguments.%n", args.length);
usage();
System.exit(-1);
return -1;
}
File outputdir = new File(args[1]);
if (!outputdir.exists()) {
System.out.println(String.format("Output directory %s does not exist.", args[1]));
File outputDir = new File(args[1]);
if (!outputDir.exists()) {
System.out.printf("Output directory %s does not exist.%n", args[1]);
usage();
System.exit(-1);
return -1;
}
if (!outputdir.isDirectory()) {
System.out.println(String.format("Output directory %s is not a directory.", args[1]));
if (!outputDir.isDirectory()) {
System.out.printf("Output directory %s is not a directory.%n", args[1]);
usage();
System.exit(-1);
return -1;
}
try (InputStream is = new FileInputStream(new File(args[0]))) {
OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
model.read(is, "", "Turtle");
ShaclToJava s2j = new ShaclToJava(model);
List<String> warnings = s2j.generate(outputdir);
if (warnings.size() > 0) {
System.out.println("Shacl2Java completed with the following warnings:");
for (String warning:warnings) {
System.out.print('\t');
System.out.println(warning);
}
System.exit(1);
} else {
System.out.println("Java code generated successfully");
System.exit(0);
}
} catch (IOException e) {
System.out.println("I/O Error reading ontology file");
File inputDir = new File(args[0]);
if (!inputDir.exists()) {
System.out.printf("Input directory %s does not exist.%n", args[1]);
usage();
System.exit(-1);
} catch (ShaclToJavaException e) {
System.out.println(String.format("Error generating Java code: %s", e.getMessage()));
return -1;
}
if (!inputDir.isDirectory()) {
System.out.printf("Input directory %s is not a directory.%n", args[1]);
usage();
return -1;
}
File[] inputFiles = inputDir.listFiles();
if (Objects.isNull(inputFiles) || inputFiles.length < 1) {
System.out.printf("Input directory %s contains no files.%n", args[1]);
usage();
System.exit(-1);
return -1;
}
List<OntModel> models = new ArrayList<>();
for (File f : Objects.requireNonNull(inputDir.listFiles())) {
try (InputStream is = new FileInputStream(f)) {
OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
model.read(is, "", "Turtle");
models.add(model);
} catch (IOException e) {
System.out.println("I/O Error reading ontology file");
usage();
return -1;
}
}
models.sort(new Comparator<OntModel>() {
@Override
public int compare(OntModel o1, OntModel o2) {
return(getSpecVersion(o1).compareTo(getSpecVersion(o2)));
}
});
List<String> warnings = new ArrayList<>();
for (int i = 0; i < models.size(); i++) {
try {
String specVersion = getSpecVersion(models.get(i));
String[] specVersionParts = specVersion.split("\\.");
String versionForPackageName = "v3_" +
(specVersionParts.length > 1 ? specVersionParts[1] : "0");
ShaclToJava s2j = new ShaclToJava(models.get(i), versionForPackageName);
warnings.addAll(s2j.generate(outputDir));
if ("3.0.1".equals(specVersion)) {
// generate the package version for the 3.0.1 patch version only - for compatibility
// going forward, we will only generate minor versions
ShaclToJava s2jdot = new ShaclToJava(models.get(i), "v3_0_1");
warnings.addAll(s2jdot.generate(outputDir));
}
if (i == models.size()-1) {
// for the latest version, create a package for the latest version
ShaclToJava s2jlatest = new ShaclToJava(models.get(i), "v3");
warnings.addAll(s2jlatest.generate(outputDir));
}
} catch (IOException e) {
System.out.println("I/O Error writing output directory");
usage();
return -1;
} catch (ShaclToJavaException e) {
System.out.printf("Error generating Java code: %s%n", e.getMessage());
usage();
return -1;
}
}
if (!warnings.isEmpty()) {
System.out.println("Shacl2Java completed with the following warnings:");
for (String warning:warnings) {
System.out.print('\t');
System.out.println(warning);
}
return 1;
} else {
System.out.println("Java code generated successfully");
return 0;
}
}

private static String getSpecVersion(OntModel model) {
String spdxUri = model.getNsPrefixURI("spdx");
String versionSemVer = spdxUri.substring("https://spdx.org/rdf/".length());
versionSemVer = versionSemVer.substring(0, versionSemVer.indexOf('/'));
return versionSemVer;
}

private static void usage() {
System.out.println("Usage: ShaclToJavaCli spdx-model.ttl outputdirectory");
System.out.println("Usage: ShaclToJavaCli inputdirectory outputdirectory");
}

}
33 changes: 29 additions & 4 deletions src/test/java/org/spdx/tools/model2java/ShaclToJavaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
* @author Gary O'Neall
*/
public class ShaclToJavaTest extends TestCase {

static final String MODEL_FILE_PATH = "testResources" + File.separator + "spdx-model.ttl";

static final String MODEL_DIR_PATH = "testResources";
static final String MODEL_FILE_PATH = MODEL_DIR_PATH + File.separator + "spdx-model-3-0-1.ttl";

protected void setUp() throws Exception {
super.setUp();
Expand All @@ -34,15 +35,15 @@ protected void setUp() throws Exception {
protected void tearDown() throws Exception {
super.tearDown();
}

public void testConvertToJava() throws IOException, ShaclToJavaException {
ShaclToJava otj;
File tempDir = Files.createTempDirectory("spdx_test").toFile();
try {
try (InputStream is = new FileInputStream(MODEL_FILE_PATH)) {
OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
model.read(is, "", "Turtle");
otj = new ShaclToJava(model);
otj = new ShaclToJava(model, "v3_0_1");
List<String> warnings = otj.generate(tempDir);
assertTrue(warnings.isEmpty());
Path aIPath = tempDir.toPath().resolve("src")
Expand All @@ -66,6 +67,30 @@ public void testConvertToJava() throws IOException, ShaclToJavaException {
}
}

public void testConvertToJavaCLI() throws IOException, ShaclToJavaException {
File tempDir = Files.createTempDirectory("spdx_test").toFile();
try {
assertEquals(0, ShaclToJavaCli.run(new String[] {MODEL_DIR_PATH, tempDir.getAbsolutePath()}));
Path modelPath = tempDir.toPath().resolve("src")
.resolve("main")
.resolve("java")
.resolve("org")
.resolve("spdx")
.resolve("library")
.resolve("model");
File v3Dir = modelPath.resolve("v3").toFile();
assertTrue(v3Dir.exists());
File v31Dir = modelPath.resolve("v3_1").toFile();
assertTrue(v31Dir.exists());
File v30Dir = modelPath.resolve("v3_0").toFile();
assertTrue(v30Dir.exists());
File v301Dir = modelPath.resolve("v3_0_1").toFile();
assertTrue(v301Dir.exists());
} finally {
assertTrue(deleteDirectory(tempDir));
}
}

/**
* @param tempDir directory to delete
*/
Expand Down
File renamed without changes.
Loading
Loading