diff --git a/pom.xml b/pom.xml index cd299dc..438e41d 100644 --- a/pom.xml +++ b/pom.xml @@ -33,15 +33,15 @@ org.junit.jupiter - junit-jupiter + junit-jupiter-engine 5.8.2 test UTF-8 - 1.8 - 1.8 + 11 + 11 Vocabulary generator for Eclipse RDF4j and Apache Jena @@ -50,6 +50,16 @@ src/test/resources + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + + **/*Tests.java + + + org.apache.maven.plugins maven-jar-plugin diff --git a/src/main/java/be/belgif/vocgen/Constant.java b/src/main/java/be/belgif/vocgen/Constant.java new file mode 100644 index 0000000..c552965 --- /dev/null +++ b/src/main/java/be/belgif/vocgen/Constant.java @@ -0,0 +1,27 @@ +package be.belgif.vocgen; + +public class Constant { + private String name; + private String label; + + public Constant(String name, String label) { + this.name = name; + this.label = label; + } + + public String getName() { + return name; + } + + public String getLabel() { + return label; + } + + public void setName(String name) { + this.name = name; + } + + public void setLabel(String label) { + this.label = label; + } +} diff --git a/src/main/java/be/belgif/vocgen/Main.java b/src/main/java/be/belgif/vocgen/Main.java index d81ea6b..478eae0 100644 --- a/src/main/java/be/belgif/vocgen/Main.java +++ b/src/main/java/be/belgif/vocgen/Main.java @@ -1,477 +1,494 @@ -/* - * Copyright (c) 2017, Bart Hanssens - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package be.belgif.vocgen; - -import freemarker.template.Configuration; -import freemarker.template.Template; -import freemarker.template.TemplateException; -import freemarker.template.TemplateExceptionHandler; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.ParseException; - -import org.eclipse.rdf4j.model.BNode; -import org.eclipse.rdf4j.model.Literal; -import org.eclipse.rdf4j.model.Model; -import org.eclipse.rdf4j.model.Resource; -import org.eclipse.rdf4j.model.impl.SimpleValueFactory; -import org.eclipse.rdf4j.model.vocabulary.OWL; -import org.eclipse.rdf4j.model.vocabulary.RDF; -import org.eclipse.rdf4j.model.vocabulary.RDFS; -import org.eclipse.rdf4j.rio.RDFFormat; -import org.eclipse.rdf4j.rio.Rio; - -import static java.util.function.Function.identity; - -/** - * Quick Vocabulary class generator for Eclipse RDF4j - * - * @author Bart.Hanssens - */ -public class Main { - private Set owlClasses; - private Set owlProperties; - private Set owlIndivs; - - /** - * Option builder - * - * @param c short option name - * @param s long option name - * @param desc description - * @return option - */ - private static Option opt(String c, String s, String desc) { - return Option.builder(c).longOpt(s).required().hasArg().desc(desc).build(); - } - - /** - * Command line options - */ - private static final Options OPTS = new Options() - .addOption(opt("f", "file", "OWL vocabulary file in TTL format")) - .addOption(opt("d", "doc", "Documentation URL")) - .addOption(Option.builder("a").longOpt("author").hasArg().desc( "Name of the java class author").required(false).build()) - .addOption(opt("n", "ns", "Namespace URL")) - .addOption(opt("s", "short", "Short vocabulary name")) - .addOption(opt("l", "long", "Long vocabulary name")) - .addOption(opt("p", "prefix", "Namespace prefix")) - .addOption(opt("t", "template", "one of: rdf4j, jena")) - .addOption(Option.builder("sc").longOpt("snake-case").desc("use all caps snake case constants instead of as-is local names").required(false).build()) - .addOption(Option.builder("jp").longOpt("package").hasArg().desc( "java package").build()) - .addOption(Option.builder("o").longOpt("output-dir").required(false).hasArg().desc( "output directory").build()) - .addOption(Option.builder("c").longOpt("copyright").hasArg().required(false).desc("file containing the copyright snippet").build()) - .addOption(Option.builder("cp").longOpt("searchClasspath").hasArg(false).desc("look for input files on classpath, then in filesystem").required(false).build()); - - - - - /** - * Get data for template from command line - * - * @param cmd command line - * @return map with common data - */ - private static Map getData(CommandLine cmd) { - Map m = new HashMap(); - if (cmd.hasOption("a")){ - m.put("author", cmd.getOptionValue('a')); - } - m.put("fullname", cmd.getOptionValue('l')); - m.put("url", cmd.getOptionValue('d')); - m.put("nsAlias", cmd.getOptionValue('s')); - m.put("prefix", cmd.getOptionValue('p')); - return m; - } - - /** - * Capitalize and transform string to a valid constant for RDF4J, i.e. ALL_CAPS_SNAKE_CASE - * - * @param s name of the class / property - * @return normalized string - */ - private static String snakeCase(String s) { - // namespace and prefix are already used in RDF4J vocabulary class - if (s.equals("namespace") || s.equals("prefix")) { - return s.toUpperCase() + "_PROP"; - } - return s.replaceFirst("^_", "") - .replaceAll("-", "_") - .replaceAll("([a-z]+)([A-Z])", "$1_$2") - .toUpperCase(); - } - - /** - * Get local (without namespace) class names mapped to constants for RDF4J , i.e. ALL_CAPS_SNAKE_CASE - * - * @param m RDF Model - * @param base namespace URI as string - * @return map with class names and constants - */ - private SortedMap getSnakeCaseClasses(Model m, String base) { - SortedMap classes = new TreeMap(); - getClasses(m, base).forEach(c -> classes.put(c, snakeCase(c))); - return classes; - } - - - /** - * Get local (without namespace) properties mapped to constants for RDF4J, i.e. ALL_CAPS_SNAKE_CASE - * - * @param m RDF Model - * @param base namespace URI as string - * @param classes - * @return map with properties and constants - */ - private SortedMap getSnakeCaseProps(Model m, String base, - Map classes) { - SortedMap props = new TreeMap(); - // prevent duplicates when uppercasing property "name" and class "Name" to NAME - getProps(m, base).forEach(p -> { - String cte = snakeCase(p); - String key = classes.containsValue(cte) ? cte + "_PROP" : cte; - props.put(p, key); - }); - return props; - } - - /** - * Get local (without namespace) individuals mapped to constants for RDF4J, i.e. ALL_CAPS_SNAKE_CASE. - * - * @param m RDF Model - * @param base namespace URI as string - * @return map with individuals - */ - private Map getSnakeCaseIndivs(Model m, String base, - Map classes, Map props) { - Map indivs = new TreeMap(); - // prevent duplicates when uppercasing property "name" and class "Name" to NAME - getIndivs(m, base).forEach(p -> { - String cte = snakeCase(p); - String key = (classes.containsValue(cte) || props.containsValue(cte)) ? cte + "_INDIV" : cte; - indivs.put(p, key); - }); - return indivs; - } - - /** - * Get a set of local (without namespace) class names - * - * @param m RDF Model - * @param base namespace URI as string - * @return set of local class names - */ - private Set getClasses(Model m, String base) { - owlClasses = m.filter(null, RDF.TYPE, OWL.CLASS).subjects(); - owlClasses.addAll(m.filter(null, RDF.TYPE, RDFS.CLASS).subjects()); - - // discard classes outside namespace - owlClasses.removeIf(s -> !s.toString().startsWith(base)); - - // add subclasses - owlClasses.addAll(owlClasses.stream() - .flatMap(s -> m.filter(null, RDFS.SUBCLASSOF, s).subjects().stream()) - .collect(Collectors.toSet())); - - // discard named individuals - owlClasses.removeAll(m.filter(null, RDF.TYPE, OWL.NAMEDINDIVIDUAL).subjects()); - - // discard blank nodes - owlClasses.removeIf(c -> c instanceof BNode); - - // discard blank nodes and return class names (without prefix) - return owlClasses.stream() - .map(c -> c.stringValue().replaceFirst(base, "")) - .collect(Collectors.toSet()); - } - - /** - * Get a set of local (without namespace) properties - * - * @param m RDF Model - * @param base namespace URI as string - * @return set of local property names - */ - private Set getProps(Model m, String base) { - owlProperties = m.filter(null, RDF.TYPE, OWL.OBJECTPROPERTY).subjects(); - owlProperties.addAll(m.filter(null, RDF.TYPE, OWL.DATATYPEPROPERTY).subjects()); - owlProperties.addAll(m.filter(null, RDF.TYPE, RDF.PROPERTY).subjects()); - - // add subproperties - owlProperties.addAll(owlProperties.stream() - .flatMap(s -> m.filter(null, RDFS.SUBPROPERTYOF, s).subjects().stream()) - .collect(Collectors.toSet())); - - return owlProperties.stream() - .filter(p -> p.stringValue().startsWith(base)) // only use properties from the base namespace - .map(p -> p.stringValue().replaceFirst(base, "")) - .collect(Collectors.toSet()); - } - - /** - * Get a set of individuals (without namespace) - * - * @param m RDF Model - * @param base namespace URI as string - */ - private Set getIndivs(Model m, String base) { - owlIndivs = m.filter(null, RDF.TYPE, OWL.NAMEDINDIVIDUAL).subjects(); - owlIndivs.addAll(m.filter(null, RDF.TYPE, OWL.INDIVIDUAL).subjects()); - - // check for subclasses derived from other classes in this ontology - owlIndivs.addAll(owlClasses.stream() - .flatMap(s -> m.filter(null, RDF.TYPE, s).subjects().stream()) - .collect(Collectors.toSet())); - - // discard blank nodes - owlIndivs.removeIf(c -> c instanceof BNode); - - // return indiv names (without prefix) - return owlIndivs.stream() - .filter(p -> p.stringValue().startsWith(base)) // only use individuals from the base namespace - .map(c -> c.stringValue().replaceFirst(base, "")) - .collect(Collectors.toSet()); - } - - /** - * Get deprecated classes and properties - * - * @param m model - * @param base namespace URI as string - * @return set of deprecated classes / properties as string - */ - private static Set getDeprecated(Model m, String base) { - SimpleValueFactory f = SimpleValueFactory.getInstance(); - Literal tr = f.createLiteral(true); - - Set deprecated = m.filter(null, OWL.DEPRECATEDCLASS, tr).subjects(); - deprecated.addAll(m.filter(null, OWL.DEPRECATEDPROPERTY, tr).subjects()); - deprecated.addAll(m.filter(null, OWL.DEPRECATED, tr).subjects()); - - return deprecated.stream() - .map(d -> d.stringValue().replaceFirst(base, "")) - .collect(Collectors.toSet()); - } - - /** - * Read an OWL file into and RDF model - * - * @param file input file - * @param base namespace URI - * @return RDF model - * @throws IOException - */ - private static Model getModel(String file, String base, boolean searchForFileOnClasspath) throws IOException { - if (searchForFileOnClasspath) { - try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(file)) { - if (in != null) { - RDFFormat fmt = Rio.getParserFormatForFileName(file).orElse(RDFFormat.TURTLE); - return Rio.parse(in, base, fmt); - } - } - } - InputStream in = new FileInputStream(file); - RDFFormat fmt = Rio.getParserFormatForFileName(file).orElse(RDFFormat.TURTLE); - return Rio.parse(in, base, fmt); - } - - /** - * Write output for Rdf4J or Jena - * - * @param cfg freemarker configuration - * @param template project: jena or rdf4j - * @param map template data - * @throws IOException - * @throws TemplateException - */ - private static void source(Configuration cfg, TemplateType template, Map map, File outputDir) throws IOException, TemplateException { - Template ftl = cfg.getTemplate(template.toString().toLowerCase() + ".ftl"); - String className = (String) map.get("nsAlias"); - if (!outputDir.exists()) { - System.out.println("creating output directory " + outputDir.getAbsolutePath()); - if (!outputDir.mkdirs()) { - throw new IOException("Unable to create output directory"); - } - } - File outFile = new File(outputDir, className + ".java"); - try (Writer out = new FileWriter(outFile)) { - ftl.process(map, out); - } - } - - /** - * Get Freemarker configuration. - * - * @return freemarker configuration - */ - private static Configuration getConfig() { - Configuration cfg = new Configuration(Configuration.VERSION_2_3_25); - cfg.setClassLoaderForTemplateLoading(Main.class.getClassLoader(), "be/belgif/vocgen"); - cfg.setDefaultEncoding("UTF-8"); - cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); - - return cfg; - } - - /** - * Write java class for RDF4J - * - * @param cfg freemarker configuration - * @param m model - * @param base namespace URI as string - * @param root template data - * @throws IOException - * @throws TemplateException - */ - private void writeVocab(Configuration cfg, Model m, String base, Map root, File outputDir, boolean snakeCase, TemplateType template) - throws IOException, TemplateException { - Map classes = snakeCase - ? getSnakeCaseClasses(m, base) - : getSafeNameMap(getClasses(m, base)); - Map props = snakeCase - ? getSnakeCaseProps(m, base, classes) - : getSafeNameMap(getProps(m, base)); - Map indivs = snakeCase - ? getSnakeCaseIndivs(m, base, classes, props) - : getSafeNameMap(getIndivs(m, base)); - - root.put("classMap", classes); - root.put("propMap", props); - root.put("indivMap", indivs); - - source(cfg, template, root, outputDir); - } - - private static Map getSafeNameMap(Set localNames) { - return localNames.stream() - .collect(Collectors.toMap(identity(), Main::toSafeJavaName)); - } - - private static Pattern javaLanguageKeywords = Pattern.compile( - "(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|" - + "break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|" - + "instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|" - + "void|class|finally|long|strictfp|volatile|const|float|native|super|while)" - ); - - private static String toSafeJavaName(String localName){ - Matcher m = javaLanguageKeywords.matcher(localName); - if (m.matches()){ - return localName + "_"; - } - return localName - .replaceAll("^_", "") - .replaceAll("[+\\-~*#&%§!]", "_"); - } - - - - - /** - * Main - * - * @param args - * @throws java.io.IOException - * @throws freemarker.template.TemplateException - */ - public static void main(String[] args) throws IOException, TemplateException { - Main main = new Main(); - try { - main.generateVocabulary(args); - } catch (ParseException ex) { - System.exit(-1); - } - - - } - - public void generateVocabulary(String[] args) throws ParseException, IOException, TemplateException { - CommandLine cmd; - CommandLineParser parser = new DefaultParser(); - try { - cmd = parser.parse(OPTS, args); - } catch (ParseException e) { - HelpFormatter help = new HelpFormatter(); - System.out.println(e.getMessage()); - help.printHelp("VocabGen", OPTS); - throw e; - } - String ontologyFile = cmd.getOptionValue('f'); - String base = cmd.getOptionValue('n'); // namespace URI - String javaPackage = Optional.ofNullable(cmd.getOptionValue("jp")).orElse("org.eclipse.rdf4j.model.vocabulary"); - String outputDirStr = Optional.ofNullable(cmd.getOptionValue("o")).orElse("."); - boolean snakeCase = cmd.hasOption("sc"); - File outputDir = new File(outputDirStr); - boolean searchFilesOnClasspath = cmd.hasOption("cp"); - String copyrightFileName = cmd.getOptionValue("c"); - String copyright = null; - copyright = getCopyright(copyrightFileName, copyright, searchFilesOnClasspath); - TemplateType template = TemplateType.valueOf(cmd.getOptionValue("t").toUpperCase()); - Model m = getModel(ontologyFile, base, searchFilesOnClasspath); - Set deprecated = getDeprecated(m, base); - // Template - Configuration cfg = getConfig(); - Map root = getData(cmd); - root.put("nsURL", base); - root.put("depr", deprecated); - root.put("package", javaPackage); - root.put("copyright", copyright); - writeVocab(cfg, m, base, root, outputDir, snakeCase, template); - } - - private String getCopyright(String copyrightFileName, String copyright, boolean searchFilesOnClasspath) throws IOException { - if (copyrightFileName != null){ - if (searchFilesOnClasspath) { - try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(copyrightFileName)) { - if (in != null) { - return new String(in.readAllBytes(), StandardCharsets.UTF_8); - } - } - } - return Files.readString(Path.of(copyrightFileName), StandardCharsets.UTF_8); - } - return null; - } - - private static enum TemplateType { - RDF4J, JENA - } +/* + * Copyright (c) 2017, Bart Hanssens + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package be.belgif.vocgen; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; +import org.apache.commons.cli.*; +import org.eclipse.rdf4j.model.*; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; +import org.eclipse.rdf4j.model.vocabulary.OWL; +import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.model.vocabulary.RDFS; +import org.eclipse.rdf4j.rio.RDFFormat; +import org.eclipse.rdf4j.rio.Rio; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.joining; + +/** + * Quick Vocabulary class generator for Eclipse RDF4j + * + * @author Bart.Hanssens + */ +public class Main { + private Set owlClasses; + private Set owlProperties; + private Set owlIndivs; + + /** + * Option builder + * + * @param c short option name + * @param s long option name + * @param desc description + * @return option + */ + private static Option opt(String c, String s, String desc) { + return Option.builder(c).longOpt(s).required().hasArg().desc(desc).build(); + } + + /** + * Command line options + */ + private static final Options OPTS = new Options() + .addOption(opt("f", "file", "OWL vocabulary file in TTL format")) + .addOption(opt("d", "doc", "Documentation URL")) + .addOption(Option.builder("a").longOpt("author").hasArg().desc( "Name of the java class author").required(false).build()) + .addOption(opt("n", "ns", "Namespace URL")) + .addOption(opt("s", "short", "Short vocabulary name")) + .addOption(opt("l", "long", "Long vocabulary name")) + .addOption(opt("p", "prefix", "Namespace prefix")) + .addOption(opt("t", "template", "one of: rdf4j, jena, plain")) + .addOption(Option.builder("sc").longOpt("snake-case").desc("use all caps snake case constants instead of as-is local names").required(false).build()) + .addOption(Option.builder("jp").longOpt("package").hasArg().desc( "java package").build()) + .addOption(Option.builder("o").longOpt("output-dir").required(false).hasArg().desc( "output directory").build()) + .addOption(Option.builder("c").longOpt("copyright").hasArg().required(false).desc("file containing the copyright snippet").build()) + .addOption(Option.builder("cp").longOpt("searchClasspath").hasArg(false).desc("look for input files on classpath, then in filesystem").required(false).build()); + + + + + /** + * Get data for template from command line + * + * @param cmd command line + * @return map with common data + */ + private static Map getData(CommandLine cmd) { + Map m = new HashMap(); + if (cmd.hasOption("a")){ + m.put("author", cmd.getOptionValue('a')); + } + m.put("fullname", cmd.getOptionValue('l')); + m.put("url", cmd.getOptionValue('d')); + m.put("nsAlias", cmd.getOptionValue('s')); + m.put("prefix", cmd.getOptionValue('p')); + return m; + } + + /** + * Capitalize and transform string to a valid constant for RDF4J, i.e. ALL_CAPS_SNAKE_CASE + * + * @param s name of the class / property + * @return normalized string + */ + private static String snakeCase(String s) { + // namespace and prefix are already used in RDF4J vocabulary class + if (s.equals("namespace") || s.equals("prefix")) { + return s.toUpperCase() + "_PROP"; + } + return s.replaceFirst("^_", "") + .replaceAll("-", "_") + .replaceAll("([a-z]+)([A-Z])", "$1_$2") + .toUpperCase(); + } + + /** + * Get local (without namespace) class names mapped to constants for RDF4J , i.e. ALL_CAPS_SNAKE_CASE + * + * @param m RDF Model + * @param base namespace URI as string + * @return map with class names and constants + */ + private Map getSnakeCaseClasses(Model m, String base) { + Map classes = new TreeMap<>(); + getClasses(m, base).forEach(c -> classes.put(c.getName(), new Constant(snakeCase(c.getName()), c.getLabel()))); + return classes; + } + + + /** + * Get local (without namespace) properties mapped to constants for RDF4J, i.e. ALL_CAPS_SNAKE_CASE + * + * @param m RDF Model + * @param base namespace URI as string + * @param classes + * @return map with properties and constants + */ + private Map getSnakeCaseProps(Model m, String base, + Map classes) { + Map props = new TreeMap<>(); + // prevent duplicates when uppercasing property "name" and class "Name" to NAME + getProps(m, base).forEach(p -> { + String cte = snakeCase(p.getName()); + String key = classes.containsValue(cte) ? cte + "_PROP" : cte; + props.put(p.getName(), new Constant(key, p.getLabel())); + }); + return props; + } + + /** + * Get local (without namespace) individuals mapped to constants for RDF4J, i.e. ALL_CAPS_SNAKE_CASE. + * + * @param m RDF Model + * @param base namespace URI as string + * @return map with individuals + */ + private Map getSnakeCaseIndivs(Model m, String base, + Map classes, Map props) { + Map indivs = new TreeMap<>(); + // prevent duplicates when uppercasing property "name" and class "Name" to NAME + getIndivs(m, base).forEach(p -> { + String cte = snakeCase(p.getName()); + String key = (classes.containsValue(cte) || props.containsValue(cte)) ? cte + "_INDIV" : cte; + indivs.put(p.getName(), new Constant(key, p.getLabel())); + }); + return indivs; + } + + /** + * Get a set of local (without namespace) class names + * + * @param m RDF Model + * @param base namespace URI as string + * @return set of local class names + */ + private Set getClasses(Model m, String base) { + owlClasses = m.filter(null, RDF.TYPE, OWL.CLASS).subjects(); + owlClasses.addAll(m.filter(null, RDF.TYPE, RDFS.CLASS).subjects()); + + // discard classes outside namespace + owlClasses.removeIf(s -> !s.toString().startsWith(base)); + + // add subclasses + owlClasses.addAll(owlClasses.stream() + .flatMap(s -> m.filter(null, RDFS.SUBCLASSOF, s).subjects().stream()) + .collect(Collectors.toSet())); + + // discard named individuals + owlClasses.removeAll(m.filter(null, RDF.TYPE, OWL.NAMEDINDIVIDUAL).subjects()); + + // discard blank nodes + owlClasses.removeIf(c -> c instanceof BNode); + + // discard blank nodes and return class names (without prefix) + return owlClasses.stream() + .map(c -> new Constant(c.stringValue().replaceFirst(base, ""), getLabel(m, c))) + .collect(Collectors.toSet()); + } + + private String getLabel(Model m, Resource resource) { + Set labels =m.filter(resource, RDFS.LABEL, null).objects(); + if (labels.isEmpty()) { + return null; + } + if (labels.size() == 1) { + return labels.stream().map(Value::stringValue).findFirst().get(); + } + return labels.stream().map(Value::stringValue).collect(joining(" or ")); + } + + /** + * Get a set of local (without namespace) properties + * + * @param m RDF Model + * @param base namespace URI as string + * @return set of local property names + */ + private Set getProps(Model m, String base) { + owlProperties = m.filter(null, RDF.TYPE, OWL.OBJECTPROPERTY).subjects(); + owlProperties.addAll(m.filter(null, RDF.TYPE, OWL.DATATYPEPROPERTY).subjects()); + owlProperties.addAll(m.filter(null, RDF.TYPE, RDF.PROPERTY).subjects()); + + // add subproperties + owlProperties.addAll(owlProperties.stream() + .flatMap(s -> m.filter(null, RDFS.SUBPROPERTYOF, s).subjects().stream()) + .collect(Collectors.toSet())); + + return owlProperties.stream() + .filter(p -> p.stringValue().startsWith(base)) // only use properties from the base namespace + .map(c -> new Constant(c.stringValue().replaceFirst(base, ""), getLabel(m, c))) + .collect(Collectors.toSet()); + } + + /** + * Get a set of individuals (without namespace) + * + * @param m RDF Model + * @param base namespace URI as string + */ + private Set getIndivs(Model m, String base) { + owlIndivs = m.filter(null, RDF.TYPE, OWL.NAMEDINDIVIDUAL).subjects(); + owlIndivs.addAll(m.filter(null, RDF.TYPE, OWL.INDIVIDUAL).subjects()); + + // check for subclasses derived from other classes in this ontology + owlIndivs.addAll(owlClasses.stream() + .flatMap(s -> m.filter(null, RDF.TYPE, s).subjects().stream()) + .collect(Collectors.toSet())); + + // avoid duplication + owlIndivs.removeAll(owlClasses); + owlIndivs.removeAll(owlProperties); + + // discard blank nodes + owlIndivs.removeIf(c -> c instanceof BNode); + + // return indiv names (without prefix) + return owlIndivs.stream() + .filter(p -> p.stringValue().startsWith(base)) // only use individuals from the base namespace + .map(c -> new Constant(c.stringValue().replaceFirst(base, ""), getLabel(m, c))) + .collect(Collectors.toSet()); + } + + /** + * Get deprecated classes and properties + * + * @param m model + * @param base namespace URI as string + * @return set of deprecated classes / properties as string + */ + private static Set getDeprecated(Model m, String base) { + SimpleValueFactory f = SimpleValueFactory.getInstance(); + Literal tr = f.createLiteral(true); + + Set deprecated = m.filter(null, OWL.DEPRECATEDCLASS, tr).subjects(); + deprecated.addAll(m.filter(null, OWL.DEPRECATEDPROPERTY, tr).subjects()); + deprecated.addAll(m.filter(null, OWL.DEPRECATED, tr).subjects()); + + return deprecated.stream() + .map(d -> d.stringValue().replaceFirst(base, "")) + .collect(Collectors.toSet()); + } + + /** + * Read an OWL file into and RDF model + * + * @param file input file + * @param base namespace URI + * @return RDF model + * @throws IOException + */ + private static Model getModel(String file, String base, boolean searchForFileOnClasspath) throws IOException { + if (searchForFileOnClasspath) { + try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(file)) { + if (in != null) { + RDFFormat fmt = Rio.getParserFormatForFileName(file).orElse(RDFFormat.TURTLE); + return Rio.parse(in, base, fmt); + } + } + } + InputStream in = new FileInputStream(file); + RDFFormat fmt = Rio.getParserFormatForFileName(file).orElse(RDFFormat.TURTLE); + return Rio.parse(in, base, fmt); + } + + /** + * Write output for Rdf4J or Jena + * + * @param cfg freemarker configuration + * @param template project: jena or rdf4j + * @param map template data + * @throws IOException + * @throws TemplateException + */ + private static void source(Configuration cfg, TemplateType template, Map map, File outputDir) throws IOException, TemplateException { + Template ftl = cfg.getTemplate(template.toString().toLowerCase() + ".ftl"); + String className = (String) map.get("nsAlias"); + if (!outputDir.exists()) { + System.out.println("creating output directory " + outputDir.getAbsolutePath()); + if (!outputDir.mkdirs()) { + throw new IOException("Unable to create output directory"); + } + } + File outFile = new File(outputDir, className + ".java"); + try (Writer out = new FileWriter(outFile)) { + ftl.process(map, out); + } + } + + /** + * Get Freemarker configuration. + * + * @return freemarker configuration + */ + private static Configuration getConfig() { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_25); + cfg.setClassLoaderForTemplateLoading(Main.class.getClassLoader(), "be/belgif/vocgen"); + cfg.setDefaultEncoding("UTF-8"); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + + return cfg; + } + + /** + * Write java class for RDF4J + * + * @param cfg freemarker configuration + * @param m model + * @param base namespace URI as string + * @param root template data + * @throws IOException + * @throws TemplateException + */ + private void writeVocab(Configuration cfg, Model m, String base, Map root, File outputDir, boolean snakeCase, TemplateType template) + throws IOException, TemplateException { + Map classes = snakeCase + ? getSnakeCaseClasses(m, base) + : getSafeNameMap(getClasses(m, base)); + Map props = snakeCase + ? getSnakeCaseProps(m, base, classes) + : getSafeNameMap(getProps(m, base)); + Map indivs = snakeCase + ? getSnakeCaseIndivs(m, base, classes, props) + : getSafeNameMap(getIndivs(m, base)); + + setDefaultLabelsIfMissing(classes, (String) root.get("prefix")); + setDefaultLabelsIfMissing(props, (String) root.get("prefix")); + setDefaultLabelsIfMissing(indivs, (String) root.get("prefix")); + + root.put("classMap", classes); + root.put("propMap", props); + root.put("indivMap", indivs); + + source(cfg, template, root, outputDir); + } + + private void setDefaultLabelsIfMissing(Map constantMap, String nsPrefix) { + constantMap.values() + .stream() + .forEach(c -> { + if (c.getLabel() == null) { + c.setLabel(nsPrefix + ":" + c.getName()); + } + }); + } + + private static Map getSafeNameMap(Set localNames) { + return new TreeMap<>(localNames.stream() + .collect(Collectors.toMap(c -> c.getName(), c -> new Constant(toSafeJavaName(c.getName()), c.getLabel())))); + } + + private static Pattern javaLanguageKeywords = Pattern.compile( + "(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|" + + "break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|" + + "instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|" + + "void|class|finally|long|strictfp|volatile|const|float|native|super|while)" + ); + + private static String toSafeJavaName(String localName){ + Matcher m = javaLanguageKeywords.matcher(localName); + if (m.matches()){ + return localName + "_"; + } + return localName + .replaceAll("^_", "") + .replaceAll("[+\\-~*#&%§!]", "_"); + } + + + + + /** + * Main + * + * @param args + * @throws java.io.IOException + * @throws freemarker.template.TemplateException + */ + public static void main(String[] args) throws IOException, TemplateException { + Main main = new Main(); + try { + main.generateVocabulary(args); + } catch (ParseException ex) { + System.exit(-1); + } + + + } + + public void generateVocabulary(String[] args) throws ParseException, IOException, TemplateException { + CommandLine cmd; + CommandLineParser parser = new DefaultParser(); + try { + cmd = parser.parse(OPTS, args); + } catch (ParseException e) { + HelpFormatter help = new HelpFormatter(); + System.out.println(e.getMessage()); + help.printHelp("VocabGen", OPTS); + throw e; + } + String ontologyFile = cmd.getOptionValue('f'); + String base = cmd.getOptionValue('n'); // namespace URI + String javaPackage = Optional.ofNullable(cmd.getOptionValue("jp")).orElse("org.eclipse.rdf4j.model.vocabulary"); + String outputDirStr = Optional.ofNullable(cmd.getOptionValue("o")).orElse("."); + boolean snakeCase = cmd.hasOption("sc"); + File outputDir = new File(outputDirStr); + boolean searchFilesOnClasspath = cmd.hasOption("cp"); + String copyrightFileName = cmd.getOptionValue("c"); + String copyright = null; + copyright = getCopyright(copyrightFileName, copyright, searchFilesOnClasspath); + TemplateType template = TemplateType.valueOf(cmd.getOptionValue("t").toUpperCase()); + Model m = getModel(ontologyFile, base, searchFilesOnClasspath); + Set deprecated = getDeprecated(m, base); + // Template + Configuration cfg = getConfig(); + Map root = getData(cmd); + root.put("nsURL", base); + root.put("depr", deprecated); + root.put("package", javaPackage); + root.put("copyright", copyright); + writeVocab(cfg, m, base, root, outputDir, snakeCase, template); + } + + private String getCopyright(String copyrightFileName, String copyright, boolean searchFilesOnClasspath) throws IOException { + if (copyrightFileName != null){ + if (searchFilesOnClasspath) { + try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(copyrightFileName)) { + if (in != null) { + return new String(in.readAllBytes(), StandardCharsets.UTF_8); + } + } + } + return Files.readString(Path.of(copyrightFileName), StandardCharsets.UTF_8); + } + return null; + } + + private static enum TemplateType { + RDF4J, JENA, PLAIN + } } \ No newline at end of file diff --git a/src/main/resources/be/belgif/vocgen/jena.ftl b/src/main/resources/be/belgif/vocgen/jena.ftl index 79b00d8..8f1515a 100644 --- a/src/main/resources/be/belgif/vocgen/jena.ftl +++ b/src/main/resources/be/belgif/vocgen/jena.ftl @@ -33,25 +33,34 @@ public class ${nsAlias} { // Classes <#list classMap as class, const> + <#if const.label??> + /** ${const.label} **/ + <#if depr?seq_contains(class)> @Deprecated - public static final Resource ${const} = m.createResource(NS + "${class}"); + public static final Resource ${const.name} = m.createResource(NS + "${class}"); // Properties <#list propMap as prop, const> + <#if const.label??> + /** ${const.label} **/ + <#if depr?seq_contains(prop)> @Deprecated - public static final Property ${const} = m.createProperty(NS + "${prop}"); + public static final Property ${const.name} = m.createProperty(NS + "${prop}"); // Individuals <#list indivMap as ind, const> + <#if const.label??> + /** ${const.label} **/ + <#if depr?seq_contains(ind)> @Deprecated - public static final Individual ${ind} = m.createProperty(NS + "${ind}"); + public static final Individual ${const.name} = m.createProperty(NS + "${ind}"); } diff --git a/src/main/resources/be/belgif/vocgen/plain.ftl b/src/main/resources/be/belgif/vocgen/plain.ftl new file mode 100644 index 0000000..51c81bd --- /dev/null +++ b/src/main/resources/be/belgif/vocgen/plain.ftl @@ -0,0 +1,58 @@ +<#if copyright??> + ${copyright} + +package ${package}; + +/** + * Constants for the ${fullname}. + * + * @see ${fullname} + */ +public class ${nsAlias} { + + /** + * The ${nsAlias} namespace: ${nsURL} + */ + public static final String NS = "${nsURL}"; + + /** + * Returns the URI for this schema + * @return URI + */ + public static String getURI() { + return NS; + } + + // Classes + <#list classMap as class, const> + <#if const.label??> + /** ${const.label} **/ + + <#if depr?seq_contains(class)> + @Deprecated + + public static final String ${const.name} = NS + "${class}"; + + + // Properties + <#list propMap as prop, const> + <#if const.label??> + /** ${const.label} **/ + + <#if depr?seq_contains(prop)> + @Deprecated + + public static final String ${const.name} = NS + "${prop}"; + + + // Individuals + <#list indivMap as ind, const> + <#if const.label??> + /** ${const.label} **/ + + <#if depr?seq_contains(ind)> + @Deprecated + + public static final String ${const.name} = NS + "${ind}"; + +} diff --git a/src/main/resources/be/belgif/vocgen/rdf4j.ftl b/src/main/resources/be/belgif/vocgen/rdf4j.ftl index 8679af9..be08ee1 100644 --- a/src/main/resources/be/belgif/vocgen/rdf4j.ftl +++ b/src/main/resources/be/belgif/vocgen/rdf4j.ftl @@ -35,31 +35,37 @@ public class ${nsAlias} { // Classes <#list classMap as class, const> - /** ${prefix}:${class} */ + <#if const.label??> + /** ${const.label} **/ + <#if depr?seq_contains(class)> @Deprecated - public static final IRI ${const} = create("${class}"); + public static final IRI ${const.name} = create("${class}"); // Properties <#list propMap as prop, const> - /** ${prefix}:${prop} */ + <#if const.label??> + /** ${const.label} **/ + <#if depr?seq_contains(prop)> @Deprecated - public static final IRI ${const} = create("${prop}"); + public static final IRI ${const.name} = create("${prop}"); // Individuals <#list indivMap as indiv, const> - /** ${prefix}:${indiv} */ + <#if const.label??> + /** ${const.label} **/ + <#if depr?seq_contains(indiv)> @Deprecated - public static final IRI ${const} = create("${indiv}"); + public static final IRI ${const.name} = create("${indiv}"); diff --git a/src/test/java/be/belgif/vocgen/VocGenTests.java b/src/test/java/be/belgif/vocgen/VocGenTests.java index dfde593..4dfb985 100644 --- a/src/test/java/be/belgif/vocgen/VocGenTests.java +++ b/src/test/java/be/belgif/vocgen/VocGenTests.java @@ -6,8 +6,7 @@ import org.apache.commons.cli.ParseException; import org.junit.jupiter.api.Test; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; @@ -17,8 +16,26 @@ public class VocGenTests { Main main = new Main(); @Test - public void testNoArgs() { - assertThrows(MissingOptionException.class, () -> main.generateVocabulary(new String[] {})); + public void testNoArgs() throws IOException { + String testName = "testNoArgs"; + String filename = "System.out"; + Path outputFilePath = getOutputFilePath(testName, filename); + File outputFile = outputFilePath.toFile(); + File dir = outputFile.getParentFile(); + if (!dir.exists()) { + if (!dir.mkdirs()) { + fail("could not create output directory " + dir.getAbsolutePath()); + } + } + PrintStream outContent = new PrintStream(outputFile); + PrintStream originalSystemOut = System.out; + System.setOut(new PrintStream(outContent)); + try { + assertThrows(MissingOptionException.class, () -> main.generateVocabulary(new String[] {})); + assertFileEqualsExpected(testName, filename); + } finally { + System.setOut(originalSystemOut); + } } @Test @@ -57,6 +74,24 @@ public void testBasic_Jena() throws TemplateException, ParseException, IOExcepti assertFileEqualsExpected(testName, "RDF.java"); } + @Test + public void testBasic_Plain() throws TemplateException, ParseException, IOException { + String testName = "testBasic_Plain"; + main.generateVocabulary(new String[] { + "--file", "src/test/resources/rdf.ttl", + "--template", "plain", + "--long", "The RDF vocabulary", + "--short", "RDF", + "--ns", "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "--prefix", "rdf", + "--doc", "https://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/", + "--author", "The Author", + "--package", "org.w3.vocab", + "--output-dir", testOutputDir(testName), + }); + assertFileEqualsExpected(testName, "RDF.java"); + } + @Test public void testBasic_SearchClasspath() throws TemplateException, ParseException, IOException { String testName = "testBasic_SearchClasspath"; @@ -115,33 +150,60 @@ public void testBasic_Copyright_SearchClasspath() throws TemplateException, Pars assertFileEqualsExpected(testName, "RDF.java"); } + @Test + public void testBasic_NoLabels() throws TemplateException, ParseException, IOException { + String testName = "testBasic_NoLabels"; + main.generateVocabulary(new String[] { + "--searchClasspath", + "--file", "src/test/resources/rdf-nolabels.ttl", + "--template", "rdf4j", + "--long", "The RDF vocabulary", + "--short", "RDF", + "--ns", "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "--prefix", "rdf", + "--doc", "https://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/", + "--author", "The Author", + "--package", "org.w3.vocab", + "--output-dir", testOutputDir(testName), + }); + assertFileEqualsExpected(testName, "RDF.java"); + } private String testOutputDir(String testName) { return "target/test-output/" + testName; } private void assertFileEqualsExpected(String directory, String filename) throws IOException { - Path outputFilePath = Path.of("target/test-output/" + directory + "/" + filename); - Path expectedFilePath = Path.of("src/test/resources/expected/" + directory + "/" + filename); - File outputFile = outputFilePath.toFile(); + Path expectedFilePath = getExpectedFilePath(directory, filename); File expectedFile = expectedFilePath.toFile(); - assertTrue(outputFile::exists, "output file " + outputFile.getAbsolutePath() + " does not exist"); assertTrue(expectedFile::exists, "expected file " + expectedFile.getAbsolutePath() + " does not exist"); + String expectedFileContent = Files.readString(expectedFilePath, Charsets.UTF_8); + Path outputFilePath = getOutputFilePath(directory, filename); + File outputFile = outputFilePath.toFile(); + assertTrue(outputFile::exists, "output file " + outputFile.getAbsolutePath() + " does not exist"); + String actualFileContent = Files.readString(outputFilePath, Charsets.UTF_8); assertEquals( - format(Files.readString(expectedFilePath, Charsets.UTF_8)), - format(Files.readString(outputFilePath, Charsets.UTF_8)), + format(expectedFileContent), + format(actualFileContent), "Output file " + outputFile.getAbsolutePath() + " content differs from expected content in " + expectedFile.getAbsolutePath()); } + private Path getOutputFilePath(String directory, String filename) { + return Path.of("target/test-output/" + directory + "/" + filename); + } + private Path getExpectedFilePath(String directory, String filename) { + return Path.of("src/test/resources/expected/" + directory + "/" + filename); + } /** * Remove any IDE formatting we might have introduced by managing the expected file in the IDE. + * * @param input the generated file's content. * @return the content, hopefully formatted in such a way that comparing output and expected output shows only interesting differences. */ - private String format(String input){ + private String format(String input) { return input.replaceAll("[\\s]+\n", "\n").replaceAll("\n[\\s\\t]+(\\S)", "\n $1"); } } diff --git a/src/test/resources/expected/testBasic/RDF.java b/src/test/resources/expected/testBasic/RDF.java index c6c49d6..27923cc 100644 --- a/src/test/resources/expected/testBasic/RDF.java +++ b/src/test/resources/expected/testBasic/RDF.java @@ -31,87 +31,60 @@ public class RDF { public static final Namespace NS = new SimpleNamespace(PREFIX, NAMESPACE); // Classes - /** rdf:Statement */ - public static final IRI Statement = create("Statement"); - - /** rdf:Alt */ + /** Alt **/ public static final IRI Alt = create("Alt"); - /** rdf:Bag */ + /** Bag **/ public static final IRI Bag = create("Bag"); - /** rdf:List */ + /** CompoundLiteral **/ + public static final IRI CompoundLiteral = create("CompoundLiteral"); + + /** List **/ public static final IRI List = create("List"); - /** rdf:Property */ + /** Property **/ public static final IRI Property = create("Property"); - /** rdf:CompoundLiteral */ - public static final IRI CompoundLiteral = create("CompoundLiteral"); - - /** rdf:Seq */ + /** Seq **/ public static final IRI Seq = create("Seq"); + /** Statement **/ + public static final IRI Statement = create("Statement"); - // Properties - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - /** rdf:rest */ - public static final IRI rest = create("rest"); + // Properties + /** direction **/ + public static final IRI direction = create("direction"); - /** rdf:subject */ - public static final IRI subject = create("subject"); + /** first **/ + public static final IRI first = create("first"); - /** rdf:language */ + /** language **/ public static final IRI language = create("language"); - /** rdf:type */ - public static final IRI type = create("type"); - - /** rdf:value */ - public static final IRI value = create("value"); - - /** rdf:first */ - public static final IRI first = create("first"); - - /** rdf:object */ + /** object **/ public static final IRI object = create("object"); - /** rdf:direction */ - public static final IRI direction = create("direction"); - - - // Individuals - /** rdf:nil */ - public static final IRI nil = create("nil"); + /** predicate **/ + public static final IRI predicate = create("predicate"); - /** rdf:rest */ + /** rest **/ public static final IRI rest = create("rest"); - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - - /** rdf:subject */ + /** subject **/ public static final IRI subject = create("subject"); - /** rdf:language */ - public static final IRI language = create("language"); - - /** rdf:type */ + /** type **/ public static final IRI type = create("type"); - /** rdf:value */ + /** value **/ public static final IRI value = create("value"); - /** rdf:first */ - public static final IRI first = create("first"); - /** rdf:object */ - public static final IRI object = create("object"); - - /** rdf:direction */ - public static final IRI direction = create("direction"); + // Individuals + /** nil **/ + public static final IRI nil = create("nil"); private static IRI create(String localName) { diff --git a/src/test/resources/expected/testBasic_Copyright/RDF.java b/src/test/resources/expected/testBasic_Copyright/RDF.java index f1cd399..1e17a3f 100644 --- a/src/test/resources/expected/testBasic_Copyright/RDF.java +++ b/src/test/resources/expected/testBasic_Copyright/RDF.java @@ -38,87 +38,60 @@ public class RDF { public static final Namespace NS = new SimpleNamespace(PREFIX, NAMESPACE); // Classes - /** rdf:Statement */ - public static final IRI Statement = create("Statement"); - - /** rdf:Alt */ + /** Alt **/ public static final IRI Alt = create("Alt"); - /** rdf:Bag */ + /** Bag **/ public static final IRI Bag = create("Bag"); - /** rdf:List */ + /** CompoundLiteral **/ + public static final IRI CompoundLiteral = create("CompoundLiteral"); + + /** List **/ public static final IRI List = create("List"); - /** rdf:Property */ + /** Property **/ public static final IRI Property = create("Property"); - /** rdf:CompoundLiteral */ - public static final IRI CompoundLiteral = create("CompoundLiteral"); - - /** rdf:Seq */ + /** Seq **/ public static final IRI Seq = create("Seq"); + /** Statement **/ + public static final IRI Statement = create("Statement"); - // Properties - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - /** rdf:rest */ - public static final IRI rest = create("rest"); + // Properties + /** direction **/ + public static final IRI direction = create("direction"); - /** rdf:subject */ - public static final IRI subject = create("subject"); + /** first **/ + public static final IRI first = create("first"); - /** rdf:language */ + /** language **/ public static final IRI language = create("language"); - /** rdf:type */ - public static final IRI type = create("type"); - - /** rdf:value */ - public static final IRI value = create("value"); - - /** rdf:first */ - public static final IRI first = create("first"); - - /** rdf:object */ + /** object **/ public static final IRI object = create("object"); - /** rdf:direction */ - public static final IRI direction = create("direction"); - - - // Individuals - /** rdf:nil */ - public static final IRI nil = create("nil"); + /** predicate **/ + public static final IRI predicate = create("predicate"); - /** rdf:rest */ + /** rest **/ public static final IRI rest = create("rest"); - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - - /** rdf:subject */ + /** subject **/ public static final IRI subject = create("subject"); - /** rdf:language */ - public static final IRI language = create("language"); - - /** rdf:type */ + /** type **/ public static final IRI type = create("type"); - /** rdf:value */ + /** value **/ public static final IRI value = create("value"); - /** rdf:first */ - public static final IRI first = create("first"); - /** rdf:object */ - public static final IRI object = create("object"); - - /** rdf:direction */ - public static final IRI direction = create("direction"); + // Individuals + /** nil **/ + public static final IRI nil = create("nil"); private static IRI create(String localName) { diff --git a/src/test/resources/expected/testBasic_Copyright_SearchClasspath/RDF.java b/src/test/resources/expected/testBasic_Copyright_SearchClasspath/RDF.java index f1cd399..1e17a3f 100644 --- a/src/test/resources/expected/testBasic_Copyright_SearchClasspath/RDF.java +++ b/src/test/resources/expected/testBasic_Copyright_SearchClasspath/RDF.java @@ -38,87 +38,60 @@ public class RDF { public static final Namespace NS = new SimpleNamespace(PREFIX, NAMESPACE); // Classes - /** rdf:Statement */ - public static final IRI Statement = create("Statement"); - - /** rdf:Alt */ + /** Alt **/ public static final IRI Alt = create("Alt"); - /** rdf:Bag */ + /** Bag **/ public static final IRI Bag = create("Bag"); - /** rdf:List */ + /** CompoundLiteral **/ + public static final IRI CompoundLiteral = create("CompoundLiteral"); + + /** List **/ public static final IRI List = create("List"); - /** rdf:Property */ + /** Property **/ public static final IRI Property = create("Property"); - /** rdf:CompoundLiteral */ - public static final IRI CompoundLiteral = create("CompoundLiteral"); - - /** rdf:Seq */ + /** Seq **/ public static final IRI Seq = create("Seq"); + /** Statement **/ + public static final IRI Statement = create("Statement"); - // Properties - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - /** rdf:rest */ - public static final IRI rest = create("rest"); + // Properties + /** direction **/ + public static final IRI direction = create("direction"); - /** rdf:subject */ - public static final IRI subject = create("subject"); + /** first **/ + public static final IRI first = create("first"); - /** rdf:language */ + /** language **/ public static final IRI language = create("language"); - /** rdf:type */ - public static final IRI type = create("type"); - - /** rdf:value */ - public static final IRI value = create("value"); - - /** rdf:first */ - public static final IRI first = create("first"); - - /** rdf:object */ + /** object **/ public static final IRI object = create("object"); - /** rdf:direction */ - public static final IRI direction = create("direction"); - - - // Individuals - /** rdf:nil */ - public static final IRI nil = create("nil"); + /** predicate **/ + public static final IRI predicate = create("predicate"); - /** rdf:rest */ + /** rest **/ public static final IRI rest = create("rest"); - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - - /** rdf:subject */ + /** subject **/ public static final IRI subject = create("subject"); - /** rdf:language */ - public static final IRI language = create("language"); - - /** rdf:type */ + /** type **/ public static final IRI type = create("type"); - /** rdf:value */ + /** value **/ public static final IRI value = create("value"); - /** rdf:first */ - public static final IRI first = create("first"); - /** rdf:object */ - public static final IRI object = create("object"); - - /** rdf:direction */ - public static final IRI direction = create("direction"); + // Individuals + /** nil **/ + public static final IRI nil = create("nil"); private static IRI create(String localName) { diff --git a/src/test/resources/expected/testBasic_Jena/RDF.java b/src/test/resources/expected/testBasic_Jena/RDF.java index 503a8e6..7800a7f 100644 --- a/src/test/resources/expected/testBasic_Jena/RDF.java +++ b/src/test/resources/expected/testBasic_Jena/RDF.java @@ -1,4 +1,4 @@ -package org.apache.jena.vocabulary; +package org.w3.vocab; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; @@ -29,34 +29,42 @@ public static String getURI() { } // Classes - public static final Resource Statement = m.createResource(NS + "Statement"); + /** Alt **/ public static final Resource Alt = m.createResource(NS + "Alt"); + /** Bag **/ public static final Resource Bag = m.createResource(NS + "Bag"); + /** CompoundLiteral **/ + public static final Resource CompoundLiteral = m.createResource(NS + "CompoundLiteral"); + /** List **/ public static final Resource List = m.createResource(NS + "List"); + /** Property **/ public static final Resource Property = m.createResource(NS + "Property"); - public static final Resource CompoundLiteral = m.createResource(NS + "CompoundLiteral"); + /** Seq **/ public static final Resource Seq = m.createResource(NS + "Seq"); + /** Statement **/ + public static final Resource Statement = m.createResource(NS + "Statement"); // Properties + /** direction **/ + public static final Property direction = m.createProperty(NS + "direction"); + /** first **/ + public static final Property first = m.createProperty(NS + "first"); + /** language **/ + public static final Property language = m.createProperty(NS + "language"); + /** object **/ + public static final Property object = m.createProperty(NS + "object"); + /** predicate **/ public static final Property predicate = m.createProperty(NS + "predicate"); + /** rest **/ public static final Property rest = m.createProperty(NS + "rest"); + /** subject **/ public static final Property subject = m.createProperty(NS + "subject"); - public static final Property language = m.createProperty(NS + "language"); + /** type **/ public static final Property type = m.createProperty(NS + "type"); + /** value **/ public static final Property value = m.createProperty(NS + "value"); - public static final Property first = m.createProperty(NS + "first"); - public static final Property object = m.createProperty(NS + "object"); - public static final Property direction = m.createProperty(NS + "direction"); // Individuals + /** nil **/ public static final Individual nil = m.createProperty(NS + "nil"); - public static final Individual rest = m.createProperty(NS + "rest"); - public static final Individual predicate = m.createProperty(NS + "predicate"); - public static final Individual subject = m.createProperty(NS + "subject"); - public static final Individual language = m.createProperty(NS + "language"); - public static final Individual type = m.createProperty(NS + "type"); - public static final Individual value = m.createProperty(NS + "value"); - public static final Individual first = m.createProperty(NS + "first"); - public static final Individual object = m.createProperty(NS + "object"); - public static final Individual direction = m.createProperty(NS + "direction"); } diff --git a/src/test/resources/expected/testBasic_NoLabels/RDF.java b/src/test/resources/expected/testBasic_NoLabels/RDF.java new file mode 100644 index 0000000..c70a109 --- /dev/null +++ b/src/test/resources/expected/testBasic_NoLabels/RDF.java @@ -0,0 +1,94 @@ +package org.w3.vocab; + +import org.eclipse.rdf4j.model.IRI; +import org.eclipse.rdf4j.model.Namespace; +import org.eclipse.rdf4j.model.ValueFactory; +import org.eclipse.rdf4j.model.impl.SimpleNamespace; +import org.eclipse.rdf4j.model.impl.SimpleValueFactory; + + +/** + * Constants for the The RDF vocabulary. + * + * @see The RDF vocabulary + * + * @author The Author + */ +public class RDF { + /** + * The RDF namespace: http://www.w3.org/1999/02/22-rdf-syntax-ns# + */ + public static final String NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + + /** + * Recommended prefix for the namespace: "rdf" + */ + public static final String PREFIX = "rdf"; + + /** + * An immutable {@link Namespace} constant that represents the namespace. + */ + public static final Namespace NS = new SimpleNamespace(PREFIX, NAMESPACE); + + // Classes + /** rdf:Alt **/ + public static final IRI Alt = create("Alt"); + + /** rdf:Bag **/ + public static final IRI Bag = create("Bag"); + + /** rdf:CompoundLiteral **/ + public static final IRI CompoundLiteral = create("CompoundLiteral"); + + /** rdf:List **/ + public static final IRI List = create("List"); + + /** rdf:Property **/ + public static final IRI Property = create("Property"); + + /** rdf:Seq **/ + public static final IRI Seq = create("Seq"); + + /** rdf:Statement **/ + public static final IRI Statement = create("Statement"); + + + // Properties + /** rdf:direction **/ + public static final IRI direction = create("direction"); + + /** rdf:first **/ + public static final IRI first = create("first"); + + /** rdf:language **/ + public static final IRI language = create("language"); + + /** rdf:object **/ + public static final IRI object = create("object"); + + /** rdf:predicate **/ + public static final IRI predicate = create("predicate"); + + /** rdf:rest **/ + public static final IRI rest = create("rest"); + + /** rdf:subject **/ + public static final IRI subject = create("subject"); + + /** rdf:type **/ + public static final IRI type = create("type"); + + /** rdf:value **/ + public static final IRI value = create("value"); + + + // Individuals + /** rdf:nil **/ + public static final IRI nil = create("nil"); + + + private static IRI create(String localName) { + return SimpleValueFactory.getInstance().createIRI(RDF.NAMESPACE, localName); + } +} + diff --git a/src/test/resources/expected/testBasic_Plain/RDF.java b/src/test/resources/expected/testBasic_Plain/RDF.java new file mode 100644 index 0000000..d0062fe --- /dev/null +++ b/src/test/resources/expected/testBasic_Plain/RDF.java @@ -0,0 +1,62 @@ +package org.w3.vocab; + +/** + * Constants for the The RDF vocabulary. + * + * @see The RDF vocabulary + */ +public class RDF { + + /** + * The RDF namespace: http://www.w3.org/1999/02/22-rdf-syntax-ns# + */ + public static final String NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + + /** + * Returns the URI for this schema + * @return URI + */ + public static String getURI() { + return NS; + } + + // Classes + /** Alt **/ + public static final String Alt = NS + "Alt"; + /** Bag **/ + public static final String Bag = NS + "Bag"; + /** CompoundLiteral **/ + public static final String CompoundLiteral = NS + "CompoundLiteral"; + /** List **/ + public static final String List = NS + "List"; + /** Property **/ + public static final String Property = NS + "Property"; + /** Seq **/ + public static final String Seq = NS + "Seq"; + /** Statement **/ + public static final String Statement = NS + "Statement"; + + // Properties + /** direction **/ + public static final String direction = NS + "direction"; + /** first **/ + public static final String first = NS + "first"; + /** language **/ + public static final String language = NS + "language"; + /** object **/ + public static final String object = NS + "object"; + /** predicate **/ + public static final String predicate = NS + "predicate"; + /** rest **/ + public static final String rest = NS + "rest"; + /** subject **/ + public static final String subject = NS + "subject"; + /** type **/ + public static final String type = NS + "type"; + /** value **/ + public static final String value = NS + "value"; + + // Individuals + /** nil **/ + public static final String nil = NS + "nil"; +} diff --git a/src/test/resources/expected/testBasic_SearchClasspath/RDF.java b/src/test/resources/expected/testBasic_SearchClasspath/RDF.java index c6c49d6..27923cc 100644 --- a/src/test/resources/expected/testBasic_SearchClasspath/RDF.java +++ b/src/test/resources/expected/testBasic_SearchClasspath/RDF.java @@ -31,87 +31,60 @@ public class RDF { public static final Namespace NS = new SimpleNamespace(PREFIX, NAMESPACE); // Classes - /** rdf:Statement */ - public static final IRI Statement = create("Statement"); - - /** rdf:Alt */ + /** Alt **/ public static final IRI Alt = create("Alt"); - /** rdf:Bag */ + /** Bag **/ public static final IRI Bag = create("Bag"); - /** rdf:List */ + /** CompoundLiteral **/ + public static final IRI CompoundLiteral = create("CompoundLiteral"); + + /** List **/ public static final IRI List = create("List"); - /** rdf:Property */ + /** Property **/ public static final IRI Property = create("Property"); - /** rdf:CompoundLiteral */ - public static final IRI CompoundLiteral = create("CompoundLiteral"); - - /** rdf:Seq */ + /** Seq **/ public static final IRI Seq = create("Seq"); + /** Statement **/ + public static final IRI Statement = create("Statement"); - // Properties - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - /** rdf:rest */ - public static final IRI rest = create("rest"); + // Properties + /** direction **/ + public static final IRI direction = create("direction"); - /** rdf:subject */ - public static final IRI subject = create("subject"); + /** first **/ + public static final IRI first = create("first"); - /** rdf:language */ + /** language **/ public static final IRI language = create("language"); - /** rdf:type */ - public static final IRI type = create("type"); - - /** rdf:value */ - public static final IRI value = create("value"); - - /** rdf:first */ - public static final IRI first = create("first"); - - /** rdf:object */ + /** object **/ public static final IRI object = create("object"); - /** rdf:direction */ - public static final IRI direction = create("direction"); - - - // Individuals - /** rdf:nil */ - public static final IRI nil = create("nil"); + /** predicate **/ + public static final IRI predicate = create("predicate"); - /** rdf:rest */ + /** rest **/ public static final IRI rest = create("rest"); - /** rdf:predicate */ - public static final IRI predicate = create("predicate"); - - /** rdf:subject */ + /** subject **/ public static final IRI subject = create("subject"); - /** rdf:language */ - public static final IRI language = create("language"); - - /** rdf:type */ + /** type **/ public static final IRI type = create("type"); - /** rdf:value */ + /** value **/ public static final IRI value = create("value"); - /** rdf:first */ - public static final IRI first = create("first"); - /** rdf:object */ - public static final IRI object = create("object"); - - /** rdf:direction */ - public static final IRI direction = create("direction"); + // Individuals + /** nil **/ + public static final IRI nil = create("nil"); private static IRI create(String localName) { diff --git a/src/test/resources/expected/testNoArgs/System.out b/src/test/resources/expected/testNoArgs/System.out new file mode 100644 index 0000000..4960b45 --- /dev/null +++ b/src/test/resources/expected/testNoArgs/System.out @@ -0,0 +1,17 @@ +Missing required options: f, d, n, s, l, p, t +usage: VocabGen + -a,--author Name of the java class author + -c,--copyright file containing the copyright snippet + -cp,--searchClasspath look for input files on classpath, then in + filesystem + -d,--doc Documentation URL + -f,--file OWL vocabulary file in TTL format + -jp,--package java package + -l,--long Long vocabulary name + -n,--ns Namespace URL + -o,--output-dir output directory + -p,--prefix Namespace prefix + -s,--short Short vocabulary name + -sc,--snake-case use all caps snake case constants instead of + as-is local names + -t,--template one of: rdf4j, jena, plain diff --git a/src/test/resources/rdf-nolabels.ttl b/src/test/resources/rdf-nolabels.ttl new file mode 100644 index 0000000..ed76bd6 --- /dev/null +++ b/src/test/resources/rdf-nolabels.ttl @@ -0,0 +1,132 @@ +@prefix rdf: . +@prefix rdfs: . +@prefix owl: . +@prefix dc: . + + a owl:Ontology ; + dc:title "The RDF Concepts Vocabulary (RDF)" ; + dc:date "2019-12-16" ; + dc:description "This is the RDF Schema for the RDF vocabulary terms in the RDF Namespace, defined in RDF 1.1 Concepts." . + +rdf:HTML a rdfs:Datatype ; + rdfs:subClassOf rdfs:Literal ; + rdfs:isDefinedBy ; + rdfs:seeAlso ; + rdfs:comment "The datatype of RDF literals storing fragments of HTML content" . + +rdf:langString a rdfs:Datatype ; + rdfs:subClassOf rdfs:Literal ; + rdfs:isDefinedBy ; + rdfs:seeAlso ; + rdfs:comment "The datatype of language-tagged string values" . + +rdf:PlainLiteral a rdfs:Datatype ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Literal ; + rdfs:seeAlso ; + rdfs:comment "The class of plain (i.e. untyped) literal values, as used in RIF and OWL 2" . + +rdf:type a rdf:Property ; + rdfs:isDefinedBy ; + rdfs:comment "The subject is an instance of a class." ; + rdfs:range rdfs:Class ; + rdfs:domain rdfs:Resource . + +rdf:Property a rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:comment "The class of RDF properties." ; + rdfs:subClassOf rdfs:Resource . + +rdf:Statement a rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource ; + rdfs:comment "The class of RDF statements." . + +rdf:subject a rdf:Property ; + rdfs:isDefinedBy ; + rdfs:comment "The subject of the subject RDF statement." ; + rdfs:domain rdf:Statement ; + rdfs:range rdfs:Resource . + +rdf:predicate a rdf:Property ; + rdfs:isDefinedBy ; + rdfs:comment "The predicate of the subject RDF statement." ; + rdfs:domain rdf:Statement ; + rdfs:range rdfs:Resource . + +rdf:object a rdf:Property ; + rdfs:isDefinedBy ; + rdfs:comment "The object of the subject RDF statement." ; + rdfs:domain rdf:Statement ; + rdfs:range rdfs:Resource . + +rdf:Bag a rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:comment "The class of unordered containers." ; + rdfs:subClassOf rdfs:Container . + +rdf:Seq a rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:comment "The class of ordered containers." ; + rdfs:subClassOf rdfs:Container . + +rdf:Alt a rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:comment "The class of containers of alternatives." ; + rdfs:subClassOf rdfs:Container . + +rdf:value a rdf:Property ; + rdfs:isDefinedBy ; + rdfs:comment "Idiomatic property used for structured values." ; + rdfs:domain rdfs:Resource ; + rdfs:range rdfs:Resource . + +rdf:List a rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:comment "The class of RDF Lists." ; + rdfs:subClassOf rdfs:Resource . + +rdf:nil a rdf:List ; + rdfs:isDefinedBy ; + rdfs:comment "The empty list, with no items in it. If the rest of a list is nil then the list has no more items in it." . + +rdf:first a rdf:Property ; + rdfs:isDefinedBy ; + rdfs:comment "The first item in the subject RDF list." ; + rdfs:domain rdf:List ; + rdfs:range rdfs:Resource . + +rdf:rest a rdf:Property ; + rdfs:isDefinedBy ; + rdfs:comment "The rest of the subject RDF list after the first item." ; + rdfs:domain rdf:List ; + rdfs:range rdf:List . + +rdf:XMLLiteral a rdfs:Datatype ; + rdfs:subClassOf rdfs:Literal ; + rdfs:isDefinedBy ; + rdfs:comment "The datatype of XML literal values." . + +rdf:JSON a rdfs:Datatype ; + rdfs:comment "The datatype of RDF literals storing JSON content." ; + rdfs:subClassOf rdfs:Literal ; + rdfs:isDefinedBy ; + rdfs:seeAlso . + +rdf:CompoundLiteral a rdfs:Class ; + rdfs:comment "A class representing a compound literal." ; + rdfs:subClassOf rdfs:Resource ; + rdfs:isDefinedBy ; + rdfs:seeAlso . + +rdf:language a rdf:Property ; + rdfs:comment "The language component of a CompoundLiteral." ; + rdfs:domain rdf:CompoundLiteral ; + rdfs:isDefinedBy ; + rdfs:seeAlso . + +rdf:direction a rdf:Property ; + rdfs:comment "The base direction component of a CompoundLiteral." ; + rdfs:domain rdf:CompoundLiteral ; + rdfs:isDefinedBy ; + rdfs:seeAlso .