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 10 Feb 2003
0009: * Filename $RCSfile: OntDocumentManager.java,v $
0010: * Revision $Revision: 1.63 $
0011: * Release status $State: Exp $
0012: *
0013: * Last modified on $Date: 2008/01/10 19:03:31 $
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 com.hp.hpl.jena.ontology;
0019:
0020: // Imports
0021: ///////////////
0022: import java.io.*;
0023: import java.util.*;
0024:
0025: import org.apache.commons.logging.Log;
0026: import org.apache.commons.logging.LogFactory;
0027: import org.apache.xerces.util.XMLChar;
0028:
0029: import com.hp.hpl.jena.rdf.model.*;
0030: import com.hp.hpl.jena.util.*;
0031: import com.hp.hpl.jena.vocabulary.OntDocManagerVocab;
0032: import com.hp.hpl.jena.vocabulary.RDF;
0033: import com.hp.hpl.jena.shared.*;
0034: import com.hp.hpl.jena.shared.impl.PrefixMappingImpl;
0035:
0036: /**
0037: * <p>
0038: * Provides services for managing ontology documents, including loading imported
0039: * documents, and locally caching documents from resolvable URL's to improve
0040: * load performance. This class now delegates some of the responsibility for
0041: * resolving URI's and caching models to {@link com.hp.hpl.jena.util.FileManager FileManager}.
0042: * By default, OntDocumentManager will use a copy of the
0043: * singleton global FileManager. Alternatively, a specific <code>FileManager</code>
0044: * can be given to a document manager instance to use when resolving URI's and file paths.
0045: * Note that the default behaviour is to hold a <strong>copy</strong> of the global file
0046: * manager. In order to ensure that the document manager directly uses the global
0047: * file manager (e.g. so that document manager sees updates to the location mappings
0048: * held by the file manager), use the {@link #setFileManager(FileManager)} method. For
0049: * example:
0050: * </p>
0051: * <pre>OntDocumentManager dm = OntDocumentManager.getInstance();
0052: * dm.setFileManager( FileManager.get() );</pre>
0053: * <p>Note that in Jena 2.3, we have deprecated the capability of the document manager
0054: * to store a table of known prefixes, and a table mapping document URI's to ontology language
0055: * types. <strong>The intention is to remove both of these capabilities from
0056: * Jena 2.4 onwards</strong>. If this change would be problematic, please send email to the
0057: * <a href="http://groups.yahoo.com/group/jena-dev">Jena support
0058: * list</a>.</p>
0059: * @author Ian Dickinson, HP Labs
0060: * (<a href="mailto:Ian.Dickinson@hp.com" >email</a>)
0061: * @version CVS $Id: OntDocumentManager.java,v 1.63 2008/01/10 19:03:31 ian_dickinson Exp $
0062: */
0063: public class OntDocumentManager {
0064: // Constants
0065: ////////////////////////////////////
0066:
0067: /** The default path for searching for the metadata on locally cached ontologies */
0068: public static final String DEFAULT_METADATA_PATH = "file:ont-policy.rdf;file:etc/ont-policy.rdf";
0069:
0070: /** Namespace for ontology metadata resources and properties */
0071: public static final String NS = "http://jena.hpl.hp.com/schemas/2003/03/ont-manager#";
0072:
0073: /** The anchor char is added to the end of namespace prefix expansions */
0074: public static final String ANCHOR = "#";
0075:
0076: /** rdf:type for ontology specification nodes in meta-data file */
0077: public static final Resource ONTOLOGY_SPEC = OntDocManagerVocab.OntologySpec;
0078:
0079: /** Represents the public URI of an ontology; also used to derive the namespace */
0080: public static final Property PUBLIC_URI = OntDocManagerVocab.publicURI;
0081:
0082: /** Represents the alternative local copy of the public ontology; assumed to be resolvable, hence URL not URI */
0083: public static final Property ALT_URL = OntDocManagerVocab.altURL;
0084:
0085: /** Represents the standard prefix for this namespace */
0086: public static final Property PREFIX = OntDocManagerVocab.prefix;
0087:
0088: /** Represents the ontology language used to encode the ontology */
0089: public static final Property LANGUAGE = OntDocManagerVocab.language;
0090:
0091: /** rdf:type for document manager policy nodes */
0092: public static final Resource DOC_MGR_POLICY = OntDocManagerVocab.DocumentManagerPolicy;
0093:
0094: /** Defines boolean policy choice of caching loaded models */
0095: public static final Property CACHE_MODELS = OntDocManagerVocab.cacheModels;
0096:
0097: /** Defines boolean policy choice of loading the imports closure */
0098: public static final Property PROCESS_IMPORTS = OntDocManagerVocab.processImports;
0099:
0100: /** Specifies the URI of an ontology that we do not want to import, even if processImports is true. */
0101: public static final Property IGNORE_IMPORT = OntDocManagerVocab.ignoreImport;
0102:
0103: /** The policy property for including the pre-declared namespace prefixes in a model. */
0104: public static final Property USE_DECLARED_NS_PREFIXES = OntDocManagerVocab.useDeclaredNsPrefixes;
0105:
0106: // Static variables
0107: //////////////////////////////////
0108:
0109: /** Default document manager instance */
0110: private static OntDocumentManager s_instance = null;
0111:
0112: /** Log for this class */
0113: private static Log log = LogFactory
0114: .getLog(OntDocumentManager.class);
0115:
0116: // Instance variables
0117: //////////////////////////////////
0118:
0119: /** The search path for metadata */
0120: protected String m_searchPath = DEFAULT_METADATA_PATH;
0121:
0122: /** FileManager instance that provides location resolution service - defaults to global instance */
0123: protected FileManager m_fileMgr;
0124:
0125: /** Flag to indicate we're using a copy of the global file manager */
0126: protected boolean m_usingGlobalFileMgr = false;
0127:
0128: /** Mapping of public public URI's to language resources */
0129: protected Map m_languageMap = new HashMap();
0130:
0131: /** Flag: process the imports closure */
0132: protected boolean m_processImports = true;
0133:
0134: /** List of URI's that will be ignored when doing imports processing */
0135: protected Set m_ignoreImports = new HashSet();
0136:
0137: /** Default prefix mapping to use to seed all models */
0138: protected PrefixMapping m_prefixMap = new PrefixMappingImpl();
0139:
0140: /** Flag to control whether we include the standard prefixes in generated models - default true. */
0141: protected boolean m_useDeclaredPrefixes = true;
0142:
0143: /** The URL of the policy file that was loaded, or null if no external policy file has yet been loaded */
0144: protected String m_policyURL;
0145:
0146: /** Optional handler for failed read */
0147: protected ReadFailureHandler m_rfHandler;
0148:
0149: /** Read hook that can intercept the process of reading a file or URL */
0150: protected ReadHook m_readHook = new DefaultReadHook();
0151:
0152: // Constructors
0153: //////////////////////////////////
0154:
0155: /**
0156: * <p>
0157: * Initialise a document manager by searching the default path for ontology
0158: * metadata about known ontologies cached locally.
0159: * </p>
0160: */
0161: public OntDocumentManager() {
0162: this (DEFAULT_METADATA_PATH);
0163: }
0164:
0165: /**
0166: * <p>
0167: * Initialise a document manager by searching the given path for ontology
0168: * metadata about known ontologies cached locally.
0169: * </p>
0170: *
0171: * @param path The search path to search for initial metadata, which will
0172: * also replace the current search path for this document manager. Use
0173: * null to prevent loading of any initial ontology metadata. The path is a series
0174: * of URL's, separated by a semi-colon (;).
0175: */
0176: public OntDocumentManager(String path) {
0177: this (null, path);
0178: }
0179:
0180: /**
0181: * <p>
0182: * Initialise a document manager by with the given FileManager, and
0183: * then searching the given path for ontology
0184: * metadata about known ontologies cached locally.
0185: * </p>
0186: *
0187: * @param path The search path to search for initial metadata
0188: * @see #OntDocumentManager(String)
0189: */
0190: public OntDocumentManager(FileManager fileMgr, String path) {
0191: setFileManager(fileMgr);
0192: setDefaults();
0193: m_searchPath = (path == null) ? "" : path;
0194: initialiseMetadata(m_searchPath);
0195: }
0196:
0197: /**
0198: * <p>Initialise a document manager with the given configuration model. This model
0199: * is used in place of any model that might be
0200: * found by searching the meta-data search path. Uses the default file manager,
0201: * i.e. a copy of the global file manager.</p>
0202: * @param config An RDF model containing configuration information for this document manager.
0203: */
0204: public OntDocumentManager(Model config) {
0205: this (null, config);
0206: }
0207:
0208: /**
0209: * <p>Initialise a document manager with the given configuration model. This model
0210: * is used in place of any model that might be
0211: * found by searching the meta-data search path.</p>
0212: * @param fileMgr A file manager to use when locating source files for ontologies
0213: * @param config An RDF model containing configuration information for this document manager.
0214: */
0215: public OntDocumentManager(FileManager fileMgr, Model config) {
0216: // we don't need to reset first since this is a new doc mgr
0217: setFileManager(fileMgr);
0218: setDefaults();
0219: configure(config, false);
0220: }
0221:
0222: // External signature methods
0223: //////////////////////////////////
0224:
0225: /**
0226: * <p>
0227: * OntDocumentManager is not a singleton, but a global default instance is available
0228: * for applications where a single shared document manager is sufficient.
0229: * </p>
0230: *
0231: * @return The default, global instance of a document manager
0232: */
0233: public static OntDocumentManager getInstance() {
0234: if (s_instance == null) {
0235: s_instance = new OntDocumentManager();
0236: }
0237: return s_instance;
0238: }
0239:
0240: /**
0241: * <p>Answer the file manager instance being used by this document manager.</p>
0242: * @return This object's file manager
0243: */
0244: public FileManager getFileManager() {
0245: return m_fileMgr;
0246: }
0247:
0248: /**
0249: * Replace the existing ReadHook with the given value. The previous read
0250: * hook is returned.
0251: * @param hook The new read hook
0252: * @return The old read hook
0253: * @exception IllegalArgumentException if the new read hook is null
0254: */
0255: public ReadHook setReadHook(ReadHook hook) {
0256: if (hook == null) {
0257: throw new IllegalArgumentException(
0258: "ReadHook cannot be null");
0259: }
0260: ReadHook rh = m_readHook;
0261: m_readHook = hook;
0262: return rh;
0263: }
0264:
0265: /**
0266: * Answer the current ReadHook for this document manager instance
0267: * @return The read hook
0268: */
0269: public ReadHook getReadHook() {
0270: return m_readHook;
0271: }
0272:
0273: /**
0274: * <p>Set the file manager used by this ODM instance to <strong>a
0275: * copy</strong> of the global file manager (and, by extension, the
0276: * global location mapper).</p>
0277: */
0278: public void setFileManager() {
0279: setFileManager(new FileManager(FileManager.get()));
0280: m_usingGlobalFileMgr = true;
0281: }
0282:
0283: /**
0284: * <p>Set the file manager used by this ODM instance to <strong>a
0285: * copy</strong> of the global file manager (and, by extension, the
0286: * global location mapper).</p>
0287: * @param fileMgr The new file manager
0288: */
0289: public void setFileManager(FileManager fileMgr) {
0290: if (fileMgr == null) {
0291: // use default fm
0292: setFileManager();
0293: } else {
0294: m_fileMgr = fileMgr;
0295: m_usingGlobalFileMgr = false;
0296: }
0297: }
0298:
0299: /**
0300: * <p>
0301: * Answer the path used to search for the ontology metadata to load. The format is
0302: * a ';' separated list of URI's. The first URI on the path that is readable is
0303: * taken to be the location of the local ontology metadata.
0304: * </p>
0305: *
0306: * @return The ontology metadata search path, as a string.
0307: */
0308: public String getMetadataSearchPath() {
0309: return m_searchPath;
0310: }
0311:
0312: /**
0313: * <p>
0314: * Change the search path for loading ontology metadata to the given path. If
0315: * <code>replace</code> is true, any existing mappings are removed before the
0316: * new path is searched. Otherwise, existing data will only be replaced if
0317: * it is clobbered by keys loaded from the metadata loaded from the new path.
0318: * </p>
0319: *
0320: * @param path The new metadata search path (see {@link #getMetadataSearchPath} for format)
0321: * @param replace If true, clear existing mappings first
0322: */
0323: public void setMetadataSearchPath(String path, boolean replace) {
0324: if (replace) {
0325: reset();
0326: }
0327: m_searchPath = path;
0328: m_policyURL = null;
0329: initialiseMetadata(path);
0330: }
0331:
0332: /**
0333: * Set the handler for read failures, overwriting any existing value.
0334: * @param rfHandler The new handler for failed document read attempts.
0335: */
0336: public void setReadFailureHandler(ReadFailureHandler rfHandler) {
0337: m_rfHandler = rfHandler;
0338: }
0339:
0340: /**
0341: * Answer the handler object that handles failed document read attempts,
0342: * or null if not defined.
0343: * @return The current read failure handler, or null
0344: */
0345: public ReadFailureHandler getReadFailureHandler() {
0346: return m_rfHandler;
0347: }
0348:
0349: /**
0350: * <p>Configure this document manager using the given configuration information, after
0351: * first resetting the model back to all default values.</p>
0352: * @param config Document manager configuration as an RDF model
0353: * @see #configure( Model, boolean )
0354: */
0355: public void configure(Model config) {
0356: configure(config, true);
0357: }
0358:
0359: /**
0360: * <p>Configure this document manager according to the configuration options
0361: * supplied by the given configuration model. If <code>reset</code> is true, the
0362: * document manager is first reset back to all default values.</p>
0363: * @param config Document manager configuration as an RDF model
0364: * @param reset If true, reset the document manager to default values, before
0365: * attempting to configure the document manager using the given model.
0366: * @see #reset
0367: */
0368: public void configure(Model config, boolean reset) {
0369: if (reset) {
0370: reset(false);
0371: }
0372:
0373: processMetadata(config);
0374: }
0375:
0376: /**
0377: * <p>Reset all state in this document manager back to the default
0378: * values it would have had when the object was created. Optionally
0379: * reload the profile metadata from the search path. <strong>Note</strong>
0380: * that the metadata search path is not changed by this reset.</p>
0381: * @param reload If true, reload the configuration file from the
0382: * search path.
0383: */
0384: public void reset(boolean reload) {
0385: // first check if we are using the global file manager, or a local one
0386: if (m_usingGlobalFileMgr) {
0387: // we can do a general reset by throwing away the old FM and creating a new one
0388: setFileManager();
0389: } else {
0390: // not using the global default FM, so we reset to best effort
0391: getFileManager().resetCache();
0392: }
0393:
0394: m_languageMap.clear();
0395: m_ignoreImports.clear();
0396: m_prefixMap = new PrefixMappingImpl();
0397:
0398: setDefaults();
0399:
0400: if (reload) {
0401: initialiseMetadata(m_searchPath);
0402: }
0403: }
0404:
0405: /**
0406: * <p>Reset all state in this document manager back to the default
0407: * values it would have had when the object was created. This does
0408: * <strong>not</strong> reload the configuration information from
0409: * the search path. Note also that the metadata search path is one
0410: * of the values that is reset back to its default value.</p>
0411: * @see #reset( boolean )
0412: */
0413: public void reset() {
0414: reset(false);
0415: }
0416:
0417: /**
0418: * <p>
0419: * Answer an iterator over the ontology document URI's that this document manager
0420: * knows to re-direct to other URL's. <strong>Note</strong> that being in this
0421: * iteration does <em>not</em> mean that a document with the given name is in
0422: * the set of cached models held by this document manager.
0423: * </p>
0424: *
0425: * @return An Iterator ranging over the public URI strings for the known
0426: * document URI's.
0427: */
0428: public Iterator listDocuments() {
0429: return getFileManager().getLocationMapper().listAltEntries();
0430: }
0431:
0432: /**
0433: * <p>
0434: * Answer the URL of the alternative copy of the ontology document with the given URI, if known,
0435: * or the URI unchanged if no alternative is known.
0436: * </p>
0437: *
0438: * @param uri The ontology document to lookup
0439: * @return The resolvable location of the alternative copy, if known, or <code>uri</code> otherwise
0440: */
0441: public String doAltURLMapping(String uri) {
0442: return getFileManager().mapURI(uri);
0443: }
0444:
0445: /**
0446: * <p>
0447: * Answer the representation of the ontology document with the given URI, if known.
0448: * </p>
0449: *
0450: * @param uri The ontology document to lookup
0451: * @return The URI of the representation language, or null.
0452: * @deprecated Language determination via the ODM will be removed from Jena 2.4 onwards
0453: */
0454: public String getLanguage(String uri) {
0455: return (String) m_languageMap.get(uri);
0456: }
0457:
0458: /**
0459: * <p>
0460: * Answer the prefix for the qnames in the given document, if known.
0461: * </p>
0462: *
0463: * @param uri The ontology document to lookup
0464: * @return The string to use as a prefix when serialising qnames in the
0465: * given document's namespace, or null if not known
0466: * @deprecated Prefix management via the ODM is very likely to be removed from Jena 2.4 onwards
0467: */
0468: public String getPrefixForURI(String uri) {
0469: return m_prefixMap.getNsURIPrefix(uri);
0470: }
0471:
0472: /**
0473: * <p>
0474: * Answer the base URI for qnames with the given prefix, if known.
0475: * </p>
0476: *
0477: * @param prefix A prefix string
0478: * @return The basename that the prefix expands to, or null
0479: * @deprecated Prefix management via the ODM is very likely to be removed from Jena 2.4 onwards
0480: */
0481: public String getURIForPrefix(String prefix) {
0482: return m_prefixMap.getNsPrefixURI(prefix);
0483: }
0484:
0485: /**
0486: * <p>
0487: * Answer the cached model corresponding to the given document, if known.
0488: * </p>
0489: *
0490: * @param uri The ontology document to lookup
0491: * @return The model for the document, or null if the model is not known.
0492: * @see #getOntology
0493: */
0494: public Model getModel(String uri) {
0495: Model m = getFileManager().getFromCache(uri);
0496:
0497: // if a previously cached model has been closed, we ignore it
0498: if (m != null && m.isClosed()) {
0499: getFileManager().removeCacheModel(uri);
0500: m = null;
0501: }
0502:
0503: return m;
0504: }
0505:
0506: /**
0507: * <p>Answer true if, according to the policy expressed by this document manager, newly
0508: * generated ontology models should include the pre-declared namespace prefixes.
0509: * </p>
0510: *
0511: * @return True if pre-declared prefixes should be added to the models
0512: * @deprecated Prefix management via the ODM is very likely to be removed from Jena 2.4 onwards
0513: */
0514: public boolean useDeclaredPrefixes() {
0515: return m_useDeclaredPrefixes;
0516: }
0517:
0518: /**
0519: * <p>Set the flag that determines whether pre-declared namespace prefixes will be added to newly
0520: * generated ontology models.</p>
0521: *
0522: * @param useDeclaredPrefixes If true, new models will include the pre-declared prefixes set held
0523: * by this document manager.
0524: * @deprecated Prefix management via the ODM is very likely to be removed from Jena 2.4 onwards
0525: */
0526: public void setUseDeclaredPrefixes(boolean useDeclaredPrefixes) {
0527: m_useDeclaredPrefixes = useDeclaredPrefixes;
0528: }
0529:
0530: /**
0531: * <p>Answer the namespace prefix map that contains the shared prefixes managed by this
0532: * document manager.</p>
0533: *
0534: * @return The namespace prefix mapping
0535: * @deprecated Prefix management via the ODM is very likely to be removed from Jena 2.4 onwards
0536: */
0537: public PrefixMapping getDeclaredPrefixMapping() {
0538: return m_prefixMap;
0539: }
0540:
0541: /**
0542: * <p>
0543: * Add a prefix mapping between the given public base URI and the
0544: * given prefix.
0545: * </p>
0546: *
0547: * @param uri The base URI that <code>prefix</code> expands to
0548: * @param prefix A qname prefix
0549: * @deprecated Prefix management via the ODM is very likely to be removed from Jena 2.4 onwards
0550: */
0551: public void addPrefixMapping(String uri, String prefix) {
0552: m_prefixMap.setNsPrefix(prefix, uri);
0553: }
0554:
0555: /**
0556: * <p>
0557: * Add an entry for an alternative copy of the document with the given document
0558: * URI.
0559: * </p>
0560: *
0561: * @param docURI The public URI of the ontology document
0562: * @param locationURL A locally resolvable URL where an alternative copy of the
0563: * ontology document can be found
0564: */
0565: public void addAltEntry(String docURI, String locationURL) {
0566: getFileManager().getLocationMapper().addAltEntry(docURI,
0567: locationURL);
0568: }
0569:
0570: /**
0571: * <p>
0572: * Add an entry that <code>model</code> is the appropriate model to use
0573: * for the given ontology document. Will not replace any existing
0574: * model that is cached for this URI (see
0575: * {@link #addModel(String, Model, boolean)} for an alternative
0576: * that can replace existing models).
0577: * </p>
0578: *
0579: * @param docURI The public URI of the ontology document
0580: * @param model A model containing the triples from the document
0581: */
0582: public void addModel(String docURI, Model model) {
0583: addModel(docURI, model, false);
0584: }
0585:
0586: /**
0587: * <p>
0588: * Add an entry that <code>model</code> is the appropriate model to use
0589: * for the given ontology document
0590: * </p>
0591: *
0592: * @param docURI The public URI of the ontology document
0593: * @param model A model containing the triples from the document
0594: * @param replace If true, replace any existing entry with this one.
0595: */
0596: public void addModel(String docURI, Model model, boolean replace) {
0597: if (getFileManager().getCachingModels()
0598: && (replace || !getFileManager().hasCachedModel(docURI))) {
0599: getFileManager().addCacheModel(docURI, model);
0600: }
0601: }
0602:
0603: /**
0604: * <p>
0605: * Add an entry that <code>language</code> is the URI defining the
0606: * representation language for the given document
0607: * </p>
0608: *
0609: * @param docURI The public URI of the ontology document
0610: * @param language A string defining the URI of the language
0611: * @deprecated Language determination via the ODM will be removed from Jena 2.4 onwards
0612: */
0613: public void addLanguageEntry(String docURI, String language) {
0614: m_languageMap.put(docURI, language);
0615: }
0616:
0617: /**
0618: * <p>
0619: * Remove all managed entries for the given document. Note does not side-effect
0620: * the prefixes table: this will have to be done separately.
0621: * </p>
0622: *
0623: * @param docURI The public URI for an ontology document
0624: */
0625: public void forget(String docURI) {
0626: getFileManager().getLocationMapper().removeAltEntry(docURI);
0627: getFileManager().removeCacheModel(docURI);
0628: m_languageMap.remove(docURI);
0629: }
0630:
0631: /**
0632: * <p>
0633: * Answer the ontology model that results from loading the document with the
0634: * given URI. This may be a cached model, if this document manager's policy
0635: * is to cache loaded models. If not, or if no model is cached, the document
0636: * will be read into a suitable model. The model will contain the imports closure
0637: * of the ontology, if that is the current policy of this document manager.
0638: * </p>
0639: *
0640: * @param uri Identifies the model to load.
0641: * @param spec Specifies the structure of the ontology model to create
0642: * @return An ontology model containing the statements from the ontology document.
0643: * @see #getModel
0644: */
0645: public OntModel getOntology(String uri, OntModelSpec spec) {
0646: // ensure consistency of document managers (to allow access to cached documents)
0647: OntModelSpec _spec = spec;
0648: if (_spec.getDocumentManager() != this ) {
0649: _spec = new OntModelSpec(spec);
0650: _spec.setDocumentManager(this );
0651: }
0652:
0653: // cached already?
0654: if (getFileManager().hasCachedModel(uri)) {
0655: Model cached = getFileManager().getFromCache(uri);
0656: if (cached instanceof OntModel) {
0657: return (OntModel) cached;
0658: } else {
0659: return ModelFactory.createOntologyModel(_spec, cached);
0660: }
0661: } else {
0662: OntModel m = ModelFactory.createOntologyModel(_spec, null);
0663: read(m, uri, true);
0664:
0665: // cache this model for future re-use
0666: addModel(uri, m);
0667: return m;
0668: }
0669: }
0670:
0671: /**
0672: * <p>
0673: * Answer the policy flag indicating whether the imports statements of
0674: * loaded ontologies will be processed to build a union of s.
0675: * </p>
0676: *
0677: * @return True if imported models will be included in a loaded model
0678: */
0679: public boolean getProcessImports() {
0680: return m_processImports;
0681: }
0682:
0683: /**
0684: * <p>
0685: * Answer true if the models loaded by this document manager from a given
0686: * URI will be cached, so that they can be re-used in other compound
0687: * ontology models.
0688: * </p>
0689: *
0690: * @return If true, a cache is maintained of loaded models from their URI's.
0691: */
0692: public boolean getCacheModels() {
0693: return getFileManager().getCachingModels();
0694: }
0695:
0696: /**
0697: * <p>
0698: * Set the policy flag for processing imports of loaded ontologies.
0699: * </p>
0700: *
0701: * @param processImports If true, load imported ontologies during load
0702: * @see #getProcessImports
0703: */
0704: public void setProcessImports(boolean processImports) {
0705: m_processImports = processImports;
0706: }
0707:
0708: /**
0709: * <p>
0710: * Set the policy flag that indicates whether loaded models are cached by URI
0711: * </p>
0712: *
0713: * @param cacheModels If true, models will be cached by URI
0714: * @see #getCacheModels()
0715: */
0716: public void setCacheModels(boolean cacheModels) {
0717: getFileManager().setModelCaching(cacheModels);
0718: }
0719:
0720: /**
0721: * <p>Add the given URI to the set of URI's we ignore in imports statements</p>
0722: * @param uri A URI to ignore when importing
0723: */
0724: public void addIgnoreImport(String uri) {
0725: m_ignoreImports.add(uri);
0726: }
0727:
0728: /**
0729: * <p>Remove the given URI from the set of URI's we ignore in imports statements</p>
0730: * @param uri A URI to ignore no longer when importing
0731: */
0732: public void removeIgnoreImport(String uri) {
0733: m_ignoreImports.remove(uri);
0734: }
0735:
0736: /**
0737: * <p>Answer an iterator over the set of URI's we're ignoring</p>
0738: * @return An iterator over ignored imports
0739: */
0740: public Iterator listIgnoredImports() {
0741: return m_ignoreImports.iterator();
0742: }
0743:
0744: /**
0745: * <p>Answer true if the given URI is one that will be ignored during imports </p>
0746: * @param uri A URI to test
0747: * @return True if uri will be ignored as an import
0748: */
0749: public boolean ignoringImport(String uri) {
0750: return m_ignoreImports.contains(uri);
0751: }
0752:
0753: /**
0754: * <p>
0755: * Remove all entries from the model cache
0756: * </p>
0757: */
0758: public void clearCache() {
0759: getFileManager().resetCache();
0760: }
0761:
0762: /**
0763: * <p>
0764: * Inspect the statements in the graph expressed by the given model, and load
0765: * into the model any imported documents. Imports statements are recognised according
0766: * to the model's language profile. An occurs check allows cycles of importing
0767: * safely. This method will do nothing if the {@linkplain #getProcessImports policy}
0768: * for this manager is not to process imports. If the {@linkplain #getCacheModels cache policy}
0769: * for this doc manager allows, models will be cached by URI and re-used where possible.
0770: * </p>
0771: *
0772: * @param model An ontology model whose imports are to be loaded.
0773: */
0774: public void loadImports(OntModel model) {
0775: if (m_processImports) {
0776: List readQueue = new ArrayList();
0777:
0778: // add the imported statements from the given model to the processing queue
0779: queueImports(model, readQueue, model.getProfile());
0780: loadImports(model, readQueue);
0781: }
0782: }
0783:
0784: /**
0785: * <p>Add the given model from the given URI as an import to the given model. Any models imported by the given
0786: * URI will also be imported.</p>
0787: *
0788: * @param model A model to import into
0789: * @param uri The URI of a document to import
0790: */
0791: public void loadImport(OntModel model, String uri) {
0792: if (m_processImports) {
0793: List readQueue = new ArrayList();
0794: readQueue.add(uri);
0795: loadImports(model, readQueue);
0796: }
0797: }
0798:
0799: /**
0800: * <p>Remove from the given model the import denoted by the given URI.</p>
0801: *
0802: * @param model A model
0803: * @param uri The URI of a document to no longer import
0804: */
0805: public void unloadImport(OntModel model, String uri) {
0806: if (m_processImports) {
0807: List unloadQueue = new ArrayList();
0808: unloadQueue.add(uri);
0809: unloadImports(model, unloadQueue);
0810: }
0811: }
0812:
0813: /**
0814: * <p>Answer the URL of the most recently loaded policy URL, or null
0815: * if no document manager policy has yet been loaded since the metadata
0816: * search path was last set.</p>
0817: * @return The most recently loaded policy URL or null.
0818: */
0819: public String getLoadedPolicyURL() {
0820: return m_policyURL;
0821: }
0822:
0823: // Internal implementation methods
0824: //////////////////////////////////
0825:
0826: /**
0827: * <p>Load all of the imports in the queue</p>
0828: * @param model The model to load the imports into
0829: * @param readQueue The queue of imports to load
0830: */
0831: protected void loadImports(OntModel model, List readQueue) {
0832: while (!readQueue.isEmpty()) {
0833: // we process the import statements as a FIFO queue
0834: String importURI = (String) readQueue.remove(0);
0835:
0836: if (!model.hasLoadedImport(importURI)
0837: && !ignoringImport(importURI)) {
0838: // this file has not been processed yet
0839: loadImport(model, importURI, readQueue);
0840: }
0841: }
0842:
0843: // ensure that the reasoner gets to see the updated axioms
0844: model.rebind();
0845: }
0846:
0847: /**
0848: * <p>Unload all of the imports in the queue</p>
0849: * @param model The model to unload the imports from
0850: * @param unloadQueue The queue of imports to unload
0851: */
0852: protected void unloadImports(OntModel model, List unloadQueue) {
0853: while (!unloadQueue.isEmpty()) {
0854: // we process the import statements as a FIFO queue
0855: String importURI = (String) unloadQueue.remove(0);
0856:
0857: if (model.hasLoadedImport(importURI)) {
0858: // this import has not been unloaded yet
0859:
0860: // look up the cached model - if we can't find it, we can't unload the import
0861: Model importModel = getModel(importURI);
0862: if (importModel != null) {
0863: List imports = new ArrayList();
0864:
0865: // collect a list of the imports from the model that is scheduled for removal
0866: for (StmtIterator i = importModel.listStatements(
0867: null, model.getProfile().IMPORTS(),
0868: (RDFNode) null); i.hasNext();) {
0869: imports.add(i.nextStatement().getResource()
0870: .getURI());
0871: }
0872:
0873: // now remove the sub-model
0874: model.removeSubModel(importModel, false);
0875: model.removeLoadedImport(importURI);
0876:
0877: // check the list of imports of the model we have removed - if they are not
0878: // imported by other imports that remain, we should remove them as well
0879: for (StmtIterator i = model.listStatements(null,
0880: model.getProfile().IMPORTS(),
0881: (RDFNode) null); i.hasNext();) {
0882: imports.remove(i.nextStatement().getResource()
0883: .getURI());
0884: }
0885:
0886: // any imports that remain are scheduled for removal
0887: unloadQueue.addAll(imports);
0888: }
0889: }
0890: }
0891:
0892: model.rebind();
0893: }
0894:
0895: /**
0896: * <p>Add the ontologies imported by the given model to the end of the queue.</p>
0897: */
0898: protected void queueImports(Model model, List readQueue,
0899: Profile profile) {
0900: if (model instanceof OntModel) {
0901: // add the imported ontologies to the queue
0902: readQueue.addAll(((OntModel) model)
0903: .listImportedOntologyURIs());
0904: } else {
0905: // we have to do the query manually
0906: StmtIterator i = model.listStatements(null, profile
0907: .IMPORTS(), (RDFNode) null);
0908:
0909: while (i.hasNext()) {
0910: // read the next import statement and add to the queue
0911: readQueue.add(i.nextStatement().getResource().getURI());
0912: }
0913: }
0914: }
0915:
0916: /**
0917: * <p>
0918: * Initialise the mappings for uri's and prefixes by loading metadata
0919: * from an RDF model.
0920: * </p>
0921: *
0922: * @param path The URI path to search for a loadable model
0923: */
0924: protected void initialiseMetadata(String path) {
0925: // search the path for metadata about locally cached models
0926: Model metadata = findMetadata(path);
0927:
0928: if (metadata != null) {
0929: processMetadata(metadata);
0930: }
0931: }
0932:
0933: /**
0934: * <p>
0935: * Search the given path for a resolvable URI, from which we load a model
0936: * (assuming RDF/XML).
0937: * </p>
0938: *
0939: * @param configPath The path to search
0940: * @return A model loaded by resolving an entry on the path, or null if
0941: * no path entries succeed.
0942: */
0943: protected Model findMetadata(String configPath) {
0944: if (configPath == null) {
0945: return null;
0946: }
0947:
0948: // Make a temporary file manager to look for the metadata file
0949: FileManager fm = new FileManager();
0950: fm.addLocatorFile();
0951: fm.addLocatorURL();
0952: fm.addLocatorClassLoader(fm.getClass().getClassLoader());
0953:
0954: try {
0955: String uri = null;
0956: InputStream in = null;
0957:
0958: StringTokenizer pathElems = new StringTokenizer(configPath,
0959: FileManager.PATH_DELIMITER);
0960: while (in == null && pathElems.hasMoreTokens()) {
0961: uri = pathElems.nextToken();
0962: in = fm.openNoMap(uri);
0963: }
0964:
0965: if (in != null) {
0966: String syntax = FileUtils.guessLang(uri);
0967: Model model = ModelFactory.createDefaultModel();
0968: model.read(in, uri, syntax);
0969: m_policyURL = uri;
0970: return model;
0971: }
0972: } catch (JenaException e) {
0973: log.warn("Exception while reading configuration: "
0974: + e.getMessage(), e);
0975: }
0976:
0977: return null;
0978: }
0979:
0980: /**
0981: * <p>
0982: * Load the ontology specification metadata from the model into the local
0983: * mapping tables.
0984: * </p>
0985: *
0986: * @param metadata A model containing metadata about ontologies.
0987: */
0988: protected void processMetadata(Model metadata) {
0989: // there may be configuration for the location mapper in the ODM metadata file
0990: getFileManager().getLocationMapper().processConfig(metadata);
0991:
0992: // first we process the general policy statements for this document manager
0993: for (ResIterator i = metadata.listResourcesWithProperty(
0994: RDF.type, DOC_MGR_POLICY); i.hasNext();) {
0995: Resource policy = i.nextResource();
0996:
0997: // iterate over each policy statement
0998: for (StmtIterator j = policy.listProperties(); j.hasNext();) {
0999: Statement s = j.nextStatement();
1000: Property pred = s.getPredicate();
1001:
1002: if (pred.equals(CACHE_MODELS)) {
1003: setCacheModels(s.getBoolean());
1004: } else if (pred.equals(PROCESS_IMPORTS)) {
1005: setProcessImports(s.getBoolean());
1006: } else if (pred.equals(IGNORE_IMPORT)) {
1007: addIgnoreImport(s.getResource().getURI());
1008: } else if (pred.equals(USE_DECLARED_NS_PREFIXES)) {
1009: setUseDeclaredPrefixes(s.getBoolean());
1010: }
1011: }
1012: }
1013:
1014: // then we look up individual meta-data for particular ontologies
1015: for (ResIterator i = metadata.listResourcesWithProperty(
1016: RDF.type, ONTOLOGY_SPEC); i.hasNext();) {
1017: Resource root = i.nextResource();
1018:
1019: Statement s = root.getProperty(PUBLIC_URI);
1020: if (s != null) {
1021: // this will be the key in the mappings
1022: String publicURI = s.getResource().getURI();
1023:
1024: // there may be a cached copy for this ontology
1025: s = root.getProperty(ALT_URL);
1026: if (s != null)
1027: addAltEntry(publicURI, s.getResource().getURI());
1028:
1029: // there may be a standard prefix for this ontology
1030: s = root.getProperty(PREFIX);
1031: if (s != null) {
1032: // if the namespace doesn't end with a suitable split point character, add a #
1033: boolean endWithNCNameCh = XMLChar
1034: .isNCName(publicURI.charAt(publicURI
1035: .length() - 1));
1036: String prefixExpansion = endWithNCNameCh ? (publicURI + ANCHOR)
1037: : publicURI;
1038:
1039: addPrefixMapping(prefixExpansion, s.getString());
1040: }
1041:
1042: // there may be a language specified for this ontology
1043: s = root.getProperty(LANGUAGE);
1044: if (s != null)
1045: addLanguageEntry(publicURI, s.getResource()
1046: .getURI());
1047: } else {
1048: log
1049: .warn("Ontology specification node lists no public URI - node ignored");
1050: }
1051: }
1052: }
1053:
1054: /**
1055: * <p>
1056: * Load the document referenced by the given URI into the model. The cache will be
1057: * used if permitted by the policy, and the imports of loaded model will be added to
1058: * the end of the queue.
1059: * </p>
1060: *
1061: * @param model The composite model to load into
1062: * @param importURI The URI of the document to load
1063: * @param readQueue Cumulative read queue for this operation
1064: */
1065: protected void loadImport(OntModel model, String importURI,
1066: List readQueue) {
1067: if (m_processImports) {
1068: log.debug("OntDocumentManager loading " + importURI);
1069:
1070: // add this model to occurs check list
1071: model.addLoadedImport(importURI);
1072:
1073: Model in = fetchPossiblyCachedImportModel(model, importURI);
1074:
1075: // we trap the case of importing ourself (which may happen via an indirect imports chain)
1076: if (in != model) {
1077: // queue the imports from the input model on the end of the read queue
1078: queueImports(in, readQueue, model.getProfile());
1079:
1080: // add to the imports union graph, but don't do the rebind yet
1081: model.addSubModel(in, false);
1082:
1083: // we also cache the model if we haven't seen it before (and caching is on)
1084: addModel(importURI, in);
1085: }
1086: }
1087: }
1088:
1089: /**
1090: if we have a cached version get that, otherwise load from the URI but don't do the imports closure
1091: * @param model
1092: * @param importURI
1093: * @return
1094: */
1095: private Model fetchPossiblyCachedImportModel(OntModel model,
1096: String importURI) {
1097: Model in = getModel(importURI);
1098:
1099: // if not cached, we must load it from source
1100: if (in == null) {
1101: in = fetchLoadedImportModel(model.getSpecification(),
1102: importURI);
1103: }
1104: return in;
1105: }
1106:
1107: /**
1108: * @param spec
1109: * @param importURI
1110: * @return
1111: */
1112: private Model fetchLoadedImportModel(OntModelSpec spec,
1113: String importURI) {
1114: // workaround - default model maker can apparently create models that are closed
1115: // TODO: this really suggests a bug in ModelMaker, kers to investigate
1116: ModelMaker maker = spec.getImportModelMaker();
1117: if (maker.hasModel(importURI)) {
1118: Model m = maker.getModel(importURI);
1119: if (!m.isClosed()) {
1120: return m;
1121: } else {
1122: // we don't want to hang on to closed models
1123: maker.removeModel(importURI);
1124: }
1125: }
1126:
1127: // otherwise, we use the model maker to get the model anew
1128: Model m = spec.getImportModelGetter().getModel(importURI,
1129: new ModelReader() {
1130: public Model readModel(Model toRead, String URL) {
1131: read(toRead, URL, true);
1132: return toRead;
1133: }
1134: });
1135:
1136: return m;
1137: }
1138:
1139: /**
1140: * <p>
1141: * Load into the given model from the given URI, or from a local cache URI if defined.
1142: * </p>
1143: *
1144: * @param model A model to read statements into
1145: * @param uri A URI string
1146: * @param warn If true, warn on RDF exception
1147: * @return True if the uri was read successfully
1148: */
1149: protected boolean read(Model model, String uri, boolean warn) {
1150: boolean success = false;
1151: try {
1152: // invoke the pre-read hook
1153: String source = m_readHook.beforeRead(model, uri, this );
1154: if (source == null) {
1155: log
1156: .warn("Read hook returned null source, so assuming old value: "
1157: + uri);
1158: source = uri;
1159: } else {
1160: // do the actual read
1161: getFileManager().readModel(model, source);
1162: }
1163:
1164: // now the post-read hook
1165: m_readHook.afterRead(model, source, this );
1166: success = true;
1167: } catch (Exception e) {
1168: // if there is a read failure handler, invoke it now
1169: if (getReadFailureHandler() != null) {
1170: getReadFailureHandler().handleFailedRead(uri, model, e);
1171: } else {
1172: // otherwise, log the error
1173: log.warn(
1174: "An error occurred while attempting to read from "
1175: + uri + ". Msg was '" + e.getMessage()
1176: + "'.", e);
1177: }
1178: }
1179: return success;
1180: }
1181:
1182: /**
1183: * <p>Set the default option settings.</p>
1184: */
1185: protected void setDefaults() {
1186: setCacheModels(true);
1187: setUseDeclaredPrefixes(true);
1188: setProcessImports(true);
1189: setDefaultPrefixMappings();
1190: }
1191:
1192: /**
1193: * Set the default prefix mappings.
1194: */
1195: protected void setDefaultPrefixMappings() {
1196: m_prefixMap.setNsPrefixes(PrefixMapping.Standard);
1197:
1198: // PrefixMapping.Standard includes dc:, which OntModels traditionally haven't included
1199: m_prefixMap.removeNsPrefix("dc");
1200: }
1201:
1202: //==============================================================================
1203: // Inner class definitions
1204: //==============================================================================
1205:
1206: /**
1207: * Interface defining a handler call-back in the case that the {@link OntDocumentManager}
1208: * fails in an attempt to read the contents of a URL into a model.
1209: */
1210: public static interface ReadFailureHandler {
1211: /**
1212: * Behaviour to invoke when the {@link OntDocumentManager} tries and fails
1213: * to read an ontology document from a given URL.
1214: * @param url The URL that the OntDocumentManager was trying to read
1215: * @param model The model that the OntDocumentManager is reading into
1216: * @param e An exception indicating the reason for the failure to read the document
1217: */
1218: public void handleFailedRead(String url, Model model,
1219: Exception e);
1220: }
1221:
1222: /**
1223: * Interface denoting a handler class that can intervene in the process of
1224: * reading a source document into a model.
1225: */
1226: public static interface ReadHook {
1227: /**
1228: * <p>Behaviour that is invoked <strong>before</strong> the contents of the
1229: * given source (URI or filename) are read into the given model. The return
1230: * value from this method denotes a revised string to use in place of the
1231: * supplied source string. Handlers are permitted to make state changes
1232: * to the model and the ODM, but carefully!</p>
1233: *
1234: * @param model The model that is going to receive the contents of the source
1235: * @param source The identity of the source, as a file name or URI
1236: * @param odm The Ont Document Manager invoking this handler
1237: * @return The revised name of the source (or the same string if no
1238: * change to the source is required). Note that if this method returns
1239: * <code>null</code>, the source <strong>will not be subsequently read.</strong>
1240: */
1241: public String beforeRead(Model model, String source,
1242: OntDocumentManager odm);
1243:
1244: /**
1245: * <p>Behaviour that is invoked <strong>just after</strong> the contents of the
1246: * given source (URI or filename) have been read into the given model.
1247: * Handlers are permitted to make state changes
1248: * to the model and the ODM, but carefully!</p>
1249: *
1250: * @param model The model that is going to receive the contents of the source
1251: * @param source The identity of the source, as a file name or URI
1252: * @param odm The Ont Document Manager invoking this handler
1253: */
1254: public void afterRead(Model model, String source,
1255: OntDocumentManager odm);
1256: }
1257:
1258: /**
1259: * The default implementation of {@link OntDocumentManager.ReadHook} makes no changes.
1260: */
1261: public static class DefaultReadHook implements ReadHook {
1262: public void afterRead(Model model, String source,
1263: OntDocumentManager odm) {
1264: // do nothing
1265: }
1266:
1267: public String beforeRead(Model model, String source,
1268: OntDocumentManager odm) {
1269: return source;
1270: }
1271:
1272: }
1273: }
1274:
1275: /*
1276: (c) Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
1277: All rights reserved.
1278:
1279: Redistribution and use in source and binary forms, with or without
1280: modification, are permitted provided that the following conditions
1281: are met:
1282:
1283: 1. Redistributions of source code must retain the above copyright
1284: notice, this list of conditions and the following disclaimer.
1285:
1286: 2. Redistributions in binary form must reproduce the above copyright
1287: notice, this list of conditions and the following disclaimer in the
1288: documentation and/or other materials provided with the distribution.
1289:
1290: 3. The name of the author may not be used to endorse or promote products
1291: derived from this software without specific prior written permission.
1292:
1293: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1294: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1295: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1296: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1297: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1298: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1299: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1300: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1301: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1302: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1303: */
|