0001: /*****************************************************************************
0002: * Source code information
0003: * -----------------------
0004: * Original author Ian Dickinson, HP Labs Bristol
0005: * Author email Ian.Dickinson@hp.com
0006: * Package Jena 2
0007: * Web http://sourceforge.net/projects/jena/
0008: * Created 14-Apr-2003
0009: * Filename $RCSfile: schemagen.java,v $
0010: * Revision $Revision: 1.55 $
0011: * Release status $State: Exp $
0012: *
0013: * Last modified on $Date: 2008/01/02 13:42:25 $
0014: * by $Author: ian_dickinson $
0015: *
0016: * (c) Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
0017: * (see footer for full conditions)
0018: *****************************************************************************/package jena;
0019:
0020: // Imports
0021: ///////////////
0022: import java.util.*;
0023: import java.util.regex.*;
0024: import java.io.*;
0025: import java.net.MalformedURLException;
0026: import java.net.URL;
0027: import java.text.SimpleDateFormat;
0028:
0029: import org.apache.commons.logging.LogFactory;
0030: import org.apache.xerces.util.XMLChar;
0031:
0032: import com.hp.hpl.jena.ontology.*;
0033: import com.hp.hpl.jena.rdf.model.*;
0034: import com.hp.hpl.jena.util.FileManager;
0035: import com.hp.hpl.jena.util.iterator.*;
0036: import com.hp.hpl.jena.vocabulary.*;
0037: import com.hp.hpl.jena.shared.*;
0038:
0039: /**
0040: * <p>
0041: * A vocabulary generator, that will consume an ontology or other vocabulary file,
0042: * and generate a Java file with the constants from the vocabulary compiled in.
0043: * Designed to be highly flexible and customisable.
0044: * </p>
0045: *
0046: * @author Ian Dickinson, HP Labs
0047: * (<a href="mailto:Ian.Dickinson@hp.com" >email</a>)
0048: * @version CVS $Id: schemagen.java,v 1.55 2008/01/02 13:42:25 ian_dickinson Exp $
0049: */
0050: public class schemagen {
0051: // Constants
0052: //////////////////////////////////
0053:
0054: /** The namespace for the configuration model is {@value} */
0055: public static final String NS = "http://jena.hpl.hp.com/2003/04/schemagen#";
0056:
0057: /** The default location of the configuration model is {@value} */
0058: public static final String DEFAULT_CONFIG_URI = "file:schemagen.rdf";
0059:
0060: /** The default marker string for denoting substitutions is {@value} */
0061: public static final String DEFAULT_MARKER = "%";
0062:
0063: /** Default template for writing out value declarations */
0064: public static final String DEFAULT_TEMPLATE = "public static final %valclass% %valname% = m_model.%valcreator%( \"%valuri%\" );";
0065:
0066: /** Default template for writing out individual declarations */
0067: public static final String DEFAULT_INDIVIDUAL_TEMPLATE = "public static final %valclass% %valname% = m_model.%valcreator%( \"%valuri%\", %valtype% );";
0068:
0069: /** Default template for the file header */
0070: public static final String DEFAULT_HEADER_TEMPLATE = "/* CVS $"
0071: + "Id: $ */%nl%%package% %nl%%imports% %nl%/**%nl% * Vocabulary definitions from %sourceURI% %nl% * @author Auto-generated by schemagen on %date% %nl% */";
0072:
0073: /** Default line length for comments before wrap */
0074: public static final int COMMENT_LENGTH_LIMIT = 80;
0075:
0076: /* Constants for the various options we can set */
0077:
0078: /** Select an alternative config file; use <code>-c <filename></code> on command line */
0079: protected static final Object OPT_CONFIG_FILE = new Object();
0080:
0081: /** Turn off all comment output; use <code>--nocomments</code> on command line; use <code>sgen:noComments</code> in config file */
0082: protected static final Object OPT_NO_COMMENTS = new Object();
0083:
0084: /** Nominate the URL of the input document; use <code>-i <URL></code> on command line; use <code>sgen:input</code> in config file */
0085: protected static final Object OPT_INPUT = new Object();
0086:
0087: /** 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 */
0088: protected static final Object OPT_LANG_DAML = new Object();
0089:
0090: /** 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 */
0091: protected static final Object OPT_LANG_OWL = new Object();
0092:
0093: /** Specify that the language of the source is RDFS; use <code>--rdfs</code> on command line; use <code>sgen:rdfs</code> in config file */
0094: protected static final Object OPT_LANG_RDFS = new Object();
0095:
0096: /** Specify that destination file; use <code>-o <fileName></code> on command line; use <code>sgen:output</code> in config file */
0097: protected static final Object OPT_OUTPUT = new Object();
0098:
0099: /** Specify the file header; use <code>--header "..."</code> on command line; use <code>sgen:header</code> in config file */
0100: protected static final Object OPT_HEADER = new Object();
0101:
0102: /** Specify the file footer; use <code>--footer "..."</code> on command line; use <code>sgen:footer</code> in config file */
0103: protected static final Object OPT_FOOTER = new Object();
0104:
0105: /** Specify the uri of the configuration root node; use <code>--root <URL></code> on command line */
0106: protected static final Object OPT_ROOT = new Object();
0107:
0108: /** Specify the marker string for substitutions, default is '%'; use <code>-m "..."</code> on command line; use <code>sgen:marker</code> in config file */
0109: protected static final Object OPT_MARKER = new Object();
0110:
0111: /** Specify the packagename; use <code>--package <packagename></code> on command line; use <code>sgen:package</code> in config file */
0112: protected static final Object OPT_PACKAGENAME = new Object();
0113:
0114: /** Use ontology terms in preference to vanilla RDF; use <code>--ontology</code> on command line; use <code>sgen:ontology</code> in config file */
0115: protected static final Object OPT_ONTOLOGY = new Object();
0116:
0117: /** The name of the generated class; use <code>-n <classname></code> on command line; use <code>sgen:classname</code> in config file */
0118: protected static final Object OPT_CLASSNAME = new Object();
0119:
0120: /** Additional decoration for class header (such as implements); use <code>--classdec <classname></code> on command line; use <code>sgen:classdec</code> in config file */
0121: protected static final Object OPT_CLASSDEC = new Object();
0122:
0123: /** The namespace URI for the vocabulary; use <code>-a <uri></code> on command line; use <code>sgen:namespace</code> in config file */
0124: protected static final Object OPT_NAMESPACE = new Object();
0125:
0126: /** 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 */
0127: protected static final Object OPT_DECLARATIONS = new Object();
0128:
0129: /** Section declaration for properties section; use <code>--propSection <...></code> on command line; use <code>sgen:propSection</code> in config file */
0130: protected static final Object OPT_PROPERTY_SECTION = new Object();
0131:
0132: /** Section declaration for class section; use <code>--classSection <...></code> on command line; use <code>sgen:classSection</code> in config file */
0133: protected static final Object OPT_CLASS_SECTION = new Object();
0134:
0135: /** Section declaration for individuals section; use <code>--individualsSection <...></code> on command line; use <code>sgen:individualsSection</code> in config file */
0136: protected static final Object OPT_INDIVIDUALS_SECTION = new Object();
0137:
0138: /** Option to suppress properties in vocab file; use <code>--noproperties <...></code> on command line; use <code>sgen:noproperties</code> in config file */
0139: protected static final Object OPT_NOPROPERTIES = new Object();
0140:
0141: /** Option to suppress classes in vocab file; use <code>--noclasses <...></code> on command line; use <code>sgen:noclasses</code> in config file */
0142: protected static final Object OPT_NOCLASSES = new Object();
0143:
0144: /** Option to suppress individuals in vocab file; use <code>--noindividuals <...></code> on command line; use <code>sgen:noindividuals</code> in config file */
0145: protected static final Object OPT_NOINDIVIDUALS = new Object();
0146:
0147: /** Option for no file header; use <code>--noheader <...></code> on command line; use <code>sgen:noheader</code> in config file */
0148: protected static final Object OPT_NOHEADER = new Object();
0149:
0150: /** Template for writing out property declarations; use <code>--propTemplate <...></code> on command line; use <code>sgen:propTemplate</code> in config file */
0151: protected static final Object OPT_PROP_TEMPLATE = new Object();
0152:
0153: /** Template for writing out class declarations; use <code>--classTemplate <...></code> on command line; use <code>sgen:classTemplate</code> in config file */
0154: protected static final Object OPT_CLASS_TEMPLATE = new Object();
0155:
0156: /** Template for writing out individual declarations; use <code>--individualTemplate <...></code> on command line; use <code>sgen:individualTemplate</code> in config file */
0157: protected static final Object OPT_INDIVIDUAL_TEMPLATE = new Object();
0158:
0159: /** Option for mapping constant names to uppercase; use <code>--uppercase <...></code> on command line; use <code>sgen:uppercase</code> in config file */
0160: protected static final Object OPT_UC_NAMES = new Object();
0161:
0162: /** 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 */
0163: protected static final Object OPT_INCLUDE = new Object();
0164:
0165: /** 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 */
0166: protected static final Object OPT_CLASSNAME_SUFFIX = new Object();
0167:
0168: /** 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 */
0169: protected static final Object OPT_ENCODING = new Object();
0170:
0171: /** Option to show the usage message; use --help on command line */
0172: protected static final Object OPT_HELP = new Object();
0173:
0174: /** Option to generate an output file with DOS (\r\n) line endings. Default is Unix line endings. */
0175: protected static final Object OPT_DOS = new Object();
0176:
0177: /** Option to generate to force the model to perform inference, off by default. */
0178: protected static final Object OPT_USE_INF = new Object();
0179:
0180: /** 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 */
0181: protected static final Object OPT_STRICT_INDIVIDUALS = new Object();
0182:
0183: /** Option to include the ontology source code in the generated file */
0184: protected static final Object OPT_INCLUDE_SOURCE = new Object();
0185:
0186: /** List of Java reserved keywords, see <a href="http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html">this list</a>. */
0187: public static final String[] JAVA_KEYWORDS = { "abstract",
0188: "continue", "for", "new", "switch", "assert", "default",
0189: "goto", "package", "synchronized", "boolean", "do", "if",
0190: "private", "this", "break", "double", "implements",
0191: "protected", "throw", "byte", "else", "import", "public",
0192: "throws", "case", "enum", "instanceof", "return",
0193: "transient", "catch", "extends", "int", "short", "try",
0194: "char", "final", "interface", "static", "void", "class",
0195: "finally", "long", "strictfp", "volatile", "const",
0196: "float", "native", "super", "while" };
0197:
0198: // Static variables
0199: //////////////////////////////////
0200:
0201: private static List KEYWORD_LIST;
0202: static {
0203: KEYWORD_LIST = Arrays.asList(JAVA_KEYWORDS);
0204: }
0205:
0206: // Instance variables
0207: //////////////////////////////////
0208:
0209: /** The list of command line arguments */
0210: protected List m_cmdLineArgs;
0211:
0212: /** The root of the options in the config file */
0213: protected Resource m_root;
0214:
0215: /** The model that contains the configuration information */
0216: protected Model m_config = ModelFactory.createDefaultModel();
0217:
0218: /** The model that contains the input source */
0219: protected OntModel m_source;
0220:
0221: /** The output stream we write to */
0222: protected PrintStream m_output;
0223:
0224: /** Option definitions */
0225: protected Object[][] m_optionDefinitions = new Object[][] {
0226: { OPT_CONFIG_FILE, new OptionDefinition("-c", null) },
0227: { OPT_ROOT, new OptionDefinition("-r", null) },
0228: { OPT_NO_COMMENTS,
0229: new OptionDefinition("--nocomments", "noComments") },
0230: { OPT_INPUT, new OptionDefinition("-i", "input") },
0231: { OPT_LANG_DAML, new OptionDefinition("--daml", "daml") },
0232: { OPT_LANG_OWL, new OptionDefinition("--owl", "owl") },
0233: { OPT_LANG_RDFS, new OptionDefinition("--rdfs", "rdfs") },
0234: { OPT_OUTPUT, new OptionDefinition("-o", "output") },
0235: { OPT_HEADER, new OptionDefinition("--header", "header") },
0236: { OPT_FOOTER, new OptionDefinition("--footer", "footer") },
0237: { OPT_MARKER, new OptionDefinition("--marker", "marker") },
0238: { OPT_PACKAGENAME,
0239: new OptionDefinition("--package", "package") },
0240: { OPT_ONTOLOGY,
0241: new OptionDefinition("--ontology", "ontology") },
0242: { OPT_CLASSNAME, new OptionDefinition("-n", "classname") },
0243: { OPT_CLASSDEC,
0244: new OptionDefinition("--classdec", "classdec") },
0245: { OPT_NAMESPACE, new OptionDefinition("-a", "namespace") },
0246: {
0247: OPT_DECLARATIONS,
0248: new OptionDefinition("--declarations",
0249: "declarations") },
0250: {
0251: OPT_PROPERTY_SECTION,
0252: new OptionDefinition("--propSection", "propSection") },
0253: {
0254: OPT_CLASS_SECTION,
0255: new OptionDefinition("--classSection",
0256: "classSection") },
0257: {
0258: OPT_INDIVIDUALS_SECTION,
0259: new OptionDefinition("--individualsSection",
0260: "individualsSection") },
0261: {
0262: OPT_NOPROPERTIES,
0263: new OptionDefinition("--noproperties",
0264: "noproperties") },
0265: { OPT_NOCLASSES,
0266: new OptionDefinition("--noclasses", "noclasses") },
0267: {
0268: OPT_NOINDIVIDUALS,
0269: new OptionDefinition("--noindividuals",
0270: "noindividuals") },
0271: {
0272: OPT_PROP_TEMPLATE,
0273: new OptionDefinition("--propTemplate",
0274: "propTemplate") },
0275: {
0276: OPT_CLASS_TEMPLATE,
0277: new OptionDefinition("--classTemplate",
0278: "classTemplate") },
0279: {
0280: OPT_INDIVIDUAL_TEMPLATE,
0281: new OptionDefinition("--individualTemplate",
0282: "individualTemplate") },
0283: { OPT_UC_NAMES,
0284: new OptionDefinition("--uppercase", "uppercase") },
0285: { OPT_INCLUDE, new OptionDefinition("--include", "include") },
0286: {
0287: OPT_CLASSNAME_SUFFIX,
0288: new OptionDefinition("--classnamesuffix",
0289: "classnamesuffix") },
0290: { OPT_NOHEADER,
0291: new OptionDefinition("--noheader", "noheader") },
0292: { OPT_ENCODING, new OptionDefinition("-e", "encoding") },
0293: { OPT_HELP, new OptionDefinition("--help", null) },
0294: { OPT_DOS, new OptionDefinition("--dos", "dos") },
0295: { OPT_USE_INF,
0296: new OptionDefinition("--inference", "inference") },
0297: {
0298: OPT_STRICT_INDIVIDUALS,
0299: new OptionDefinition("--strictIndividuals",
0300: "strictIndividuals") },
0301: {
0302: OPT_INCLUDE_SOURCE,
0303: new OptionDefinition("--includeSource",
0304: "includeSource") }, };
0305:
0306: /** Stack of replacements to apply */
0307: protected List m_replacements = new ArrayList();
0308:
0309: /** Output file newline char - default is Unix, override with --dos */
0310: protected String m_nl = "\n";
0311:
0312: /** Size of indent step */
0313: protected int m_indentStep = 4;
0314:
0315: /** Set of names used so far */
0316: protected Set m_usedNames = new HashSet();
0317:
0318: /** Map from resources to java names */
0319: protected Map m_resourcesToNames = new HashMap();
0320:
0321: /** List of allowed namespace URI strings for admissible values */
0322: protected List m_includeURI = new ArrayList();
0323:
0324: // Constructors
0325: //////////////////////////////////
0326:
0327: // External signature methods
0328: //////////////////////////////////
0329:
0330: /* Main entry point. See Javadoc for details of the many command line arguments */
0331: public static void main(String[] args) {
0332: new schemagen().go(args);
0333: }
0334:
0335: // Internal implementation methods
0336: //////////////////////////////////
0337:
0338: /** Read the configuration parameters and do setup */
0339: protected void go(String[] args) {
0340: // save the command line parameters
0341: m_cmdLineArgs = Arrays.asList(args);
0342:
0343: // check for user requesting help
0344: if (m_cmdLineArgs.contains(getOpt(OPT_HELP).m_cmdLineForm)) {
0345: usage();
0346: }
0347:
0348: // check to see if there's a specified config file
0349: String configURL = DEFAULT_CONFIG_URI;
0350: if (hasValue(OPT_CONFIG_FILE)) {
0351: // check for protocol; add file: if not specified
0352: configURL = urlCheck(getValue(OPT_CONFIG_FILE));
0353: }
0354:
0355: // try to read the config URI
0356: try {
0357: FileManager.get().readModel(m_config, configURL);
0358: } catch (Exception e) {
0359: // if the user left the default config URI in place, it's not an error to fail to read it
0360: if (!configURL.equals(DEFAULT_CONFIG_URI)) {
0361: abort("Failed to read configuration from URI "
0362: + configURL, e);
0363: }
0364: }
0365:
0366: // got the configuration, now we can begin processing
0367: processInput();
0368: }
0369:
0370: /** The sequence of steps to process an entire file */
0371: protected void processInput() {
0372: determineConfigRoot();
0373: determineLanguage();
0374: selectInput();
0375: selectOutput();
0376: setGlobalReplacements();
0377:
0378: processHeader();
0379: writeClassDeclaration();
0380: writeInitialDeclarations();
0381: writeProperties();
0382: writeClasses();
0383: writeIndividuals();
0384: writeClassClose();
0385: processFooter();
0386: closeOutput();
0387: }
0388:
0389: /** Determine the root resource in the configuration file */
0390: protected void determineConfigRoot() {
0391: if (hasValue(OPT_ROOT)) {
0392: String rootURI = getValue(OPT_ROOT);
0393: m_root = m_config.getResource(rootURI);
0394: } else {
0395: // no specified root, we assume there is only one with type sgen:Config
0396: StmtIterator i = m_config.listStatements(null, RDF.type,
0397: m_config.getResource(NS + "Config"));
0398: if (i.hasNext()) {
0399: m_root = i.nextStatement().getSubject();
0400: } else {
0401: // no configuration root, so we invent one
0402: m_root = m_config.createResource();
0403: }
0404: }
0405:
0406: // add any extra uri's that are allowed in the filter
0407: m_includeURI.addAll(getAllValues(OPT_INCLUDE));
0408: }
0409:
0410: /** Create the source model after determining which input language */
0411: protected void determineLanguage() {
0412: OntModelSpec s = null;
0413: if (isTrue(OPT_LANG_DAML)) {
0414: // DAML language specified
0415: if (isTrue(OPT_USE_INF)) {
0416: s = OntModelSpec.DAML_MEM_RULE_INF;
0417: } else {
0418: s = OntModelSpec.DAML_MEM;
0419: }
0420: } else if (isTrue(OPT_LANG_RDFS)) {
0421: // RDFS language specified
0422: if (isTrue(OPT_USE_INF)) {
0423: s = OntModelSpec.RDFS_MEM_RDFS_INF;
0424: } else {
0425: s = OntModelSpec.RDFS_MEM;
0426: }
0427: } else {
0428: // owl is the default
0429: // s = OntModelSpec.getDefaultSpec( ProfileRegistry.OWL_LANG );
0430: if (isTrue(OPT_USE_INF)) {
0431: s = OntModelSpec.OWL_MEM_RULE_INF;
0432: } else {
0433: s = OntModelSpec.OWL_MEM;
0434: }
0435: }
0436:
0437: m_source = ModelFactory.createOntologyModel(s, null);
0438: m_source.getDocumentManager().setProcessImports(false);
0439: }
0440:
0441: /** Identify the URL that is to be read in and translated to a vocabulary file, and load the source into the source model */
0442: protected void selectInput() {
0443: if (!hasResourceValue(OPT_INPUT)) {
0444: usage();
0445: }
0446:
0447: String input = urlCheck(getValue(OPT_INPUT));
0448: String syntax = getValue(OPT_ENCODING);
0449:
0450: try {
0451: FileManager.get().readModel(m_source, input, syntax);
0452: } catch (JenaException e) {
0453: abort("Failed to read input source " + input, e);
0454: }
0455: }
0456:
0457: /** Identify the file we are to write the output to */
0458: protected void selectOutput() {
0459: String outFile = getValue(OPT_OUTPUT);
0460: if (outFile == null) {
0461: m_output = System.out;
0462: } else {
0463: try {
0464: File out = new File(outFile);
0465:
0466: if (out.isDirectory()) {
0467: // create a file in this directory named classname.java
0468: String fileName = outFile
0469: + System.getProperty("file.separator")
0470: + getClassName() + ".java";
0471: out = new File(fileName);
0472: }
0473:
0474: m_output = new PrintStream(new FileOutputStream(out));
0475: } catch (Exception e) {
0476: abort(
0477: "I/O error while trying to open file for writing: "
0478: + outFile, e);
0479: }
0480: }
0481:
0482: // check for DOS line endings
0483: if (isTrue(OPT_DOS)) {
0484: m_nl = "\r\n";
0485: }
0486: }
0487:
0488: /** Process the header at the start of the file, if defined */
0489: protected void processHeader() {
0490: String header = hasValue(OPT_HEADER) ? getValue(OPT_HEADER)
0491: : DEFAULT_HEADER_TEMPLATE;
0492:
0493: // user can turn of header processing, default is to have it on
0494: if (!isTrue(OPT_NOHEADER)) {
0495: writeln(0, substitute(header));
0496: } else {
0497: // we have to do the imports at least
0498: writeln(0, "import com.hp.hpl.jena.rdf.model.*;");
0499: if (isTrue(OPT_ONTOLOGY)) {
0500: writeln(0, "import com.hp.hpl.jena.ontology.*;");
0501: }
0502: if (isTrue(OPT_INCLUDE_SOURCE)) {
0503: writeln(0, "import java.io.ByteArrayInputStream;");
0504: }
0505: }
0506: }
0507:
0508: /** Process the footer at the end of the file, if defined */
0509: protected void processFooter() {
0510: String footer = getValue(OPT_FOOTER);
0511:
0512: if (footer != null) {
0513: writeln(0, substitute(footer));
0514: }
0515: }
0516:
0517: /** The list of replacements that are always available */
0518: protected void setGlobalReplacements() {
0519: addReplacementPattern("date", new SimpleDateFormat(
0520: "dd MMM yyyy HH:mm").format(new Date()));
0521: addReplacementPattern("package",
0522: hasValue(OPT_PACKAGENAME) ? ("package "
0523: + getValue(OPT_PACKAGENAME) + ";") : "");
0524: addReplacementPattern("imports", getImports());
0525: addReplacementPattern("classname", getClassName());
0526: addReplacementPattern("sourceURI", getResource(OPT_INPUT)
0527: .getURI());
0528: addReplacementPattern("nl", m_nl);
0529: }
0530:
0531: /** Add a pattern-value pair to the list of available patterns */
0532: protected void addReplacementPattern(String key, String replacement) {
0533: if (replacement != null && key != null) {
0534: String marker = getValue(OPT_MARKER);
0535: marker = (marker == null) ? DEFAULT_MARKER : marker;
0536:
0537: try {
0538: m_replacements.add(new Replacement(Pattern
0539: .compile(marker + key + marker), replacement));
0540: } catch (PatternSyntaxException e) {
0541: abort("Malformed regexp pattern " + marker + key
0542: + marker, e);
0543: }
0544: }
0545: }
0546:
0547: /** Pop n replacements off the stack */
0548: protected void pop(int n) {
0549: for (int i = 0; i < n; i++) {
0550: m_replacements.remove(m_replacements.size() - 1);
0551: }
0552: }
0553:
0554: /** Close the output file */
0555: protected void closeOutput() {
0556: m_output.flush();
0557: m_output.close();
0558: }
0559:
0560: /** Answer true if the given option is set to true */
0561: protected boolean isTrue(Object option) {
0562: return getOpt(option).isTrue();
0563: }
0564:
0565: /** Answer true if the given option has value */
0566: protected boolean hasValue(Object option) {
0567: return getOpt(option).hasValue();
0568: }
0569:
0570: /** Answer true if the given option has a resource value */
0571: protected boolean hasResourceValue(Object option) {
0572: return getOpt(option).hasResourceValue();
0573: }
0574:
0575: /** Answer the value of the option or null */
0576: protected String getValue(Object option) {
0577: return getOpt(option).getValue();
0578: }
0579:
0580: /** Answer all values for the given options as Strings */
0581: protected List getAllValues(Object option) {
0582: List values = new ArrayList();
0583: OptionDefinition opt = getOpt(option);
0584:
0585: // look in the command line arguments
0586: for (Iterator i = m_cmdLineArgs.iterator(); i.hasNext();) {
0587: String s = (String) i.next();
0588: if (s.equals(opt.m_cmdLineForm)) {
0589: // next iterator value is the arg value
0590: values.add(i.next());
0591: }
0592: }
0593:
0594: // now look in the config file
0595: for (StmtIterator i = m_root.listProperties(opt.m_prop); i
0596: .hasNext();) {
0597: Statement s = i.nextStatement();
0598:
0599: if (s.getObject() instanceof Literal) {
0600: values.add(s.getString());
0601: } else {
0602: values.add(s.getResource().getURI());
0603: }
0604: }
0605:
0606: return values;
0607: }
0608:
0609: /** Answer the value of the option or null */
0610: protected Resource getResource(Object option) {
0611: return getOpt(option).getResource();
0612: }
0613:
0614: /** Answer the option object for the given option */
0615: protected OptionDefinition getOpt(Object option) {
0616: for (int i = 0; i < m_optionDefinitions.length; i++) {
0617: if (m_optionDefinitions[i][0] == option) {
0618: return (OptionDefinition) m_optionDefinitions[i][1];
0619: }
0620: }
0621:
0622: return null;
0623: }
0624:
0625: /** Abort due to exception */
0626: protected void abort(String msg, Exception e) {
0627: System.err.println(msg);
0628: if (e != null) {
0629: System.err.println(e);
0630: }
0631: System.exit(1);
0632: }
0633:
0634: /** Print usage message and abort */
0635: protected void usage() {
0636: System.err.println("Usage:");
0637: System.err.println(" java jena.schemagen [options ...]");
0638: System.err.println();
0639: System.err.println("Commonly used options include:");
0640: System.err
0641: .println(" -i <input> the source document as a file or URL.");
0642: System.err
0643: .println(" -n <name> the name of the created Java class.");
0644: System.err
0645: .println(" -a <uri> the namespace URI of the source document.");
0646: System.err
0647: .println(" -o <file> the file to write the generated class into.");
0648: System.err
0649: .println(" -o <dir> the directory in which the generated Java class is created.");
0650: System.err
0651: .println(" By default, output goes to stdout.");
0652: System.err
0653: .println(" -e <encoding> the encoding of the input document (N3, RDF/XML, etc).");
0654: System.err
0655: .println(" -c <config> a filename or URL for an RDF document containing ");
0656: System.err.println(" configuration parameters.");
0657: System.err.println();
0658: System.err
0659: .println("Many other options are available. See the schemagen HOWTO in the ");
0660: System.err.println("Jena documentation for full details.");
0661: System.exit(1);
0662: }
0663:
0664: /** Use the current replacements list to do the subs in the given string */
0665: protected String substitute(String sIn) {
0666: String s = sIn;
0667:
0668: for (Iterator i = m_replacements.iterator(); i.hasNext();) {
0669: Replacement r = (Replacement) i.next();
0670:
0671: s = r.pattern.matcher(s).replaceAll(r.sub);
0672: }
0673:
0674: return s;
0675: }
0676:
0677: /** Add the appropriate indent to a buffer */
0678: protected int indentTo(int i, StringBuffer buf) {
0679: int indent = i * m_indentStep;
0680: for (int j = 0; j < indent; j++) {
0681: buf.append(' ');
0682: }
0683:
0684: return indent;
0685: }
0686:
0687: /** Write a blank line, with indent and newline */
0688: protected void writeln(int indent) {
0689: writeln(indent, "");
0690: }
0691:
0692: /** Write out the given string with n spaces of indent, with newline */
0693: protected void writeln(int indent, String s) {
0694: write(indent, s);
0695: m_output.print(m_nl);
0696: }
0697:
0698: /** Write out the given string with n spaces of indent */
0699: protected void write(int indentLevel, String s) {
0700: for (int i = 0; i < (m_indentStep * indentLevel); i++) {
0701: m_output.print(" ");
0702: }
0703:
0704: m_output.print(s);
0705: }
0706:
0707: /** Determine the list of imports to include in the file */
0708: protected String getImports() {
0709: StringBuffer buf = new StringBuffer();
0710: buf.append("import com.hp.hpl.jena.rdf.model.*;");
0711: buf.append(m_nl);
0712:
0713: if (useOntology()) {
0714: buf.append("import com.hp.hpl.jena.ontology.*;");
0715: buf.append(m_nl);
0716: }
0717:
0718: if (includeSource()) {
0719: buf.append("import java.io.ByteArrayInputStream;");
0720: buf.append(m_nl);
0721: }
0722:
0723: return buf.toString();
0724: }
0725:
0726: /** Determine the class name of the vocabulary from the URI */
0727: protected String getClassName() {
0728: // if a class name is given, just use that
0729: if (hasValue(OPT_CLASSNAME)) {
0730: return getValue((OPT_CLASSNAME));
0731: }
0732:
0733: // otherwise, we generate a name based on the URI
0734: String uri = getValue(OPT_INPUT);
0735:
0736: // remove any suffixes
0737: uri = (uri.endsWith("#")) ? uri.substring(0, uri.length() - 1)
0738: : uri;
0739: uri = (uri.endsWith(".daml")) ? uri.substring(0,
0740: uri.length() - 5) : uri;
0741: uri = (uri.endsWith(".owl")) ? uri.substring(0,
0742: uri.length() - 4) : uri;
0743: uri = (uri.endsWith(".rdf")) ? uri.substring(0,
0744: uri.length() - 4) : uri;
0745: uri = (uri.endsWith(".rdfs")) ? uri.substring(0,
0746: uri.length() - 5) : uri;
0747: uri = (uri.endsWith(".n3")) ? uri
0748: .substring(0, uri.length() - 3) : uri;
0749: uri = (uri.endsWith(".xml")) ? uri.substring(0,
0750: uri.length() - 4) : uri;
0751:
0752: // now work back to the first non name character from the end
0753: int i = uri.length() - 1;
0754: for (; i > 0; i--) {
0755: if (!Character.isUnicodeIdentifierPart(uri.charAt(i))
0756: && uri.charAt(i) != '-') {
0757: i++;
0758: break;
0759: }
0760: }
0761:
0762: String name = uri.substring(i);
0763:
0764: // optionally add name suffix
0765: if (hasValue(OPT_CLASSNAME_SUFFIX)) {
0766: name = name + getValue(OPT_CLASSNAME_SUFFIX);
0767: }
0768:
0769: // now we make the name into a legal Java identifier
0770: return asLegalJavaID(name, true);
0771: }
0772:
0773: /** Answer true if we are using ontology terms in this vocabulary */
0774: protected boolean useOntology() {
0775: return isTrue(OPT_ONTOLOGY);
0776: }
0777:
0778: /** Answer true if all comments are suppressed */
0779: protected boolean noComments() {
0780: return isTrue(OPT_NO_COMMENTS);
0781: }
0782:
0783: /** Answer true if ontology source code is to be included */
0784: protected boolean includeSource() {
0785: return isTrue(OPT_INCLUDE_SOURCE);
0786: }
0787:
0788: /** Convert s to a legal Java identifier; capitalise first char if cap is true */
0789: protected String asLegalJavaID(String s, boolean cap) {
0790: StringBuffer buf = new StringBuffer();
0791: int i = 0;
0792:
0793: // treat the first character specially - must be able to start a Java ID, may have to up-case
0794: try {
0795: for (; !Character.isJavaIdentifierStart(s.charAt(i)); i++) { /**/
0796: }
0797: } catch (StringIndexOutOfBoundsException e) {
0798: System.err
0799: .println("Could not identify legal Java identifier start character in '"
0800: + s + "', replacing with __");
0801: return "__";
0802: }
0803: buf.append(cap ? Character.toUpperCase(s.charAt(i)) : s
0804: .charAt(i));
0805:
0806: // copy the remaining characters - replace non-legal chars with '_'
0807: for (++i; i < s.length(); i++) {
0808: char c = s.charAt(i);
0809: buf.append(Character.isJavaIdentifierPart(c) ? c : '_');
0810: }
0811:
0812: // check for illegal keyword
0813: if (KEYWORD_LIST.contains(buf.toString())) {
0814: buf.append('_');
0815: }
0816:
0817: return buf.toString();
0818: }
0819:
0820: /** The opening class declaration */
0821: protected void writeClassDeclaration() {
0822: write(0, "public class ");
0823: write(0, getClassName());
0824: write(0, " ");
0825:
0826: if (hasValue(OPT_CLASSDEC)) {
0827: write(0, getValue(OPT_CLASSDEC));
0828: }
0829:
0830: writeln(0, "{");
0831: }
0832:
0833: /** The close of the class decoration */
0834: protected void writeClassClose() {
0835: writeln(0, "}");
0836: }
0837:
0838: /** Write the declarations at the head of the class */
0839: protected void writeInitialDeclarations() {
0840: writeModelDeclaration();
0841: writeSource();
0842: writeNamespace();
0843:
0844: if (hasValue(OPT_DECLARATIONS)) {
0845: writeln(0, getValue(OPT_DECLARATIONS));
0846: }
0847: }
0848:
0849: /** Write the declaration of the model */
0850: protected void writeModelDeclaration() {
0851: if (useOntology()) {
0852: String lang = "OWL";
0853: if (isTrue(OPT_LANG_DAML)) {
0854: lang = "DAML";
0855: } else if (isTrue(OPT_LANG_RDFS)) {
0856: lang = "RDFS";
0857: }
0858: writeln(1,
0859: "/** <p>The ontology model that holds the vocabulary terms</p> */");
0860: writeln(
0861: 1,
0862: "private static OntModel m_model = ModelFactory.createOntologyModel( OntModelSpec."
0863: + lang + "_MEM, null );");
0864: } else {
0865: writeln(1,
0866: "/** <p>The RDF model that holds the vocabulary terms</p> */");
0867: writeln(1,
0868: "private static Model m_model = ModelFactory.createDefaultModel();");
0869: }
0870:
0871: writeln(1);
0872: }
0873:
0874: /** Write the source code of the input model into the file itself */
0875: protected void writeSource() {
0876: if (includeSource()) {
0877: // first save a copy of the source in compact form into a buffer
0878: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0879: m_source.write(bos, "N3");
0880: String output = bos.toString();
0881:
0882: // now we embed each line of the source in the output
0883: writeln(1, "private static final String SOURCE = ");
0884: boolean first = true;
0885:
0886: StringTokenizer st = new StringTokenizer(output, "\n");
0887: while (st.hasMoreTokens()) {
0888: String tok = st.nextToken();
0889: if (tok.endsWith("\r")) {
0890: tok = tok.substring(0, tok.length() - 1);
0891: }
0892: write(2, first ? " " : " + ");
0893: writeln(2, protectQuotes(tok));
0894: first = false;
0895: }
0896:
0897: // then we reference the string constant when reading the source
0898: // note that we avoid StringReader due to charset encoding issues
0899: writeln(1, ";");
0900: writeln(0, "");
0901: writeln(1,
0902: "/** Read the ontology definition into the source model */ ");
0903: writeln(1, "static { ");
0904: writeln(2,
0905: "m_model.read( new ByteArrayInputStream( SOURCE.getBytes() ), null, \"N3\" );");
0906: writeln(1, "}");
0907: writeln(0, "");
0908: }
0909: }
0910:
0911: /** Protect any double quotes in the given string so that it's a legal Java String */
0912: private String protectQuotes(String s) {
0913: return "\"" + s.replaceAll("\"", "\\\\\"") + "\\n\"";
0914: }
0915:
0916: /** Write the string and resource that represent the namespace */
0917: protected void writeNamespace() {
0918: String nsURI = determineNamespaceURI();
0919:
0920: writeln(1,
0921: "/** <p>The namespace of the vocabulary as a string</p> */");
0922: writeln(1, "public static final String NS = \"" + nsURI + "\";");
0923: writeln(1);
0924:
0925: writeln(1,
0926: "/** <p>The namespace of the vocabulary as a string</p>");
0927: writeln(1, " * @see #NS */");
0928: writeln(1, "public static String getURI() {return NS;}");
0929: writeln(1);
0930:
0931: writeln(1,
0932: "/** <p>The namespace of the vocabulary as a resource</p> */");
0933: writeln(1,
0934: "public static final Resource NAMESPACE = m_model.createResource( NS );");
0935: writeln(1);
0936: }
0937:
0938: /** Determine what the namespace URI for this vocabulary is */
0939: protected String determineNamespaceURI() {
0940: // we have a sequence of strategies for determining the ontology namespace
0941: String ns = getOptionNamespace();
0942: if (ns == null) {
0943: ns = getDefaultPrefixNamespace();
0944: }
0945: if (ns == null) {
0946: ns = getOntologyElementNamespace();
0947: }
0948: if (ns == null) {
0949: ns = guessNamespace();
0950: }
0951:
0952: // did we get one?
0953: if (ns == null) {
0954: abort(
0955: "Could not determine the base URI for the input vocabulary",
0956: null);
0957: }
0958:
0959: m_includeURI.add(ns);
0960: return ns;
0961: }
0962:
0963: /** User has set namespace via a schemagen option */
0964: protected String getOptionNamespace() {
0965: return hasResourceValue(OPT_NAMESPACE) ? getResource(
0966: OPT_NAMESPACE).getURI() : null;
0967: }
0968:
0969: /** Document has set an empty prefix for the model */
0970: protected String getDefaultPrefixNamespace() {
0971: // alternatively, the default namespace may be set in the prefix mapping read from the input document
0972: String defaultNS = m_source.getNsPrefixURI("");
0973: if (defaultNS == null) {
0974: defaultNS = m_source.getBaseModel().getNsPrefixURI("");
0975: }
0976:
0977: return defaultNS;
0978: }
0979:
0980: /** Document has an owl:Ontology or daml:Ontology element */
0981: protected String getOntologyElementNamespace() {
0982: // if we are using an ontology model, we can get the namespace URI from the ontology element
0983: String uri = null;
0984:
0985: StmtIterator i = m_source.getBaseModel().listStatements(null,
0986: RDF.type, m_source.getProfile().ONTOLOGY());
0987:
0988: if (i.hasNext()) {
0989: Resource ont = i.nextStatement().getSubject();
0990: uri = ont.getURI();
0991:
0992: // ensure ends with namespace separator char
0993: char ch = uri.charAt(uri.length() - 1);
0994: boolean endsWithNCNameCh = XMLChar.isNCName(ch);
0995: uri = endsWithNCNameCh ? uri + "#" : uri;
0996: }
0997:
0998: return uri;
0999: }
1000:
1001: /** Guess the URI from the most prevalent URI */
1002: protected String guessNamespace() {
1003: Map nsCount = new HashMap();
1004:
1005: // count all of the namespaces used in the model
1006: for (StmtIterator i = m_source.listStatements(); i.hasNext();) {
1007: Statement s = (Statement) i.next();
1008: countNamespace(s.getSubject(), nsCount);
1009: countNamespace(s.getPredicate(), nsCount);
1010: if (s.getObject().isResource()) {
1011: countNamespace(s.getResource(), nsCount);
1012: }
1013: }
1014:
1015: // now find the maximal element
1016: String ns = null;
1017: int max = 0;
1018: for (Iterator i = nsCount.keySet().iterator(); i.hasNext();) {
1019: String nsKey = (String) i.next();
1020:
1021: // we ignore the usual suspects
1022: if (!(OWL.getURI().equals(nsKey)
1023: || RDF.getURI().equals(nsKey)
1024: || RDFS.getURI().equals(nsKey) || XSD.getURI()
1025: .equals(nsKey))) {
1026: // not an ignorable namespace
1027: int count = ((Integer) nsCount.get(nsKey)).intValue();
1028:
1029: if (count > max) {
1030: // highest count seen so far
1031: max = count;
1032: ns = nsKey;
1033: }
1034: }
1035: }
1036:
1037: return ns;
1038: }
1039:
1040: /** Record a use of the given namespace in the count map */
1041: private void countNamespace(Resource r, Map nsCount) {
1042: if (!r.isAnon()) {
1043: String ns = r.getNameSpace();
1044:
1045: // increment the count for this namespace
1046: Integer count = nsCount.containsKey(ns) ? (Integer) nsCount
1047: .get(ns) : new Integer(0);
1048: Integer count1 = new Integer(count.intValue() + 1);
1049:
1050: nsCount.put(ns, count1);
1051: }
1052: }
1053:
1054: /** Write the list of properties */
1055: protected void writeProperties() {
1056: if (isTrue(OPT_NOPROPERTIES)) {
1057: return;
1058: }
1059:
1060: if (hasValue(OPT_PROPERTY_SECTION)) {
1061: writeln(0, getValue(OPT_PROPERTY_SECTION));
1062: }
1063:
1064: if (useOntology()) {
1065: writeObjectProperties();
1066: writeDatatypeProperties();
1067: writeAnnotationProperties();
1068:
1069: // we also write out the RDF properties, to mop up any props that are not stated as
1070: // object, datatype or annotation properties
1071: writeRDFProperties(true);
1072: } else {
1073: writeRDFProperties(false);
1074: }
1075: }
1076:
1077: /** Write any object properties in the vocabulary */
1078: protected void writeObjectProperties() {
1079: String template = hasValue(OPT_PROP_TEMPLATE) ? getValue(OPT_PROP_TEMPLATE)
1080: : DEFAULT_TEMPLATE;
1081:
1082: if (!isTrue(OPT_LANG_RDFS)) {
1083: for (Iterator i = sorted(m_source.listObjectProperties()); i
1084: .hasNext();) {
1085: writeValue((Resource) i.next(), template,
1086: "ObjectProperty", "createObjectProperty",
1087: "_PROP");
1088: }
1089: }
1090: }
1091:
1092: /** Write any datatype properties in the vocabulary */
1093: protected void writeDatatypeProperties() {
1094: String template = hasValue(OPT_PROP_TEMPLATE) ? getValue(OPT_PROP_TEMPLATE)
1095: : DEFAULT_TEMPLATE;
1096:
1097: if (!isTrue(OPT_LANG_RDFS)) {
1098: for (Iterator i = sorted(m_source.listDatatypeProperties()); i
1099: .hasNext();) {
1100: writeValue((Resource) i.next(), template,
1101: "DatatypeProperty", "createDatatypeProperty",
1102: "_PROP");
1103: }
1104: }
1105: }
1106:
1107: /** Write any annotation properties in the vocabulary */
1108: protected void writeAnnotationProperties() {
1109: String template = hasValue(OPT_PROP_TEMPLATE) ? getValue(OPT_PROP_TEMPLATE)
1110: : DEFAULT_TEMPLATE;
1111:
1112: if (!isTrue(OPT_LANG_RDFS)) {
1113: for (Iterator i = sorted(m_source
1114: .listAnnotationProperties()); i.hasNext();) {
1115: writeValue((Resource) i.next(), template,
1116: "AnnotationProperty",
1117: "createAnnotationProperty", "_PROP");
1118: }
1119: }
1120: }
1121:
1122: /** Write any vanilla RDF properties in the vocabulary */
1123: protected void writeRDFProperties(boolean useOntProperty) {
1124: String template = hasValue(OPT_PROP_TEMPLATE) ? getValue(OPT_PROP_TEMPLATE)
1125: : DEFAULT_TEMPLATE;
1126: String propType = useOntProperty ? "OntProperty" : "Property";
1127:
1128: // select the appropriate properties based on the language choice
1129: Resource[] props;
1130: if (isTrue(OPT_LANG_OWL)) {
1131: props = new Resource[] { OWL.ObjectProperty,
1132: OWL.DatatypeProperty, RDF.Property };
1133: } else if (isTrue(OPT_LANG_DAML)) {
1134: props = new Resource[] { DAML_OIL.ObjectProperty,
1135: DAML_OIL.DatatypeProperty, RDF.Property };
1136: } else {
1137: props = new Resource[] { RDF.Property };
1138: }
1139:
1140: // collect the properties to be written
1141: List propertyResources = new ArrayList();
1142: for (int j = 0; j < props.length; j++) {
1143: for (StmtIterator i = m_source.listStatements(null,
1144: RDF.type, props[j]); i.hasNext();) {
1145: propertyResources.add(i.nextStatement().getSubject());
1146: }
1147: }
1148:
1149: // now write the properties
1150: for (Iterator i = sorted(propertyResources); i.hasNext();) {
1151: writeValue((Resource) i.next(), template, propType,
1152: "create" + propType, "_PROP");
1153: }
1154: }
1155:
1156: /** Write any classes in the vocabulary */
1157: protected void writeClasses() {
1158: if (isTrue(OPT_NOCLASSES)) {
1159: return;
1160: }
1161:
1162: if (hasValue(OPT_CLASS_SECTION)) {
1163: writeln(0, getValue(OPT_CLASS_SECTION));
1164: }
1165:
1166: if (useOntology()) {
1167: writeOntClasses();
1168: } else {
1169: writeRDFClasses();
1170: }
1171: }
1172:
1173: /** Write classes as ontology terms */
1174: protected void writeOntClasses() {
1175: String template = hasValue(OPT_CLASS_TEMPLATE) ? getValue(OPT_CLASS_TEMPLATE)
1176: : DEFAULT_TEMPLATE;
1177:
1178: for (Iterator i = sorted(m_source.listClasses()); i.hasNext();) {
1179: writeValue((Resource) i.next(), template, "OntClass",
1180: "createClass", "_CLASS");
1181: }
1182: }
1183:
1184: /** Write classes as vanilla RDF terms */
1185: protected void writeRDFClasses() {
1186: String template = hasValue(OPT_CLASS_TEMPLATE) ? getValue(OPT_CLASS_TEMPLATE)
1187: : DEFAULT_TEMPLATE;
1188:
1189: // make sure we're looking for the appropriate type of class
1190: Resource cls = OWL.Class;
1191: if (isTrue(OPT_LANG_DAML)) {
1192: cls = DAML_OIL.Class;
1193: } else if (isTrue(OPT_LANG_RDFS)) {
1194: cls = RDFS.Class;
1195: }
1196:
1197: // collect the classes to list
1198: List classes = m_source.listStatements(null, RDF.type, cls)
1199: .mapWith(new Map1() {
1200: public Object map1(Object o) {
1201: return ((Statement) o).getSubject();
1202: }
1203: }).toList();
1204:
1205: for (Iterator i = sorted(classes); i.hasNext();) {
1206: writeValue((Resource) i.next(), template, "Resource",
1207: "createResource", "_CLASS");
1208: }
1209: }
1210:
1211: /** Write any instances (individuals) in the vocabulary */
1212: protected void writeIndividuals() {
1213: if (isTrue(OPT_NOINDIVIDUALS)) {
1214: return;
1215: }
1216:
1217: if (hasValue(OPT_INDIVIDUALS_SECTION)) {
1218: writeln(0, getValue(OPT_INDIVIDUALS_SECTION));
1219: }
1220:
1221: if (useOntology()) {
1222: writeOntIndividuals();
1223: } else {
1224: writeRDFIndividuals();
1225: }
1226: }
1227:
1228: /** Write individuals as ontology terms */
1229: protected void writeOntIndividuals() {
1230: String template = hasValue(OPT_INDIVIDUAL_TEMPLATE) ? getValue(OPT_INDIVIDUAL_TEMPLATE)
1231: : DEFAULT_INDIVIDUAL_TEMPLATE;
1232:
1233: for (Iterator i = selectIndividuals(); i.hasNext();) {
1234: Individual ind = (Individual) ((Resource) i.next())
1235: .as(Individual.class);
1236:
1237: // do we have a local class resource
1238: Resource cls = ind.getOntClass();
1239: if (cls == null) {
1240: cls = OWL.Thing;
1241: }
1242:
1243: String varName = (String) m_resourcesToNames.get(cls);
1244: String valType = (varName != null) ? varName
1245: : "m_model.createClass( \"" + cls.getURI() + "\" )";
1246:
1247: // push the individuals type onto the stack
1248: addReplacementPattern("valtype", valType);
1249: writeValue(ind, template, "Individual", "createIndividual",
1250: "_INSTANCE");
1251: pop(1);
1252:
1253: }
1254: }
1255:
1256: /** Write individuals as vanilla RDF terms */
1257: protected void writeRDFIndividuals() {
1258: String template = hasValue(OPT_INDIVIDUAL_TEMPLATE) ? getValue(OPT_INDIVIDUAL_TEMPLATE)
1259: : DEFAULT_TEMPLATE;
1260:
1261: for (Iterator i = selectIndividuals(); i.hasNext();) {
1262: writeValue((Resource) i.next(), template, "Resource",
1263: "createResource", "_INSTANCE");
1264: }
1265: }
1266:
1267: /** Answer an iterator over the individuals selected for output */
1268: protected ExtendedIterator selectIndividuals() {
1269: List candidates = new ArrayList();
1270: for (StmtIterator i = m_source.listStatements(null, RDF.type,
1271: (RDFNode) null); i.hasNext();) {
1272: Statement candidate = i.nextStatement();
1273:
1274: if (candidate.getObject().isResource()) {
1275: Resource candObj = candidate.getResource();
1276: Resource candSubj = candidate.getSubject();
1277:
1278: // note that whether candSubj is included is tested later on by {@link #filter}
1279: if (!candSubj.isAnon() && isIncluded(candObj)) {
1280: candidates.add(candSubj);
1281: }
1282: }
1283: }
1284:
1285: return sorted(candidates);
1286: }
1287:
1288: /**
1289: * Answer true if the given resource is accepted for presentation in the output, which
1290: * is true iff it is a URI node, whose namespace is one of the accepted namespaces in
1291: * {@link #m_includeURI}.
1292: * @param r A resource to test
1293: * @return True if the resource is to be included in the generated output
1294: */
1295: protected boolean isIncluded(Resource r) {
1296: boolean accepted = false;
1297:
1298: if (!r.isAnon()) {
1299: String uri = r.getURI();
1300: for (Iterator j = m_includeURI.iterator(); !accepted
1301: && j.hasNext();) {
1302: accepted = uri.startsWith((String) j.next());
1303: }
1304: }
1305:
1306: return accepted;
1307: }
1308:
1309: /** Write the value declaration out using the given template, optionally creating comments */
1310: protected void writeValue(Resource r, String template,
1311: String valueClass, String creator, String disambiguator) {
1312: if (!filter(r)) {
1313: if (!noComments() && hasComment(r)) {
1314: writeln(1, formatComment(getComment(r)));
1315: }
1316:
1317: // push the local bindings for the substitution onto the stack
1318: addReplacementPattern("valuri", r.getURI());
1319: addReplacementPattern("valname", getValueName(r,
1320: disambiguator));
1321: addReplacementPattern("valclass", valueClass);
1322: addReplacementPattern("valcreator", creator);
1323:
1324: // write out the value
1325: writeln(1, substitute(template));
1326: writeln(1);
1327:
1328: // pop the local replacements off the stack
1329: pop(4);
1330: }
1331: }
1332:
1333: /** Answer true if the given resource has an rdf:comment or daml:comment */
1334: protected boolean hasComment(Resource r) {
1335: return r.hasProperty(RDFS.comment)
1336: || r.hasProperty(DAML_OIL.comment);
1337: }
1338:
1339: /** Answer all of the commentary on the given resource, as a string */
1340: protected String getComment(Resource r) {
1341: StringBuffer comment = new StringBuffer();
1342:
1343: // collect any RDFS or DAML comments attached to the node
1344: for (NodeIterator ni = m_source.listObjectsOfProperty(r,
1345: RDFS.comment); ni.hasNext();) {
1346: RDFNode n = ni.nextNode();
1347: if (n instanceof Literal) {
1348: comment.append(((Literal) n).getLexicalForm().trim());
1349: } else {
1350: LogFactory.getLog(getClass()).debug(
1351: "Not a literal: " + n);
1352: }
1353: }
1354:
1355: for (NodeIterator ni = m_source.listObjectsOfProperty(r,
1356: DAML_OIL.comment); ni.hasNext();) {
1357: comment.append(((Literal) ni.nextNode()).getLexicalForm()
1358: .trim());
1359: }
1360:
1361: return comment.toString();
1362: }
1363:
1364: /** Format the comment as Javadoc, and limit the line width */
1365: protected String formatComment(String comment) {
1366: StringBuffer buf = new StringBuffer();
1367: buf.append("/** <p>");
1368:
1369: boolean inSpace = false;
1370: int pos = buf.length();
1371: boolean singleLine = true;
1372:
1373: // now format the comment by compacting whitespace and limiting the line length
1374: // add the prefix to the start of each line
1375: for (int i = 0; i < comment.length(); i++) {
1376: char c = comment.charAt(i);
1377:
1378: // compress whitespace
1379: if (Character.isWhitespace(c)) {
1380: if (inSpace) {
1381: continue; // more than one space is ignored
1382: } else {
1383: c = ' '; // map all whitespace to 0x20
1384: inSpace = true;
1385: }
1386: } else {
1387: inSpace = false;
1388: }
1389:
1390: // escapes?
1391: if (c == '\\') {
1392: c = comment.charAt(++i);
1393:
1394: switch (c) {
1395: case 'n':
1396: buf.append(m_nl);
1397: pos = indentTo(1, buf);
1398: buf.append(" * ");
1399: pos += 3;
1400: singleLine = false;
1401: break;
1402:
1403: default:
1404: // add other escape sequences above
1405: break;
1406: }
1407: } else if (c == '<') {
1408: buf.append("<");
1409: pos += 4;
1410: } else if (c == '>') {
1411: buf.append(">");
1412: pos += 4;
1413: } else if (c == '&') {
1414: buf.append("&");
1415: pos += 5;
1416: } else {
1417: // add the char
1418: buf.append(c);
1419: pos++;
1420: }
1421:
1422: // wrap any very long lines at 120 chars
1423: if ((pos > COMMENT_LENGTH_LIMIT) && (inSpace)) {
1424: buf.append(m_nl);
1425: pos = indentTo(1, buf);
1426: buf.append(" * ");
1427: pos += 3;
1428: singleLine = false;
1429: }
1430: }
1431:
1432: buf.append("</p>");
1433: buf.append(singleLine ? "" : m_nl);
1434: indentTo(singleLine ? 0 : 1, buf);
1435: buf.append(" */");
1436: return buf.toString();
1437: }
1438:
1439: /** Answer true if resource r <b>does not</b> show in output */
1440: protected boolean filter(Resource r) {
1441: if (r.isAnon()) {
1442: return true;
1443: }
1444:
1445: // if we've already processed this resource once, ignore it next time
1446: if (m_resourcesToNames.containsKey(r)) {
1447: return true;
1448: }
1449:
1450: // search the allowed URI's
1451: for (Iterator i = m_includeURI.iterator(); i.hasNext();) {
1452: String uri = (String) i.next();
1453: if (r.getURI().startsWith(uri)) {
1454: // in
1455: return false;
1456: }
1457: }
1458:
1459: // we allow individuals whose class is not in the included NS's, unless opt strict-individuals is true */
1460: if (!isTrue(OPT_STRICT_INDIVIDUALS)) {
1461: for (StmtIterator j = r.listProperties(RDF.type); j
1462: .hasNext();) {
1463: // we search the rdf:types of this resource
1464: Resource typeRes = j.nextStatement().getResource();
1465:
1466: if (!typeRes.isAnon()) {
1467: String typeURI = typeRes.getURI();
1468:
1469: // for any type that is in a permitted NS
1470: for (Iterator i = m_includeURI.iterator(); i
1471: .hasNext();) {
1472: String uri = (String) i.next();
1473: if (typeURI.startsWith(uri)) {
1474: // in
1475: return false;
1476: }
1477: }
1478: }
1479: }
1480: }
1481:
1482: // default is out
1483: return true;
1484: }
1485:
1486: /** Answer the Java value name for the URI */
1487: protected String getValueName(Resource r, String disambiguator) {
1488: // the id name is basically the local name of the resource, possibly in upper case
1489: String name = isTrue(OPT_UC_NAMES) ? getUCValueName(r) : r
1490: .getLocalName();
1491:
1492: // must be legal java
1493: name = asLegalJavaID(name, false);
1494:
1495: // must not clash with an existing name
1496: int attempt = 0;
1497: String baseName = name;
1498: while (m_usedNames.contains(name)) {
1499: name = (attempt == 0) ? (name + disambiguator) : (baseName
1500: + disambiguator + attempt);
1501: attempt++;
1502: }
1503:
1504: // record this name so that we don't use it again (which will stop the vocabulary from compiling)
1505: m_usedNames.add(name);
1506:
1507: // record the mapping from resource to name
1508: m_resourcesToNames.put(r, name);
1509:
1510: return name;
1511: }
1512:
1513: /** Answer the local name of resource r mapped to upper case */
1514: protected String getUCValueName(Resource r) {
1515: StringBuffer buf = new StringBuffer();
1516: String localName = r.getLocalName();
1517: char lastChar = 0;
1518:
1519: for (int i = 0; i < localName.length(); i++) {
1520: char c = localName.charAt(i);
1521:
1522: if (Character.isLowerCase(lastChar)
1523: && Character.isUpperCase(c)) {
1524: buf.append('_');
1525: }
1526: buf.append(Character.toUpperCase(c));
1527: lastChar = c;
1528: }
1529:
1530: return buf.toString();
1531: }
1532:
1533: /** Return a URI formed from the given string, unchanged if it's already a URI or
1534: * converted to a file URI otherwise. If not recognisable as a URL, abort.
1535: */
1536: protected String urlCheck(String uriOrFile) {
1537: boolean legal = true;
1538: String url = uriOrFile;
1539:
1540: // is it a URI already? to check, we make a URL and see what happens!
1541: try {
1542: new URL(url);
1543: } catch (MalformedURLException ignore) {
1544: legal = false;
1545: }
1546:
1547: // if not a legal url, assume it's a file
1548: if (!legal) {
1549: legal = true;
1550: String slash = System.getProperty("file.separator");
1551: url = "file:"
1552: + (uriOrFile.startsWith(slash) ? (slash + slash)
1553: : "") + uriOrFile;
1554:
1555: try {
1556: new URL(url);
1557: } catch (MalformedURLException ignore) {
1558: legal = false;
1559: }
1560: }
1561:
1562: if (!legal) {
1563: abort("Could not parse " + uriOrFile
1564: + " as a legal URL or a file reference. Aborting.",
1565: null);
1566: }
1567:
1568: return url;
1569: }
1570:
1571: /** Answer an iterator that contains the elements of the given list, but sorted by URI */
1572: protected ExtendedIterator sorted(ExtendedIterator i) {
1573: return sorted(i.toList());
1574: }
1575:
1576: /** Answer an iterator that contains the elements of the given iterator, but sorted by URI */
1577: protected ExtendedIterator sorted(List members) {
1578: Collections.sort(members, new Comparator() {
1579: public int compare(Object arg0, Object arg1) {
1580: RDFNode n0 = (RDFNode) arg0;
1581: RDFNode n1 = (RDFNode) arg1;
1582:
1583: if (n0.isLiteral() || n1.isLiteral()) {
1584: if (n0.isLiteral() && n1.isLiteral()) {
1585: // two literals
1586: Literal l0 = (Literal) n0;
1587: Literal l1 = (Literal) n1;
1588: return l0.getLexicalForm().compareTo(
1589: l1.getLexicalForm());
1590: } else {
1591: return n0.isLiteral() ? -1 : 1;
1592: }
1593: } else {
1594: Resource r0 = (Resource) n0;
1595: Resource r1 = (Resource) n1;
1596: if (r0.isAnon() && r1.isAnon()) {
1597: // two anonID's - the order is important as long as its consistent
1598: return r0.getId().toString().compareTo(
1599: r1.getId().toString());
1600: } else if (r0.isAnon()) {
1601: return -1;
1602: } else if (r1.isAnon()) {
1603: return 1;
1604: } else {
1605: // two named resources
1606: return r0.getURI().compareTo(r1.getURI());
1607: }
1608: }
1609: }
1610: });
1611:
1612: return WrappedIterator.create(members.iterator());
1613: }
1614:
1615: //==============================================================================
1616: // Inner class definitions
1617: //==============================================================================
1618:
1619: /** An option that can be set either on the command line or in the RDF config */
1620: protected class OptionDefinition {
1621: protected String m_cmdLineForm;
1622: protected Property m_prop;
1623:
1624: protected OptionDefinition(String cmdLineForm, String name) {
1625: m_cmdLineForm = cmdLineForm;
1626: if (name != null) {
1627: m_prop = m_config.getProperty(NS, name);
1628: }
1629: }
1630:
1631: /**
1632: * Answer true if this option is set to true, either on the command line
1633: * or in the config model
1634: *
1635: * @return boolean
1636: */
1637: protected boolean isTrue() {
1638: if (m_cmdLineArgs.contains(m_cmdLineForm)) {
1639: return true;
1640: }
1641:
1642: if (m_root.hasProperty(m_prop)) {
1643: return m_root.getRequiredProperty(m_prop).getBoolean();
1644: }
1645:
1646: return false;
1647: }
1648:
1649: /**
1650: * Answer the string value of the parameter if set, or null otherwise. Note command line
1651: * has precedence.
1652: *
1653: * @return String
1654: */
1655: protected String getValue() {
1656: int index = m_cmdLineArgs.indexOf(m_cmdLineForm);
1657:
1658: if (index >= 0) {
1659: try {
1660: return (String) m_cmdLineArgs.get(index + 1);
1661: } catch (IndexOutOfBoundsException e) {
1662: System.err.println("Value for parameter "
1663: + m_cmdLineForm + " not set! Aborting.");
1664: }
1665: }
1666:
1667: if (m_prop != null && m_root.hasProperty(m_prop)) {
1668: RDFNode val = m_root.getRequiredProperty(m_prop)
1669: .getObject();
1670: if (val.isLiteral()) {
1671: return ((Literal) val).getLexicalForm();
1672: } else {
1673: return ((Resource) val).getURI().toString();
1674: }
1675: }
1676:
1677: // not set
1678: return null;
1679: }
1680:
1681: /**
1682: * Answer true if the parameter has a value at all.
1683: *
1684: * @return boolean
1685: */
1686: protected boolean hasValue() {
1687: return getValue() != null;
1688: }
1689:
1690: /**
1691: * Answer the resource value of the parameter if set, or null otherwise.
1692: *
1693: * @return String
1694: */
1695: protected Resource getResource() {
1696: int index = m_cmdLineArgs.indexOf(m_cmdLineForm);
1697:
1698: if (index >= 0) {
1699: try {
1700: return m_config.getResource((String) m_cmdLineArgs
1701: .get(index + 1));
1702: } catch (IndexOutOfBoundsException e) {
1703: System.err.println("Value for parameter "
1704: + m_cmdLineForm + " not set! Aborting.");
1705: }
1706: }
1707:
1708: if (m_prop != null && m_root.hasProperty(m_prop)) {
1709: return m_root.getRequiredProperty(m_prop).getResource();
1710: }
1711:
1712: // not set
1713: return null;
1714: }
1715:
1716: /**
1717: * Answer true if the parameter has a value at all.
1718: *
1719: * @return boolean
1720: */
1721: protected boolean hasResourceValue() {
1722: return getResource() != null;
1723: }
1724: } // end inner class OptionDefinition
1725:
1726: /** A pairing of pattern and substitution we want to apply to output */
1727: protected class Replacement {
1728: protected String sub;
1729: protected Pattern pattern;
1730:
1731: protected Replacement(Pattern pattern, String sub) {
1732: this .sub = sub;
1733: this .pattern = pattern;
1734: }
1735: } // end inner class Replacement
1736: }
1737:
1738: /*
1739: (c) Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
1740: All rights reserved.
1741:
1742: Redistribution and use in source and binary forms, with or without
1743: modification, are permitted provided that the following conditions
1744: are met:
1745:
1746: 1. Redistributions of source code must retain the above copyright
1747: notice, this list of conditions and the following disclaimer.
1748:
1749: 2. Redistributions in binary form must reproduce the above copyright
1750: notice, this list of conditions and the following disclaimer in the
1751: documentation and/or other materials provided with the distribution.
1752:
1753: 3. The name of the author may not be used to endorse or promote products
1754: derived from this software without specific prior written permission.
1755:
1756: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1757: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1758: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1759: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1760: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1761: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1762: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1763: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1764: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1765: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1766: */
|