Java tutorial
/* MIT License - Copyright (c) 2016 Kevin Haller <kevin.haller@outofbits.com> */ package org.outofbits.sesame.schemagen; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHeaders; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.eclipse.rdf4j.model.*; import org.eclipse.rdf4j.model.impl.SimpleValueFactory; import org.eclipse.rdf4j.model.vocabulary.*; import org.eclipse.rdf4j.rio.RDFFormat; import org.eclipse.rdf4j.rio.RDFParseException; import org.eclipse.rdf4j.rio.Rio; import org.eclipse.rdf4j.rio.UnsupportedRDFormatException; import org.outofbits.sesame.schemagen.exception.RDFFormatNotSupportedException; import org.outofbits.sesame.schemagen.exception.SchemaGenerationException; import org.outofbits.sesame.schemagen.exception.VocabularyNotFoundException; import org.outofbits.sesame.schemagen.utils.JavaIdentifier; import org.outofbits.sesame.schemagen.utils.StringMapFormatter; import java.io.*; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * This is the core class of Sesame-Schemagen and provides the capability to generate a Java * class from given {@link VocabularyOptions} with the given input document. In order to start the * generation process {@code generate()} must be called. * * @author Kevin Haller <kevin.haller@outofbits.com> * @version 1.0 * @since 1.0 */ public class SchemaGeneration { /* User agent string for HTTP requests. */ private static final String HTTP_USER_AGENT = "Mozilla/5.0 (Sesame-SchemaGen; version 1.0) Gecko/20100101 Firefox/10.0"; /* Templates */ private static final String DEFAULT_GLOBAL_TEMPLATE = "%(header)s%n%(class)s"; private static final String DEFAULT_HEADER_TEMPLATE = "%(package)s%n%(imports)s%n%(class-comment)s"; private static final String DEFAULT_CLASS_COMMENT_TEMPLATE = "/**%n * %(vocab-label)s%n * <p/>%n * %(vocab-comment$80$%n * )w%n *%n" + " * @author Auto-generated by Sesame-Schemagen on %(generation-date)s%n" + " * @version %(vocab-version-info)s%n * @see <a href=\"%(namespace)s\">%(vocab-label)s</a>%n */"; private static final String DEFAULT_CLASS_TEMPLATE = "public final class %(class-name)s {%n%n%(class-body)s%n}%n"; private static final String DEFAULT_CLASS_BODY_TEMPLATE = "%(namespace)s%(prefix)s%(resource-declarations)s%n%istatic {%n%(resource-definitions)s%n%i}"; private static final String DEFAULT_NAMESPACE_TEMPLATE = "%i/** Describes the base namespace of this vocabulary */%n%ipublic static final String NS = \"%(namespace-iri)s\";%n%n"; private static final String DEFAULT_PREFIX_TEMPLATE = "%i/** Describes the preferred prefix of this vocabulary. */%n%ipublic static final String PREFIX = \"%(prefix)s\";%n%n"; private static final String DEFAULT_RESOURCE_DEC_TEMPLATE = "%n%i/**%n%i * %(resource-label$80$%n%i * )w%n%i * <p/>%n%i * %(resource-comment$80$%n%i * )w%n%i * %n%i * @see <a href=\"%(resource-iri)s\">%(resource-name)s</a>%n%i */%n%ipublic static final %(sesame-resource-class)s %(resource-name)s;%n"; private static final String DEFAULT_VALUE_FACTORY_TEMPLATE = "%(2)iValueFactory valueFactory = %(sesame-value-factory)s.getInstance();%n%n"; private static final String DEFAULT_RESOURCE_DEF_TEMPLATE = "%(2)i%(resource-name)s = valueFactory.create%(sesame-resource-class)s(\"%(resource-iri)s\");%n"; /** * All well-known properties that give a short description of a resource. */ public static final IRI[] LABEL_PROPERTIES = new IRI[] { RDFS.LABEL, DCTERMS.TITLE, DC.TITLE }; /** * All well-known properties that could describe a resource. */ public static final IRI[] COMMENT_PROPERTIES = new IRI[] { RDFS.COMMENT, DCTERMS.DESCRIPTION, DCTERMS.ABSTRACT, DC.DESCRIPTION }; /** * All well-known properties that could indicate the resource describing the vocabulary. */ public static final IRI[] VOCAB_CLASSES = new IRI[] { OWL.ONTOLOGY }; /** * All well-known properties that could describe the version of the vocabulary. */ public static final IRI[] VOCAB_VERSION_PROPERTIES = new IRI[] { OWL.VERSIONINFO, OWL.VERSIONIRI }; /** * A map that links the name of formats to the corresponding {@link RDFFormat} instance. */ private static final Map<String, RDFFormat> supportedRDFFormats = new HashMap<>(); static { supportedRDFFormats.put(RDFFormat.RDFXML.getName().toLowerCase(), RDFFormat.RDFXML); supportedRDFFormats.put(RDFFormat.TURTLE.getName().toLowerCase(), RDFFormat.TURTLE); supportedRDFFormats.put(RDFFormat.JSONLD.getName().toLowerCase(), RDFFormat.JSONLD); } /** * Checks if {@link SchemaGeneration} supports the given format in form of a string. * * @param rdfFormatString the rdf format in form of a string. * @return true, if {@link SchemaGeneration} supports the given format, otherwise false. */ public static boolean supportsRDFFormat(String rdfFormatString) { return supportedRDFFormats.containsKey(rdfFormatString.toLowerCase()); } /** * Gets the corresponding {@link RDFFormat} of the given format in form of a string. A * {@link RDFFormatNotSupportedException} will be thrown, if the given format is not supported * by {@link SchemaGeneration}. {@code supportsRDFFormat(String rdfFormatString)} can be used to * avoid the exception beforehand. * * @param rdfFormatString the format in form a string for which the corresponding * {@link RDFFormat} shall be returned. * @return the corresponding {@link RDFFormat} of the given format in form of a string. * @throws RDFFormatNotSupportedException if the given format is not supported. */ public static RDFFormat rdfFormatOf(String rdfFormatString) throws RDFFormatNotSupportedException { if (!supportsRDFFormat(rdfFormatString)) { throw new RDFFormatNotSupportedException( String.format("The given format '%s' is not supported by Schemagen. Supported formats are %s.", rdfFormatString, supportedRDFFormats.keySet())); } return supportedRDFFormats.get(rdfFormatString); } private ValueFactory valueFactory = SimpleValueFactory.getInstance(); private VocabularyOptions vocabularyOptions; private String[] sesamePackagesToImport; private StringMapFormatter stringMapFormatter; private Map<String, String> formattingMap = new HashMap<>(); /** * Creates a new instance of {@link SchemaGeneration} by considering the given * {@link VocabularyOptions}. * * @param vocabularyOptions the {@link VocabularyOptions} that shall be considered for the * schema generation. */ public SchemaGeneration(VocabularyOptions vocabularyOptions) { this.vocabularyOptions = vocabularyOptions; this.sesamePackagesToImport = vocabularyOptions.sesameLowerThanV4() ? new String[] { "model.URI", "model.ValueFactory", "model.impl.ValueFactoryImpl" } : new String[] { "model.IRI", "model.ValueFactory", "model.impl.SimpleValueFactory" }; this.stringMapFormatter = new StringMapFormatter(); // Initializes the global formatting map. formattingMap.put("class-name", JavaIdentifier.getJavaIdFrom(vocabularyOptions.className())); formattingMap.put("generation-date", new SimpleDateFormat("MM/dd/yyyy").format(new Date())); formattingMap.put("sesame-package-base", vocabularyOptions.rdfFramework().equals(RDFFramework.RDF4J) ? "org.eclipse.rdf4j" : "org.openrdf"); formattingMap.put("sesame-resource-class", vocabularyOptions.sesameLowerThanV4() ? "URI" : "IRI"); formattingMap.put("sesame-value-factory", vocabularyOptions.sesameLowerThanV4() ? "ValueFactoryImpl" : "SimpleValueFactory"); } /** * Gets the vocabulary the given input is pointing to. The input can either be a file path or * a HTTP {@link java.net.URL}, otherwise a {@link VocabularyNotFoundException} will be thrown. * Also if the vocabulary cannot be accessed or read, a {@link VocabularyNotFoundException} will * be thrown. * * @param input path to a file or a valid {@link java.net.URL}. Schemagen supports the * protocols 'http' and 'file'. Input must not be null. * @param format the format of the document representing the vocabulary. The format can be * null. * @param workingDir the working directory. * @return {@link Model} containing all statements of the vocabulary pointed to by input. * @throws VocabularyNotFoundException if the vocabulary cannot be found or accessed. */ private Model getVocabularyModel(String input, RDFFormat format, String workingDir) throws VocabularyNotFoundException { assert input != null; try { URL documentUrl = new URL(input); switch (documentUrl.getProtocol()) { case "file": File sourceFile = new File(documentUrl.getFile()); if (sourceFile.isAbsolute()) { return readVocabularyFromFile(sourceFile, format); } else { return readVocabularyFromFile(new File(workingDir, sourceFile.getPath()), format); } case "http": return readVocabularyFromHTTPSource(documentUrl, format); default: throw new VocabularyNotFoundException( String.format("The protocol '%s' is not supported.", documentUrl.getProtocol())); } } catch (MalformedURLException e) { try { File sourceFile = new File(input); if (sourceFile.isAbsolute()) { return readVocabularyFromFile(sourceFile, format); } else { return readVocabularyFromFile(new File(workingDir, sourceFile.getPath()), format); } } catch (IOException io) { throw new VocabularyNotFoundException(e); } } catch (Exception e) { throw new VocabularyNotFoundException(e); } } /** * Requests the RDF file of the vocabulary by using the given HTTP {@link java.net.URL}. The * header-entry ACCEPT contains all supported {@link RDFFormat}s, where the given * {@link RDFFormat} is the preferred one. The responded rdf file will be parsed and a * {@link Model} containing all statements will be returned. An {@link IOException} will be * thrown, if the rdf file cannot be accessed or read. {@link URISyntaxException} will be thrown, * if the given {@link java.net.URL} has a syntax error. * * @param url the url, where the vocabulary is located and accessible by using HTTP. It must * not be null. * @param format the format of the document representing the vocabulary. The format can be null. * @return {@link Model} containing all statements of the vocabulary located at the given * {@link java.net.URL}. * @throws IOException if the rdf file cannot be accessed or read. * @throws URISyntaxException if the given {@link java.net.URL} has a syntax error. */ private Model readVocabularyFromHTTPSource(java.net.URL url, RDFFormat format) throws IOException, URISyntaxException { assert url != null && url.getProtocol().equals("http"); HttpClientBuilder clientBuilder = HttpClientBuilder.create().setUserAgent(HTTP_USER_AGENT); HttpUriRequest vocabularyGETRequest = RequestBuilder.get().setUri(url.toURI()) .setHeader(HttpHeaders.ACCEPT, String.join(", ", RDFFormat.getAcceptParams(supportedRDFFormats.values(), false, format))) .build(); try (CloseableHttpClient client = clientBuilder.build()) { CloseableHttpResponse response = client.execute(vocabularyGETRequest); if (response.getStatusLine().getStatusCode() != 200) { throw new IOException(String.format("The given vocabulary can not requested from '%s'. %d: %s", url, response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase())); } String responseContentType = response.getEntity().getContentType().getValue(); Optional<RDFFormat> optionalResponseRdfFormat = Rio.getParserFormatForMIMEType(responseContentType); try (InputStream vocabResponseStream = response.getEntity().getContent()) { if (optionalResponseRdfFormat.isPresent()) { return Rio.parse(vocabResponseStream, "", optionalResponseRdfFormat.get()); } else { if (format == null) { throw new IOException( String.format("The returned content type (%s) from '%s' is not supported", responseContentType, url)); } try { return Rio.parse(vocabResponseStream, "", format); } catch (RDFParseException | UnsupportedRDFormatException e) { throw new IOException(String.format( "The returned content type (%s) from '%s' is not supported. Fallback to the given format %s, but an error occurred.", responseContentType, url, format), e); } } } } } /** * Reads the given file that contains the vocabulary and stores the statements in a * {@link Model} that will be returned. A {@link FileNotFoundException} will be thrown, if the * given file does not exist. Important for interpreting the given file is the used * {@link RDFFormat}. The format can either be given explicitly or be null. If the given * {@link RDFFormat} is null, this method will try to detect the format. In case of a failed * detection {@code RDFFormat.RDFXML} will be used per default. * * @param vocabularyFile the {@link File} that represents the vocabulary in the given format. * The file must not be null. * @param format the {@link RDFFormat} of the given {@link File}. * @return {@link Model} containing the vocabulary. * @throws IOException if an io error occurs. f.e. the given file does not exist. */ private Model readVocabularyFromFile(File vocabularyFile, RDFFormat format) throws IOException { assert vocabularyFile != null; if (!vocabularyFile.exists()) { throw new IOException(String.format("%s cannot be found at '%s'.", vocabularyFile.getName(), vocabularyFile.getAbsolutePath())); } if (format == null) { Optional<RDFFormat> optionalFileFormat = Rio.getParserFormatForFileName(vocabularyFile.getName()); if (optionalFileFormat.isPresent()) { format = optionalFileFormat.get(); } else { format = RDFFormat.RDFXML; } } try (InputStream inputStream = new BufferedInputStream(new FileInputStream(vocabularyFile))) { return Rio.parse(inputStream, "", format); } } /** * Prepares for the class file that shall be generated. * * @return {@link File} for the generated Java class. */ private File prepareFileForGeneration() { File outputDirectory = new File(vocabularyOptions.outputDir()); File packageDirectory = vocabularyOptions.classPackage() != null ? new File(outputDirectory.getAbsolutePath(), vocabularyOptions.classPackage().replace(".", "/")) : outputDirectory; packageDirectory.mkdirs(); return new File(packageDirectory.getAbsolutePath(), JavaIdentifier.getJavaIdFrom(vocabularyOptions.className()) + ".java"); } /** * Gets the root resource of the given vocabulary, or null if n such reosurce could be found. * * @param vocabulary {@link Model} containing all statements of the vocabulary. * @return the root {@link Resource} or null, if no such resource could be found. * @throws SchemaGenerationException if there are more than one root resource. */ private Resource getRootResource(Model vocabulary) throws SchemaGenerationException { assert vocabulary != null; Resource vocabularyRootResource = null; // Using Namespace for getting the root resource. if (vocabularyOptions.baseNamespace() != null) { Optional<Resource> optionalVocabRootResource = vocabulary .filter(valueFactory.createIRI(vocabularyOptions.baseNamespace()), null, null).subjects() .stream().findFirst(); if (optionalVocabRootResource.isPresent()) { vocabularyRootResource = optionalVocabRootResource.get(); } } if (vocabularyRootResource == null) { // Getting the root resource that is an instance of a known vocabulary class. for (IRI clazz : VOCAB_CLASSES) { Set<Resource> vocabRootResources = vocabulary.filter(null, RDF.TYPE, clazz).subjects(); if (vocabRootResources.size() > 1) { throw new SchemaGenerationException(String.format("Ambiguous root resources (%s).", String.join( ", ", vocabRootResources.stream().map(Resource::stringValue).collect(Collectors.toList())))); } else if (vocabRootResources.size() == 1) { Resource currentVocabularyRootResource = vocabRootResources.iterator().next(); if (vocabularyRootResource == null) { vocabularyRootResource = currentVocabularyRootResource; } else if (!currentVocabularyRootResource.equals(vocabularyRootResource)) { throw new SchemaGenerationException(String.format("Ambiguous root resources (%s, %s).", vocabularyRootResource, currentVocabularyRootResource)); } } } } return vocabularyRootResource; } /** * Gets the first object literal that can be found using the given properties for the given * {@link Resource} in the vocabulary {@link Model}. * * @param vocabulary {@link Model} containing all statements of the vocabulary. * @param resource {@link Resource} for which the first object literal shall be returned. * @param properties the properties that shall be used for finding a object literal * @return {@link Optional} string value that is potentially empty, if no object literal could * be found. */ private Optional<String> getFirstLiteralFor(Model vocabulary, Resource resource, IRI... properties) { assert vocabulary != null && resource != null; assert properties.length != 0; return getFirstLiteralFor(vocabulary, vocabularyOptions.preferredLanguage(), resource, properties); } /** * Gets the first object literal that can be found using the given properties for the given * {@link Resource} in the vocabulary {@link Model}. * * @param vocabulary {@link Model} containing all statements of the vocabulary. * @param language the language that is preferred. * @param resource {@link Resource} for which the first object literal shall be returned. * @param properties the properties that shall be used for finding a object literal * @return {@link Optional} string value that is potentially empty, if no object literal could * be found. */ private Optional<String> getFirstLiteralFor(Model vocabulary, String language, Resource resource, IRI... properties) { assert vocabulary != null && resource != null; assert properties.length != 0; for (IRI property : properties) { Map<String, String> literalMap = new HashMap<>(); vocabulary.filter(resource, property, (IRI) null).objects().stream() .filter(objectValue -> objectValue instanceof Literal).forEach(objectValue -> { Literal literal = (Literal) objectValue; Optional<String> optionalLanguageCode = literal.getLanguage(); if (optionalLanguageCode.isPresent()) { literalMap.put(optionalLanguageCode.get(), literal.getLabel()); } else { literalMap.put(null, literal.getLabel()); } }); if (literalMap.containsKey(language)) { return Optional.of(literalMap.get(language)); } else if (literalMap.containsKey(null)) { return Optional.of(literalMap.get(null)); } else if (literalMap.containsKey("en")) { return Optional.of(literalMap.get("en")); } else { if (!literalMap.isEmpty()) { return Optional.of(literalMap.values().iterator().next()); } } } return Optional.empty(); } /** * Generates the Java class for the given input and options. * * @throws VocabularyNotFoundException if the vocabulary cannot be found o accessed. */ public void generate() throws VocabularyNotFoundException, SchemaGenerationException { generate("."); } /** * Generates the Java class for the given input and options. * * @param workingDir the working directory. * @throws VocabularyNotFoundException if the vocabulary cannot be found o accessed. */ public void generate(String workingDir) throws VocabularyNotFoundException, SchemaGenerationException { Model vocabularyModel; try { vocabularyModel = getVocabularyModel(vocabularyOptions.input(), vocabularyOptions.inputFormat(), workingDir); } catch (VocabularyNotFoundException e) { if (vocabularyOptions.hasAlternativeInput()) { vocabularyModel = getVocabularyModel(vocabularyOptions.alternativeInput(), vocabularyOptions.inputFormat(), workingDir); } else { throw e; } } Resource vocabularyRootResource = getRootResource(vocabularyModel); if (vocabularyRootResource != null) { formattingMap .put("vocab-label", getFirstLiteralFor(vocabularyModel, vocabularyOptions.preferredLanguage(), vocabularyRootResource, LABEL_PROPERTIES) .orElse(vocabularyOptions.className())); formattingMap.put("vocab-comment", getFirstLiteralFor(vocabularyModel, vocabularyOptions.preferredLanguage(), vocabularyRootResource, COMMENT_PROPERTIES).orElse("")); formattingMap.put("vocab-version-info", getFirstLiteralFor(vocabularyModel, vocabularyOptions.preferredLanguage(), vocabularyRootResource, VOCAB_VERSION_PROPERTIES).orElse("unknown")); formattingMap.put("namespace", vocabularyRootResource.stringValue()); } else { formattingMap.put("vocab-label", vocabularyOptions.className()); formattingMap.put("vocab-comment", ""); formattingMap.put("vocab-version-info", "unknown"); formattingMap.put("namespace", vocabularyOptions.baseNamespace() != null ? vocabularyOptions.baseNamespace() : "#"); } // Output Java class File classFile = prepareFileForGeneration(); try (PrintWriter printWriter = new PrintWriter(new FileOutputStream(classFile))) { Map<String, String> valueMap = new HashMap<>(formattingMap); valueMap.put("header", generateHeader(vocabularyModel)); valueMap.put("class", generateClass(vocabularyRootResource, vocabularyModel)); printWriter.write(stringMapFormatter.format(DEFAULT_GLOBAL_TEMPLATE, valueMap)); } catch (FileNotFoundException | StringMapFormatter.StringMapFormatterException e) { throw new SchemaGenerationException(e); } } /** * Generates the header of the Java class that shall be created. * * @param vocabModel {@link Model} that contains all statements of the vocabulary for which the * class shall be created. * @return the header of the Java class. * @throws StringMapFormatter.StringMapFormatterException if the formatting fails. */ private String generateHeader(Model vocabModel) throws StringMapFormatter.StringMapFormatterException { assert vocabModel != null; Map<String, String> headerValueMap = new HashMap<>(formattingMap); // Package headerValueMap.put("package", vocabularyOptions.classPackage() != null ? stringMapFormatter.format("package %(name)s;%n", Collections.singletonMap("name", vocabularyOptions.classPackage())) : ""); // Imports StringBuilder importsStringBuilder = new StringBuilder(); Map<String, String> importMap = new HashMap<>(formattingMap); for (String packageToImport : sesamePackagesToImport) { importMap.put("pack", packageToImport); importsStringBuilder .append(stringMapFormatter.format("import %(sesame-package-base)s.%(pack)s;%n", importMap)); } headerValueMap.put("imports", importsStringBuilder.toString()); // Class comment headerValueMap.put("class-comment", stringMapFormatter.format(DEFAULT_CLASS_COMMENT_TEMPLATE, formattingMap)); return stringMapFormatter.format(DEFAULT_HEADER_TEMPLATE, headerValueMap); } /** * Generates the class. * * @param vocabModel {@link Model} that contains all statements of the vocabulary for which the * class shall be created. * @return the class in form of a string. * @throws StringMapFormatter.StringMapFormatterException if the formatting fails. */ private String generateClass(Resource rootResource, Model vocabModel) throws StringMapFormatter.StringMapFormatterException, SchemaGenerationException { assert vocabModel != null; Map<String, String> classValueMap = new HashMap<>(formattingMap); classValueMap.put("class-body", generateClassBody(rootResource, vocabModel)); return stringMapFormatter.format(DEFAULT_CLASS_TEMPLATE, classValueMap); } /** * Tries to detect the common namespace of all subjects of the given vocabulary. If the root * resource is given, it will be ignored in the list for detection. * * @param rootResource the root resource of the vocabulary, or null if unknown. * @return detected the common namespace of all subjects of the given vocabulary or null, if * no commonalities can be detected. */ private String detectBaseNamespace(Model vocabModel, Resource rootResource) { String baseNamespace = StringUtils .getCommonPrefix(vocabModel.subjects().stream().filter(resource -> !resource.equals(rootResource)) .map(Resource::stringValue).collect(Collectors.toList()).toArray(new String[0])); return !baseNamespace.isEmpty() ? baseNamespace : null; } /** * Generates the body of the class that shall be created. At first the base namespace will be * detected. If the base namespace is given in the {@link VocabularyOptions}, this namespace * will be preferred. An {@link SchemaGenerationException} will be thrown, if the namespace * cannot be detected automatically. * * @param vocabModel {@link Model} that contains all statements of the vocabulary for which the * class shall be created. * @return the body of the class in form of a string. * @throws SchemaGenerationException if the body of the class cannot be generated. */ private String generateClassBody(Resource rootResource, Model vocabModel) throws StringMapFormatter.StringMapFormatterException, SchemaGenerationException { assert vocabModel != null; Map<String, String> classBodyValueMap = new HashMap<>(formattingMap); // Detect the base namespace String basenameSpace; if (vocabularyOptions.baseNamespace() != null) { basenameSpace = vocabularyOptions.baseNamespace(); } else { basenameSpace = detectBaseNamespace(vocabModel, rootResource); if (basenameSpace == null) { if (rootResource != null) { basenameSpace = rootResource.stringValue(); } else { throw new SchemaGenerationException( "The base namespace could not be detected. Please specify it manually."); } } } // Detect the preferred prefix String prefix = null; if (vocabularyOptions.preferredPrefix() != null) { prefix = vocabularyOptions.preferredPrefix(); } else { for (Namespace ns : vocabModel.getNamespaces()) { if (ns.getName().equals(basenameSpace)) { prefix = ns.getPrefix(); break; } } } // Declaration of resources Pattern nsPattern = Pattern.compile(String.format("%s(.+)", basenameSpace)); TreeMap<String, Resource> resourceMap = new TreeMap<>(); for (Resource subject : vocabModel.subjects()) { Matcher matcher = nsPattern.matcher(subject.stringValue()); if (matcher.find()) { resourceMap.put(JavaIdentifier.getJavaIdFrom(matcher.group(1)), subject); } } // Definition and declaration of resources. StringBuilder declarationStringBuilder = new StringBuilder(); StringBuilder definitionStringBuilder = new StringBuilder( stringMapFormatter.format(DEFAULT_VALUE_FACTORY_TEMPLATE, formattingMap)); for (Map.Entry<String, Resource> entry : resourceMap.entrySet()) { Resource subject = entry.getValue(); Map<String, String> valueMap = new HashMap<>(formattingMap); valueMap.put("resource-name", entry.getKey()); valueMap.put("resource-iri", subject.stringValue()); valueMap.put("resource-label", getFirstLiteralFor(vocabModel, subject, LABEL_PROPERTIES).orElse(entry.getKey())); valueMap.put("resource-comment", getFirstLiteralFor(vocabModel, subject, COMMENT_PROPERTIES).orElse("")); declarationStringBuilder.append(stringMapFormatter.format(DEFAULT_RESOURCE_DEC_TEMPLATE, valueMap)); definitionStringBuilder.append(stringMapFormatter.format(DEFAULT_RESOURCE_DEF_TEMPLATE, valueMap)); } classBodyValueMap.put("resource-declarations", declarationStringBuilder.toString()); classBodyValueMap.put("resource-definitions", definitionStringBuilder.toString()); // Namespace and prefix. Map<String, String> valueMap = new HashMap<>(formattingMap); valueMap.put("namespace-iri", basenameSpace); valueMap.put("prefix", prefix); classBodyValueMap.put("namespace", stringMapFormatter.format(DEFAULT_NAMESPACE_TEMPLATE, valueMap)); classBodyValueMap.put("prefix", prefix == null ? "" : stringMapFormatter.format(DEFAULT_PREFIX_TEMPLATE, valueMap)); return stringMapFormatter.format(DEFAULT_CLASS_BODY_TEMPLATE, classBodyValueMap); } }