View Javadoc

1   /*
2    * $Id: TagNode.java,v 1.21 2005/07/08 18:19:12 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 org.eclipse.jface.text.IRegion;
12  
13  import com.bbn.swede.core.dom.owl.AllDifferent;
14  import com.bbn.swede.core.dom.owl.AllValuesFrom;
15  import com.bbn.swede.core.dom.owl.AnnotationProperty;
16  import com.bbn.swede.core.dom.owl.BackwardCompatibleWith;
17  import com.bbn.swede.core.dom.owl.Cardinality;
18  import com.bbn.swede.core.dom.owl.ComplementOf;
19  import com.bbn.swede.core.dom.owl.DataRange;
20  import com.bbn.swede.core.dom.owl.DatatypeProperty;
21  import com.bbn.swede.core.dom.owl.DeprecatedClass;
22  import com.bbn.swede.core.dom.owl.DeprecatedProperty;
23  import com.bbn.swede.core.dom.owl.DifferentFrom;
24  import com.bbn.swede.core.dom.owl.DisjointWith;
25  import com.bbn.swede.core.dom.owl.DistinctMembers;
26  import com.bbn.swede.core.dom.owl.EquivalentClass;
27  import com.bbn.swede.core.dom.owl.EquivalentProperty;
28  import com.bbn.swede.core.dom.owl.FunctionalProperty;
29  import com.bbn.swede.core.dom.owl.HasValue;
30  import com.bbn.swede.core.dom.owl.Imports;
31  import com.bbn.swede.core.dom.owl.IncompatibleWith;
32  import com.bbn.swede.core.dom.owl.IntersectionOf;
33  import com.bbn.swede.core.dom.owl.InverseFunctionalProperty;
34  import com.bbn.swede.core.dom.owl.InverseOf;
35  import com.bbn.swede.core.dom.owl.MaxCardinality;
36  import com.bbn.swede.core.dom.owl.MinCardinality;
37  import com.bbn.swede.core.dom.owl.ObjectProperty;
38  import com.bbn.swede.core.dom.owl.OnProperty;
39  import com.bbn.swede.core.dom.owl.OneOf;
40  import com.bbn.swede.core.dom.owl.Ontology;
41  import com.bbn.swede.core.dom.owl.OntologyProperty;
42  import com.bbn.swede.core.dom.owl.PriorVersion;
43  import com.bbn.swede.core.dom.owl.Restriction;
44  import com.bbn.swede.core.dom.owl.SameAs;
45  import com.bbn.swede.core.dom.owl.SomeValuesFrom;
46  import com.bbn.swede.core.dom.owl.SymmetricProperty;
47  import com.bbn.swede.core.dom.owl.Thing;
48  import com.bbn.swede.core.dom.owl.TransitiveProperty;
49  import com.bbn.swede.core.dom.owl.UnionOf;
50  import com.bbn.swede.core.dom.owl.VersionInfo;
51  import com.bbn.swede.core.dom.rdf.Description;
52  import com.bbn.swede.core.dom.rdf.First;
53  import com.bbn.swede.core.dom.rdf.List;
54  import com.bbn.swede.core.dom.rdf.Property;
55  import com.bbn.swede.core.dom.rdf.Rdf;
56  import com.bbn.swede.core.dom.rdf.Rest;
57  import com.bbn.swede.core.dom.rdf.Type;
58  import com.bbn.swede.core.dom.rdfs.Comment;
59  import com.bbn.swede.core.dom.rdfs.Domain;
60  import com.bbn.swede.core.dom.rdfs.Label;
61  import com.bbn.swede.core.dom.rdfs.Range;
62  import com.bbn.swede.core.dom.rdfs.SeeAlso;
63  import com.bbn.swede.core.dom.rdfs.SubClassOf;
64  import com.bbn.swede.core.dom.rdfs.SubPropertyOf;
65  
66  /***
67   * Common implementation for nodes representing XML tags.
68   * @author jlerner
69   */
70  public abstract class TagNode extends OASTNode
71  {
72     /***
73      * Creates a parentless tag node with discrete begin and end tags.
74      */
75     public TagNode()
76     {
77        initRegions(false);
78     }
79  
80     /***
81      * Creates a parentless tag node.  The tag will be either a singleton or 
82      * discrete begin and end tags depending on the parameter passed.  A 
83      * singleton tag node will be split into begin and end tags automatically if
84      * a non-attribute child is added to it later.
85      * @param bSingleton <code>true</code> to create a singleton tag, 
86      * <code>false</code> to create discrete begin and end tags.
87      */
88     public TagNode(boolean bSingleton)
89     {
90        initRegions(bSingleton);
91     }
92     
93     /***
94      * Initializes the simple partitioning of the tag node.
95      * @param bSingleton <code>true</code> to partition the tag as a singleton,
96      *                   <code>false</code> to partition it as discrete begin
97      *                   and end tags.
98      */
99     protected void initRegions(boolean bSingleton)
100    {
101       if (bSingleton)
102       {
103          createSingletonRegion();
104       }
105       else
106       {
107          createRegions();
108       }
109    }
110 
111    /***
112     * Creates a begin region for the node, marking it as a singleton.  The 
113     * length of the node will also be set to match the size of the singleton
114     * region.
115     */
116    protected void createSingletonRegion()
117    {
118       String sQName = getQName();
119       //ensure that null QNames coming back for not-fully-constructed generics
120       //don't break anything
121       if (sQName == null)
122       {
123          return;
124       }
125       _iLength = sQName.length() + 3;
126       setBeginRegion(_iLength);
127       _drMiddle = _drEnd = null;
128       _regions = new IRegion[]{_drBegin};
129    }
130    
131    /***
132     * Creates regions for the begin and end tags of the node.  The length of
133     * the node will be set to match the combined size of the regions.
134     */
135    protected void createRegions()
136    {
137       String sQName = getQName();
138       //ensure that null QNames coming back for not-full-constructed generics
139       //don't break anything
140       if (sQName == null)
141       {
142          return;
143       }
144       int iLength = sQName.length() + 2;
145       _iLength = iLength * 2 + 1;
146       setBeginRegion(iLength);
147       //should create an end region starting right after the begin region
148       //and no middle region
149       setEndRegion(iLength);
150    }
151    
152    private DisplaceableRegion _drBegin;
153    private DisplaceableRegion _drMiddle; //may be null
154    private DisplaceableRegion _drEnd; //may be null
155    private IRegion[] _regions;
156    /*
157     *  (non-Javadoc)
158     * @see com.bbn.swede.core.dom.OASTNode#simplePartitioning()
159     */
160    public IRegion[] simplePartitioning()
161    {
162       return _regions;
163    }
164    
165    /***
166     * Indicates whether this tag node represents a singleton tag.
167     * @return <code>true</code> if the tag is a singleton, <code>false</code>
168     *         if it has begin and end tags.
169     */
170    protected boolean isSingleton()
171    {
172       return (_drEnd == null);
173    }
174 
175    /***
176     * Sets the begin region of the tag-based node's simple partitioning.  The
177     * offset of the region is based on the node's starting offset as returned
178     * by getOffset()
179     * @param length The length of the beginning region.  This is assumed to be
180     *        less than or equal to  the total length of the node, though this
181     *        method may be called before the node length has been set.
182     */
183    protected void setBeginRegion(int length)
184    {
185       _drBegin = new DisplaceableRegion(getOffset(), length, true);
186    }
187 
188    /***
189     * Sets the end region of the tag-based node's simple partitioning.  The
190     * length of the end region is calculated automatically based on the values
191     * returned by getOffset() and getLength().  A middle region will be
192     * created automatically if there is a gap between the beginning and end
193     * regions.
194     * @param offset Starting offset of the end region.  Must be greater than
195     *               or equal to the offset plus the length of the begin region.
196     */
197    protected void setEndRegion(int offset)
198    {
199       int length = getOffset() + getLength() - offset;
200       if (length <= 0)
201       {
202          _drMiddle = null;
203          _drEnd = null;
204          _regions = new IRegion[] {_drBegin};
205          return;
206       }
207       _drEnd = new DisplaceableRegion(offset, length, true);
208       if (_drBegin.getOffset() + _drBegin.getLength() == offset)
209       {
210          _drMiddle = null;
211          _regions = new IRegion[] {_drBegin, _drEnd};
212          return;
213       }
214       _drMiddle =
215          new DisplaceableRegion(
216             _drBegin.getOffset() + _drBegin.getLength(),
217             getLength() - _drBegin.getLength() - _drEnd.getLength());
218       _regions = new IRegion[] {_drBegin, _drMiddle, _drEnd};
219    }
220 
221    /*
222     *  (non-Javadoc)
223     * @see com.bbn.swede.core.dom.OASTNode#displace(int, int)
224     */
225    public boolean displace(int offset, int length)
226    {
227       if (!super.displace(offset, length))
228       {
229          return false;
230       }
231 
232       if (_drBegin != null)
233       {
234          _drBegin.displace(offset, length);
235       }
236       if (_drMiddle != null)
237       {
238          _drMiddle.displace(offset, length);
239       }
240       if (_drEnd != null)
241       {
242          if (_drMiddle == null && _drEnd.getOffset() == offset)
243          {
244             _drMiddle = new DisplaceableRegion(offset, length, true);
245 //            _drEnd = new DisplaceableRegion(offset + length,_drEnd.getLength());
246             _regions = new IRegion[]{_drBegin, _drMiddle, _drEnd};
247          }
248          _drEnd.displace(offset, length);
249       }
250 
251       return true;
252    }
253 
254    /***
255     * Adds an attribute to the tag, after all existing attributes.  The tag
256     * must not already contain an attribute with the same QName.
257     * @param sQName the qualified name of the attribute to add
258     * @param sValue the value of the attribute to add.  If this is an empty
259     *               string, the attribute will be added with an empty value.
260     *               If it is <code>null</code>, the attribute will not be added.
261     * @throws OASTException if this node is attached to a read-only OAST or
262     *                       the attribute is already defined for this object.
263     */
264    public void addAttribute(String sQName, String sValue) throws OASTException
265    {
266       if (sValue == null)
267       {
268          return;
269       }
270       AttributeNode att = getAttribute(sQName);
271       if (att != null)
272       {
273          throw new OASTException("Node already has an attribute with that qname", this);
274       }
275       synchronized (getLockObject())
276       {
277          att = AttributeNode.create(sQName, sValue);
278          
279          int iOffset = 0;
280          AttributeNode attLast = getLastAttribute();
281          if (attLast != null)
282          {
283             iOffset = attLast.getOffset() + attLast.getLength();
284          }
285          else
286          {
287             iOffset = _drBegin.getOffset() + _drBegin.getLength();
288             iOffset -= (isSingleton() ? 2 : 1);
289          }
290          att.accept(new NodeDisplacementVisitor(0, iOffset));
291          insert(att, true);
292       }
293    }
294    
295    /***
296     * Sets the value of a child attribute of this tag.  If the tag does not
297     * already have an attribute with the specified qname, one will be added.
298     * @param sQName the qualified name of the attribute to set or add
299     * @param sValue the new value of the attribute.  If this is an empty
300     *               string, the attribute will be given an empty value.
301     *               If it is <code>null</code>, the attribute will be removed.
302     * @throws OASTException if this node is attached to a read-only OAST
303     */
304    public void setAttribute(String sQName, String sValue) throws OASTException
305    {
306       if (sValue == null)
307       {
308          removeAttribute(sQName);
309          return;
310       }
311       AttributeNode att = getAttribute(sQName);
312       if (att == null)
313       {
314          addAttribute(sQName, sValue);
315       }
316       else
317       {
318          att.setValue(sValue);
319       }
320    }
321    
322    /***
323     * Retrieves a child attribute with a specified QName.  
324     * @param sQName the qualified name of the attribute to retrieve
325     * @return The requested attribute, or <code>null</code> if this tag
326     *         does not have an attribute with with that qname.
327     */
328    public AttributeNode getAttribute(String sQName)
329    {
330       OASTNode[] children = getChildren();
331       OASTNode node = null;
332       AttributeNode attr = null;
333       AttributeNode toReturn = null;
334       for (int i = 0; i < children.length; i++)
335       {
336          node = children[i];
337          if(node instanceof AttributeNode)
338          {
339             attr = (AttributeNode) node;
340             if (attr.getQName().equals(sQName))
341             {
342                toReturn = attr;
343                break;
344             }
345          }
346       }
347       return toReturn;
348    }
349    
350    /***
351     * Retrieves all the attributes of this tag.
352     * @return An array of AttributeNodes that are children of this tag.  If the
353     *         tag has no attributes, an empty array is returned.
354     */
355    public AttributeNode[] getAttributes()
356    {
357       AttributeNode last = getLastAttribute();
358       if (last == null)
359       {
360          return new AttributeNode[0];
361       }
362       
363       OASTNode[] children = getChildren();
364       int index = 0;
365       while (children[index] != last)
366       {
367          index++;
368       }
369       AttributeNode[] attributes = new AttributeNode[index + 1];
370       for (int i = 0; i < attributes.length; i++)
371       {
372          attributes[i] = (AttributeNode)children[i];
373       }
374       
375       return attributes;
376    }
377    
378    /***
379     * <p>Removes an attribute from the tag.  If the tag does not have an 
380     * attribute with the specified QName, there is no effect.</p>
381     * 
382     * <p>This is a convenience method, fully equivalent to:
383     * <blockquote>removeAttribute(getAttribute(sQName))</blockquote></p>
384     * @param sQName the qualified name of the attribute to remove
385     * @throws OASTException if this node is attached to a read-only OAST
386     */
387    public void removeAttribute(String sQName) throws OASTException
388    {
389       removeAttribute(getAttribute(sQName));
390    }
391    
392    /***
393     * Removes an attribute from the tag.
394     * @param attribute the attribute node to remove
395     * @throws OASTException if this node is attached to a read-only OAST
396     */
397    public void removeAttribute(AttributeNode attribute) throws OASTException
398    {
399       if (attribute == null)
400       {
401          return;
402       }
403       else
404       {
405          remove(attribute, true);
406       }
407    }
408 
409    /***
410     * <p>Changes the tag from a singleton to discrete begin and end tags.  If
411     * the tag is connected to an OWL Abstract Syntax Tree, this node will be
412     * replaced to ensure that the editor updates its text properly.  As a
413     * result, callers should use the return value of this method for
414     * any further changes they need to make to the tag, since this instance
415     * may no longer be valid after the split.</p>
416     * 
417     * <p>Whenever possible, callers should also wrap calls to 
418     * <code>splitSingleton</code> and their other OAST processing in an OAST 
419     * runnable so that only one event notification is sent to OAST change 
420     * listeners.</p>
421     * 
422     * <p>If this tag is not a singleton, it will simply be returned without
423     * making any changes to the OAST.</p>
424     * @see IOASTRunnable
425     * @see IOWLAbstractSyntaxTree#run(IOASTRunnable)
426     * @return The resulting non-singleton tag
427     * @throws OASTException if this node is attached to a read-only OAST
428     */
429    protected TagNode splitSingleton() throws OASTException
430    {
431       if (!isSingleton())
432       {
433          return this;
434       }
435 
436       if (getOWLAbstractSyntaxTree() == null)
437       {
438          //Set regions directly so text generates correctly when the node
439          //is attached to an OAST.
440          //Can't just call initRegions again because the begin region may contain
441          //attribute nodes now and its length should continue to include them.
442          //drBegin already has the '/', just need to compensate for the extra
443          //qname and the '<' and '>'
444          _iLength = _drBegin.getLength() + getQName().length() + 2;
445          _drBegin.displace(_drBegin.getOffset() + _drBegin.getLength() - 1, -1);
446          setEndRegion(_drBegin.getOffset() + _drBegin.getLength());
447          return this;
448       }
449       else
450       {
451          //Replace the node so the editor shows the correct non-singleton text
452          TagNode tag = TagNode.create(getQName(), getParent());
453          if (tag.isSingleton())
454          {
455             tag.splitSingleton();
456          }
457          AttributeNode[] attributes = getAttributes();
458          for(int i = 0; i < attributes.length; i++)
459          {
460             tag.addAttribute(attributes[i].getQName(), attributes[i].getValue());
461          }
462          tag.accept(new NodeDisplacementVisitor(0, getOffset()));
463          getParent().replace(this, tag);
464          return tag;
465       }
466    }
467    
468    /***
469     * Inserts a tag node as the first non-attribute child of this node.  If
470     * the tag is a singleton, it will be split into discrete begin/end tags 
471     * first.  The insertion position will be immediately after the begin tag
472     * of this node.  The child node will be displaced to the target offset
473     * and the tree containing this node will be displaced to make 
474     * room for the inserted child.
475     * @param child The tag node to add as a child
476     * @throws OASTException if this node is attached to a read-only OAST
477     */
478    public void prependChildTag(final TagNode child) throws OASTException
479    {
480       IOASTRunnable action = new DefaultOASTRunnable()
481       {
482          /* (non-Javadoc)
483           * @see com.bbn.swede.core.dom.IOASTRunnable#run()
484           */
485          public void run() throws OASTException
486          {
487             TagNode tag = splitSingleton();
488             
489             int iOffset = tag._drBegin.getOffset() + tag._drBegin.getLength();
490             child.accept(new NodeDisplacementVisitor(0, iOffset));
491             tag.insert(child, true);
492          }
493       };
494       if (getOWLAbstractSyntaxTree() != null)
495       {
496          getOWLAbstractSyntaxTree().run(action);
497       }
498       else
499       {
500          action.run();
501       }
502    }
503    
504    /***
505     * Inserts a tag node as the last non-attribute child of this node.  If
506     * the tag is a singleton, it will be split into discrete begin/end tags 
507     * first.  The insertion position will be immediately before the end tag
508     * of this node.  The child node will be displaced to the target offset
509     * and the tree containing this node will be displaced to make 
510     * room for the inserted child.
511     * @param child The tag node to add as a child
512     * @throws OASTException if this node is attached to a read-only OAST
513     */
514    public void appendChildTag(final TagNode child) throws OASTException
515    {
516       IOASTRunnable action = new DefaultOASTRunnable()
517       {
518          /* (non-Javadoc)
519           * @see com.bbn.swede.core.dom.IOASTRunnable#run()
520           */
521          public void run() throws OASTException
522          {
523             TagNode tag = splitSingleton();
524             
525             int iOffset = tag._drEnd.getOffset();
526             child.accept(new NodeDisplacementVisitor(0, iOffset));
527             tag.insert(child, true);
528          }
529       };
530       if (getOWLAbstractSyntaxTree() != null)
531       {
532          getOWLAbstractSyntaxTree().run(action);
533       }
534       else
535       {
536          action.run();
537       }
538    }
539    
540    /***
541     * Inserts an attribute node as the first attribute child of this node.  The 
542     * insertion position will be immediately after the qname in this node's 
543     * begin tag, plus one character of whitespace.  The child node will be 
544     * displaced to the target offset and the tree containing this node will be 
545     * displaced to make room for the inserted child.
546     * @param child The attribute node to add as a child
547     * @throws OASTException if this node is attached to a read-only OAST
548     */
549    public void prependChildAttribute(AttributeNode child) throws OASTException
550    {
551       int iOffset = _drBegin.getOffset() + getQName().length() + 1;
552       child.accept(new NodeDisplacementVisitor(0, iOffset));
553       insert(child, true);
554    }
555 
556    /*
557     *  (non-Javadoc)
558     * @see com.bbn.swede.core.dom.OASTNode#generateNodeText()
559     */
560    public String generateNodeText()
561    {
562       StringBuffer sb = new StringBuffer(getLength());
563       for (int i = 0; i < sb.capacity(); i++)
564       {
565          sb.append(' ');
566       }
567 
568       //Generate boilerplate text for begin and, if necessary, end tag
569       int iOffset = _drBegin.getOffset() - _iOffset;
570       sb.replace(iOffset, iOffset + 1, "<");
571       iOffset++;
572       String s = getQName();
573       sb.replace(iOffset, iOffset + s.length(), s);
574       if (isSingleton())
575       {
576          iOffset = _drBegin.getOffset() + _drBegin.getLength() - _iOffset - 2;
577          sb.replace(iOffset, iOffset + 2, "/>");
578       }
579       else
580       {
581          iOffset = _drBegin.getOffset() + _drBegin.getLength() - _iOffset - 1;
582          sb.replace(iOffset, iOffset + 1, ">");
583          iOffset = _drEnd.getOffset() - _iOffset;
584          sb.replace(iOffset, iOffset + s.length() + 3, "</" + s + ">");
585       }
586       
587       //now generate child text.  This should work properly for both attribute
588       //and non-attribute children.
589       OASTNode[] children = getChildren();
590       for (int i = 0; i < children.length; i++)
591       {
592          OASTNode node = children[i];
593          iOffset = node.getOffset() - _iOffset;
594          s = node.generateNodeText();
595          sb.replace(iOffset, iOffset + s.length(), s);
596       }
597       
598       return sb.toString();
599    }
600    /***
601     * Inserts an attribute node as the last attribute child of this node.  The 
602     * insertion position will be immediately before the '>' or '/>' terminating
603     * this node's begin tag.  The child node will be 
604     * displaced to the target offset and the tree containing this node will be 
605     * displaced to make room for the inserted child, plus one character of
606     * whitespace to separate the new attribute from the previous one.
607     * @param child The attribute node to add as a child
608     * @throws OASTException if this node is attached to a read-only OAST
609     */
610    public void appendChildAttribute(AttributeNode child) throws OASTException
611    {
612       int iOffset = _drBegin.getOffset() + _drBegin.getLength();
613       if (isSingleton())
614       {
615          iOffset -= 2;
616       }
617       else
618       {
619          iOffset--;
620       }
621       child.accept(new NodeDisplacementVisitor(0, iOffset));
622       insert(child, true);
623    }
624    
625    /*
626     *  (non-Javadoc)
627     * @see com.bbn.swede.core.dom.OASTNode#duplicateType()
628     */
629    protected OASTNode duplicateType()
630    {
631       OASTNode newTag = TagNode.create(getQName(), getParent());
632       if (newTag != null)
633       {
634          return newTag;
635       }
636       else if (getNodeType() == OASTNode.GENERICTHING)
637       {
638          return new GenericThing(getQName());
639       }
640       else if (getNodeType() == OASTNode.GENERICPREDICATE)
641       {
642          return new GenericPredicate(getQName());
643       }
644       
645       return null;
646    }
647    
648    /*
649     *  (non-Javadoc)
650     * @see java.lang.Object#clone()
651     */
652    public Object clone()
653    {
654       TagNode newTag = (TagNode)super.clone();
655       newTag._drBegin = new DisplaceableRegion(_drBegin.getOffset(), _drBegin.getLength(), _drBegin.moveIfEqual());
656       newTag._drMiddle = (_drMiddle == null ? null 
657          : new DisplaceableRegion(_drMiddle.getOffset(), _drMiddle.getLength(), _drMiddle.moveIfEqual()));
658       newTag._drEnd = (_drEnd == null ? null
659          : new DisplaceableRegion(_drEnd.getOffset(), _drEnd.getLength(), _drEnd.moveIfEqual()));
660       int iLength = 1;
661       if (newTag._drEnd != null)
662       {
663          iLength++;
664       }
665       if (newTag._drMiddle != null)
666       {
667          iLength++;
668       }
669       newTag._regions = new IRegion[iLength];
670       newTag._regions[0] = newTag._drBegin;
671       if (newTag._drMiddle != null)
672       {
673          newTag._regions[1] = newTag._drMiddle;
674       }
675       if (newTag._drEnd != null)
676       {
677          newTag._regions[iLength - 1] = newTag._drEnd;
678       }
679       return newTag;
680    }
681 
682    /***
683     * Factory method for creating tag nodes.  Returns a node of the specified
684     * type with no parent and no children.
685     * @param iNodeType A node type constant specifying a tag type.  This
686     *        includes all RDF_, RDFS_, and OWL_ node types not handled by
687     *        AttributeNode.create().
688     * @see AttributeNode#create(int, String)
689     * @return A node of the specified type, or <code>null</code> if an unknown
690     *         or non-tag node type is specified. 
691     */
692    private static TagNode create(int iNodeType)
693    {
694       switch (iNodeType)
695       {
696          case RDF_RDF :
697             return new Rdf();
698          case RDF_DESCRIPTION :
699             return new Description();
700          case RDF_PROPERTY :
701             return new Property();
702          case RDF_LIST :
703             return new List();
704          case RDF_TYPE :
705             return new Type();
706          case RDF_FIRST :
707             return new First();
708          case RDF_REST :
709             return new Rest();
710          case RDFS_SUBCLASSOF :
711             return new SubClassOf();
712          case RDFS_COMMENT :
713             return new Comment();
714          case RDFS_LABEL :
715             return new Label();
716          case RDFS_CLASS :
717             return new com.bbn.swede.core.dom.rdfs.Klass();
718          case RDFS_SUBPROPERTYOF :
719             return new SubPropertyOf();
720          case RDFS_DOMAIN :
721             return new Domain();
722          case RDFS_RANGE :
723             return new Range();
724          case RDFS_SEEALSO :
725             return new SeeAlso();
726          case OWL_CLASS :
727             return new com.bbn.swede.core.dom.owl.Klass();
728          case OWL_DATATYPEPROPERTY :
729             return new DatatypeProperty();
730          case OWL_OBJECTPROPERTY :
731             return new ObjectProperty();
732          case OWL_RESTRICTION :
733             return new Restriction();
734          case OWL_ONPROPERTY :
735             return new OnProperty();
736          case OWL_ALLDIFFERENT :
737             return new AllDifferent();
738          case OWL_ANNOTATIONPROPERTY :
739             return new AnnotationProperty();
740          case OWL_DATARANGE :
741             return new DataRange();
742          case OWL_DEPRECATEDCLASS :
743             return new DeprecatedClass();
744          case OWL_DEPRECATEDPROPERTY :
745             return new DeprecatedProperty();
746          case OWL_FUNCTIONALPROPERTY :
747             return new FunctionalProperty();
748          case OWL_INVERSEFUNCTIONALPROPERTY :
749             return new InverseFunctionalProperty();
750          case OWL_ONTOLOGY :
751             return new Ontology();
752          case OWL_ONTOLOGYPROPERTY :
753             return new OntologyProperty();
754          case OWL_SYMMETRICPROPERTY :
755             return new SymmetricProperty();
756          case OWL_THING :
757             return new Thing();
758          case OWL_TRANSITIVEPROPERTY :
759             return new TransitiveProperty();
760          case OWL_ALLVALUESFROM :
761             return new AllValuesFrom();
762          case OWL_BACKWARDCOMPATIBLEWITH :
763             return new BackwardCompatibleWith();
764          case OWL_CARDINALITY :
765             return new Cardinality();
766          case OWL_COMPLEMENTOF :
767             return new ComplementOf();
768          case OWL_DIFFERENTFROM :
769             return new DifferentFrom();
770          case OWL_DISJOINTWITH :
771             return new DisjointWith();
772          case OWL_DISTINCTMEMBERS :
773             return new DistinctMembers();
774          case OWL_EQUIVALENTCLASS :
775             return new EquivalentClass();
776          case OWL_EQUIVALENTPROPERTY :
777             return new EquivalentProperty();
778          case OWL_HASVALUE :
779             return new HasValue();
780          case OWL_IMPORTS :
781             return new Imports();
782          case OWL_INCOMPATIBLEWITH :
783             return new IncompatibleWith();
784          case OWL_INTERSECTIONOF :
785             return new IntersectionOf();
786          case OWL_INVERSEOF :
787             return new InverseOf();
788          case OWL_MAXCARDINALITY :
789             return new MaxCardinality();
790          case OWL_MINCARDINALITY :
791             return new MinCardinality();
792          case OWL_ONEOF :
793             return new OneOf();
794          case OWL_PRIORVERSION :
795             return new PriorVersion();
796          case OWL_SAMEAS :
797             return new SameAs();
798          case OWL_SOMEVALUESFROM :
799             return new SomeValuesFrom();
800          case OWL_UNIONOF :
801             return new UnionOf();
802          case OWL_VERSIONINFO :
803             return new VersionInfo();
804          default :
805             return null;
806       }
807    }
808    
809    /***
810     * <p>Factory method for creating tag nodes.  If the specified QName is
811     * recognized as a tag from the RDF, RDFS, or OWL language, a custom node
812     * of the appropriate type will be returned.</p>
813     * 
814     * <p>Optionally, a parent node may be specified.  No parent/child relationships
815     * are created, but if an unrecognized QName is specified the parent node
816     * will be used to determine which kind of generic tag to create in order
817     * to maintain proper thing/predicate striping.</p>
818     * 
819     * <p>If no parent and a non-language qname are provided, no node will be 
820     * created.</p>
821     * @param sQName The qualified name for the tag node
822     * @param parent The intended parent of the new node, or <code>null</code>
823     * @return A tag node of the specified type, a generic predicate or thing,
824     *         or <code>null</code> if the appropriate generic type cannot be
825     *         determined.
826     */
827    public static TagNode create(String sQName, OASTNode parent)
828    {
829       int iPos = sQName.indexOf(':');
830       if (iPos < 0)
831       {
832          if (parent == null)
833          {
834             return null;
835          }
836          else if (parent instanceof PropertyNode || parent instanceof Rdf)
837          {
838             return new GenericThing(sQName);
839          }
840          else
841          {
842             return new GenericPredicate(sQName);
843          }
844       }
845 
846       String sNS = sQName.substring(0, iPos);
847       String sName = sQName.substring(iPos + 1);
848       int iNodeType = 0;
849       int iBase = -1;
850       String[] asTags = null;
851       if (sNS.equals("rdf"))
852       {
853          iBase = RDF;
854          asTags = AS_RDF;
855       }
856       else if (sNS.equals("rdfs"))
857       {
858          iBase = RDFS;
859          asTags = AS_RDFS;
860       }
861       else if (sNS.equals("owl"))
862       {
863          iBase = OWL;
864          asTags = AS_OWL;
865       }
866       
867       if (iBase >= 0)
868       {
869          int i;
870          for (i = 0; i < asTags.length; i++)
871          {
872             if (asTags[i].equals(sName))
873             {
874                break;
875             }
876          }
877          OASTNode nodeReturn = null;
878          if (i < asTags.length)
879          {
880             iNodeType = iBase + i;
881             nodeReturn = TagNode.create(iNodeType);
882          }
883          if (i >= asTags.length || nodeReturn == null)
884          {
885             if (parent == null)
886             {
887                return null;
888             }
889             else if (parent instanceof PropertyNode || parent instanceof Rdf)
890             {
891                nodeReturn = new GenericThing(sQName);
892             }
893             else
894             {
895                nodeReturn = new GenericPredicate(sQName);
896             }
897          }
898 
899          return (TagNode)nodeReturn;
900       }
901       
902       if (parent == null)
903       {
904          return null;
905       }
906       else if (parent instanceof PropertyNode || parent instanceof Rdf)
907       {
908          return new GenericThing(sQName);
909       }
910       else
911       {
912          return new GenericPredicate(sQName);
913       }
914    }
915    
916    /***
917     * <p>Factory method for creating language tag nodes.  Returns a node of the 
918     * specified type with no parent and no children.  If a non-language
919     * qname or the qname of a non-tag language element is specified, no node 
920     * will be created.</p>
921     * 
922     * <p>This is a convenience method, fully equivalent to:
923     * <blockquote>create(sQName, null)</blockquote></p>
924     * @param sQName A string specifying a qualified name for the node
925     * @return A node with the specified qname, or <code>null</code> if an 
926     *         unknown or non-tag node qname is specified. 
927     */
928    public static TagNode create(String sQName)
929    {
930       return create(sQName, null);
931    }
932 }