/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package
///////////////
package jena;
import static org.apache.jena.atlas.logging.LogCtl.setCmdLogging;
import java.io.ByteArrayOutputStream ;
import java.io.File ;
import java.io.FileOutputStream ;
import java.io.PrintStream ;
import java.net.MalformedURLException ;
import java.net.URL ;
import java.text.SimpleDateFormat ;
import java.util.* ;
import java.util.regex.Pattern ;
import java.util.regex.PatternSyntaxException ;
import org.apache.jena.ontology.Individual ;
import org.apache.jena.ontology.OntModel ;
import org.apache.jena.ontology.OntModelSpec ;
import org.apache.jena.rdf.model.* ;
import org.apache.jena.shared.JenaException ;
import org.apache.jena.util.FileManager ;
import org.apache.jena.util.iterator.ExtendedIterator ;
import org.apache.jena.util.iterator.WrappedIterator ;
import org.apache.jena.vocabulary.OWL ;
import org.apache.jena.vocabulary.RDF ;
import org.apache.jena.vocabulary.RDFS ;
import org.apache.jena.vocabulary.XSD ;
import org.apache.xerces.util.XMLChar ;
/**
* <p>
* A vocabulary generator, that will consume an ontology or other vocabulary file,
* and generate a Java file with the constants from the vocabulary compiled in.
* Designed to be highly flexible and customisable.
* </p>
*/
public class schemagen {
static { setCmdLogging(); }
// Constants
//////////////////////////////////
/** The namespace for the configuration model is {@value} */
public static final String NS = "http://jena.hpl.hp.com/2003/04/schemagen#";
/** The default location of the configuration model is {@value} */
public static final String DEFAULT_CONFIG_URI = "file:schemagen.rdf";
/** The default marker string for denoting substitutions is {@value} */
public static final String DEFAULT_MARKER = "%";
/** Default template for writing out value declarations */
public static final String DEFAULT_TEMPLATE = "public static final %valclass% %valname% = M_MODEL.%valcreator%( \"%valuri%\" );";
/** Default template for writing out individual declarations */
public static final String DEFAULT_INDIVIDUAL_TEMPLATE = "public static final %valclass% %valname% = M_MODEL.%valcreator%( \"%valuri%\", %valtype% );";
/** Default template for writing out individual declarations for non-ontology vocabularies */
public static final String DEFAULT_RDFS_INDIVIDUAL_TEMPLATE = "public static final %valclass% %valname% = M_MODEL.%valcreator%( \"%valuri%\" );";
/** Default template for the file header */
public static final String DEFAULT_HEADER_TEMPLATE = "/* CVS $" + "Id: $ */%nl%%package% %nl%%imports% %nl%/**%nl% * Vocabulary definitions from %sourceURI% %nl% * @author Auto-generated by schemagen on %date% %nl% */";
/** Default line length for comments before wrap */
public static final int COMMENT_LENGTH_LIMIT = 80;
/** List of Java reserved keywords, see <a href="http://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html">this list</a>. */
public static final String[] JAVA_KEYWORDS = {
"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"
};
// Static variables
//////////////////////////////////
private static List<String> KEYWORD_LIST;
static {
KEYWORD_LIST = Arrays.asList( JAVA_KEYWORDS );
}
// Instance variables
//////////////////////////////////
/** Schemagen options interface */
protected SchemagenOptions m_options;
/** The model that contains the input source */
protected OntModel m_source;
/** The output stream we write to */
protected PrintStream m_output;
/** Option definitions */
/** Stack of replacements to apply */
protected List<Replacement> m_replacements = new ArrayList<>();
/** Output file newline char - default is Unix, override with --dos */
protected String m_nl = "\n";
/** Size of indent step */
protected int m_indentStep = 4;
/** Set of names used so far */
protected Set<String> m_usedNames = new HashSet<>();
/** Map from resources to java names */
protected Map<Resource,String> m_resourcesToNames = new HashMap<>();
/** List of allowed namespace URI strings for admissible values */
protected List<String> m_includeURI = new ArrayList<>();
// Constructors
//////////////////////////////////
// External signature methods
//////////////////////////////////
/* Main entry point. See Javadoc for details of the many command line arguments */
public static void main( String... args ) {
try {
new schemagen().go( args );
}
catch (SchemagenException e) {
System.err.println( "Schemagen failed to run:" );
System.err.println( e.getMessage() );
if (e.getCause() != null) {
System.err.println( "Caused by: " + e.getCause().getMessage() );
}
System.exit( 1 );
}
}
// Internal implementation methods
//////////////////////////////////
/** Read the configuration parameters and do setup */
protected void go( String[] args ) {
go( new SchemagenOptionsImpl( args ) );
}
/** Handle initial configuration options, then initiate processing */
protected void go( SchemagenOptions options ) {
m_options = options;
// check for user requesting help
if (m_options.hasHelpOption()) {
usage();
}
// got the configuration, now we can begin processing
processInput();
}
/** The sequence of steps to process an entire file */
protected void processInput() {
addIncludes();
determineLanguage();
selectInput();
selectOutput();
setGlobalReplacements();
processHeader();
writeClassDeclaration();
writeInitialDeclarations();
writeProperties();
writeClasses();
writeIndividuals();
writeDatatypes();
writeClassClose();
processFooter();
closeOutput();
}
/** Add the included files */
protected void addIncludes() {
// add any extra uri's that are allowed in the filter
m_includeURI.addAll( m_options.getIncludeOption() );
}
/** Create the source model after determining which input language */
protected void determineLanguage() {
OntModelSpec s = null;
if (m_options.hasLangRdfsOption()) {
// RDFS language specified
if (m_options.hasUseInfOption()) {
s = OntModelSpec.RDFS_MEM_RDFS_INF;
}
else {
s = OntModelSpec.RDFS_MEM;
}
}
else {
// owl is the default
// s = OntModelSpec.getDefaultSpec( ProfileRegistry.OWL_LANG );
if (m_options.hasUseInfOption()) {
s = OntModelSpec.OWL_MEM_RULE_INF;
}
else {
s = OntModelSpec.OWL_MEM;
}
}
m_source = ModelFactory.createOntologyModel( s, null );
m_source.getDocumentManager().setProcessImports( false );
// turn off strict checking on request
if (m_options.hasNoStrictOption()) {
m_source.setStrictMode( false );
}
}
/** Identify the URL that is to be read in and translated to a vocabulary file, and load the source into the source model */
protected void selectInput() {
if (!m_options.hasInputOption()) {
usage();
}
String input = SchemagenUtils.urlCheck( m_options.getInputOption().getURI() );
String syntax = m_options.getEncodingOption();
try {
FileManager.get().readModel( m_source, input, syntax );
}
catch (JenaException e) {
abort( "Failed to read input source " + input, e );
}
}
/** Identify the file we are to write the output to */
protected void selectOutput() {
String outFile = m_options.getOutputOption();
if (outFile == null) {
m_output = System.out;
}
else {
try {
// check for package name
String packageName = m_options.getPackagenameOption();
if (packageName != null) {
String packagePath = "";
// build the package path (e.g. com.foo.bar -> /com/foo/bar)
for (String p: packageName.split( "\\." )) {
packagePath = packagePath + File.separator + p;
}
if (!outFile.endsWith( packagePath )) {
outFile = outFile + packagePath;
}
}
File out = new File( outFile );
if (!out.exists() && !outFile.endsWith( ".java" )) {
// create the directory if needed
out.mkdirs();
}
if (out.isDirectory()) {
// create a file in this directory named classname.java
String fileName = outFile + File.separator + getClassName() + ".java";
out = new File( fileName );
}
m_output = new PrintStream( new FileOutputStream( out ) );
}
catch (Exception e) {
abort( "I/O error while trying to open file for writing: " + outFile, e );
}
}
// check for DOS line endings
if (m_options.hasDosOption()) {
m_nl = "\r\n";
}
}
/** Process the header at the start of the file, if defined */
protected void processHeader() {
String header = m_options.hasHeaderOption() ? m_options.getHeaderOption() : DEFAULT_HEADER_TEMPLATE;
// user can turn of header processing, default is to have it on
if (!m_options.hasNoheaderOption()) {
writeln( 0, substitute( header ) );
}
else {
// we have to do the imports at least
writeln( 0, "import org.apache.jena.rdf.model.*;" );
if (m_options.hasOntologyOption()) {
writeln( 0, "import org.apache.jena.ontology.*;" );
}
if (m_options.hasIncludeSourceOption()) {
writeln( 0, "import java.io.ByteArrayInputStream;" );
}
}
}
/** Process the footer at the end of the file, if defined */
protected void processFooter() {
String footer = m_options.getFooterOption();
if (footer != null) {
writeln( 0, substitute( footer ) );
}
}
/** The list of replacements that are always available */
protected void setGlobalReplacements() {
addReplacementPattern( "date", new SimpleDateFormat( "dd MMM yyyy HH:mm").format( new Date() ) );
addReplacementPattern( "package", m_options.hasPackagenameOption() ? ("package " + m_options.getPackagenameOption() + ";") : "" );
addReplacementPattern( "imports", getImports() );
addReplacementPattern( "classname", getClassName() );
addReplacementPattern( "nl", m_nl );
// protect \ in Windows file pathnames
// looks for file:.* or C:.* (or variants thereof)
String source = m_options.getInputOption().getURI();
if (source.matches( "(file:|[A-Za-z]:).*$" )) {
source = source.replace( "\\", "\\\\" );
}
addReplacementPattern( "sourceURI", source );
}
/** Add a pattern-value pair to the list of available patterns */
protected void addReplacementPattern( String key, String replacement ) {
if (replacement != null && key != null) {
String marker = m_options.getMarkerOption();
marker = (marker == null) ? DEFAULT_MARKER : marker;
try {
m_replacements.add( new Replacement( Pattern.compile( marker + key + marker ),
replacement ) );
}
catch (PatternSyntaxException e) {
abort( "Malformed regexp pattern " + marker + key + marker, e );
}
}
}
/** Pop n replacements off the stack */
protected void pop( int n ) {
for (int i = 0; i < n; i++) {
m_replacements.remove( m_replacements.size() - 1 );
}
}
/** Close the output file */
protected void closeOutput() {
m_output.flush();
m_output.close();
}
/** Abort due to exception */
protected void abort( String msg, Exception cause ) {
throw new SchemagenException( msg, cause );
}
/** Print usage message and abort */
protected void usage() {
System.err.println( "Usage:" );
System.err.println( " java jena.schemagen [options ...]" );
System.err.println();
System.err.println( "Commonly used options include:" );
System.err.println( " -i <input> the source document as a file or URL." );
System.err.println( " -n <name> the name of the created Java class." );
System.err.println( " -a <uri> the namespace URI of the source document." );
System.err.println( " -o <file> the file to write the generated class into." );
System.err.println( " -o <dir> the directory in which the generated Java class is created." );
System.err.println( " By default, output goes to stdout." );
System.err.println( " -e <encoding> the encoding of the input document (N3, RDF/XML, etc)." );
System.err.println( " -c <config> a filename or URL for an RDF document containing " );
System.err.println( " configuration parameters." );
System.err.println();
System.err.println( "Many other options are available. See the schemagen HOWTO in the " );
System.err.println( "Jena documentation for full details." );
System.exit( 1 );
}
/** Use the current replacements list to do the subs in the given string */
protected String substitute( String sIn ) {
String s = sIn;
for ( Replacement r : m_replacements )
{
s = r.pattern.matcher( s ).replaceAll( r.sub );
}
return s;
}
/** Add the appropriate indent to a buffer */
protected int indentTo( int i, StringBuffer buf ) {
int indent = i * m_indentStep;
for (int j = 0; j < indent; j++) {
buf.append( ' ' );
}
return indent;
}
/** Write a blank line, with indent and newline */
protected void writeln( int indent ) {
writeln( indent, "" );
}
/** Write out the given string with n spaces of indent, with newline */
protected void writeln( int indent, String s ) {
write( indent, s );
m_output.print( m_nl );
}
/** Write out the given string with n spaces of indent */
protected void write( int indentLevel, String s ) {
for (int i = 0; i < (m_indentStep * indentLevel); i++) {
m_output.print( " " );
}
m_output.print( s );
}
/** Determine the list of imports to include in the file */
protected String getImports() {
StringBuffer buf = new StringBuffer();
buf.append( "import org.apache.jena.rdf.model.*;" );
buf.append( m_nl );
if (useOntology()) {
buf.append( "import org.apache.jena.ontology.*;" );
buf.append( m_nl );
}
if (includeSource()) {
buf.append( "import java.io.ByteArrayInputStream;" );
buf.append( m_nl );
}
return buf.toString();
}
/** Determine the class name of the vocabulary from the URI */
protected String getClassName() {
// if a class name is given, just use that
if (m_options.hasClassnameOption()) {
return m_options.getClassnameOption();
}
// otherwise, we generate a name based on the URI
String uri = m_options.getInputOption().getURI();
// remove any suffixes
uri = (uri.endsWith( "#" )) ? uri.substring( 0, uri.length() - 1 ) : uri;
uri = (uri.endsWith( ".daml" )) ? uri.substring( 0, uri.length() - 5 ) : uri;
uri = (uri.endsWith( ".owl" )) ? uri.substring( 0, uri.length() - 4 ) : uri;
uri = (uri.endsWith( ".rdf" )) ? uri.substring( 0, uri.length() - 4 ) : uri;
uri = (uri.endsWith( ".rdfs" )) ? uri.substring( 0, uri.length() - 5 ) : uri;
uri = (uri.endsWith( ".n3" )) ? uri.substring( 0, uri.length() - 3 ) : uri;
uri = (uri.endsWith( ".xml" )) ? uri.substring( 0, uri.length() - 4 ) : uri;
uri = (uri.endsWith( ".ttl" )) ? uri.substring( 0, uri.length() - 4 ) : uri;
// now work back to the first non name character from the end
int i = uri.length() - 1;
for (; i > 0; i--) {
if (!Character.isUnicodeIdentifierPart( uri.charAt( i ) ) &&
uri.charAt( i ) != '-') {
i++;
break;
}
}
String name = uri.substring( i );
// optionally add name suffix
if (m_options.hasClassnameSuffixOption()) {
name = name + m_options.getClassnameSuffixOption();
}
// now we make the name into a legal Java identifier
return asLegalJavaID( name, true );
}
/** Answer true if we are using ontology terms in this vocabulary */
protected boolean useOntology() {
return m_options.hasOntologyOption();
}
/** Answer true if all comments are suppressed */
protected boolean noComments() {
return m_options.hasNoCommentsOption();
}
/** Answer true if ontology source code is to be included */
protected boolean includeSource() {
return m_options.hasIncludeSourceOption();
}
/** Converts to a legal Java identifier; capitalise first char if cap is true */
protected String asLegalJavaID( String s, boolean cap ) {
StringBuffer buf = new StringBuffer();
int i = 0;
// treat the first character specially - must be able to start a Java ID, may have to up-case
try {
for (; !Character.isJavaIdentifierStart( s.charAt( i )); i++) { /**/ }
}
catch (StringIndexOutOfBoundsException e) {
System.err.println( "Could not identify legal Java identifier start character in '" + s + "', replacing with __" );
return "__";
}
buf.append( cap ? Character.toUpperCase( s.charAt( i ) ) : s.charAt( i ) );
// copy the remaining characters - replace non-legal chars with '_'
for (++i; i < s.length(); i++) {
char c = s.charAt( i );
buf.append( Character.isJavaIdentifierPart( c ) ? c : '_' );
}
// check for illegal keyword
if (KEYWORD_LIST.contains( buf.toString() )) {
buf.append( '_' );
}
return buf.toString();
}
/** The opening class declaration */
protected void writeClassDeclaration() {
write( 0, "public class " );
write( 0, getClassName() );
write( 0, " " );
if (m_options.hasClassdecOption()) {
write( 0, m_options.getClassdecOption() );
}
writeln( 0, "{" );
}
/** The close of the class decoration */
protected void writeClassClose() {
writeln( 0, "}" );
}
/** Write the declarations at the head of the class */
protected void writeInitialDeclarations() {
writeModelDeclaration();
writeSource();
writeNamespace();
writeOntologyVersionInfo();
if (m_options.hasDeclarationsOption()) {
writeln( 0, m_options.getDeclarationsOption() );
}
}
/** Write the declaration of the model */
protected void writeModelDeclaration() {
if (useOntology()) {
String lang = "OWL";
if (m_options.hasLangRdfsOption()) {
lang = "RDFS";
}
writeln( 1, "/** <p>The ontology model that holds the vocabulary terms</p> */" );
writeln( 1, "private static final OntModel M_MODEL = ModelFactory.createOntologyModel( OntModelSpec." + lang + "_MEM, null );" );
}
else {
writeln( 1, "/** <p>The RDF model that holds the vocabulary terms</p> */" );
writeln( 1, "private static final Model M_MODEL = ModelFactory.createDefaultModel();" );
}
writeln( 1 );
}
/** Write the source code of the input model into the file itself */
protected void writeSource() {
if (includeSource()) {
// first save a copy of the source in compact form into a buffer
ByteArrayOutputStream bos = new ByteArrayOutputStream();
RDFWriter rw = m_source.getWriter( "Turtle" );
rw.setProperty( "objectLists", Boolean.FALSE.toString() );
rw.write( m_source, bos, null );
String output = bos.toString();
// now we embed each line of the source in the output
writeln( 1, "private static final String SOURCE = " );
boolean first = true;
StringTokenizer st = new StringTokenizer( output, "\n" );
while (st.hasMoreTokens()) {
String tok = st.nextToken();
if (tok.endsWith( "\r" )) {
tok = tok.substring( 0, tok.length() - 1 );
}
write( 2, first ? " " : " + " );
write( 0, "\"" );
write( 0, protectQuotes( tok ) );
writeln( 2, "\\n\"" );
first = false;
}
// then we reference the string constant when reading the source
// note that we avoid StringReader due to charset encoding issues
writeln( 1, ";" );
writeln( 0, "" );
writeln( 1, "/** Read the ontology definition into the source model */ " );
writeln( 1, "static { " );
writeln( 2, "M_MODEL.read( new ByteArrayInputStream( SOURCE.getBytes() ), null, \"N3\" );" );
writeln( 1, "}" );
writeln( 0, "" );
}
}
/** Protect any double quotes in the given string so that it's a legal Java String */
private String protectQuotes( String s ) {
return s.replaceAll( "\\\\", "\\\\\\\\" ).replaceAll( "\"", "\\\\\"" );
}
/** Write owl:versionInfo string if it exists **/
protected void writeOntologyVersionInfo() {
String versionInfo = getOntologyElementVersionInfo();
if (null != versionInfo) {
writeln( 1, "/** <p>The ontology's owl:versionInfo as a string</p> */" );
writeln( 1, "public static final String VERSION_INFO = \"" + protectQuotes(versionInfo) + "\";" );
writeln( 1 );
}
}
/** Write the string and resource that represent the namespace */
protected void writeNamespace() {
String nsURI = determineNamespaceURI();
writeln( 1, "/** <p>The namespace of the vocabulary as a string</p> */" );
writeln( 1, "public static final String NS = \"" + nsURI + "\";" );
writeln( 1 );
writeln( 1, "/** <p>The namespace of the vocabulary as a string</p>" );
writeln( 1, " * @return namespace as String" );
writeln( 1, " * @see #NS */" );
writeln( 1, "public static String getURI() {return NS;}" );
writeln( 1 );
writeln( 1, "/** <p>The namespace of the vocabulary as a resource</p> */" );
writeln( 1, "public static final Resource NAMESPACE = M_MODEL.createResource( NS );" );
writeln( 1 );
}
/** Determine what the namespace URI for this vocabulary is */
protected String determineNamespaceURI() {
// we have a sequence of strategies for determining the ontology namespace
String ns = getOptionNamespace();
if (ns == null) {
ns = getDefaultPrefixNamespace();
}
if (ns == null) {
ns = getOntologyElementNamespace();
}
if (ns == null) {
ns = guessNamespace();
}
// did we get one?
if (ns == null) {
abort( "Could not determine the base URI for the input vocabulary", null );
}
m_includeURI.add( ns );
return ns;
}
/** User has set namespace via a schemagen option */
protected String getOptionNamespace() {
return m_options.hasNamespaceOption() ? m_options.getNamespaceOption().getURI() : null;
}
/** Document has set an empty prefix for the model */
protected String getDefaultPrefixNamespace() {
// alternatively, the default namespace may be set in the prefix mapping read from the input document
String defaultNS = m_source.getNsPrefixURI( "" );
if (defaultNS == null) {
defaultNS = m_source.getBaseModel().getNsPrefixURI( "" );
}
return defaultNS;
}
/** Document has an owl:Ontology or daml:Ontology element */
protected String getOntologyElementVersionInfo() {
String versionInfo = null;
Resource ontologyClass = m_source.getProfile().ONTOLOGY();
if (null != ontologyClass) {
StmtIterator i = m_source.getBaseModel().listStatements( null, RDF.type, ontologyClass );
if (i.hasNext()) {
Resource ont = i.nextStatement().getSubject();
StmtIterator j = m_source.getBaseModel().listStatements( ont, OWL.versionInfo, (RDFNode)null );
if (j.hasNext()) {
versionInfo = j.nextStatement().getObject().asLiteral().getLexicalForm();
// check for ambiguous answers
if (j.hasNext()) {
System.err.println( "Warning: ambiguous owl:versionInfo - there are more than one owl:versionInfo statements." );
System.err.println( "Picking first choice: " + versionInfo + ". Other choices are:" );
while (j.hasNext()) {
System.err.print( " " );
System.err.print( j.nextStatement().getObject().toString() );
}
System.err.println();
}
}
// check for ambiguous answers
if (i.hasNext()) {
System.err.println( "Warning: ambiguous owl:versionInfo - there is more than one owl:Ontology element." );
System.err.println( "Picking first choice: " + ont.getURI() + ". Other choices are:" );
while (i.hasNext()) {
System.err.print( " " );
System.err.print( i.nextStatement().getObject().toString() );
}
System.err.println();
}
}
}
return versionInfo;
}
/** Document has an owl:Ontology or daml:Ontology element */
protected String getOntologyElementNamespace() {
// if we are using an ontology model, we can get the namespace URI from the ontology element
String uri = null;
StmtIterator i = m_source.getBaseModel()
.listStatements( null, RDF.type, m_source.getProfile().ONTOLOGY() );
if (i.hasNext()) {
Resource ont = i.nextStatement().getSubject();
uri = ont.getURI();
// ensure ends with namespace separator char
char ch = uri.charAt( uri.length() - 1 );
boolean endsWithNCNameCh = XMLChar.isNCName( ch );
uri = endsWithNCNameCh ? uri + "#" : uri;
// check for ambiguous answers
if (i.hasNext()) {
System.err.println( "Warning: ambiguous default namespace - there is more than one owl:Ontology element." );
System.err.println( "Picking first choice: " + uri + ". Other choices are:" );
while (i.hasNext()) {
System.err.print( " " );
System.err.print( i.nextStatement().getString() );
}
System.err.println();
System.err.println( "Use the -a option to specify a particular namespace if required." );
}
}
return uri;
}
/** Guess the URI from the most prevalent URI */
protected String guessNamespace() {
Map<String,Integer> nsCount = new HashMap<>();
// count all of the namespaces used in the model
for (StmtIterator i = m_source.listStatements(); i.hasNext(); ) {
Statement s = i.next();
countNamespace( s.getSubject(), nsCount );
countNamespace( s.getPredicate(), nsCount );
if (s.getObject().isResource()) {
countNamespace( s.getResource(), nsCount );
}
}
// now find the maximal element
String ns = null;
int max = 0;
for ( String nsKey : nsCount.keySet() )
{
// we ignore the usual suspects
if ( !( OWL.getURI().equals( nsKey ) ||
RDF.getURI().equals( nsKey ) ||
RDFS.getURI().equals( nsKey ) ||
XSD.getURI().equals( nsKey ) ) )
{
// not an ignorable namespace
int count = nsCount.get( nsKey ).intValue();
if ( count > max )
{
// highest count seen so far
max = count;
ns = nsKey;
}
}
}
return ns;
}
/** Record a use of the given namespace in the count map */
private void countNamespace( Resource r, Map<String,Integer> nsCount ) {
if (!r.isAnon()) {
String ns = r.getNameSpace();
// increment the count for this namespace
Integer count = nsCount.containsKey( ns ) ? (Integer) nsCount.get( ns ) : new Integer( 0 );
Integer count1 = new Integer( count.intValue() + 1 );
nsCount.put( ns, count1 );
}
}
/** Write the list of properties */
protected void writeProperties() {
if (m_options.hasNopropertiesOption()) {
return;
}
if (m_options.hasPropertySectionOption()) {
writeln( 0, m_options.getPropertySectionOption());
}
if (useOntology()) {
writeObjectProperties();
writeDatatypeProperties();
writeAnnotationProperties();
// we also write out the RDF properties, to mop up any props that are not stated as
// object, datatype or annotation properties
writeRDFProperties( true );
}
else {
writeRDFProperties( false );
}
}
/** Write any object properties in the vocabulary */
protected void writeObjectProperties() {
String template = m_options.hasPropTemplateOption() ? m_options.getPropTemplateOption() : DEFAULT_TEMPLATE;
if (!m_options.hasLangRdfsOption()) {
for (Iterator<? extends RDFNode> i = sorted( m_source.listObjectProperties() ); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, "ObjectProperty", "createObjectProperty", "_PROP" );
}
}
}
/** Write any datatype properties in the vocabulary */
protected void writeDatatypeProperties() {
String template = m_options.hasPropTemplateOption() ? m_options.getPropTemplateOption() : DEFAULT_TEMPLATE;
if (!m_options.hasLangRdfsOption()) {
for (Iterator<? extends RDFNode> i = sorted( m_source.listDatatypeProperties() ); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, "DatatypeProperty", "createDatatypeProperty", "_PROP" );
}
}
}
/** Write any annotation properties in the vocabulary */
protected void writeAnnotationProperties() {
String template = m_options.hasPropTemplateOption() ? m_options.getPropTemplateOption() : DEFAULT_TEMPLATE;
if (!m_options.hasLangRdfsOption()) {
for (Iterator<? extends RDFNode> i = sorted( m_source.listAnnotationProperties() ); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, "AnnotationProperty", "createAnnotationProperty", "_PROP" );
}
}
}
/** Write any vanilla RDF properties in the vocabulary */
protected void writeRDFProperties( boolean useOntProperty ) {
String template = m_options.hasPropTemplateOption() ? m_options.getPropTemplateOption() : DEFAULT_TEMPLATE;
String propType = useOntProperty ? "OntProperty" : "Property";
// select the appropriate properties based on the language choice
Resource[] props;
if (m_options.hasLangOwlOption()) {
props = new Resource[] {OWL.ObjectProperty, OWL.DatatypeProperty, RDF.Property};
}
else {
props = new Resource[] {RDF.Property};
}
// collect the properties to be written
List<Resource> propertyResources = new ArrayList<>();
for ( Resource prop : props )
{
for ( StmtIterator i = m_source.listStatements( null, RDF.type, prop ); i.hasNext(); )
{
propertyResources.add( i.nextStatement().getSubject() );
}
}
// now write the properties
for (Iterator<? extends RDFNode> i = sorted( propertyResources ); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, propType, "create" + propType, "_PROP" );
}
}
/** Write any classes in the vocabulary */
protected void writeClasses() {
if (m_options.hasNoclassesOption()) {
return;
}
if (m_options.hasClassSectionOption()) {
writeln( 0, m_options.getClassSectionOption());
}
if (useOntology()) {
writeOntClasses();
}
else {
writeRDFClasses();
}
}
/** Write classes as ontology terms */
protected void writeOntClasses() {
String template = m_options.hasClassTemplateOption() ? m_options.getClassTemplateOption() : DEFAULT_TEMPLATE;
for (Iterator<? extends RDFNode> i = sorted( m_source.listClasses() ); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, "OntClass", "createClass", "_CLASS" );
}
}
/** Write classes as vanilla RDF terms */
protected void writeRDFClasses() {
String template = m_options.hasClassTemplateOption() ? m_options.getClassTemplateOption() : DEFAULT_TEMPLATE;
// make sure we're looking for the appropriate type of class
Resource cls = OWL.Class;
if (m_options.hasLangRdfsOption()) {
cls = RDFS.Class;
}
// collect the classes to list
List<Resource> classes = m_source.listStatements( null, RDF.type, cls ).mapWith( s -> s.getSubject()).toList();
for (Iterator<? extends RDFNode> i = sorted( classes ); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, "Resource", "createResource", "_CLASS" );
}
}
/** Write any instances (individuals) in the vocabulary */
protected void writeIndividuals() {
if (m_options.hasNoindividualsOption()) {
return;
}
if (m_options.hasIndividualsSectionOption()) {
writeln( 0, m_options.getIndividualsSectionOption() );
}
if (useOntology()) {
writeOntIndividuals();
}
else {
writeRDFIndividuals();
}
}
/** Write individuals as ontology terms */
protected void writeOntIndividuals() {
String template = m_options.hasIndividualTemplateOption() ? m_options.getIndividualTemplateOption() : DEFAULT_INDIVIDUAL_TEMPLATE;
for (Iterator<? extends RDFNode> i = selectIndividuals(); i.hasNext(); ) {
Individual ind = ((Resource) i.next()).as( Individual.class );
// do we have a local class resource
Resource cls = ind.getOntClass();
if (cls == null) { cls = OWL.Thing; }
String varName = m_resourcesToNames.get( cls );
String valType = (varName != null) ? varName : "M_MODEL.createClass( \"" + cls.getURI() + "\" )";
// push the individuals type onto the stack
addReplacementPattern( "valtype", valType );
writeValue( ind, template, "Individual", "createIndividual", "_INSTANCE" );
pop( 1 );
}
}
/** Write individuals as vanilla RDF terms */
protected void writeRDFIndividuals() {
String template = m_options.hasIndividualTemplateOption() ? m_options.getIndividualTemplateOption() : DEFAULT_RDFS_INDIVIDUAL_TEMPLATE;
for (Iterator<? extends RDFNode> i = selectIndividuals(); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, "Resource", "createResource", "_INSTANCE" );
}
}
/** Answer an iterator over the individuals selected for output */
protected ExtendedIterator<? extends RDFNode> selectIndividuals() {
List<Resource> candidates = new ArrayList<>();
for (StmtIterator i = m_source.listStatements( null, RDF.type, (RDFNode) null ); i.hasNext(); ) {
Statement candidate = i.nextStatement();
if (candidate.getObject().isResource()) {
Resource candObj = candidate.getResource();
Resource candSubj = candidate.getSubject();
// ignore RDFS and OWL builtins
if (!candObj.isAnon()) {
String candTypeURI = candObj.getURI();
if (candTypeURI.startsWith( RDF.getURI() ) ||
candTypeURI.startsWith( OWL.getURI() ) ||
candTypeURI.startsWith( RDFS.getURI() )) {
continue;
}
}
// note that whether candSubj is included is tested later on by {@link #filter}
if (!candSubj.isAnon() && (isIncluded( candObj ) || isIncluded( candSubj )) && !candidates.contains( candSubj )) {
candidates.add( candSubj );
}
}
}
return sorted( candidates );
}
/**
* Answer true if the given resource is accepted for presentation in the output, which
* is true iff it is a URI node, whose namespace is one of the accepted namespaces in
* {@link #m_includeURI}.
* @param r A resource to test
* @return True if the resource is to be included in the generated output
*/
protected boolean isIncluded( Resource r ) {
boolean accepted = false;
if (!r.isAnon()) {
String uri = r.getURI();
for (Iterator<String> j = m_includeURI.iterator(); !accepted && j.hasNext(); ) {
accepted = uri.startsWith( j.next() );
}
}
return accepted;
}
/** Write any datatypes in the vocabulary */
protected void writeDatatypes() {
if (m_options.hasNodatatypesOption()) {
return;
}
if (m_options.hasDatatypesSectionOption()) {
writeln( 0, m_options.getDatatypesSectionOption() );
}
String template = m_options.hasDatatypeTemplateOption() ? m_options.getDatatypeTemplateOption() : DEFAULT_TEMPLATE;
// Cannot create a full RDFDatatype object since we don't know how to parse these custom types, but we can at least specify a Resource
for (Iterator<? extends RDFNode> i = selectDatatypes(); i.hasNext(); ) {
writeValue( (Resource) i.next(), template, "Resource", "createResource", "_DATATYPE" );
}
}
/** Answer an iterator over the datatypes selected for output */
protected ExtendedIterator<? extends RDFNode> selectDatatypes() {
List<Resource> candidates = new ArrayList<>();
for (StmtIterator i = m_source.listStatements( null, RDF.type, RDFS.Datatype ); i.hasNext(); ) {
Statement candidate = i.nextStatement();
if (candidate.getObject().isResource()) {
Resource candSubj = candidate.getSubject();
// ignore XSD builtins
if (!candSubj.isAnon()) {
String candTypeURI = candSubj.getURI();
if (candTypeURI.startsWith( XSD.getURI() )) {
continue;
}
}
// note that whether candSubj is included is tested later on by {@link #filter}
if (!candSubj.isAnon() && !candidates.contains( candSubj )) {
candidates.add( candSubj );
}
}
}
return sorted( candidates );
}
/** Write the value declaration out using the given template, optionally creating comments */
protected void writeValue( Resource r, String template, String valueClass, String creator, String disambiguator ) {
if (!filter( r )) {
if (!noComments() && hasComment( r )) {
writeln( 1, formatComment( getComment( r ) ) );
}
// push the local bindings for the substitution onto the stack
addReplacementPattern( "valuri", r.getURI() );
addReplacementPattern( "valname", getValueName( r, disambiguator ));
addReplacementPattern( "valclass", valueClass );
addReplacementPattern( "valcreator", creator );
// write out the value
writeln( 1, substitute( template ) );
writeln( 1 );
// pop the local replacements off the stack
pop( 4 );
}
}
/** Answer true if the given resource has an rdf:comment */
protected boolean hasComment( Resource r ) {
return r.hasProperty( RDFS.comment ) ;
}
/** Answer all of the commentary on the given resource, as a string */
protected String getComment( Resource r ) {
StringBuffer comment = new StringBuffer();
// collect any RDFS or DAML comments attached to the node
for (NodeIterator ni = m_source.listObjectsOfProperty( r, RDFS.comment ); ni.hasNext(); ) {
RDFNode n = ni.nextNode();
if (n instanceof Literal) {
comment.append( ((Literal) n).getLexicalForm().trim() );
}
else {
System.err.println( "Warning: Comment on resource <" + r.getURI() + "> is not a literal: " + n );
}
}
return comment.toString();
}
/** Format the comment as Javadoc, and limit the line width */
protected String formatComment( String comment ) {
StringBuffer buf = new StringBuffer();
buf.append( "/** <p>" );
boolean inSpace = false;
int pos = buf.length();
boolean singleLine = true;
// now format the comment by compacting whitespace and limiting the line length
// add the prefix to the start of each line
for (int i = 0; i < comment.length(); i++ ) {
char c = comment.charAt( i );
// compress whitespace
if (Character.isWhitespace( c )) {
if (inSpace) {
continue; // more than one space is ignored
}
else {
c = ' '; // map all whitespace to 0x20
inSpace = true;
}
}
else {
inSpace = false;
}
// escapes?
if (c == '\\') {
c = comment.charAt( ++i );
switch (c) {
case 'n':
buf.append( m_nl );
pos = indentTo( 1, buf );
buf.append( " * " );
pos += 3;
singleLine = false;
break;
default:
// add other escape sequences above
break;
}
}
else if (c == '<') {
buf.append( "<" );
pos += 4;
}
else if (c == '>') {
buf.append( ">" );
pos += 4;
}
else if (c == '&') {
buf.append( "&" );
pos += 5;
}
else {
// add the char
buf.append( c );
pos++;
}
// wrap any very long lines at 120 chars
if ((pos > COMMENT_LENGTH_LIMIT) && (inSpace)) {
buf.append( m_nl );
pos = indentTo( 1, buf );
buf.append( " * " );
pos += 3;
singleLine = false;
}
}
buf.append( "</p>" );
buf.append( singleLine ? "" : m_nl );
indentTo( singleLine ? 0 : 1, buf );
buf.append( " */" );
return buf.toString();
}
/** Answer true if resource r <b>does not</b> show in output */
protected boolean filter( Resource r ) {
if (r.isAnon()) {
return true;
}
// if we've already processed this resource once, ignore it next time
if (m_resourcesToNames.containsKey( r )) {
return true;
}
// search the allowed URI's
for ( String uri : m_includeURI )
{
if ( r.getURI().startsWith( uri ) )
{
// in
return false;
}
}
// we allow individuals whose class is not in the included NS's, unless opt strict-individuals is true */
if (!m_options.hasStrictIndividualsOption()) {
for (StmtIterator j = r.listProperties( RDF.type ); j.hasNext(); ) {
// we search the rdf:types of this resource
Resource typeRes = j.nextStatement().getResource();
if (!typeRes.isAnon()) {
String typeURI = typeRes.getURI();
// for any type that is in a permitted NS
for ( String uri : m_includeURI )
{
if ( typeURI.startsWith( uri ) )
{
// in
return false;
}
}
}
}
}
// default is out
return true;
}
/** Answer the Java value name for the URI */
protected String getValueName( Resource r, String disambiguator ) {
// the id name is basically the local name of the resource, possibly in upper case
String name = m_options.hasUcNamesOption() ? getUCValueName( r ) : r.getLocalName();
// must be legal java
name = asLegalJavaID( name, false );
// must not clash with an existing name
int attempt = 0;
String baseName = name;
while (m_usedNames.contains( name )) {
name = (attempt == 0) ? (name + disambiguator) : (baseName + disambiguator + attempt);
attempt++;
}
// record this name so that we don't use it again (which will stop the vocabulary from compiling)
m_usedNames.add( name );
// record the mapping from resource to name
m_resourcesToNames.put( r, name );
return name;
}
/** Answer the local name of resource r mapped to upper case */
protected String getUCValueName( Resource r ) {
StringBuffer buf = new StringBuffer();
String localName = r.getLocalName();
char lastChar = 0;
for (int i = 0; i < localName.length(); i++) {
char c = localName.charAt(i);
if (Character.isLowerCase(lastChar) && Character.isUpperCase(c)) {
buf.append( '_' );
}
buf.append( Character.toUpperCase(c) );
lastChar = c;
}
return buf.toString();
}
/** Answer an iterator that contains the elements of the given list, but sorted by URI */
protected ExtendedIterator<? extends RDFNode> sorted( ExtendedIterator<? extends RDFNode> i ) {
return sorted( i.toList() );
}
/** Answer an iterator that contains the elements of the given iterator, but sorted by URI */
protected ExtendedIterator<? extends RDFNode> sorted( List<? extends RDFNode> members ) {
Collections.sort( members, new Comparator<RDFNode>() {
@Override
public int compare( RDFNode n0, RDFNode n1 ) {
if (n0.isLiteral() || n1.isLiteral()) {
if (n0.isLiteral() && n1.isLiteral()) {
// two literals
Literal l0 = (Literal) n0;
Literal l1 = (Literal) n1;
return l0.getLexicalForm().compareTo( l1.getLexicalForm() );
}
else {
return n0.isLiteral() ? -1 : 1;
}
}
else {
Resource r0 = (Resource) n0;
Resource r1 = (Resource) n1;
if (r0.isAnon() && r1.isAnon()) {
// two anonID's - the order is important as long as its consistent
return r0.getId().toString().compareTo( r1.getId().toString() );
}
else if (r0.isAnon()) {
return -1;
}
else if (r1.isAnon()) {
return 1;
}
else {
// two named resources
return r0.getURI().compareTo( r1.getURI() );
}
}
}} );
return WrappedIterator.create( members.iterator() );
}
//==============================================================================
// Inner class definitions
//==============================================================================
public interface SchemagenOptions {
/* Constants for the various options we can set */
public enum OPT {
/** Select an alternative config file; use <code>-c <filename></code> on command line */
CONFIG_FILE,
/** Turn off all comment output; use <code>--nocomments</code> on command line; use <code>sgen:noComments</code> in config file */
NO_COMMENTS,
/** Nominate the URL of the input document; use <code>-i <URL></code> on command line; use <code>sgen:input</code> in config file */
INPUT,
/** Specify that the language of the source is DAML+OIL; use <code>--daml</code> on command line; use <code>sgen:daml</code> in config file */
LANG_DAML,
/** Specify that the language of the source is OWL (the default); use <code>--owl</code> on command line; use <code>sgen:owl</code> in config file */
LANG_OWL,
/** Specify that the language of the source is RDFS; use <code>--rdfs</code> on command line; use <code>sgen:rdfs</code> in config file */
LANG_RDFS,
/** Specify that destination file; use <code>-o <fileName></code> on command line; use <code>sgen:output</code> in config file */
OUTPUT,
/** Specify the file header; use <code>--header "..."</code> on command line; use <code>sgen:header</code> in config file */
HEADER,
/** Specify the file footer; use <code>--footer "..."</code> on command line; use <code>sgen:footer</code> in config file */
FOOTER,
/** Specify the uri of the configuration root node; use <code>--root <URL></code> on command line */
ROOT,
/** Specify the marker string for substitutions, default is '%'; use <code>-m "..."</code> on command line; use <code>sgen:marker</code> in config file */
MARKER,
/** Specify the packagename; use <code>--package <packagename></code> on command line; use <code>sgen:package</code> in config file */
PACKAGENAME,
/** Use ontology terms in preference to vanilla RDF; use <code>--ontology</code> on command line; use <code>sgen:ontology</code> in config file */
ONTOLOGY,
/** The name of the generated class; use <code>-n <classname></code> on command line; use <code>sgen:classname</code> in config file */
CLASSNAME,
/** Additional decoration for class header (such as implements); use <code>--classdec <classname></code> on command line; use <code>sgen:classdec</code> in config file */
CLASSDEC,
/** The namespace URI for the vocabulary; use <code>-a <uri></code> on command line; use <code>sgen:namespace</code> in config file */
NAMESPACE,
/** Additional declarations to add at the top of the class; use <code>--declarations <...></code> on command line; use <code>sgen:declarations</code> in config file */
DECLARATIONS,
/** Section declaration for properties section; use <code>--propSection <...></code> on command line; use <code>sgen:propSection</code> in config file */
PROPERTY_SECTION,
/** Section declaration for class section; use <code>--classSection <...></code> on command line; use <code>sgen:classSection</code> in config file */
CLASS_SECTION,
/** Section declaration for individuals section; use <code>--individualsSection <...></code> on command line; use <code>sgen:individualsSection</code> in config file */
INDIVIDUALS_SECTION,
/** Section declaration for datatypes section; use <code>--datatypesSection <...></code> on command line; use <code>sgen:datatypesSection</code> in config file */
DATATYPES_SECTION,
/** Option to suppress properties in vocab file; use <code>--noproperties <...></code> on command line; use <code>sgen:noproperties</code> in config file */
NOPROPERTIES,
/** Option to suppress classes in vocab file; use <code>--noclasses <...></code> on command line; use <code>sgen:noclasses</code> in config file */
NOCLASSES,
/** Option to suppress individuals in vocab file; use <code>--noindividuals <...></code> on command line; use <code>sgen:noindividuals</code> in config file */
NOINDIVIDUALS,
/** Option to suppress datatypes in vocab file; use <code>--nodatatypes <...></code> on command line; use <code>sgen:nodatatypes</code> in config file */
NODATATYPES,
/** Option for no file header; use <code>--noheader <...></code> on command line; use <code>sgen:noheader</code> in config file */
NOHEADER,
/** Template for writing out property declarations; use <code>--propTemplate <...></code> on command line; use <code>sgen:propTemplate</code> in config file */
PROP_TEMPLATE,
/** Template for writing out class declarations; use <code>--classTemplate <...></code> on command line; use <code>sgen:classTemplate</code> in config file */
CLASS_TEMPLATE,
/** Template for writing out individual declarations; use <code>--individualTemplate <...></code> on command line; use <code>sgen:individualTemplate</code> in config file */
INDIVIDUAL_TEMPLATE,
/** Template for writing out datatype declarations; use <code>--datatypeTemplate <...></code> on command line; use <code>sgen:datatypeTemplate</code> in config file */
DATATYPE_TEMPLATE,
/** Option for mapping constant names to uppercase; use <code>--uppercase <...></code> on command line; use <code>sgen:uppercase</code> in config file */
UC_NAMES,
/** Option for including non-local URI's in vocabulary; use <code>--include <uri></code> on command line; use <code>sgen:include</code> in config file */
INCLUDE,
/** Option for adding a suffix to the generated class name; use <code>--classnamesuffix <uri></code> on command line; use <code>sgen:classnamesuffix</code> in config file */
CLASSNAME_SUFFIX,
/** Option for the presentation syntax (encoding) of the file; use <code>-e <i>encoding</i></code> on command line; use <code>sgen:encoding</code> in config file */
ENCODING,
/** Option to show the usage message; use --help on command line */
HELP,
/** Option to generate an output file with DOS (\r\n) line endings. Default is Unix line endings. */
DOS,
/** Option to generate to force the model to perform inference, off by default. */
USE_INF,
/** Option to exclude instances of classes in the allowed namespaces, where the individuals themselves are in other namespaces; use <code>--strictIndividuals</code> on command line; use <code>sgen:strictIndividuals</code> in config file */
STRICT_INDIVIDUALS,
/** Option to include the ontology source code in the generated file */
INCLUDE_SOURCE,
/** Option to turn off strict checking in .a() */
NO_STRICT
}
public static final Object[][] m_optionDefinitions = new Object[][] {
{OPT.CONFIG_FILE, new OptionDefinition( "-c", "configFile" ) },
{OPT.ROOT, new OptionDefinition( "-r", "root" ) },
{OPT.NO_COMMENTS, new OptionDefinition( "--nocomments", "noComments" ) },
{OPT.INPUT, new OptionDefinition( "-i", "input" ) },
{OPT.LANG_DAML, new OptionDefinition( "--daml", "daml" ) },
{OPT.LANG_OWL, new OptionDefinition( "--owl", "owl" ) },
{OPT.LANG_RDFS, new OptionDefinition( "--rdfs", "rdfs" ) },
{OPT.OUTPUT, new OptionDefinition( "-o", "output" ) },
{OPT.HEADER, new OptionDefinition( "--header", "header" ) },
{OPT.FOOTER, new OptionDefinition( "--footer", "footer" ) },
{OPT.MARKER, new OptionDefinition( "--marker", "marker" ) },
{OPT.PACKAGENAME, new OptionDefinition( "--package", "package" ) },
{OPT.ONTOLOGY, new OptionDefinition( "--ontology", "ontology" ) },
{OPT.CLASSNAME, new OptionDefinition( "-n", "classname" ) },
{OPT.CLASSDEC, new OptionDefinition( "--classdec", "classdec" ) },
{OPT.NAMESPACE, new OptionDefinition( "-a", "namespace" ) },
{OPT.DECLARATIONS, new OptionDefinition( "--declarations", "declarations" ) },
{OPT.PROPERTY_SECTION, new OptionDefinition( "--propSection", "propSection" ) },
{OPT.CLASS_SECTION, new OptionDefinition( "--classSection", "classSection" ) },
{OPT.INDIVIDUALS_SECTION, new OptionDefinition( "--individualsSection", "individualsSection" ) },
{OPT.DATATYPES_SECTION, new OptionDefinition( "--datatypesSection", "datatypesSection" ) },
{OPT.NOPROPERTIES, new OptionDefinition( "--noproperties", "noproperties" ) },
{OPT.NOCLASSES, new OptionDefinition( "--noclasses", "noclasses" ) },
{OPT.NOINDIVIDUALS, new OptionDefinition( "--noindividuals", "noindividuals" ) },
{OPT.NODATATYPES, new OptionDefinition( "--nodatatypes", "nodatatypes" ) },
{OPT.PROP_TEMPLATE, new OptionDefinition( "--propTemplate", "propTemplate" ) },
{OPT.CLASS_TEMPLATE, new OptionDefinition( "--classTemplate", "classTemplate" ) },
{OPT.INDIVIDUAL_TEMPLATE, new OptionDefinition( "--individualTemplate", "individualTemplate" ) },
{OPT.DATATYPE_TEMPLATE, new OptionDefinition( "--datatypeTemplate", "datatypeTemplate" ) },
{OPT.UC_NAMES, new OptionDefinition( "--uppercase", "uppercase" ) },
{OPT.INCLUDE, new OptionDefinition( "--include", "include" ) },
{OPT.CLASSNAME_SUFFIX, new OptionDefinition( "--classnamesuffix", "classnamesuffix" )},
{OPT.NOHEADER, new OptionDefinition( "--noheader", "noheader" )},
{OPT.ENCODING, new OptionDefinition( "-e", "encoding" )},
{OPT.HELP, new OptionDefinition( "--help", "help" )},
{OPT.DOS, new OptionDefinition( "--dos", "dos" )},
{OPT.USE_INF, new OptionDefinition( "--inference", "inference" )},
{OPT.STRICT_INDIVIDUALS, new OptionDefinition( "--strictIndividuals", "strictIndividuals" )},
{OPT.INCLUDE_SOURCE, new OptionDefinition( "--includeSource", "includeSource" )},
{OPT.NO_STRICT, new OptionDefinition( "--nostrict", "noStrict")},
};
public boolean hasConfigFileOption();
public String getConfigFileOption();
public boolean hasRootOption();
public String getRootOption();
public boolean hasNoCommentsOption();
public String getNoCommentsOption();
public boolean hasInputOption();
public Resource getInputOption();
public boolean hasLangOwlOption();
public String getLangOwlOption();
public boolean hasLangRdfsOption();
public String getLangRdfsOption();
public boolean hasOutputOption();
public String getOutputOption();
public boolean hasHeaderOption();
public String getHeaderOption();
public boolean hasFooterOption();
public String getFooterOption();
public boolean hasMarkerOption();
public String getMarkerOption();
public boolean hasPackagenameOption();
public String getPackagenameOption();
public boolean hasOntologyOption();
public String getOntologyOption();
public boolean hasClassnameOption();
public String getClassnameOption();
public boolean hasClassdecOption();
public String getClassdecOption();
public boolean hasNamespaceOption();
public Resource getNamespaceOption();
public boolean hasDeclarationsOption();
public String getDeclarationsOption();
public boolean hasPropertySectionOption();
public String getPropertySectionOption();
public boolean hasClassSectionOption();
public String getClassSectionOption();
public boolean hasIndividualsSectionOption();
public String getIndividualsSectionOption();
public boolean hasDatatypesSectionOption();
public String getDatatypesSectionOption();
public boolean hasNopropertiesOption();
public boolean hasNoclassesOption();
public boolean hasNoindividualsOption();
public boolean hasNodatatypesOption();
public boolean hasPropTemplateOption();
public String getPropTemplateOption();
public boolean hasClassTemplateOption();
public String getClassTemplateOption();
public boolean hasIndividualTemplateOption();
public String getIndividualTemplateOption();
public boolean hasDatatypeTemplateOption();
public String getDatatypeTemplateOption();
public boolean hasUcNamesOption();
public boolean hasIncludeOption();
public List<String> getIncludeOption();
public boolean hasClassnameSuffixOption();
public String getClassnameSuffixOption();
public boolean hasNoheaderOption();
public boolean hasEncodingOption();
public String getEncodingOption();
public boolean hasHelpOption();
public String getHelpOption();
public boolean hasDosOption();
public boolean hasUseInfOption();
public boolean hasStrictIndividualsOption();
public boolean hasIncludeSourceOption();
public boolean hasNoStrictOption();
}
public static class SchemagenOptionsImpl
implements SchemagenOptions
{
// Instance variables
/** The list of command line arguments */
private List<String> m_cmdLineArgs = new ArrayList<>();
/** The root of the options in the config file */
private Resource m_root;
/** The model that contains the configuration information */
private Model m_config = ModelFactory.createDefaultModel();
// Constructor
public SchemagenOptionsImpl( String[] args ) {
m_cmdLineArgs = Arrays.asList( args );
// check to see if there's a specified config file
String configURL = DEFAULT_CONFIG_URI;
if (hasConfigFileOption()) {
// check for protocol; add file: if not specified
configURL = SchemagenUtils.urlCheck( getConfigFileOption() );
}
// try to read the config URI
try {
FileManager.get().readModel( m_config, configURL );
}
catch (Exception e) {
// if the user left the default config URI in place, it's not an error to fail to read it
if (!configURL.equals( DEFAULT_CONFIG_URI )) {
throw new SchemagenException( "Failed to read configuration from URL: " + configURL, e );
}
}
// ensure we have a root URI for the configuration model
determineConfigRoot();
}
/**
* Return the configuration model used to hold config information
* @return Model
*/
protected Model getConfigModel() {
return m_config;
}
/**
* Return the root resource to which configuration information is attached
* @return Resource
*/
protected Resource getConfigRoot() {
if (m_root == null) {
determineConfigRoot();
}
return m_root;
}
// Internal implementation methods
/** Determine the root resource in the configuration file */
protected void determineConfigRoot() {
if (hasValue( OPT.ROOT )) {
m_root = m_config.getResource( getStringValue( OPT.ROOT ) );
}
else {
// no specified root, we assume there is only one with type sgen:Config
StmtIterator i = m_config.listStatements( null, RDF.type, m_config.getResource( NS + "Config" ) );
if (i.hasNext()) {
m_root = i.nextStatement().getSubject();
}
else {
// no configuration root, so we invent one
m_root = m_config.createResource();
}
}
}
/** Answer true if the given option is set to true */
protected boolean isTrue( OPT option ) {
return getOpt( option ).isTrue( m_cmdLineArgs, m_root );
}
/** Answer true if the given option has value */
protected boolean hasValue( OPT option ) {
return getOpt( option ).hasValue( m_cmdLineArgs, m_root );
}
/** Answer the value of the option or null */
protected RDFNode getValue( OPT option ) {
return getOpt( option ).getValue( m_cmdLineArgs, m_root );
}
/** Answer the String value of the option or null */
protected String getStringValue( OPT option ) {
return getOpt( option ).getStringValue( m_cmdLineArgs, m_root );
}
/** Answer true if the given option has a resource value */
protected boolean hasResourceValue( OPT option ) {
return getOpt( option ).hasResourceValue( m_cmdLineArgs, m_root );
}
/** Answer the value of the option or null */
protected Resource getResource( OPT option ) {
return getOpt( option ).getResource( m_cmdLineArgs, m_root );
}
/** Answer all values for the given options as Strings */
protected List<String> getAllValues( OPT option ) {
List<String> values = new ArrayList<>();
OptionDefinition opt = getOpt( option );
// look in the command line arguments
for (Iterator<String> i = m_cmdLineArgs.iterator(); i.hasNext(); ) {
String s = i.next();
if (s.equals( opt.m_cmdLineForm )) {
// next iterator value is the arg value
values.add( i.next() );
}
}
// now look in the config file
for (StmtIterator i = m_root.listProperties( opt.m_prop ); i.hasNext(); ) {
Statement s = i.nextStatement();
if (s.getObject() instanceof Literal) {
values.add( s.getString() );
}
else {
values.add( s.getResource().getURI() );
}
}
return values;
}
/** Answer the option object for the given option */
protected OptionDefinition getOpt( OPT option ) {
for ( Object[] m_optionDefinition : m_optionDefinitions )
{
if ( m_optionDefinition[0] == option )
{
return (OptionDefinition) m_optionDefinition[1];
}
}
return null;
}
// External interface methods
@Override
public boolean hasConfigFileOption() { return hasValue( OPT.CONFIG_FILE ); }
@Override
public String getConfigFileOption() { return getStringValue( OPT.CONFIG_FILE ); }
@Override
public boolean hasRootOption() { return hasValue( OPT.ROOT ); }
@Override
public String getRootOption() { return getStringValue( OPT.ROOT ); }
@Override
public boolean hasNoCommentsOption() { return isTrue( OPT.NO_COMMENTS ); }
@Override
public String getNoCommentsOption() { return getStringValue( OPT.NO_COMMENTS ); }
@Override
public boolean hasInputOption() { return hasValue( OPT.INPUT ); }
@Override
public Resource getInputOption() { return getResource( OPT.INPUT ); }
@Override
public boolean hasLangOwlOption() { return isTrue( OPT.LANG_OWL ); }
@Override
public String getLangOwlOption() { return getStringValue( OPT.LANG_OWL ); }
@Override
public boolean hasLangRdfsOption() { return isTrue( OPT.LANG_RDFS ); }
@Override
public String getLangRdfsOption() { return getStringValue( OPT.LANG_RDFS ); }
@Override
public boolean hasOutputOption() { return hasValue( OPT.OUTPUT ); }
@Override
public String getOutputOption() { return getStringValue( OPT.OUTPUT ); }
@Override
public boolean hasHeaderOption() { return isTrue( OPT.HEADER ); }
@Override
public String getHeaderOption() { return getStringValue( OPT.HEADER ); }
@Override
public boolean hasFooterOption() { return isTrue( OPT.FOOTER ); }
@Override
public String getFooterOption() { return getStringValue( OPT.FOOTER ); }
@Override
public boolean hasMarkerOption() { return hasValue( OPT.MARKER ); }
@Override
public String getMarkerOption() { return getStringValue( OPT.MARKER ); }
@Override
public boolean hasPackagenameOption() { return hasValue( OPT.PACKAGENAME ); }
@Override
public String getPackagenameOption() { return getStringValue( OPT.PACKAGENAME ); }
@Override
public boolean hasOntologyOption() { return isTrue( OPT.ONTOLOGY ); }
@Override
public String getOntologyOption() { return getStringValue( OPT.ONTOLOGY ); }
@Override
public boolean hasClassnameOption() { return hasValue( OPT.CLASSNAME ); }
@Override
public String getClassnameOption() { return getStringValue( OPT.CLASSNAME ); }
@Override
public boolean hasClassdecOption() { return hasValue( OPT.CLASSDEC ); }
@Override
public String getClassdecOption() { return getStringValue( OPT.CLASSDEC ); }
@Override
public boolean hasNamespaceOption() { return hasValue( OPT.NAMESPACE ); }
@Override
public Resource getNamespaceOption() { return getResource( OPT.NAMESPACE ); }
@Override
public boolean hasDeclarationsOption() { return hasValue( OPT.DECLARATIONS ); }
@Override
public String getDeclarationsOption() { return getStringValue( OPT.DECLARATIONS ); }
@Override
public boolean hasPropertySectionOption() { return hasValue( OPT.PROPERTY_SECTION ); }
@Override
public String getPropertySectionOption() { return getStringValue( OPT.PROPERTY_SECTION ); }
@Override
public boolean hasClassSectionOption() { return hasValue( OPT.CLASS_SECTION ); }
@Override
public String getClassSectionOption() { return getStringValue( OPT.CLASS_SECTION ); }
@Override
public boolean hasIndividualsSectionOption() { return hasValue( OPT.INDIVIDUALS_SECTION ); }
@Override
public String getIndividualsSectionOption() { return getStringValue( OPT.INDIVIDUALS_SECTION ); }
@Override
public boolean hasDatatypesSectionOption() { return hasValue( OPT.DATATYPES_SECTION ); }
@Override
public String getDatatypesSectionOption() { return getStringValue( OPT.DATATYPES_SECTION ); }
@Override
public boolean hasNopropertiesOption() { return isTrue( OPT.NOPROPERTIES ); }
@Override
public boolean hasNoclassesOption() { return isTrue( OPT.NOCLASSES ); }
@Override
public boolean hasNoindividualsOption() { return isTrue( OPT.NOINDIVIDUALS ); }
@Override
public boolean hasNodatatypesOption() { return isTrue( OPT.NODATATYPES ); }
@Override
public boolean hasPropTemplateOption() { return hasValue( OPT.PROP_TEMPLATE ); }
@Override
public String getPropTemplateOption() { return getStringValue( OPT.PROP_TEMPLATE ); }
@Override
public boolean hasClassTemplateOption() { return hasValue( OPT.CLASS_TEMPLATE ); }
@Override
public String getClassTemplateOption() { return getStringValue( OPT.CLASS_TEMPLATE ); }
@Override
public boolean hasIndividualTemplateOption() { return hasValue( OPT.INDIVIDUAL_TEMPLATE ); }
@Override
public String getIndividualTemplateOption() { return getStringValue( OPT.INDIVIDUAL_TEMPLATE ); }
@Override
public boolean hasDatatypeTemplateOption() { return hasValue( OPT.DATATYPE_TEMPLATE ); }
@Override
public String getDatatypeTemplateOption() { return getStringValue( OPT.DATATYPE_TEMPLATE ); }
@Override
public boolean hasUcNamesOption() { return isTrue( OPT.UC_NAMES ); }
@Override
public boolean hasIncludeOption() { return hasValue( OPT.INCLUDE ); }
@Override
public List<String> getIncludeOption() { return getAllValues( OPT.INCLUDE ); }
@Override
public boolean hasClassnameSuffixOption() { return hasValue( OPT.CLASSNAME_SUFFIX ); }
@Override
public String getClassnameSuffixOption() { return getStringValue( OPT.CLASSNAME_SUFFIX ); }
@Override
public boolean hasNoheaderOption() { return isTrue( OPT.NOHEADER ); }
@Override
public boolean hasEncodingOption() { return hasValue( OPT.ENCODING ); }
@Override
public String getEncodingOption() { return getStringValue( OPT.ENCODING ); }
@Override
public boolean hasHelpOption() { return hasValue( OPT.HELP ); }
@Override
public String getHelpOption() { return getStringValue( OPT.HELP ); }
@Override
public boolean hasDosOption() { return isTrue( OPT.DOS ); }
@Override
public boolean hasUseInfOption() { return isTrue( OPT.USE_INF ); }
@Override
public boolean hasStrictIndividualsOption() { return isTrue( OPT.STRICT_INDIVIDUALS ); }
@Override
public boolean hasIncludeSourceOption() { return isTrue( OPT.INCLUDE_SOURCE ); }
@Override
public boolean hasNoStrictOption() { return isTrue( OPT.NO_STRICT ); }
}
/** An option that can be set either on the command line or in the RDF config */
public static class OptionDefinition
{
protected String m_cmdLineForm;
protected Property m_prop;
protected OptionDefinition( String cmdLineForm, String name ) {
m_cmdLineForm = cmdLineForm;
if (name != null) {
m_prop = ResourceFactory.createProperty( NS, name );
}
}
/**
* Return the RDF property that is used when configuring this option
* via a {@link Model}
* @return The declaration property, or null
*/
public Property getDeclarationProperty() {
return m_prop;
}
/**
* Return the command line form of this option
* @return The command line form as a String
*/
public String getCommandLineForm() {
return m_cmdLineForm;
}
/**
* Answer true if this option is set to true, either on the command line
* or in the config model
*
* @return boolean
*/
protected boolean isTrue( List<String> cmdLineArgs, Resource confRoot ) {
if (cmdLineArgs.contains( m_cmdLineForm )) {
return true;
}
if (confRoot.hasProperty( m_prop )) {
return confRoot.getRequiredProperty( m_prop ).getBoolean();
}
return false;
}
/**
* Answer the string value of the parameter if set, or null otherwise. Note command line
* has precedence.
*
* @return String
*/
protected String getStringValue( List<String> cmdLineArgs, Resource confRoot ) {
RDFNode n = getValue( cmdLineArgs, confRoot );
return (n == null) ? null : (n.isLiteral() ? n.asLiteral().getLexicalForm() : n.toString() );
}
/**
* Return the value of the parameter if set, or null otherwise. Note command line
* has precedence.
*
* @return The argument value as an RDFNode
*/
protected RDFNode getValue( List<String> cmdLineArgs, Resource confRoot ) {
int index = cmdLineArgs.indexOf( m_cmdLineForm );
if (index >= 0) {
try {
return ResourceFactory.createPlainLiteral( cmdLineArgs.get( index + 1 ) );
}
catch (IndexOutOfBoundsException e) {
throw new SchemagenException( "Value for parameter " + m_cmdLineForm + " not set! Aborting.", e );
}
}
if (m_prop != null && confRoot != null && confRoot.hasProperty( m_prop )) {
return confRoot.getRequiredProperty( m_prop ).getObject();
}
// not set
return null;
}
/**
* Answer true if the parameter has a value at all.
*
* @return boolean
*/
protected boolean hasValue( List<String> cmdLineArgs, Resource confRoot ) {
return getValue( cmdLineArgs, confRoot ) != null;
}
/**
* Answer the resource value of the parameter if set, or null otherwise.
*
* @return String
*/
protected Resource getResource( List<String> cmdLineArgs, Resource confRoot ) {
int index = cmdLineArgs.indexOf( m_cmdLineForm );
if (index >= 0) {
try {
return confRoot.getModel().getResource( cmdLineArgs.get( index + 1 ) );
}
catch (IndexOutOfBoundsException e) {
System.err.println( "Value for parameter " + m_cmdLineForm + " not set! Aborting.");
}
}
if (m_prop != null && confRoot.hasProperty( m_prop )) {
return confRoot.getRequiredProperty( m_prop ).getResource();
}
// not set
return null;
}
/**
* Answer true if the parameter has a value at all.
*
* @return boolean
*/
protected boolean hasResourceValue( List<String> cmdLineArgs, Resource confRoot ) {
return getResource( cmdLineArgs, confRoot ) != null;
}
} // end inner class OptionDefinition
/** A pairing of pattern and substitution we want to apply to output */
protected class Replacement
{
protected String sub;
protected Pattern pattern;
protected Replacement( Pattern pattern, String sub) {
this.sub = sub;
this.pattern = pattern;
}
} // end inner class Replacement
/**
* <p>Schemagen runtime exception</p>
*/
public static class SchemagenException
extends RuntimeException
{
public SchemagenException( String msg, Throwable cause ) {
super( msg, cause );
}
}
/** Utility method container */
public static class SchemagenUtils
{
/** Return a URI formed from the given string, unchanged if it's already a URI or
* converted to a file URI otherwise. If not recognisable as a URL, abort.
*/
public static String urlCheck( String uriOrFile ) {
boolean legal = true;
String url = uriOrFile;
// is it a URI already? to check, we make a URL and see what happens!
try {
new URL( url );
}
catch (MalformedURLException ignore) {
legal = false;
}
// if not a legal url, assume it's a file
if (!legal) {
legal = true;
String slash = System.getProperty( "file.separator" );
url = "file:" + (uriOrFile.startsWith( slash ) ? (slash + slash) : "") + uriOrFile;
try {
new URL( url );
}
catch (MalformedURLException ignore) {
legal = false;
}
}
if (!legal) {
throw new SchemagenException( "Could not parse " + uriOrFile + " as a legal URL or a file reference. Aborting.", null );
}
return url;
}
} /* End class SchemagenUtils */
}