View Javadoc

1   /*
2    * $Id: ClassNode.java,v 1.28 2005/07/12 17:06:45 jlerner Exp $
3    *
4    * Copyright (c) 1999-2004, BBN Technologies, LLC.
5    * All rights reserved.
6    * http://www.daml.org/legal/opensource/bbn_license.html
7    */
8   
9   package com.bbn.swede.core.dom;
10  
11  import java.util.ArrayList;
12  
13  import org.eclipse.jface.text.IRegion;
14  
15  import com.bbn.swede.core.OWLCore;
16  import com.hp.hpl.jena.rdf.arp.MalformedURIException;
17  import com.hp.hpl.jena.rdf.arp.URI;
18  import com.hp.hpl.jena.rdf.model.AnonId;
19  import com.hp.hpl.jena.rdf.model.Model;
20  import com.hp.hpl.jena.rdf.model.Resource;
21  import com.hp.hpl.jena.rdf.model.Statement;
22  
23  /***
24   * Common implementation for class nodes.  A class node is a tag that is
25   * interpreted as an OWL Thing, either due to the type of the tag or based on
26   * its position in the document relative to other thing or predicate tags.
27   * @author jlerner
28   */
29  public abstract class ClassNode extends TagNode
30  {
31     /***
32      * Default constructor for class nodes.  Creates a parentless and childless
33      * node with discrete begin/end tags.
34      */
35     public ClassNode()
36     {
37        
38     }
39     
40     /***
41      * Creates a parentless and childless ClassNode as either a singleton or with
42      * discrete begin and end tags.
43      * @param bSingleton <code>true</code> to create a singleton tag, 
44      * <code>false</code> to create discrete begin and end tags.
45      */
46     public ClassNode(boolean bSingleton)
47     {
48        super(bSingleton);
49     }
50     
51     /***
52      * The Jena resource associated with this class node.
53      */
54     protected Resource _resAssoc;
55     
56     /***
57      * The rdf:Type statement implied by this tag.  May be <code>null</code> if
58      * no type is implied (i.e. for rdf:Description rdf:about= nodes).
59      */
60     protected Statement _stmtImplicitType;
61     /***
62      * Associate the node with a Jena resource.
63      * @param res The resource to associate.  May be <code>null</code> to
64      *            disassociate the previous resource without setting a new one.
65      */
66     protected void setAssociatedResource(Resource res)
67     {
68        _resAssoc = res;
69     }
70     
71     /***
72      * Associates the node with an implicit type statement.
73      * @param stmt The implicit type statement to associate.  May be 
74      *             <code>null</code> to disassociate the previous implicit type
75      *             without setting a new one.
76      */
77     protected void setImplicitTypeStatement(Statement stmt)
78     {
79        _stmtImplicitType = stmt;
80     }
81  
82     /***
83      * Retrieve the Jena resource associated with the node.
84      * @return The associated resource, or <code>null</code> if no resource is
85      *         associated with this node.
86      */
87     public Resource getAssociatedResource()
88     {
89        return _resAssoc;
90     }
91     
92     /***
93      * Retrieves the Jena statement indicating the implicit type of this node.
94      * @return The implicit type statement, or <code>null</code> if this class
95      *         node does not imply a type.
96      */
97     public Statement getImplicitTypeStatement()
98     {
99        return _stmtImplicitType;
100    }
101 
102    /***
103     * <p>Creates a Jena resource to represent the class node.  If one of the node's
104     * children is an rdf:ID, or rdf:about node, the corresponding attribute
105     * value will be used as the resource's URI.  If one of its children is an
106     * rdf:NodeID, the corresponding attribute value will be used as the
107     * resource's anonymous ID.  If none of these is present, an anonymous
108     * resource is created.  If more than one of these attributes is present,
109     * the behavior is undefined.</p>
110     *
111     * <p>If an rdf:ID or rdf:about attribute specifies a URI that already exists in
112     * the model, the existing resource will be used rather than creating a new
113     * one.</p>
114     *
115     * <p>For all class nodes except rdf:Description, a statement will automatically
116     * be added to the resource to reflect its implicity type.</p>
117     * @param model The model in which to create the resource.
118     * @return The created resource
119     */
120    protected Resource createJenaResource(Model model)
121    {
122       OASTNode[] nodes = getNodesOfType(RDF_ID, 1);
123       if (nodes.length == 0)
124       {
125          nodes = getNodesOfType(RDF_ABOUT, 1);
126          if (nodes.length == 0)
127          {
128             nodes = getNodesOfType(RDF_NODEID);
129          }
130       }
131       //TODO: check for > 1 ID and place a marker
132       Resource res = null;
133       if (nodes.length > 0)
134       {
135          AttributeNode an = (AttributeNode) nodes[0];
136          String sValue = an.getValue();
137          switch (an.getNodeType())
138          {
139             case RDF_ID :
140                sValue = '#' + sValue;
141                //fall through
142             case RDF_ABOUT :
143                String sBase = getXmlBase();
144                try
145                {
146                   URI uriBase = (sBase == null ? null : new URI(sBase));
147                   URI uri = new URI(uriBase, sValue);
148                   res = model.createResource(uri.toString());
149                }
150                catch (MalformedURIException e)
151                {
152                   //TODO: place error marker indicating a relative URI with no xml:base
153                }
154                break;
155             case RDF_NODEID :
156                //TODO: precache AnonIDs before building initial model
157                //      to avoid clashes with autogenerated ones
158                res = model.createResource(new AnonId(sValue));
159                break;
160             default:
161                OWLCore.trace("ClassNode.createAssociatedResource()",
162                              "Found an impossible attribute", true);
163                break;
164          }
165       }
166       else
167       {
168          res = model.createResource();
169       }
170       if (res == null)
171       {
172          //            System.out.println("Couldn't add " + getQName() + " to model");
173          return null;
174       }
175 
176       //If this isn't a rdf:Description rdf:about= tag, create an
177       //implicit type statement
178       if (getNodeType() != RDF_DESCRIPTION)
179       {
180          String sUri = getUri();
181          if (sUri != null)
182          {
183             Statement stmtType = model.createStatement(
184                res, 
185                com.hp.hpl.jena.vocabulary.RDF.type, 
186                model.getResource(sUri));
187             model.add(stmtType);
188             setImplicitTypeStatement(stmtType);
189          }
190       }
191       setAssociatedResource(res);
192       return res;
193    }
194 
195    /***
196     * Removes the class node's associated resource from the Jena model.  If
197     * the node has no associated resource, nothing happens.
198     * @param model The model to remove the resource from.
199     */
200    protected void removeJenaResource(Model model)
201    {
202       if (_resAssoc == null)
203       {
204          return;
205       }
206       Resource res = getAssociatedResource();
207       Statement stmt = getImplicitTypeStatement();
208       setAssociatedResource(null);
209       setImplicitTypeStatement(null);
210       //Shouldn't need to check for unused resource, since all that
211       //needs to get cleaned up is the statements and that's already handled
212       //below and by GenericAttribute and PropertyNode.
213       if (stmt != null && getOWLAbstractSyntaxTree().getNode(stmt) == null)
214       {
215          //Nobody is using the implicit type statement anymore, remove it.
216          model.remove(stmt);
217       }
218    }
219 
220    /***
221     * Adds a new property as the last child of this node.  If this tag is a
222     * singleton, it will be split into discrete begin and end tags first.  This
223     * node will be displaced to make room for the property.
224     * @param property The property to add
225     * @throws OASTException if this node is attached to a read-only OAST
226     */
227    private void appendProperty(final PropertyNode property) throws OASTException
228    {
229       final TagNode tag = (TagNode)this;
230       IOASTRunnable action = new DefaultOASTRunnable()
231       {
232          /* (non-Javadoc)
233           * @see com.bbn.swede.core.dom.IOASTRunnable#run()
234           */
235          public void run() throws OASTException
236          {
237             TagNode tag = splitSingleton();
238             IRegion[] regions = tag.simplePartitioning();
239             int iOffset = regions[regions.length - 1].getOffset();
240             property.accept(new NodeDisplacementVisitor(0, iOffset));
241             tag.insert(property, true);
242          }
243       };
244       if (getOWLAbstractSyntaxTree() != null)
245       {
246          getOWLAbstractSyntaxTree().run(action);
247       }
248       else
249       {
250          action.run();
251       }
252    }
253    //TODO: allow specification of a datatype?
254    /***
255     * Adds a literal property to this individual.  An individual may have
256     * multiple properties with the same QName.
257     * @param sQName the qualified name of the property
258     * @param sLiteral the text to use as the property's object.  If this is an
259     *                 empty string, a property will be created with a zero-length
260     *                 literal as its object.  If this is <code>null</code>,
261     *                 no property will be created.
262     * @throws OASTException if this node is attached to a read-only OAST
263     *                       or <code>sQName</code> does not specifies a
264     *                       non-property node type from one of the language
265     *                       namespaces.
266     */
267    public void addPropertyLiteral(String sQName, String sLiteral) throws OASTException
268    {
269       if (sLiteral == null)
270       {
271          return;
272       }
273       OASTNode node = TagNode.create(sQName, this);
274       if (!(node instanceof PropertyNode))
275       {
276          throw new OASTException(sQName + " is not a property node type.", this);
277       }
278       PropertyNode prop = (PropertyNode)node;
279       prop.setObjectLiteral(sLiteral);
280       appendProperty(prop);
281    }
282 
283    /***
284     * Adds a property to this individual whose object is another individual.
285     * The tags for the object will be nested inside of the property tags
286     * @param sQName the qualified name of the property
287     * @param nodeObject the tag to use as the property's object
288     * @throws OASTException if this node is attached to a read-only OAST
289     *                       or <code>sQName</code> does not specifies a
290     *                       non-property node type from one of the language
291     *                       namespaces.
292     */
293    public void addPropertyTag(String sQName, ClassNode nodeObject) throws OASTException
294    {
295       OASTNode node = TagNode.create(sQName, this);
296       if (!(node instanceof PropertyNode))
297       {
298          throw new OASTException(sQName + " is not a property node type.", this);
299       }
300       PropertyNode prop = (PropertyNode)node;
301       prop.setObjectTag(nodeObject);
302       appendProperty(prop);
303    }
304 
305    /***
306     * Adds a property to this individual whose object is a resource defined
307     * by a tag elsewhere.
308     * @param sQName the qualified name of the property
309     * @param sResourceURI the URI of the resource to use as the property's 
310     *                     object
311     * @throws OASTException if this node is attached to a read-only OAST
312     *                       or <code>sQName</code> does not specifies a
313     *                       non-property node type from one of the language
314     *                       namespaces.
315     */
316    public void addPropertyResource(String sQName, String sResourceURI) throws OASTException
317    {
318       OASTNode node = TagNode.create(sQName, this);
319       if (!(node instanceof PropertyNode))
320       {
321          throw new OASTException(sQName + " is not a property node type.", this);
322       }
323       PropertyNode prop = (PropertyNode)node;
324       prop.setObjectResource(sResourceURI);
325       appendProperty(prop);
326    }
327    
328    /***
329     * Retrieves all properties of this individual with a speicifed QName.
330     * @param sQName the qualified name of the properties to retrieve
331     * @return An array (possibly empty) of all properties with the specified QName.
332     */
333    public PropertyNode[] getProperties(String sQName)
334    {
335       ArrayList al = new ArrayList();
336       OASTNode[] children = getChildren();
337       for (int i = 0; i < children.length; i++)
338       {
339          OASTNode node = children[i];
340          if (node instanceof PropertyNode)
341          {
342             if (node.getQName().equals(sQName))
343             {
344                al.add(node);
345             }
346          }
347       }
348       return (PropertyNode[])al.toArray(new PropertyNode[0]);
349    }
350    
351    /***
352     * Removes a property from this node.
353     * @param property The property to remove
354     * @throws OASTException if this node is attached to a read-only OAST
355     */
356    public void removeProperty(PropertyNode property) throws OASTException
357    {
358       if (property == null)
359       {
360          return;
361       }
362       else
363       {
364          remove(property, true);
365       }
366    }
367 }