View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */ 
17  package org.apache.commons.betwixt;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import org.apache.commons.betwixt.expression.Expression;
23  
24  /** <p><code>ElementDescriptor</code> describes the XML elements
25    * to be created for a bean instance.</p>
26    *
27    * <p> It contains <code>AttributeDescriptor</code>'s for all it's attributes
28    * and <code>ElementDescriptor</code>'s for it's child elements.
29    *
30    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
31    * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
32    */
33  public class ElementDescriptor extends NodeDescriptor {
34  
35      /** 
36       * Descriptors for attributes this element contains.
37       * <strong>Note:</strong> Constructed lazily on demand from a List.
38       * {@link #getAttributeDescriptor()} should be called rather than accessing this
39       * field directly.
40       */
41      private AttributeDescriptor[] attributeDescriptors;
42      /** 
43       * Descriptors for child elements.
44       * <strong>Note:</strong> Constructed lazily on demand from a List.
45       * {@link #getElementDescriptor()} should be called rather than accessing this
46       * field directly.
47       */
48      private ElementDescriptor[] elementDescriptors;
49      
50      /** 
51       * Descriptors for child content.
52       * <strong>Note:</strong> Constructed lazily on demand from a List.
53       * {@link #getContentDescriptor()} should be called rather than accessing this
54       * field directly.
55       */
56      private Descriptor[] contentDescriptors;
57      
58      /** 
59       * The List used on construction. It will be GC'd
60       * after initilization and the array is lazily constructed
61       */
62      private List attributeList;
63      
64      /** 
65       * The List used on construction. It will be GC'd
66       * after initilization and the array is lazily constructed
67       */
68      private List elementList;
69      
70      /** 
71       * The list used o construct array. It will be GC'd after
72       * initialization when the array is lazily constructed.
73       */
74      private List contentList;
75          
76      /** the expression used to evaluate the new context of this node 
77       * or null if the same context is to be used */
78      private Expression contextExpression;
79  
80      /** Whether this element refers to a primitive type (or property of a parent object) */
81      private boolean primitiveType;
82      /** Is this a collective type? */
83      private boolean isCollectiveType;
84      
85      /**
86       * Is this element hollow?
87       * In other words, is this descriptor a place holder indicating the name
88       * and update for a root ElementDescriptor for this type obtained by introspection
89       * TODO: this would probably be better modeled as a separate subclass
90       */
91      private boolean isHollow = false;
92      
93      /** 
94       * Whether this collection element can be used
95       * as a collection element. Defaults to true
96       */
97      private boolean wrapCollectionsInElement = true;
98      
99      /** specifies a separate implementation class that should be instantiated
100       * when reading beans
101       * or null if there is no separate implementation */
102     private Class implementationClass = null;
103     
104     /** 
105      * Should the bind time type determine the mapping? 
106      * (As opposed to the introspection time type.)
107      * Note that this attribute is write once, read many (WORM). 
108      */
109     private Boolean useBindTimeTypeForMapping = null;
110     
111     /**  
112      * Constructs an <code>ElementDescriptor</code> that refers to a primitive type.
113      */
114     public ElementDescriptor() {
115     }
116     
117     /**
118      * Base constructor.
119      * @param primitiveType if true, this element refers to a primitive type
120      * @deprecated 0.6 PrimitiveType property has been removed
121      */
122     public ElementDescriptor(boolean primitiveType) {
123         this.primitiveType = primitiveType;
124     }
125 
126     /** 
127      * Creates a ElementDescriptor with no namespace URI or prefix.
128      *
129      * @param localName the (xml) local name of this node. 
130      * This will be used to set both qualified and local name for this name.
131      */
132     public ElementDescriptor(String localName) {
133         super( localName );
134     }
135 
136 
137     
138     /** 
139      * Creates a <code>ElementDescriptor</code> with namespace URI and qualified name
140      * @param localName the (xml) local name of this  node
141      * @param qualifiedName the (xml) qualified name of this node
142      * @param uri the (xml) namespace prefix of this node
143      */
144     public ElementDescriptor(String localName, String qualifiedName, String uri) {
145         super(localName, qualifiedName, uri);
146     }
147 
148     /** 
149      * Returns true if this element has child <code>ElementDescriptors</code>
150      * @return true if this element has child elements 
151      * @see #getElementDescriptors
152      */
153     public boolean hasChildren() {
154         return getElementDescriptors().length > 0;
155     }
156     
157     /** 
158      * Returns true if this element has <code>AttributeDescriptors</code>
159      * @return true if this element has attributes
160      * @see #getAttributeDescriptors
161      */
162     public boolean hasAttributes() {
163         return getAttributeDescriptors().length > 0;
164     }
165     
166     /** 
167      * Returns true if this element has child content.
168      * @return true if this element has either child mixed content or child elements
169      * @see #getContentDescriptors
170      * @since 0.5
171      */
172     public boolean hasContent() {
173         return getContentDescriptors().length > 0; 
174      } 
175     
176     /**
177      * <p>Is this a simple element?</p>
178      * <p>
179      * A simple element is one without child elements or attributes.
180      * This corresponds to the simple type concept used in XML Schema.
181      * TODO: need to consider whether it's sufficient to calculate
182      * which are simple types (and so don't get IDs assigned etc).
183      * </p>
184      * @return true if it is a <code>SimpleType</code> element
185      */
186     public boolean isSimple() {
187         return !(hasAttributes()) && !(hasChildren());
188     }
189     
190     
191     /** 
192      * Sets whether <code>Collection</code> bean properties should wrap items in a parent element.
193      * In other words, should the mapping for bean properties which are <code>Collection</code>s 
194      * enclosed the item elements within a parent element.
195      * Normally only used when this describes a collection bean property.
196      *
197      * @param wrapCollectionsInElement true if the elements for the items in the collection 
198      * should be contained in a parent element
199      * @deprecated 0.6 moved to a declarative style of descriptors where the alrogithmic should
200      * be done during introspection
201      */
202     public void setWrapCollectionsInElement(boolean wrapCollectionsInElement) {
203         this.wrapCollectionsInElement = wrapCollectionsInElement;
204     }
205 
206     /**
207      * Returns true if collective bean properties should wrap the items in a parent element.
208      * In other words, should the mapping for bean properties which are <code>Collection</code>s 
209      * enclosed the item elements within a parent element.
210      * Normally only used when this describes a collection bean property.
211      *
212      * @return true if the elements for the items in the collection should be contained 
213      * in a parent element
214      * @deprecated 0.6 moved to a declarative style of descriptors where the alrogithmic should
215      * be done during introspection
216      */
217     public boolean isWrapCollectionsInElement() {
218         return this.wrapCollectionsInElement;
219     }
220 
221     /**
222      * Adds an attribute to the element this <code>ElementDescriptor</code> describes
223      * @param descriptor the <code>AttributeDescriptor</code> that will be added to the 
224      * attributes associated with element this <code>ElementDescriptor</code> describes
225      */
226     public void addAttributeDescriptor(AttributeDescriptor descriptor) {
227         if ( attributeList == null ) {
228             attributeList = new ArrayList();
229         }
230         getAttributeList().add( descriptor );
231         attributeDescriptors = null;
232     }
233     
234 
235     /**
236      * Removes an attribute descriptor from this element descriptor. 
237      * @param descriptor the <code>AttributeDescriptor</code> to be removed, not null
238      * @since 0.8
239      */
240     public void removeAttributeDescriptor(AttributeDescriptor descriptor) {
241         getAttributeList().remove(descriptor);
242     }
243 
244     /** 
245      * Returns the attribute descriptors for this element 
246      *
247      * @return descriptors for the attributes of the element that this 
248      * <code>ElementDescriptor</code> describes
249      */
250     public AttributeDescriptor[] getAttributeDescriptors() {
251         if ( attributeDescriptors == null ) {
252             if ( attributeList == null ) {
253                 attributeDescriptors = new AttributeDescriptor[0];
254             } else {
255                 attributeDescriptors = new AttributeDescriptor[ attributeList.size() ];
256                 attributeList.toArray( attributeDescriptors );
257                 
258                 // allow GC of List when initialized
259                 attributeList = null;
260             }
261         }
262         return attributeDescriptors;
263     }
264     
265     /**
266      * Returns an attribute descriptor with a given name or null.
267      *  
268      * @param name to search for; will be checked against the attributes' qualified name.
269      * @return <code>AttributeDescriptor</code> with the given name,
270      * or null if no descriptor has that name
271      * @since 0.8
272      */
273     public AttributeDescriptor getAttributeDescriptor(final String name) {
274         for (int i = 0, size = attributeDescriptors.length; i < size; i++) {
275             AttributeDescriptor descr = attributeDescriptors[i];
276             if (descr.getQualifiedName().equals(name)) {
277                 return descr;
278             }
279         }
280 
281         return null;
282     }
283 
284     /** 
285      * Sets the <code>AttributesDescriptors</code> for this element.
286      * This sets descriptors for the attributes of the element describe by the 
287      * <code>ElementDescriptor</code>.
288      *
289      * @param attributeDescriptors the <code>AttributeDescriptor</code> describe the attributes
290      * of the element described by this <code>ElementDescriptor</code>
291      */
292     public void setAttributeDescriptors(AttributeDescriptor[] attributeDescriptors) {
293         this.attributeDescriptors = attributeDescriptors;
294         this.attributeList = null;
295     }
296     
297     /**
298      * Adds a descriptor for a child element.
299      * 
300      * @param descriptor the <code>ElementDescriptor</code> describing the child element to add
301      */
302     public void addElementDescriptor(ElementDescriptor descriptor) {
303         if ( elementList == null ) {
304             elementList = new ArrayList();
305         }
306         getElementList().add( descriptor );
307         elementDescriptors = null;
308         addContentDescriptor( descriptor );
309     }
310 
311     /**
312      * Removes an element descriptor from this element descriptor. 
313      * @param descriptor the <code>ElementDescriptor</code> that will be removed.
314      * @since 0.8
315      */
316     public void removeElementDescriptor(ElementDescriptor descriptor) {
317         getElementList().remove(descriptor);
318         getContentList().remove(descriptor);
319     }
320     
321     /** 
322      * Returns descriptors for the child elements of the element this describes.
323      * @return the <code>ElementDescriptor</code> describing the child elements
324      * of the element that this <code>ElementDescriptor</code> describes
325      */
326     public ElementDescriptor[] getElementDescriptors() {
327         if ( elementDescriptors == null ) {
328             if ( elementList == null ) {
329                 elementDescriptors = new ElementDescriptor[0];
330             } else {
331                 elementDescriptors = new ElementDescriptor[ elementList.size() ];
332                 elementList.toArray( elementDescriptors );
333                 
334                 // allow GC of List when initialized
335                 elementList = null;
336             }
337         }
338         return elementDescriptors;
339     }
340     
341     /**
342       * Gets a child ElementDescriptor matching the given name if one exists.
343       * Note that (so long as there are no better matches), a null name
344       * acts as a wildcard. In other words, an 
345       * <code>ElementDescriptor</code> the first descriptor 
346       * with a null name will match any name
347       * passed in, unless some other matches the name exactly.
348       *
349       * @param name the localname to be matched, not null
350       * @return the child ElementDescriptor with the given name if one exists, 
351       * otherwise null
352       */
353     public ElementDescriptor getElementDescriptor(String name) {
354     
355         ElementDescriptor elementDescriptor = null;
356         ElementDescriptor descriptorWithNullName = null;
357         ElementDescriptor firstPolymorphic = null;
358         ElementDescriptor[] elementDescriptors = getElementDescriptors();
359         for (int i=0, size=elementDescriptors.length; i<size; i++) {
360             if (firstPolymorphic == null && elementDescriptors[i].isPolymorphic()) {
361                 firstPolymorphic = elementDescriptors[i];
362             }
363             String elementName = elementDescriptors[i].getQualifiedName();
364             if (name.equals(elementName)) {
365                 elementDescriptor = elementDescriptors[i];
366                 break;
367             }
368             if (descriptorWithNullName == null && elementName == null) {
369                 descriptorWithNullName = elementDescriptors[i];
370             }
371         }
372         if (elementDescriptor == null) {
373             elementDescriptor = firstPolymorphic;
374         }
375         if (elementDescriptor == null) {
376             elementDescriptor = descriptorWithNullName;
377         }
378         return elementDescriptor;
379     }
380 
381 
382     /** 
383      * Sets the descriptors for the child element of the element this describes. 
384      * Also sets the child content descriptors for this element
385      *
386      * @param elementDescriptors the <code>ElementDescriptor</code>s of the element 
387      * that this describes
388      */
389     public void setElementDescriptors(ElementDescriptor[] elementDescriptors) {
390         this.elementDescriptors = elementDescriptors;
391         this.elementList = null;
392         setContentDescriptors( elementDescriptors );
393     }
394     
395     /**
396      * Adds a descriptor for child content.
397      * 
398      * @param descriptor the <code>Descriptor</code> describing the child content to add
399      * @since 0.5
400      */
401     public void addContentDescriptor(Descriptor descriptor) {
402         if ( contentList == null ) {
403             contentList = new ArrayList();
404         }
405         getContentList().add( descriptor );
406         contentDescriptors = null;
407     }
408     
409     /** 
410      * Returns descriptors for the child content of the element this describes.
411      * @return the <code>Descriptor</code> describing the child elements
412      * of the element that this <code>ElementDescriptor</code> describes
413      * @since 0.5
414      */
415     public Descriptor[] getContentDescriptors() {
416         if ( contentDescriptors == null ) {
417             if ( contentList == null ) {
418                 contentDescriptors = new Descriptor[0];
419             } else {
420                 contentDescriptors = new Descriptor[ contentList.size() ];
421                 contentList.toArray( contentDescriptors );
422                 
423                 // allow GC of List when initialized
424                 contentList = null;
425             }
426         }
427         return contentDescriptors;
428     }
429     
430     /**
431      * <p>Gets the primary descriptor for body text of this element. 
432      * Betwixt collects all body text for any element together.
433      * This makes it rounds tripping difficult for beans that write more than one
434      * mixed content property.
435      * </p><p>
436      * The algorithm used in the default implementation is that the first TextDescriptor
437      * found amongst the descriptors is returned.
438      *
439      * @return the primary descriptor or null if this element has no mixed body content
440      * @since 0.5
441      */
442     public TextDescriptor getPrimaryBodyTextDescriptor() {
443         // todo: this probably isn't the most efficent algorithm
444         // but should avoid premature optimization
445         Descriptor[] descriptors = getContentDescriptors();
446         for (int i=0, size=descriptors.length; i<size; i++) {
447             if (descriptors[i] instanceof TextDescriptor) {
448                 return (TextDescriptor) descriptors[i];
449             }
450         }
451         // if we haven't found anything, return null.
452         return null;
453     }
454 
455     /** 
456      * Sets the descriptors for the child content of the element this describes. 
457      * @param contentDescriptors the <code>Descriptor</code>s of the element 
458      * that this describes
459      * @since 0.5
460      */
461     public void setContentDescriptors(Descriptor[] contentDescriptors) {
462         this.contentDescriptors = contentDescriptors;
463         this.contentList = null;
464     }
465     
466     /** 
467      * Returns the expression used to evaluate the new context of this element.
468      * @return the expression used to evaluate the new context of this element
469      */
470     public Expression getContextExpression() {
471         return contextExpression;
472     }
473     
474     /** 
475      * Sets the expression used to evaluate the new context of this element 
476      * @param contextExpression the expression used to evaluate the new context of this element 
477      */
478     public void setContextExpression(Expression contextExpression) {
479         this.contextExpression = contextExpression;
480     }
481     
482     /** 
483      * Returns true if this element refers to a primitive type property
484      * @return whether this element refers to a primitive type (or property of a parent object) 
485      * @deprecated 0.6 moved to a declarative style of descriptors where the alrogithmic should
486      * be done during introspection
487      */
488     public boolean isPrimitiveType() {
489         return primitiveType;
490     }
491     
492     /** 
493      * Sets whether this element refers to a primitive type (or property of a parent object) 
494      * @param primitiveType true if this element refers to a primitive type
495      * @deprecated 0.6 moved to a declarative style of descriptors where the alrogithmic should
496      * be done during introspection
497      */
498     public void setPrimitiveType(boolean primitiveType) {
499         this.primitiveType = primitiveType;
500     }
501     
502     // Implementation methods
503     //-------------------------------------------------------------------------    
504         
505     /** 
506      * Lazily creates the mutable List.
507      * This nullifies the attributeDescriptors array so that
508      * as items are added to the list the Array is ignored until it is
509      * explicitly asked for.
510      * 
511      * @return list of <code>AttributeDescriptors</code>'s describing the attributes
512      * of the element that this <code>ElementDescriptor</code> describes
513      */
514     protected List getAttributeList() {
515         if ( attributeList == null ) {
516             if ( attributeDescriptors != null ) {
517                 int size = attributeDescriptors.length;
518                 attributeList = new ArrayList( size );
519                 for ( int i = 0; i < size; i++ ) {
520                     attributeList.add( attributeDescriptors[i] );
521                 }
522                 // force lazy recreation later
523                 attributeDescriptors = null;
524             } else {
525                 attributeList = new ArrayList();
526             }            
527         }
528         return attributeList;
529     }
530     
531     /**  
532      * Lazily creates the mutable List of child elements.
533      * This nullifies the elementDescriptors array so that
534      * as items are added to the list the Array is ignored until it is
535      * explicitly asked for.
536      *
537      * @return list of <code>ElementDescriptor</code>'s describe the child elements of 
538      * the element that this <code>ElementDescriptor</code> describes
539      */
540     protected List getElementList() {
541         if ( elementList == null ) {
542             if ( elementDescriptors != null ) {
543                 int size = elementDescriptors.length;
544                 elementList = new ArrayList( size );
545                 for ( int i = 0; i < size; i++ ) {
546                     elementList.add( elementDescriptors[i] );
547                 }
548                 // force lazy recreation later
549                 elementDescriptors = null;
550             } else {
551                 elementList = new ArrayList();
552             }            
553         }
554         return elementList;
555     }
556     
557     /**  
558      * Lazily creates the mutable List of child content descriptors.
559      * This nullifies the contentDescriptors array so that
560      * as items are added to the list the Array is ignored until it is
561      * explicitly asked for.
562      *
563      * @return list of <code>Descriptor</code>'s describe the child content of 
564      * the element that this <code>Descriptor</code> describes
565      * @since 0.5
566      */
567     protected List getContentList() {
568         if ( contentList == null ) {
569             if ( contentDescriptors != null ) {
570                 int size = contentDescriptors.length;
571                 contentList = new ArrayList( size );
572                 for ( int i = 0; i < size; i++ ) {
573                     contentList.add( contentDescriptors[i] );
574                 }
575                 // force lazy recreation later
576                 contentDescriptors = null;
577             } else {
578                 contentList = new ArrayList();
579             }            
580         }
581         return contentList;
582     }
583     
584     /**
585       * Gets the class which should be used for instantiation.
586       * @return the class which should be used for instantiation of beans 
587       * mapped from this element, null if the standard class should be used
588       */
589     public Class getImplementationClass() {
590         return implementationClass;
591     }
592     
593     /**
594       * Sets the class which should be used for instantiation.
595       * @param implementationClass the class which should be used for instantiation
596       * or null to use the mapped type
597       * @since 0.5
598       */
599     public void setImplementationClass(Class implementationClass) {
600         this.implementationClass = implementationClass;
601     }
602     
603     /**
604      * Does this describe a collective?
605      */
606     public boolean isCollective() {
607         // TODO is this implementation correct?
608         // maybe this method is unnecessary
609         return isCollectiveType;
610     }
611     
612     /**
613      * Sets whether the element described is a collective.
614      * @since 0.7
615      * @param isCollectiveType
616      */
617     public void setCollective(boolean isCollectiveType) {
618         this.isCollectiveType = isCollectiveType;
619     }
620 
621     /** 
622      * Finds the parent of the given descriptor.
623      * @param elementDescriptor <code>ElementDescriptor</code>
624      * @return <code>ElementDescriptor</code>, not null
625      */
626     public ElementDescriptor findParent(ElementDescriptor elementDescriptor) {
627         //TODO: is this really a good design?
628         ElementDescriptor result = null;
629         ElementDescriptor[] elementDescriptors = getElementDescriptors();
630         for (int i=0, size=elementDescriptors.length; i<size; i++) {
631             if (elementDescriptors[i].equals(elementDescriptor)) {
632                 result = this;
633                 break;
634             }
635             else
636             {
637                 result = elementDescriptors[i].findParent(elementDescriptor);
638                 if (result != null) {
639                     break;
640                 }
641             }
642         }
643         return result;
644     }
645     
646     /**
647      * Returns something useful for logging.
648      *
649      * @return a string useful for logging
650      */ 
651     public String toString() {
652         return 
653             "ElementDescriptor[qname=" + getQualifiedName() + ",pname=" + getPropertyName() 
654             + ",class=" + getPropertyType() + ",singular=" + getSingularPropertyType()
655             + ",updater=" + getUpdater() + ",wrap=" + isWrapCollectionsInElement() + "]";
656     }
657 
658     /**
659      * <p>Is this decriptor hollow?</p>
660      * <p>
661      * A hollow descriptor is one which gives only the class that the subgraph
662      * is mapped to rather than describing the entire subgraph.
663      * A new <code>XMLBeanInfo</code> should be introspected 
664      * and that used to describe the subgraph.
665      * A hollow descriptor should not have any child descriptors. 
666      * TODO: consider whether a subclass would be better
667      * </p>
668      * @return true if this is hollow 
669      */
670     public boolean isHollow() {
671         return isHollow;
672     }    
673     
674     /**
675      * Sets whether this descriptor is hollow.
676      * A hollow descriptor is one which gives only the class that the subgraph
677      * is mapped to rather than describing the entire subgraph.
678      * A new <code>XMLBeanInfo</code> should be introspected 
679      * and that used to describe the subgraph.
680      * A hollow descriptor should not have any child descriptors. 
681      * TODO: consider whether a subclass would be better
682      * @param isHollow true if this is hollow 
683      */
684     public void setHollow(boolean isHollow) {
685         this.isHollow = isHollow;
686     }  
687     
688     /**
689      * <p>Is the bind time type to be used to determine the mapping?</p>
690      * <p>
691      * The mapping for an object property value can either be the 
692      * introspection time type (based on the logical type of the property)
693      * or the bind time type (based on the type of the actual instance).
694      * </p>
695      * @since 0.7
696      * @return true if the bind time type is to be used to determine the mapping,
697      * false if the introspection time type is to be used
698      */
699     public boolean isUseBindTimeTypeForMapping() {
700         boolean result = true;
701         if ( this.useBindTimeTypeForMapping != null ) {
702             result = this.useBindTimeTypeForMapping.booleanValue();
703         }
704         return result;
705     }
706 
707     /**
708      * <p>Sets whether the bind time type to be used to determine the mapping.
709      * The mapping for an object property value can either be the 
710      * introspection time type (based on the logical type of the property)
711      * or the bind time type (based on the type of the actual instance).
712      * </p><p>
713      * <strong>Note:</strong> this property is write once, read many.
714      * So, the first time that this method is called the value will be set
715      * but subsequent calls will be ignored.
716      * </p>
717      * @since 0.7
718      * @param useBindTimeTypeForMapping true if the bind time type is to be used to 
719      * determine the mapping, false if the introspection time type is to be used
720      */
721     public void setUseBindTimeTypeForMapping(boolean useBindTimeTypeForMapping) {
722         if ( this.useBindTimeTypeForMapping == null ) {
723             this.useBindTimeTypeForMapping = new Boolean(useBindTimeTypeForMapping);
724         }
725     }
726 
727     /**
728      * <p>Is this a polymorphic element?</p>
729      * <p>
730      * A polymorphic element's name is not fixed at 
731      * introspection time and it's resolution is postponed to bind time.
732      * </p>
733      * @since 0.7
734      * @return true if {@link #getQualifiedName} is null, 
735      * false otherwise
736      */
737     public boolean isPolymorphic() { 
738         return (getQualifiedName() == null);
739     }
740 }