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 25-Mar-2003
0009: * Filename $RCSfile: OntResourceImpl.java,v $
0010: * Revision $Revision: 1.67 $
0011: * Release status $State: Exp $
0012: *
0013: * Last modified on $Date: 2008/01/02 12:08:03 $
0014: * by $Author: andy_seaborne $
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.impl;
0019:
0020: // Imports
0021: ///////////////
0022: import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
0023: import com.hp.hpl.jena.enhanced.*;
0024: import com.hp.hpl.jena.graph.*;
0025: import com.hp.hpl.jena.shared.*;
0026: import com.hp.hpl.jena.ontology.*;
0027: import com.hp.hpl.jena.rdf.model.*;
0028: import com.hp.hpl.jena.rdf.model.impl.*;
0029: import com.hp.hpl.jena.reasoner.*;
0030: import com.hp.hpl.jena.util.ResourceUtils;
0031: import com.hp.hpl.jena.util.iterator.*;
0032: import com.hp.hpl.jena.vocabulary.*;
0033:
0034: import java.util.*;
0035:
0036: import org.apache.commons.logging.Log;
0037: import org.apache.commons.logging.LogFactory;
0038:
0039: /**
0040: * <p>
0041: * Abstract base class to provide shared implementation for implementations of ontology
0042: * resources.
0043: * </p>
0044: *
0045: * @author Ian Dickinson, HP Labs
0046: * (<a href="mailto:Ian.Dickinson@hp.com" >email</a>)
0047: * @version CVS $Id: OntResourceImpl.java,v 1.67 2008/01/02 12:08:03 andy_seaborne Exp $
0048: */
0049: public class OntResourceImpl extends ResourceImpl implements
0050: OntResource {
0051: // Constants
0052: //////////////////////////////////
0053:
0054: /** List of namespaces that are reserved for known ontology languages */
0055: public static final String[] KNOWN_LANGUAGES = new String[] {
0056: OWL.NS, RDF.getURI(), RDFS.getURI(),
0057: DAMLVocabulary.NAMESPACE_DAML_2001_03_URI, XSDDatatype.XSD };
0058:
0059: // Static variables
0060: //////////////////////////////////
0061:
0062: /**
0063: * A factory for generating OntResource facets from nodes in enhanced graphs.
0064: * Note: should not be invoked directly by user code: use
0065: * {@link com.hp.hpl.jena.rdf.model.RDFNode#as as()} instead.
0066: */
0067: public static Implementation factory = new Implementation() {
0068: public EnhNode wrap(Node n, EnhGraph eg) {
0069: if (canWrap(n, eg)) {
0070: return new OntResourceImpl(n, eg);
0071: } else {
0072: throw new ConversionException("Cannot convert node "
0073: + n.toString() + " to OntResource");
0074: }
0075: }
0076:
0077: public boolean canWrap(Node node, EnhGraph eg) {
0078: // node will support being an OntResource facet if it is a uri or bnode
0079: return node.isURI() || node.isBlank();
0080: }
0081: };
0082:
0083: private static final Log log = LogFactory
0084: .getLog(OntResourceImpl.class);
0085:
0086: // Instance variables
0087: //////////////////////////////////
0088:
0089: // Constructors
0090: //////////////////////////////////
0091:
0092: /**
0093: * <p>
0094: * Construct an ontology resource represented by the given node in the given graph.
0095: * </p>
0096: *
0097: * @param n The node that represents the resource
0098: * @param g The enh graph that contains n
0099: */
0100: public OntResourceImpl(Node n, EnhGraph g) {
0101: super (n, g);
0102: }
0103:
0104: // External signature methods
0105: //////////////////////////////////
0106:
0107: /**
0108: * <p>Answer the model that this resource is attached to, assuming that it
0109: * is an {@link OntModel}. If this resource is not attached to any model,
0110: * or is (unusually) attached to a model that is not an <code>OntModel</code>,
0111: * answer null.</p>
0112: * @return The ont model that this resource is attached to, or null.
0113: */
0114: public OntModel getOntModel() {
0115: Model m = getModel();
0116: return (m instanceof OntModel) ? (OntModel) m : null;
0117: }
0118:
0119: /**
0120: * <p>
0121: * Answer the ontology language profile that governs the ontology model to which
0122: * this ontology resource is attached.
0123: * </p>
0124: *
0125: * @return The language profile for this ontology resource
0126: * @throws JenaException if the resource is not bound to an OntModel, since
0127: * that's the only way to get the profile for the resource
0128: */
0129: public Profile getProfile() {
0130: try {
0131: return ((OntModel) getModel()).getProfile();
0132: } catch (ClassCastException e) {
0133: throw new JenaException(
0134: "Resource "
0135: + toString()
0136: + " is not attached to an OntModel, so cannot access its language profile");
0137: }
0138: }
0139:
0140: /**
0141: * <p>Answer true if this resource is a symbol in one of the standard ontology
0142: * languages supported by Jena: RDF, RDFS, OWL or DAML+OIL. Since these languages
0143: * have restricted namespaces, this check is simply a convenient way of testing whether
0144: * this resource is in one of those pre-declared namespaces.</p>
0145: * @return True if this is a term in the language namespace for OWL, RDF, RDFS or DAML+OIL.
0146: */
0147: public boolean isOntLanguageTerm() {
0148: if (!isAnon()) {
0149: for (int i = 0; i < KNOWN_LANGUAGES.length; i++) {
0150: if (getURI().startsWith(KNOWN_LANGUAGES[i])) {
0151: return true;
0152: }
0153: }
0154: }
0155: return false;
0156: }
0157:
0158: // sameAs
0159:
0160: /**
0161: * <p>Assert equivalence between the given resource and this resource. Any existing
0162: * statements for <code>sameAs</code> will be removed.</p>
0163: * @param res The resource that is declared to be the same as this resource
0164: * @exception OntProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
0165: */
0166: public void setSameAs(Resource res) {
0167: setPropertyValue(getProfile().SAME_AS(), "SAME_AS", res);
0168: }
0169:
0170: /**
0171: * <p>Add a resource that is declared to be equivalent to this resource.</p>
0172: * @param res A resource that declared to be the same as this resource
0173: * @exception OntProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
0174: */
0175: public void addSameAs(Resource res) {
0176: addPropertyValue(getProfile().SAME_AS(), "SAME_AS", res);
0177: }
0178:
0179: /**
0180: * <p>Answer a resource that is declared to be the same as this resource. If there is
0181: * more than one such resource, an arbitrary selection is made.</p>
0182: * @return res An ont resource that declared to be the same as this resource
0183: * @exception OntProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
0184: */
0185: public OntResource getSameAs() {
0186: return objectAsResource(getProfile().SAME_AS(), "SAME_AS");
0187: }
0188:
0189: /**
0190: * <p>Answer an iterator over all of the resources that are declared to be the same as
0191: * this resource. Each element of the iterator will be an {@link OntResource}.</p>
0192: * @return An iterator over the resources equivalent to this resource.
0193: * @exception OntProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
0194: */
0195: public ExtendedIterator listSameAs() {
0196: return listAs(getProfile().SAME_AS(), "SAME_AS",
0197: OntResource.class);
0198: }
0199:
0200: /**
0201: * <p>Answer true if this resource is the same as the given resource.</p>
0202: * @param res A resource to test against
0203: * @return True if the resources are declared the same via a <code>sameAs</code> statement.
0204: */
0205: public boolean isSameAs(Resource res) {
0206: return hasPropertyValue(getProfile().SAME_AS(), "SAME_AS", res);
0207: }
0208:
0209: /**
0210: * <p>Remove the statement that this resource is the same as the given resource. If this statement
0211: * is not true of the current model, nothing happens.</p>
0212: * @param res A resource that may be declared to be the sameAs this resource
0213: */
0214: public void removeSameAs(Resource res) {
0215: removePropertyValue(getProfile().SAME_AS(), "SAME_AS", res);
0216: }
0217:
0218: // differentFrom
0219:
0220: /**
0221: * <p>Assert that the given resource and this resource are distinct. Any existing
0222: * statements for <code>differentFrom</code> will be removed.</p>
0223: * @param res The resource that is declared to be distinct from this resource
0224: * @exception OntProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
0225: */
0226: public void setDifferentFrom(Resource res) {
0227: setPropertyValue(getProfile().DIFFERENT_FROM(),
0228: "DIFFERENT_FROM", res);
0229: }
0230:
0231: /**
0232: * <p>Add a resource that is declared to be equivalent to this resource.</p>
0233: * @param res A resource that declared to be the same as this resource
0234: * @exception OntProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
0235: */
0236: public void addDifferentFrom(Resource res) {
0237: addPropertyValue(getProfile().DIFFERENT_FROM(),
0238: "DIFFERENT_FROM", res);
0239: }
0240:
0241: /**
0242: * <p>Answer a resource that is declared to be distinct from this resource. If there is
0243: * more than one such resource, an arbitrary selection is made.</p>
0244: * @return res An ont resource that declared to be different from this resource
0245: * @exception OntProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
0246: */
0247: public OntResource getDifferentFrom() {
0248: return objectAsResource(getProfile().DIFFERENT_FROM(),
0249: "DIFFERENT_FROM");
0250: }
0251:
0252: /**
0253: * <p>Answer an iterator over all of the resources that are declared to be different from
0254: * this resource. Each element of the iterator will be an {@link OntResource}.</p>
0255: * @return An iterator over the resources different from this resource.
0256: * @exception OntProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
0257: */
0258: public ExtendedIterator listDifferentFrom() {
0259: return listAs(getProfile().DIFFERENT_FROM(), "DIFFERENT_FROM",
0260: OntResource.class);
0261: }
0262:
0263: /**
0264: * <p>Answer true if this resource is different from the given resource.</p>
0265: * @param res A resource to test against
0266: * @return True if the resources are declared to be distinct via a <code>differentFrom</code> statement.
0267: */
0268: public boolean isDifferentFrom(Resource res) {
0269: return hasPropertyValue(getProfile().DIFFERENT_FROM(),
0270: "DIFFERENT_FROM", res);
0271: }
0272:
0273: /**
0274: * <p>Remove the statement that this resource is different the given resource. If this statement
0275: * is not true of the current model, nothing happens.</p>
0276: * @param res A resource that may be declared to be differentFrom this resource
0277: */
0278: public void removeDifferentFrom(Resource res) {
0279: removePropertyValue(getProfile().DIFFERENT_FROM(),
0280: "DIFFERENT_FROM", res);
0281: }
0282:
0283: // seeAlso
0284:
0285: /**
0286: * <p>Assert that the given resource provides additional information about the definition of this resource</p>
0287: * @param res A resource that can provide additional information about this resource
0288: * @exception OntProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
0289: */
0290: public void setSeeAlso(Resource res) {
0291: setPropertyValue(getProfile().SEE_ALSO(), "SEE_ALSO", res);
0292: }
0293:
0294: /**
0295: * <p>Add a resource that is declared to provided additional information about the definition of this resource</p>
0296: * @param res A resource that provides extra information on this resource
0297: * @exception OntProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
0298: */
0299: public void addSeeAlso(Resource res) {
0300: addPropertyValue(getProfile().SEE_ALSO(), "SEE_ALSO", res);
0301: }
0302:
0303: /**
0304: * <p>Answer a resource that provides additional information about this resource. If more than one such resource
0305: * is defined, make an arbitrary choice.</p>
0306: * @return res A resource that provides additional information about this resource
0307: * @exception OntProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
0308: */
0309: public Resource getSeeAlso() {
0310: return objectAsResource(getProfile().SEE_ALSO(), "SEE_ALSO");
0311: }
0312:
0313: /**
0314: * <p>Answer an iterator over all of the resources that are declared to provide addition
0315: * information about this resource.</p>
0316: * @return An iterator over the resources providing additional definition on this resource.
0317: * @exception OntProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
0318: */
0319: public ExtendedIterator listSeeAlso() {
0320: checkProfile(getProfile().SEE_ALSO(), "SEE_ALSO");
0321: return WrappedIterator.create(
0322: listProperties(getProfile().SEE_ALSO())).mapWith(
0323: new ObjectAsOntResourceMapper());
0324: }
0325:
0326: /**
0327: * <p>Answer true if this resource has the given resource as a source of additional information.</p>
0328: * @param res A resource to test against
0329: * @return True if the <code>res</code> provides more information on this resource.
0330: */
0331: public boolean hasSeeAlso(Resource res) {
0332: return hasPropertyValue(getProfile().SEE_ALSO(), "SEE_ALSO",
0333: res);
0334: }
0335:
0336: /**
0337: * <p>Remove the statement indicating the given resource as a source of additional information
0338: * about this resource. If this statement
0339: * is not true of the current model, nothing happens.</p>
0340: * @param res A resource that may be declared to provide additional information about this resource
0341: */
0342: public void removeSeeAlso(Resource res) {
0343: removePropertyValue(getProfile().SEE_ALSO(), "SEE_ALSO", res);
0344: }
0345:
0346: // is defined by
0347:
0348: /**
0349: * <p>Assert that the given resource provides a source of definitions about this resource. Any existing
0350: * statements for <code>isDefinedBy</code> will be removed.</p>
0351: * @param res The resource that is declared to be a definition of this resource.
0352: * @exception OntProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
0353: */
0354: public void setIsDefinedBy(Resource res) {
0355: setPropertyValue(getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY",
0356: res);
0357: }
0358:
0359: /**
0360: * <p>Add a resource that is declared to provide a definition of this resource.</p>
0361: * @param res A defining resource
0362: * @exception OntProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
0363: */
0364: public void addIsDefinedBy(Resource res) {
0365: addPropertyValue(getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY",
0366: res);
0367: }
0368:
0369: /**
0370: * <p>Answer a resource that is declared to provide a definition of this resource. If there is
0371: * more than one such resource, an arbitrary selection is made.</p>
0372: * @return res An ont resource that is declared to provide a definition of this resource
0373: * @exception OntProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
0374: */
0375: public Resource getIsDefinedBy() {
0376: return objectAsResource(getProfile().IS_DEFINED_BY(),
0377: "IS_DEFINED_BY");
0378: }
0379:
0380: /**
0381: * <p>Answer an iterator over all of the resources that are declared to define
0382: * this resource. </p>
0383: * @return An iterator over the resources defining this resource.
0384: * @exception OntProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
0385: */
0386: public ExtendedIterator listIsDefinedBy() {
0387: checkProfile(getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY");
0388: return WrappedIterator.create(
0389: listProperties(getProfile().IS_DEFINED_BY())).mapWith(
0390: new ObjectAsOntResourceMapper());
0391: }
0392:
0393: /**
0394: * <p>Answer true if this resource is defined by the given resource.</p>
0395: * @param res A resource to test against
0396: * @return True if <code>res</code> defines this resource.
0397: */
0398: public boolean isDefinedBy(Resource res) {
0399: return hasPropertyValue(getProfile().IS_DEFINED_BY(),
0400: "IS_DEFINED_BY", res);
0401: }
0402:
0403: /**
0404: * <p>Remove the statement that this resource is defined by the given resource. If this statement
0405: * is not true of the current model, nothing happens.</p>
0406: * @param res A resource that may be declared to define this resource
0407: */
0408: public void removeDefinedBy(Resource res) {
0409: removePropertyValue(getProfile().IS_DEFINED_BY(),
0410: "IS_DEFINED_BY", res);
0411: }
0412:
0413: // version info
0414:
0415: /**
0416: * <p>Assert that the given string is the value of the version info for this resource. Any existing
0417: * statements for <code>versionInfo</code> will be removed.</p>
0418: * @param info The version information for this resource
0419: * @exception OntProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
0420: */
0421: public void setVersionInfo(String info) {
0422: checkProfile(getProfile().VERSION_INFO(), "VERSION_INFO");
0423: removeAll(getProfile().VERSION_INFO());
0424: addVersionInfo(info);
0425: }
0426:
0427: /**
0428: * <p>Add the given version information to this resource.</p>
0429: * @param info A version information string for this resource
0430: * @exception OntProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
0431: */
0432: public void addVersionInfo(String info) {
0433: checkProfile(getProfile().VERSION_INFO(), "VERSION_INFO");
0434: addProperty(getProfile().VERSION_INFO(), getModel()
0435: .createLiteral(info));
0436: }
0437:
0438: /**
0439: * <p>Answer the version information string for this object. If there is
0440: * more than one such resource, an arbitrary selection is made.</p>
0441: * @return A version info string
0442: * @exception OntProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
0443: */
0444: public String getVersionInfo() {
0445: checkProfile(getProfile().VERSION_INFO(), "VERSION_INFO");
0446: try {
0447: return getRequiredProperty(getProfile().VERSION_INFO())
0448: .getString();
0449: } catch (PropertyNotFoundException ignore) {
0450: return null;
0451: }
0452: }
0453:
0454: /**
0455: * <p>Answer an iterator over all of the version info strings for this resource.</p>
0456: * @return An iterator over the version info strings for this resource.
0457: * @exception OntProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
0458: */
0459: public ExtendedIterator listVersionInfo() {
0460: checkProfile(getProfile().VERSION_INFO(), "VERSION_INFO");
0461: return WrappedIterator.create(
0462: listProperties(getProfile().VERSION_INFO())).mapWith(
0463: new ObjectAsStringMapper());
0464: }
0465:
0466: /**
0467: * <p>Answer true if this resource has the given version information</p>
0468: * @param info Version information to test for
0469: * @return True if this resource has <code>info</code> as version information.
0470: */
0471: public boolean hasVersionInfo(String info) {
0472: checkProfile(getProfile().VERSION_INFO(), "VERSION_INFO");
0473: return hasProperty(getProfile().VERSION_INFO(), info);
0474: }
0475:
0476: /**
0477: * <p>Remove the statement that the given string provides version information about
0478: * this resource. If this statement
0479: * is not true of the current model, nothing happens.</p>
0480: * @param info A version information string to be removed
0481: */
0482: public void removeVersionInfo(String info) {
0483: checkProfile(getProfile().VERSION_INFO(), "VERSION_INFO");
0484: Literal infoAsLiteral = ResourceFactory
0485: .createPlainLiteral(info);
0486: getModel().remove(this , getProfile().VERSION_INFO(),
0487: infoAsLiteral);
0488: }
0489:
0490: // label
0491:
0492: /**
0493: * <p>Assert that the given string is the value of the label for this resource. Any existing
0494: * statements for <code>label</code> will be removed.</p>
0495: * @param label The label for this resource
0496: * @param lang The language attribute for this label (EN, FR, etc) or null if not specified.
0497: * @exception OntProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
0498: */
0499: public void setLabel(String label, String lang) {
0500: checkProfile(getProfile().LABEL(), "LABEL");
0501: removeAll(getProfile().LABEL());
0502: addLabel(label, lang);
0503: }
0504:
0505: /**
0506: * <p>Add the given label to this resource.</p>
0507: * @param label A label string for this resource
0508: * @param lang The language attribute for this label (EN, FR, etc) or null if not specified.
0509: * @exception OntProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
0510: */
0511: public void addLabel(String label, String lang) {
0512: addLabel(getModel().createLiteral(label, lang));
0513: }
0514:
0515: /**
0516: * <p>Add the given label to this resource.</p>
0517: * @param label The literal label
0518: * @exception OntProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
0519: */
0520: public void addLabel(Literal label) {
0521: addPropertyValue(getProfile().LABEL(), "LABEL", label);
0522: }
0523:
0524: /**
0525: * <p>Answer the label string for this object. If there is
0526: * more than one such resource, an arbitrary selection is made.</p>
0527: * @param lang The language attribute for the desired label (EN, FR, etc) or null for don't care. Will
0528: * attempt to retreive the most specific label matching the given language</p>
0529: * @return A label string matching the given language, or null if there is no matching label.
0530: * @exception OntProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
0531: */
0532: public String getLabel(String lang) {
0533: checkProfile(getProfile().LABEL(), "LABEL");
0534: if (lang == null || lang.length() == 0) {
0535: // don't care which language version we get
0536: try {
0537: return getRequiredProperty(getProfile().LABEL())
0538: .getString();
0539: } catch (PropertyNotFoundException ignore) {
0540: return null;
0541: }
0542: } else {
0543: // search for the best match for the specified language
0544: return selectLang(listProperties(getProfile().LABEL()),
0545: lang);
0546: }
0547: }
0548:
0549: /**
0550: * <p>Answer an iterator over all of the label literals for this resource.</p>
0551: * @param lang The language to restrict any label values to, or null to select all languages
0552: * @return An iterator over RDF {@link Literal}'s.
0553: * @exception OntProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
0554: */
0555: public ExtendedIterator listLabels(String lang) {
0556: checkProfile(getProfile().LABEL(), "LABEL");
0557: return WrappedIterator.create(
0558: listProperties(getProfile().LABEL())).filterKeep(
0559: new LangTagFilter(lang)).mapWith(new ObjectMapper());
0560: }
0561:
0562: /**
0563: * <p>Answer true if this resource has the given label</p>
0564: * @param label The label to test for
0565: * @param lang The optional language tag, or null for don't care.
0566: * @return True if this resource has <code>label</code> as a label.
0567: */
0568: public boolean hasLabel(String label, String lang) {
0569: return hasLabel(getModel().createLiteral(label, lang));
0570: }
0571:
0572: /**
0573: * <p>Answer true if this resource has the given label</p>
0574: * @param label The label to test for
0575: * @return True if this resource has <code>label</code> as a label.
0576: */
0577: public boolean hasLabel(Literal label) {
0578: boolean found = false;
0579:
0580: ExtendedIterator i = listLabels(label.getLanguage());
0581: while (!found && i.hasNext()) {
0582: found = label.equals(i.next());
0583: }
0584:
0585: i.close();
0586: return found;
0587: }
0588:
0589: /**
0590: * <p>Remove the statement that the given string is a label for
0591: * this resource. If this statement
0592: * is not true of the current model, nothing happens.</p>
0593: * @param label A label string to be removed
0594: * @param lang A lang tag
0595: */
0596: public void removeLabel(String label, String lang) {
0597: removeLabel(getModel().createLiteral(label, lang));
0598: }
0599:
0600: /**
0601: * <p>Remove the statement that the given string is a label for
0602: * this resource. If this statement
0603: * is not true of the current model, nothing happens.</p>
0604: * @param label A label literal to be removed
0605: */
0606: public void removeLabel(Literal label) {
0607: removePropertyValue(getProfile().LABEL(), "LABEL", label);
0608: }
0609:
0610: // comment
0611:
0612: /**
0613: * <p>Assert that the given string is the comment on this resource. Any existing
0614: * statements for <code>comment</code> will be removed.</p>
0615: * @param comment The comment for this resource
0616: * @param lang The language attribute for this comment (EN, FR, etc) or null if not specified.
0617: * @exception OntProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
0618: */
0619: public void setComment(String comment, String lang) {
0620: checkProfile(getProfile().COMMENT(), "COMMENT");
0621: removeAll(getProfile().COMMENT());
0622: addComment(comment, lang);
0623: }
0624:
0625: /**
0626: * <p>Add the given comment to this resource.</p>
0627: * @param comment A comment string for this resource
0628: * @param lang The language attribute for this comment (EN, FR, etc) or null if not specified.
0629: * @exception OntProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
0630: */
0631: public void addComment(String comment, String lang) {
0632: addComment(getModel().createLiteral(comment, lang));
0633: }
0634:
0635: /**
0636: * <p>Add the given comment to this resource.</p>
0637: * @param comment The literal comment
0638: * @exception OntProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
0639: */
0640: public void addComment(Literal comment) {
0641: checkProfile(getProfile().COMMENT(), "COMMENT");
0642: addProperty(getProfile().COMMENT(), comment);
0643: }
0644:
0645: /**
0646: * <p>Answer the comment string for this object. If there is
0647: * more than one such resource, an arbitrary selection is made.</p>
0648: * @param lang The language attribute for the desired comment (EN, FR, etc) or null for don't care. Will
0649: * attempt to retreive the most specific comment matching the given language</p>
0650: * @return A comment string matching the given language, or null if there is no matching comment.
0651: * @exception OntProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
0652: */
0653: public String getComment(String lang) {
0654: checkProfile(getProfile().COMMENT(), "COMMENT");
0655: if (lang == null) {
0656: // don't care which language version we get
0657: try {
0658: return getRequiredProperty(getProfile().COMMENT())
0659: .getString();
0660: } catch (PropertyNotFoundException ignore) {
0661: // no comment :-)
0662: return null;
0663: }
0664: } else {
0665: // search for the best match for the specified language
0666: return selectLang(listProperties(getProfile().COMMENT()),
0667: lang);
0668: }
0669: }
0670:
0671: /**
0672: * <p>Answer an iterator over all of the comment literals for this resource.</p>
0673: * @return An iterator over RDF {@link Literal}'s.
0674: * @exception OntProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
0675: */
0676: public ExtendedIterator listComments(String lang) {
0677: checkProfile(getProfile().COMMENT(), "COMMENT");
0678: return WrappedIterator.create(
0679: listProperties(getProfile().COMMENT())).filterKeep(
0680: new LangTagFilter(lang)).mapWith(new ObjectMapper());
0681: }
0682:
0683: /**
0684: * <p>Answer true if this resource has the given comment.</p>
0685: * @param comment The comment to test for
0686: * @param lang The optional language tag, or null for don't care.
0687: * @return True if this resource has <code>comment</code> as a comment.
0688: */
0689: public boolean hasComment(String comment, String lang) {
0690: return hasComment(getModel().createLiteral(comment, lang));
0691: }
0692:
0693: /**
0694: * <p>Answer true if this resource has the given comment.</p>
0695: * @param comment The comment to test for
0696: * @return True if this resource has <code>comment</code> as a comment.
0697: */
0698: public boolean hasComment(Literal comment) {
0699: boolean found = false;
0700:
0701: ExtendedIterator i = listComments(comment.getLanguage());
0702: while (!found && i.hasNext()) {
0703: found = comment.equals(i.next());
0704: }
0705:
0706: i.close();
0707: return found;
0708: }
0709:
0710: /**
0711: * <p>Remove the statement that the given string is a comment on
0712: * this resource. If this statement
0713: * is not true of the current model, nothing happens.</p>
0714: * @param comment A comment string to be removed
0715: * @param lang A lang tag
0716: */
0717: public void removeComment(String comment, String lang) {
0718: removeComment(getModel().createLiteral(comment, lang));
0719: }
0720:
0721: /**
0722: * <p>Remove the statement that the given string is a comment on
0723: * this resource. If this statement
0724: * is not true of the current model, nothing happens.</p>
0725: * @param comment A comment literal to be removed
0726: */
0727: public void removeComment(Literal comment) {
0728: removePropertyValue(getProfile().COMMENT(), "COMMENT", comment);
0729: }
0730:
0731: // rdf:type
0732:
0733: /**
0734: * <p>Set the RDF type (i.e. the class) for this resource, replacing any
0735: * existing <code>rdf:type</code> property. Any existing statements for the RDF type
0736: * will first be removed.</p>
0737: *
0738: * @param cls The RDF resource denoting the new value for the <code>rdf:type</code> property,
0739: * which will replace any existing type property.
0740: */
0741: public void setRDFType(Resource cls) {
0742: setPropertyValue(RDF.type, "rdf:type", cls);
0743: }
0744:
0745: /**
0746: * <p>Add the given class as one of the <code>rdf:type</code>'s for this resource.</p>
0747: *
0748: * @param cls An RDF resource denoting a new value for the <code>rdf:type</code> property.
0749: */
0750: public void addRDFType(Resource cls) {
0751: addPropertyValue(RDF.type, "rdf:type", cls);
0752: }
0753:
0754: /**
0755: * <p>
0756: * Answer the <code>rdf:type</code> (ie the class) of this resource. If there
0757: * is more than one type for this resource, the return value will be one of
0758: * the values, but it is not specified which one (nor that it will consistently
0759: * be the same one each time). Equivalent to <code>getRDFType( false )</code>.
0760: * </p>
0761: *
0762: * @return A resource that is the rdf:type for this resource, or one of them if
0763: * more than one is defined.
0764: */
0765: public Resource getRDFType() {
0766: return getRDFType(false);
0767: }
0768:
0769: /**
0770: * <p>
0771: * Answer the <code>rdf:type</code> (ie the class) of this resource. If there
0772: * is more than one type for this resource, the return value will be one of
0773: * the values, but it is not specified which one (nor that it will consistently
0774: * be the same one each time).
0775: * </p>
0776: *
0777: * @param direct If true, only consider the direct types of this resource, and not
0778: * the super-classes of the type(s).
0779: * @return A resource that is the rdf:type for this resource, or one of them if
0780: * more than one is defined.
0781: */
0782: public Resource getRDFType(boolean direct) {
0783: ExtendedIterator i = null;
0784: try {
0785: i = listRDFTypes(direct);
0786: return i.hasNext() ? (Resource) i.next() : null;
0787: } finally {
0788: i.close();
0789: }
0790: }
0791:
0792: /**
0793: * <p>
0794: * Answer an iterator over the RDF classes to which this resource belongs.
0795: * </p>
0796: *
0797: * @param direct If true, only answer those resources that are direct types
0798: * of this resource, not the super-classes of the class etc.
0799: * @return An iterator over the set of this resource's classes, each of which
0800: * will be a {@link Resource}.
0801: */
0802: public ExtendedIterator listRDFTypes(boolean direct) {
0803: ExtendedIterator i = listDirectPropertyValues(RDF.type,
0804: "rdf:type", null, getProfile().SUB_CLASS_OF(), direct,
0805: false);
0806:
0807: // we only want each result once
0808: return UniqueExtendedIterator.create(i);
0809: }
0810:
0811: /**
0812: * <p>
0813: * Answer true if this resource is a member of the class denoted by the
0814: * given URI.</p>
0815: *
0816: * @param uri Denotes the URI of a class to which this value may belong
0817: * @return True if this resource has the given class as one of its <code>rdf:type</code>'s.
0818: */
0819: public boolean hasRDFType(String uri) {
0820: return hasRDFType(getModel().getResource(uri));
0821: }
0822:
0823: /**
0824: * <p>
0825: * Answer true if this resource is a member of the class denoted by the
0826: * given class resource. Includes all available types, so is equivalent to
0827: * <code><pre>
0828: * hasRDF( ontClass, false );
0829: * </pre></code>
0830: * </p>
0831: *
0832: * @param ontClass Denotes a class to which this value may belong
0833: * @return True if this resource has the given class as one of its <code>rdf:type</code>'s.
0834: */
0835: public boolean hasRDFType(Resource ontClass) {
0836: return hasRDFType(ontClass, "unknown", false);
0837: }
0838:
0839: /**
0840: * <p>
0841: * Answer true if this resource is a member of the class denoted by the
0842: * given class resource.
0843: * </p>
0844: *
0845: * @param ontClass Denotes a class to which this value may belong
0846: * @param direct If true, only consider the direct types of this resource, ignoring
0847: * the super-classes of the stated types.
0848: * @return True if this resource has the given class as one of its <code>rdf:type</code>'s.
0849: */
0850: public boolean hasRDFType(Resource ontClass, boolean direct) {
0851: return hasRDFType(ontClass, "unknown", direct);
0852: }
0853:
0854: protected boolean hasRDFType(Resource ontClass, String name,
0855: boolean direct) {
0856: checkProfile(ontClass, name);
0857:
0858: if (!direct) {
0859: // just an ordinary query - we can answer this directly (more efficient)
0860: return hasPropertyValue(RDF.type, "rdf:type", ontClass);
0861: } else {
0862: // need the direct version - not so efficient
0863: ExtendedIterator i = null;
0864: try {
0865: i = listRDFTypes(true);
0866: while (i.hasNext()) {
0867: if (ontClass.equals(i.next())) {
0868: return true;
0869: }
0870: }
0871:
0872: return false;
0873: } finally {
0874: i.close();
0875: }
0876: }
0877: }
0878:
0879: /**
0880: * <p>Remove the statement that this resource is of the given RDF type. If this statement
0881: * is not true of the current model, nothing happens.</p>
0882: * @param cls A resource denoting a class that that is to be removed from the classes of this resource
0883: */
0884: public void removeRDFType(Resource cls) {
0885: removePropertyValue(RDF.type, "rdf:type", cls);
0886: }
0887:
0888: // utility methods
0889:
0890: /**
0891: * <p>Answer the cardinality of the given property on this resource. The cardinality
0892: * is the number of distinct values there are for the property.</p>
0893: * @param p A property
0894: * @return The cardinality for the property <code>p</code> on this resource, as an
0895: * integer greater than or equal to zero.
0896: */
0897: public int getCardinality(Property p) {
0898: int n = 0;
0899: for (Iterator i = UniqueExtendedIterator
0900: .create(listPropertyValues(p)); i.hasNext(); n++) {
0901: i.next();
0902: }
0903:
0904: return n;
0905: }
0906:
0907: /**
0908: * <p>
0909: * Set the value of the given property of this ontology resource to the given
0910: * value, encoded as an RDFNode. Maintains the invariant that there is
0911: * at most one value of the property for a given resource, so existing
0912: * property values are first removed. To add multiple properties, use
0913: * {@link #addProperty( Property, RDFNode ) addProperty}.
0914: * </p>
0915: *
0916: * @param property The property to update
0917: * @param value The new value of the property as an RDFNode, or null to
0918: * effectively remove this property.
0919: */
0920: public void setPropertyValue(Property property, RDFNode value) {
0921: // if there is an existing property, remove it
0922: removeAll(property);
0923:
0924: // now set the new value
0925: if (value != null) {
0926: addProperty(property, value);
0927: }
0928: }
0929:
0930: /**
0931: * <p>Answer the value of a given RDF property for this ontology resource, or null
0932: * if it doesn't have one. The value is returned as an RDFNode, from which
0933: * the concrete data value can be extracted for literals. If the value is
0934: * a resource, it will present the {@link OntResource} facet.
0935: * If there is more than one RDF
0936: * statement with the given property for the current value, it is not defined
0937: * which of the values will be returned.</p>
0938: *
0939: * @param property An RDF property
0940: * @return An RDFNode whose value is the value, or one of the values, of the
0941: * given property. If the property is not defined the method returns null.
0942: */
0943: public RDFNode getPropertyValue(Property property) {
0944: Statement s = getProperty(property);
0945: if (s == null) {
0946: return null;
0947: } else {
0948: return asOntResource(s.getObject());
0949: }
0950: }
0951:
0952: /**
0953: * <p>Answer an iterator over the set of all values for a given RDF property. Each
0954: * value in the iterator will be an RDFNode, representing the value (object) of
0955: * each statement in the underlying model.</p>
0956: *
0957: * @param property The property whose values are sought
0958: * @return An Iterator over the values of the property
0959: */
0960: public NodeIterator listPropertyValues(Property property) {
0961: return new NodeIteratorImpl(listProperties(property).mapWith(
0962: new ObjectAsOntResourceMapper()), null);
0963: }
0964:
0965: /**
0966: * <p>Removes this resource from the ontology by deleting any statements that refer to it,
0967: * as either statement-subject or statement-object.
0968: * If this resource is a property, this method will <strong>not</strong> remove statements
0969: * whose predicate is this property.</p>
0970: * <p><strong>Caveat:</strong> Jena RDF models contain statements, not resources <em>per se</em>,
0971: * so this method simulates removal of an object by removing all of the statements that have
0972: * this resource as subject or object, with one exception. If the resource is referenced
0973: * in an RDF List, i.e. as the object of an <code>rdf:first</code> statement in a list cell,
0974: * this reference is <strong>not</strong> removed. Removing an arbitrary <code>rdf:first</code>
0975: * statement from the midst of a list, without doing other work to repair the list, would
0976: * leave an ill-formed list in the model. Therefore, if this resource is known to appear
0977: * in a list somewhere in the model, it should be separately deleted from that list before
0978: * calling this remove method.
0979: * </p>
0980: */
0981: public void remove() {
0982: Set stmts = new HashSet();
0983: List lists = new ArrayList();
0984: List skip = new ArrayList();
0985: Property first = getProfile().FIRST();
0986:
0987: // collect statements mentioning this object
0988: for (StmtIterator i = listProperties(); i.hasNext();) {
0989: stmts.add(i.next());
0990: }
0991: for (StmtIterator i = getModel().listStatements(null, null,
0992: this ); i.hasNext();) {
0993: stmts.add(i.next());
0994: }
0995:
0996: // check for lists
0997: for (Iterator i = stmts.iterator(); i.hasNext();) {
0998: Statement s = (Statement) i.next();
0999: if (s.getPredicate().equals(first)
1000: && s.getObject().equals(this )) {
1001: // _this_ is referenced from inside a list
1002: // we don't delete this reference, since it would make the list ill-formed
1003: log
1004: .debug(toString()
1005: + " is referened from an RDFList, so will not be fully removed");
1006: skip.add(s);
1007: } else if (s.getObject() instanceof Resource) {
1008: // check for list-valued properties
1009: Resource obj = s.getResource();
1010: if (obj.canAs(RDFList.class)) {
1011: // this value is a list, so we will want to remove all of the elements
1012: lists.add(obj);
1013: }
1014: }
1015: }
1016:
1017: // add in the contents of the lists to the statements to be removed
1018: for (Iterator i = lists.iterator(); i.hasNext();) {
1019: Resource r = (Resource) i.next();
1020: stmts.addAll(((RDFListImpl) r.as(RDFList.class))
1021: .collectStatements());
1022: }
1023:
1024: // skip the contents of the skip list
1025: stmts.removeAll(skip);
1026:
1027: // and then remove the remainder
1028: for (Iterator i = stmts.iterator(); i.hasNext();) {
1029: ((Statement) i.next()).remove();
1030: }
1031: }
1032:
1033: /**
1034: * <p>Remove the specific RDF property-value pair from this DAML resource.</p>
1035: *
1036: * @param property The property to be removed
1037: * @param value The specific value of the property to be removed
1038: */
1039: public void removeProperty(Property property, RDFNode value) {
1040: getModel().remove(this , property, value);
1041: }
1042:
1043: /**
1044: * <p>Answer a view of this resource as an annotation property</p>
1045: * @return This resource, but viewed as an AnnotationProperty
1046: * @exception ConversionException if the resource cannot be converted to an annotation property
1047: */
1048: public AnnotationProperty asAnnotationProperty() {
1049: return (AnnotationProperty) as(AnnotationProperty.class);
1050: }
1051:
1052: /**
1053: * <p>Answer a view of this resource as a property</p>
1054: * @return This resource, but viewed as an OntProperty
1055: * @exception ConversionException if the resource cannot be converted to a property
1056: */
1057: public OntProperty asProperty() {
1058: return (OntProperty) as(OntProperty.class);
1059: }
1060:
1061: /**
1062: * <p>Answer a view of this resource as an object property</p>
1063: * @return This resource, but viewed as an ObjectProperty
1064: * @exception ConversionException if the resource cannot be converted to an object property
1065: */
1066: public ObjectProperty asObjectProperty() {
1067: return (ObjectProperty) as(ObjectProperty.class);
1068: }
1069:
1070: /**
1071: * <p>Answer a view of this resource as a datatype property</p>
1072: * @return This resource, but viewed as a DatatypeProperty
1073: * @exception ConversionException if the resource cannot be converted to a datatype property
1074: */
1075: public DatatypeProperty asDatatypeProperty() {
1076: return (DatatypeProperty) as(DatatypeProperty.class);
1077: }
1078:
1079: /**
1080: * <p>Answer a view of this resource as an individual</p>
1081: * @return This resource, but viewed as an Individual
1082: * @exception ConversionException if the resource cannot be converted to an individual
1083: */
1084: public Individual asIndividual() {
1085: return (Individual) as(Individual.class);
1086: }
1087:
1088: /**
1089: * <p>Answer a view of this resource as a class</p>
1090: * @return This resource, but viewed as an OntClass
1091: * @exception ConversionException if the resource cannot be converted to a class
1092: */
1093: public OntClass asClass() {
1094: return (OntClass) as(OntClass.class);
1095: }
1096:
1097: /**
1098: * <p>Answer a view of this resource as an ontology description node</p>
1099: * @return This resource, but viewed as an Ontology
1100: * @exception ConversionException if the resource cannot be converted to an ontology description node
1101: */
1102: public Ontology asOntology() {
1103: return (Ontology) as(Ontology.class);
1104: }
1105:
1106: /**
1107: * <p>Answer a view of this resource as an 'all different' declaration</p>
1108: * @return This resource, but viewed as an AllDifferent node
1109: * @exception ConversionException if the resource cannot be converted to an all different declaration
1110: */
1111: public AllDifferent asAllDifferent() {
1112: return (AllDifferent) as(AllDifferent.class);
1113: }
1114:
1115: /**
1116: * <p>Answer a view of this resource as a data range</p>
1117: * @return This resource, but viewed as a DataRange
1118: * @exception ConversionException if the resource cannot be converted to a data range
1119: */
1120: public DataRange asDataRange() {
1121: return (DataRange) as(DataRange.class);
1122: }
1123:
1124: // Conversion test methods
1125:
1126: /**
1127: * <p>Answer true if this resource can be viewed as an annotation property</p>
1128: * @return True if this resource can be viewed as an AnnotationProperty
1129: */
1130: public boolean isAnnotationProperty() {
1131: return getProfile().ANNOTATION_PROPERTY() != null
1132: && canAs(AnnotationProperty.class);
1133: }
1134:
1135: /**
1136: * <p>Answer true if this resource can be viewed as a property</p>
1137: * @return True if this resource can be viewed as an OntProperty
1138: */
1139: public boolean isProperty() {
1140: return canAs(OntProperty.class);
1141: }
1142:
1143: /**
1144: * <p>Answer true if this resource can be viewed as an object property</p>
1145: * @return True if this resource can be viewed as an ObjectProperty
1146: */
1147: public boolean isObjectProperty() {
1148: return getProfile().OBJECT_PROPERTY() != null
1149: && canAs(ObjectProperty.class);
1150: }
1151:
1152: /**
1153: * <p>Answer true if this resource can be viewed as a datatype property</p>
1154: * @return True if this resource can be viewed as a DatatypeProperty
1155: */
1156: public boolean isDatatypeProperty() {
1157: return getProfile().DATATYPE_PROPERTY() != null
1158: && canAs(DatatypeProperty.class);
1159: }
1160:
1161: /**
1162: * <p>Answer true if this resource can be viewed as an individual</p>
1163: * @return True if this resource can be viewed as an Individual
1164: */
1165: public boolean isIndividual() {
1166: OntModel m = (getModel() instanceof OntModel) ? (OntModel) getModel()
1167: : null;
1168:
1169: // can we use the reasoner's native abilities to do the isntance test?
1170: boolean useInf = false;
1171: useInf = m.getProfile().THING() != null
1172: && m.getReasoner() != null
1173: && m.getReasoner().supportsProperty(
1174: ReasonerVocabulary.individualAsThingP);
1175:
1176: StmtIterator i = null, j = null;
1177: try {
1178: if (m != null) {
1179: if (!useInf) {
1180: // either not using the OWL reasoner, or not using OWL
1181: // look for an rdf:type of this resource that is a class
1182: for (i = listProperties(RDF.type); i.hasNext();) {
1183: Resource rType = i.nextStatement()
1184: .getResource();
1185: if (rType.equals(m.getProfile().THING())) {
1186: // the resource has rdf:type owl:Thing (or equivalent)
1187: return true;
1188: }
1189: for (j = rType.listProperties(RDF.type); j
1190: .hasNext();) {
1191: if (j.nextStatement().getResource().equals(
1192: getProfile().CLASS())) {
1193: // we have found an rdf:type of the subject that is an owl, rdfs or daml Class
1194: // therefore this is an individual
1195: return true;
1196: }
1197: }
1198: }
1199:
1200: // apparently not an instance
1201: return false;
1202: } else {
1203: // using the rule reasoner on an OWL graph, so we can determine
1204: // individuals as those things that have rdf:type owl:Thing
1205: return hasProperty(RDF.type, getProfile().THING());
1206: }
1207: }
1208: } finally {
1209: if (i != null) {
1210: i.close();
1211: }
1212: if (j != null) {
1213: j.close();
1214: }
1215: }
1216:
1217: // default - try to convert and return false if fail
1218: return canAs(Individual.class);
1219: }
1220:
1221: /**
1222: * <p>Answer true if this resource can be viewed as a class</p>
1223: * @return True if this resource can be viewed as an OntClass
1224: */
1225: public boolean isClass() {
1226: return canAs(OntClass.class);
1227: }
1228:
1229: /**
1230: * <p>Answer true if this resource can be viewed as an ontology description node</p>
1231: * @return True if this resource can be viewed as an Ontology
1232: */
1233: public boolean isOntology() {
1234: return getProfile().ONTOLOGY() != null && canAs(Ontology.class);
1235: }
1236:
1237: /**
1238: * <p>Answer true if this resource can be viewed as a data range</p>
1239: * @return True if this resource can be viewed as a DataRange
1240: */
1241: public boolean isDataRange() {
1242: return getProfile().DATARANGE() != null
1243: && canAs(DataRange.class);
1244: }
1245:
1246: /**
1247: * <p>Answer true if this resource can be viewed as an 'all different' declaration</p>
1248: * @return True if this resource can be viewed as an AllDifferent node
1249: */
1250: public boolean isAllDifferent() {
1251: return getProfile().ALL_DIFFERENT() != null
1252: && canAs(AllDifferent.class);
1253: }
1254:
1255: // Internal implementation methods
1256: //////////////////////////////////
1257:
1258: /** Answer true if the node has the given type in the graph */
1259: protected static boolean hasType(Node n, EnhGraph g, Resource type) {
1260: boolean hasType = false;
1261: ClosableIterator i = g.asGraph().find(n, RDF.type.asNode(),
1262: type.asNode());
1263: hasType = i.hasNext();
1264: i.close();
1265: return hasType;
1266: }
1267:
1268: /**
1269: * Throw an exception if a term is not in the profile
1270: * @param term The term being checked
1271: * @param name The name of the term
1272: * @exception ProfileException if term is null (indicating it is not in the profile)
1273: **/
1274: protected void checkProfile(Object term, String name) {
1275: if (term == null) {
1276: throw new ProfileException(name, getProfile());
1277: }
1278: }
1279:
1280: /**
1281: * <p>Answer the literal with the language tag that best matches the required language</p>
1282: * @param stmts A StmtIterator over the candidates
1283: * @param lang The language we're searching for, assumed non-null.
1284: * @return The literal value that best matches the given language tag, or null if there are no matches
1285: */
1286: protected String selectLang(StmtIterator stmts, String lang) {
1287: String found = null;
1288:
1289: while (stmts.hasNext()) {
1290: RDFNode n = stmts.nextStatement().getObject();
1291:
1292: if (n instanceof Literal) {
1293: Literal l = (Literal) n;
1294: String lLang = l.getLanguage();
1295:
1296: // is this a better match?
1297: if (lang.equalsIgnoreCase(lLang)) {
1298: // exact match
1299: found = l.getString();
1300: break;
1301: } else if (lLang != null && lLang.length() > 1
1302: && lang.equalsIgnoreCase(lLang.substring(0, 2))) {
1303: // partial match - want EN, found EN-GB
1304: // keep searching in case there's a better
1305: found = l.getString();
1306: } else if (found == null && lLang == null) {
1307: // found a string with no (i.e. default) language - keep this unless we've got something better
1308: found = l.getString();
1309: }
1310: }
1311: }
1312:
1313: stmts.close();
1314: return found;
1315: }
1316:
1317: /** Answer true if the desired lang tag matches the target lang tag */
1318: protected boolean langTagMatch(String desired, String target) {
1319: return (desired == null)
1320: || (desired.equalsIgnoreCase(target))
1321: || (target.length() > desired.length() && desired
1322: .equalsIgnoreCase(target.substring(desired
1323: .length())));
1324: }
1325:
1326: /** Answer the object of a statement with the given property, .as() the given class */
1327: protected Object objectAs(Property p, String name, Class asClass) {
1328: checkProfile(p, name);
1329: try {
1330: return getRequiredProperty(p).getObject().as(asClass);
1331: } catch (PropertyNotFoundException e) {
1332: return null;
1333: }
1334: }
1335:
1336: /** Answer the object of a statement with the given property, .as() an OntResource */
1337: protected OntResource objectAsResource(Property p, String name) {
1338: return (OntResource) objectAs(p, name, OntResource.class);
1339: }
1340:
1341: /** Answer the object of a statement with the given property, .as() an OntProperty */
1342: protected OntProperty objectAsProperty(Property p, String name) {
1343: return (OntProperty) objectAs(p, name, OntProperty.class);
1344: }
1345:
1346: /** Answer the int value of a statement with the given property */
1347: protected int objectAsInt(Property p, String name) {
1348: checkProfile(p, name);
1349: return getRequiredProperty(p).getInt();
1350: }
1351:
1352: /** Answer an iterator for the given property, whose values are .as() some class */
1353: protected ExtendedIterator listAs(Property p, String name, Class cls) {
1354: checkProfile(p, name);
1355: return WrappedIterator.create(listProperties(p)).mapWith(
1356: new ObjectAsMapper(cls));
1357: }
1358:
1359: /** Add the property value, checking that it is supported in the profile */
1360: protected void addPropertyValue(Property p, String name,
1361: RDFNode value) {
1362: checkProfile(p, name);
1363: addProperty(p, value);
1364: }
1365:
1366: /** Set the property value, checking that it is supported in the profile */
1367: protected void setPropertyValue(Property p, String name,
1368: RDFNode value) {
1369: checkProfile(p, name);
1370: removeAll(p);
1371: addProperty(p, value);
1372: }
1373:
1374: /** Answer true if the given property is defined in the profile, and has the given value */
1375: protected boolean hasPropertyValue(Property p, String name,
1376: RDFNode value) {
1377: checkProfile(p, name);
1378: return hasProperty(p, value);
1379: }
1380:
1381: /** Add the given value to a list which is the value of the given property */
1382: protected void addListPropertyValue(Property p, String name,
1383: RDFNode value) {
1384: checkProfile(p, name);
1385:
1386: // get the list value
1387: if (hasProperty(p)) {
1388: RDFNode cur = getRequiredProperty(p).getObject();
1389: if (!cur.canAs(RDFList.class)) {
1390: throw new OntologyException(
1391: "Tried to add a value to a list-valued property "
1392: + p
1393: + " but the current value is not a list: "
1394: + cur);
1395: }
1396:
1397: RDFList values = (RDFList) cur.as(RDFList.class);
1398:
1399: // now add our value to the list
1400: if (!values.contains(value)) {
1401: RDFList newValues = values.with(value);
1402:
1403: // if the previous values was nil, the return value will be a new list
1404: if (newValues != values) {
1405: removeAll(p);
1406: addProperty(p, newValues);
1407: }
1408: }
1409: } else {
1410: // create a new list to hold the only value we know so far
1411: addProperty(p, ((OntModel) getModel())
1412: .createList(new RDFNode[] { value }));
1413: }
1414: }
1415:
1416: /** Convert this resource to the facet denoted by cls, by adding rdf:type type if necessary */
1417: protected RDFNode convertToType(Resource type, String name,
1418: Class cls) {
1419: checkProfile(type, name);
1420: if (canAs(cls)) {
1421: // don't need to update the model, we already can do the given facet
1422: return as(cls);
1423: }
1424:
1425: // we're told that adding this rdf:type will make the as() possible - let's see
1426: addProperty(RDF.type, type);
1427: return as(cls);
1428: }
1429:
1430: /**
1431: * <p>Return an iterator of values, respecting the 'direct' modifier</p>
1432: * @param p The property whose values are required
1433: * @param name The short name of the property (for generating error messages)
1434: * @param cls Class object denoting the facet to map the returned values to
1435: * @param orderRel If direct, and we are not using an inference engine, this is the property
1436: * to use to define the maximal lower elements of the partial order
1437: * @param direct If true, only return the direct (adjacent) values
1438: * @param inverse If true, use the inverse of p rather than p
1439: * @return An iterator of nodes that are in relation p to this resource (possibly inverted), which
1440: * have been mapped to the facet denoted by <code>cls</code>.
1441: */
1442: protected ExtendedIterator listDirectPropertyValues(Property p,
1443: String name, Class cls, Property orderRel, boolean direct,
1444: boolean inverse) {
1445: Iterator i = null;
1446: checkProfile(p, name);
1447:
1448: Property sc = p;
1449:
1450: // check for requesting direct versions of these properties
1451: if (direct) {
1452: sc = getModel().getProperty(
1453: ReasonerRegistry.makeDirect(sc.asNode()).getURI());
1454: }
1455:
1456: // determine the subject and object pairs for the list statements calls
1457: Resource subject = inverse ? null : this ;
1458: Resource object = inverse ? this : null;
1459: Map1 mapper = inverse ? (Map1) new SubjectAsMapper(cls)
1460: : (Map1) new ObjectAsMapper(cls);
1461:
1462: // are we working on an inference graph?
1463: OntModel m = (OntModel) getGraph();
1464: InfGraph ig = null;
1465: if (m.getGraph() instanceof InfGraph) {
1466: ig = (InfGraph) m.getGraph();
1467: }
1468:
1469: // can we go direct to the graph?
1470: if (!direct
1471: || ((ig != null) && ig.getReasoner().supportsProperty(
1472: sc))) {
1473: // either not direct, or the direct sc property is supported
1474: // ensure we have an extended iterator of statements this rdfs:subClassOf _x
1475: // NB we only want the subjects or objects of the statements
1476: i = getModel().listStatements(subject, sc, object).mapWith(
1477: mapper);
1478: } else {
1479: i = computeDirectValues(p, orderRel, inverse, subject,
1480: object, mapper);
1481: }
1482:
1483: return UniqueExtendedIterator.create(i);
1484: }
1485:
1486: /**
1487: * <p>In the absence of a reasoner that can compute direct (adjacent) property values,
1488: * we must perform the calculation of the direct values computationally here.</p>
1489: * @param p
1490: * @param orderRel
1491: * @param inverse
1492: * @param subject
1493: * @param object
1494: * @param mapper
1495: * @return
1496: */
1497: private Iterator computeDirectValues(Property p, Property orderRel,
1498: boolean inverse, Resource subject, Resource object,
1499: Map1 mapper) {
1500: // graph does not support direct directly
1501: ExtendedIterator j = getModel().listStatements(subject, p,
1502: object).mapWith(mapper);
1503:
1504: // collect a list of the candidates
1505: List s = new ArrayList();
1506: for (; j.hasNext();) {
1507: s.add(j.next());
1508: }
1509:
1510: // we need to keep this node out of the iterator for now, else it will spoil the maximal
1511: // generator compression (since all the (e.g.) sub-classes will be sub-classes of this node
1512: // and so will be excluded from the maximal lower elements calculation)
1513: ResourceUtils.removeEquiv(s, orderRel, this );
1514: boolean withheld = s.remove(this );
1515:
1516: // we now compress the list by reducing all equivalent values to a single representative
1517:
1518: // first partition the list by equivalence under orderRel
1519: List partition = ResourceUtils.partition(s, orderRel);
1520: Map equivSets = new HashMap();
1521:
1522: // then reduce each part of the partition to a singleton, but remember the others
1523: s.clear();
1524: for (Iterator i = partition.iterator(); i.hasNext();) {
1525: List part = (List) i.next();
1526: // if this is a singleton we just add it to the compressed candidates
1527: if (part.size() == 1) {
1528: s.add(part.get(0));
1529: } else {
1530: // we select a single representative
1531: Resource r = (Resource) part.remove(0);
1532: // remember the other equivalent values
1533: equivSets.put(r, part);
1534: s.add(r);
1535: }
1536: }
1537:
1538: // now s1 contains a reduced set of nodes, in which any fully-connected sub-graph under
1539: // orderRel has been reduced to a single representative
1540:
1541: // generate the short list as the maximal bound under the given partial order
1542: s = ResourceUtils.maximalLowerElements(s, orderRel, inverse);
1543:
1544: // create a list of these values lower elements, plus their equivalents (if any)
1545: List s2 = new ArrayList();
1546: for (Iterator i = s.iterator(); i.hasNext();) {
1547: Resource r = (Resource) i.next();
1548: s2.add(r);
1549: if (equivSets.containsKey(r)) {
1550: s2.addAll((List) equivSets.get(r));
1551: }
1552: }
1553:
1554: // put myself back if needed
1555: if (withheld) {
1556: s2.add(this );
1557: }
1558:
1559: return s2.iterator();
1560: }
1561:
1562: /** Remove a specified property-value pair, if it exists */
1563: protected void removePropertyValue(Property prop, String name,
1564: RDFNode value) {
1565: checkProfile(prop, name);
1566: getModel().remove(this , prop, value);
1567: }
1568:
1569: /** Answer the given node presenting the OntResource facet if it can */
1570: private static RDFNode asOntResource(RDFNode n) {
1571: return n.isResource() ? n.as(OntResource.class) : n;
1572: }
1573:
1574: //==============================================================================
1575: // Inner class definitions
1576: //==============================================================================
1577:
1578: /** Implementation of Map1 that performs as( Class ) for a given class */
1579: protected static class AsMapper implements Map1 {
1580: private Class m_as;
1581:
1582: public AsMapper(Class as) {
1583: m_as = as;
1584: }
1585:
1586: public Object map1(Object x) {
1587: return (x instanceof Resource) ? ((Resource) x).as(m_as)
1588: : x;
1589: }
1590: }
1591:
1592: /** Implementation of Map1 that performs as( Class ) for a given class, on the subject of a statement */
1593: protected static class SubjectAsMapper implements Map1 {
1594: private Class m_as;
1595:
1596: public SubjectAsMapper(Class as) {
1597: m_as = as;
1598: }
1599:
1600: public Object map1(Object x) {
1601: if (x instanceof Statement) {
1602: RDFNode subj = ((Statement) x).getSubject();
1603: return (m_as == null) ? subj : subj.as(m_as);
1604: } else {
1605: return x;
1606: }
1607: }
1608: }
1609:
1610: /** Implementation of Map1 that extracts the subject of a statement */
1611: protected static class SubjectMapper implements Map1 {
1612: public Object map1(Object x) {
1613: return (x instanceof Statement) ? ((Statement) x)
1614: .getSubject() : x;
1615: }
1616: }
1617:
1618: /** Implementation of Map1 that performs as( Class ) for a given class, on the object of a statement */
1619: protected static class ObjectAsMapper implements Map1 {
1620: private Class m_as;
1621:
1622: public ObjectAsMapper(Class as) {
1623: m_as = as;
1624: }
1625:
1626: public Object map1(Object x) {
1627: if (x instanceof Statement) {
1628: RDFNode obj = ((Statement) x).getObject();
1629: return (m_as == null) ? obj : obj.as(m_as);
1630: } else {
1631: return x;
1632: }
1633: }
1634: }
1635:
1636: /** Implementation of Map1 that performs getString on the object of a statement */
1637: protected class ObjectAsStringMapper implements Map1 {
1638: public Object map1(Object x) {
1639: return (x instanceof Statement) ? ((Statement) x)
1640: .getString() : x;
1641: }
1642: }
1643:
1644: /** Implementation of Map1 that returns the object of a statement */
1645: protected static class ObjectMapper implements Map1 {
1646: public Object map1(Object x) {
1647: return (x instanceof Statement) ? ((Statement) x)
1648: .getObject() : x;
1649: }
1650: }
1651:
1652: /** Implementation of Map1 that returns the object of a statement as an ont resource */
1653: protected static class ObjectAsOntResourceMapper extends
1654: ObjectMapper {
1655: public Object map1(Object x) {
1656: if (x instanceof Statement) {
1657: return asOntResource(((Statement) x).getObject());
1658: } else {
1659: return x;
1660: }
1661: }
1662: }
1663:
1664: /** Filter for matching language tags on literals */
1665: protected class LangTagFilter extends Filter {
1666: protected String m_lang;
1667:
1668: public LangTagFilter(String lang) {
1669: m_lang = lang;
1670: }
1671:
1672: public boolean accept(Object x) {
1673: if (x instanceof Literal) {
1674: return langTagMatch(m_lang, ((Literal) x).getLanguage());
1675: } else if (x instanceof Statement) {
1676: // we assume for a statement that we're filtering on the object of the statement
1677: return accept(((Statement) x).getObject());
1678: } else {
1679: return false;
1680: }
1681: }
1682: }
1683:
1684: /** Filter for accepting only the given value, based on .equals() */
1685: protected class SingleEqualityFilter extends Filter {
1686: private Object m_obj;
1687:
1688: public SingleEqualityFilter(Object x) {
1689: m_obj = x;
1690: }
1691:
1692: public boolean accept(Object x) {
1693: return m_obj.equals(x);
1694: }
1695: }
1696: }
1697:
1698: /*
1699: (c) Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
1700: All rights reserved.
1701:
1702: Redistribution and use in source and binary forms, with or without
1703: modification, are permitted provided that the following conditions
1704: are met:
1705:
1706: 1. Redistributions of source code must retain the above copyright
1707: notice, this list of conditions and the following disclaimer.
1708:
1709: 2. Redistributions in binary form must reproduce the above copyright
1710: notice, this list of conditions and the following disclaimer in the
1711: documentation and/or other materials provided with the distribution.
1712:
1713: 3. The name of the author may not be used to endorse or promote products
1714: derived from this software without specific prior written permission.
1715:
1716: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1717: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1718: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1719: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1720: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1721: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1722: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1723: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1724: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
1725: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1726: */
|