001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */ 
017    package org.apache.commons.betwixt;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    
022    import org.apache.commons.betwixt.expression.Expression;
023    
024    /** <p><code>ElementDescriptor</code> describes the XML elements
025      * to be created for a bean instance.</p>
026      *
027      * <p> It contains <code>AttributeDescriptor</code>'s for all it's attributes
028      * and <code>ElementDescriptor</code>'s for it's child elements.
029      *
030      * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
031      * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
032      */
033    public class ElementDescriptor extends NodeDescriptor {
034    
035        /** 
036         * Descriptors for attributes this element contains.
037         * <strong>Note:</strong> Constructed lazily on demand from a List.
038         * {@link #getAttributeDescriptor()} should be called rather than accessing this
039         * field directly.
040         */
041        private AttributeDescriptor[] attributeDescriptors;
042        /** 
043         * Descriptors for child elements.
044         * <strong>Note:</strong> Constructed lazily on demand from a List.
045         * {@link #getElementDescriptor()} should be called rather than accessing this
046         * field directly.
047         */
048        private ElementDescriptor[] elementDescriptors;
049        
050        /** 
051         * Descriptors for child content.
052         * <strong>Note:</strong> Constructed lazily on demand from a List.
053         * {@link #getContentDescriptor()} should be called rather than accessing this
054         * field directly.
055         */
056        private Descriptor[] contentDescriptors;
057        
058        /** 
059         * The List used on construction. It will be GC'd
060         * after initilization and the array is lazily constructed
061         */
062        private List attributeList;
063        
064        /** 
065         * The List used on construction. It will be GC'd
066         * after initilization and the array is lazily constructed
067         */
068        private List elementList;
069        
070        /** 
071         * The list used o construct array. It will be GC'd after
072         * initialization when the array is lazily constructed.
073         */
074        private List contentList;
075            
076        /** the expression used to evaluate the new context of this node 
077         * or null if the same context is to be used */
078        private Expression contextExpression;
079    
080        /** Whether this element refers to a primitive type (or property of a parent object) */
081        private boolean primitiveType;
082        /** Is this a collective type? */
083        private boolean isCollectiveType;
084        
085        /**
086         * Is this element hollow?
087         * In other words, is this descriptor a place holder indicating the name
088         * and update for a root ElementDescriptor for this type obtained by introspection
089         * TODO: this would probably be better modeled as a separate subclass
090         */
091        private boolean isHollow = false;
092        
093        /** 
094         * Whether this collection element can be used
095         * as a collection element. Defaults to true
096         */
097        private boolean wrapCollectionsInElement = true;
098        
099        /** 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    }