Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/axis/description/TypeDesc.java


1   /*
2    * Copyright 2002-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.axis.description;
18  
19  import org.apache.axis.utils.BeanPropertyDescriptor;
20  import org.apache.axis.utils.BeanUtils;
21  import org.apache.axis.utils.Messages;
22  import org.apache.axis.utils.cache.MethodCache;
23  
24  import javax.xml.namespace.QName;
25  import java.io.Serializable;
26  import java.lang.reflect.Method;
27  import java.util.HashMap;
28  import java.util.Hashtable;
29  import java.util.Map;
30  
31  /**
32   * A TypeDesc represents a Java<->XML data binding.  It is essentially
33   * a collection of FieldDescs describing how to map each field in a Java
34   * class to XML.
35   *
36   * @author Glen Daniels (gdaniels@apache.org)
37   */
38  public class TypeDesc implements Serializable {
39      public static final Class [] noClasses = new Class [] {};
40      public static final Object[] noObjects = new Object[] {};
41      
42      /** A map of class -> TypeDesc */
43      private static Map classMap = new Hashtable();
44  
45      /** Have we already introspected for the special "any" property desc? */
46      private boolean lookedForAny = false;
47  
48      /** Can this instance search for metadata in parents of the type it describes? */
49      private boolean canSearchParents = true;
50      private boolean hasSearchedParents = false;
51      
52      /** My superclass TypeDesc */
53      private TypeDesc parentDesc = null;
54  
55      /**
56       * Creates a new <code>TypeDesc</code> instance.  The type desc can search
57       * the metadata of its type'sparent classes.
58       *
59       * @param javaClass a <code>Class</code> value
60       */
61      public TypeDesc(Class javaClass) {
62          this(javaClass, true);
63      }
64      
65      /**
66       * Creates a new <code>TypeDesc</code> instance.
67       *
68       * @param javaClass a <code>Class</code> value
69       * @param canSearchParents whether the type desc can search the metadata of
70       * its type's parent classes.
71       */
72      public TypeDesc(Class javaClass, boolean canSearchParents) {
73          this.javaClass = javaClass;
74          this.canSearchParents = canSearchParents;
75          Class cls = javaClass.getSuperclass();
76          if (cls != null && !cls.getName().startsWith("java.")) {
77              parentDesc = getTypeDescForClass(cls);
78          }        
79      }
80  
81      /**
82       * Static function to explicitly register a type description for
83       * a given class.
84       * 
85       * @param cls the Class we're registering metadata about
86       * @param td the TypeDesc containing the metadata
87       */ 
88      public static void registerTypeDescForClass(Class cls, TypeDesc td)
89      {
90          classMap.put(cls, td);
91      }
92  
93      /**
94       * Static function for centralizing access to type metadata for a
95       * given class.  
96       *
97       * This checks for a static getTypeDesc() method on the
98       * class or _Helper class.
99       * Eventually we may extend this to provide for external
100      * metadata config (via files sitting in the classpath, etc).
101      *
102      */
103     public static TypeDesc getTypeDescForClass(Class cls)
104     {
105         // First see if we have one explicitly registered
106         // or cached from previous lookup
107         TypeDesc result = (TypeDesc)classMap.get(cls);
108 
109         if (result == null) {
110             try {
111                 Method getTypeDesc = 
112                     MethodCache.getInstance().getMethod(cls, 
113                                                         "getTypeDesc", 
114                                                         noClasses);
115                 if (getTypeDesc != null) {
116                     result = (TypeDesc)getTypeDesc.invoke(null, noObjects);
117                     if (result != null) {
118                         classMap.put(cls, result);
119                     }
120                 }
121             } catch (Exception e) {
122             }
123         }
124         
125         return result;
126     }
127 
128     /** The Java class for this type */
129     private Class javaClass = null;
130 
131     /** The XML type QName for this type */
132     private QName xmlType = null;
133 
134     /** The various fields in here */
135     private FieldDesc [] fields;
136 
137     /** A cache of FieldDescs by name */
138     private HashMap fieldNameMap = new HashMap();
139     
140     /** A cache of FieldDescs by Element QName */
141     private HashMap fieldElementMap = null;
142     
143     /** Are there any fields which are serialized as attributes? */
144     private boolean _hasAttributes = false;
145 
146     /** Introspected property descriptors */
147     private BeanPropertyDescriptor[] propertyDescriptors = null;
148     /** Map with key = property descriptor name, value = descriptor */
149     private Map propertyMap = null;
150 
151     /**
152      * Indication if this type has support for xsd:any.
153      */
154     private BeanPropertyDescriptor anyDesc = null;
155 
156     public BeanPropertyDescriptor getAnyDesc() {
157         return anyDesc;
158     }
159 
160     /**
161      * Obtain the current array of FieldDescs
162      */
163     public FieldDesc[] getFields() {
164         return fields;
165     }
166 
167     public FieldDesc[] getFields(boolean searchParents) {
168         // note that if canSearchParents is false, this is identical
169         // to getFields(), because the parent type's metadata is off
170         // limits for restricted types which are required to provide a
171         // complete description of their content model in their own
172         // metadata, per the XML schema rules for
173         // derivation-by-restriction
174         if (canSearchParents && searchParents && !hasSearchedParents) {
175             // check superclasses if they exist
176             if (parentDesc != null) {
177                 FieldDesc [] parentFields = parentDesc.getFields(true);
178 // START FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=17188
179                 if (parentFields != null) {
180                     if (fields != null) {
181                         FieldDesc [] ret = new FieldDesc[parentFields.length + fields.length];
182                         System.arraycopy(parentFields, 0, ret, 0, parentFields.length);
183                         System.arraycopy(fields, 0, ret, parentFields.length, fields.length);
184                         fields = ret;
185                     } else {
186                         FieldDesc [] ret = new FieldDesc[parentFields.length];
187                         System.arraycopy(parentFields, 0, ret, 0, parentFields.length);
188                         fields = ret;
189                     }
190 // END FIX http://nagoya.apache.org/bugzilla/show_bug.cgi?id=17188
191                 }
192             }
193             
194             hasSearchedParents = true;
195         }
196 
197         return fields;
198     }
199 
200     /**
201      * Replace the array of FieldDescs, making sure we keep our convenience
202      * caches in sync.
203      */
204     public void setFields(FieldDesc [] newFields)
205     {
206         fieldNameMap = new HashMap();
207         fields = newFields;
208         _hasAttributes = false;
209         fieldElementMap = null;
210         
211         for (int i = 0; i < newFields.length; i++) {
212             FieldDesc field = newFields[i];
213             if (!field.isElement()) {
214                 _hasAttributes = true;
215             }
216             fieldNameMap.put(field.getFieldName(), field);
217         }
218     }
219 
220     /**
221      * Add a new FieldDesc, keeping the convenience fields in sync.
222      */
223     public void addFieldDesc(FieldDesc field)
224     {
225         if (field == null) {
226             throw new IllegalArgumentException(
227                     Messages.getMessage("nullFieldDesc"));
228         }
229         
230         int numFields = 0;
231         if (fields != null) {
232             numFields = fields.length;
233         }
234         FieldDesc [] newFields = new FieldDesc[numFields + 1];
235         if (fields != null) {
236             System.arraycopy(fields, 0, newFields, 0, numFields);
237         }
238         newFields[numFields] = field;
239         fields = newFields;
240         
241         // Keep track of the field by name for fast lookup
242         fieldNameMap.put(field.getFieldName(), field);
243         
244         if (!_hasAttributes && !field.isElement())
245             _hasAttributes = true;
246     }
247 
248     /**
249      * Get the QName associated with this field, but only if it's
250      * marked as an element.
251      */
252     public QName getElementNameForField(String fieldName)
253     {
254         FieldDesc desc = (FieldDesc)fieldNameMap.get(fieldName);
255         if (desc == null) {
256             // check superclasses if they exist
257             // and we are allowed to look
258             if (canSearchParents) {
259                 if (parentDesc != null) {
260                     return parentDesc.getElementNameForField(fieldName);
261                 }
262             }
263         } else if (desc.isElement()) {
264             return desc.getXmlName();
265         } 
266         return null;
267     }
268     
269     /**
270      * Get the QName associated with this field, but only if it's
271      * marked as an attribute.
272      */
273     public QName getAttributeNameForField(String fieldName)
274     {
275         FieldDesc desc = (FieldDesc)fieldNameMap.get(fieldName);
276         if (desc == null) {
277             // check superclasses if they exist
278             // and we are allowed to look
279             if (canSearchParents) {
280                 if (parentDesc != null) {
281                     return parentDesc.getAttributeNameForField(fieldName);
282                 }
283             }
284         } else if (!desc.isElement()) {
285             QName ret = desc.getXmlName();
286             if (ret == null) {
287                 ret = new QName("", fieldName);
288             }
289             return ret;
290         }
291         return null;
292     }
293 
294     /**
295      * Get the field name associated with this QName, but only if it's
296      * marked as an element.
297      * 
298      * If the "ignoreNS" argument is true, just compare localNames.
299      */
300     public String getFieldNameForElement(QName qname, boolean ignoreNS)
301     {
302         // have we already computed the answer to this question?
303         if (fieldElementMap != null) {
304             String cached = (String) fieldElementMap.get(qname);
305             if (cached != null) return cached;
306         }
307 
308         String result = null;
309 
310         String localPart = qname.getLocalPart();
311 
312         // check fields in this class
313         for (int i = 0; fields != null && i < fields.length; i++) {
314             FieldDesc field = fields[i];
315             if (field.isElement()) {
316                 QName xmlName = field.getXmlName();
317                 if (localPart.equals(xmlName.getLocalPart())) {
318                     if (ignoreNS || qname.getNamespaceURI().
319                                         equals(xmlName.getNamespaceURI())) {
320                         result = field.getFieldName();
321                         break;
322                     }
323                 }
324             }
325         }
326         
327         // check superclasses if they exist
328         // and we are allowed to look
329         if (result == null && canSearchParents) {
330             if (parentDesc != null) {
331                 result = parentDesc.getFieldNameForElement(qname, ignoreNS);
332             }
333         }
334 
335         // cache the answer away for quicker retrieval next time.
336         if (result != null) {
337             if (fieldElementMap == null) fieldElementMap = new HashMap();
338             fieldElementMap.put(qname, result);
339         }
340 
341         return result;
342     }
343     
344     /**
345      * Get the field name associated with this QName, but only if it's
346      * marked as an attribute.
347      */
348     public String getFieldNameForAttribute(QName qname)
349     {
350         String possibleMatch = null;
351 
352         for (int i = 0; fields != null && i < fields.length; i++) {
353             FieldDesc field = fields[i];
354             if (!field.isElement()) {
355                 // It's an attribute, so if we have a solid match, return
356                 // its name.
357                 if (qname.equals(field.getXmlName())) {
358                     return field.getFieldName();
359                 }
360                 // Not a solid match, but it's still possible we might match
361                 // the default (i.e. QName("", fieldName))
362                 if (qname.getNamespaceURI().equals("") &&
363                     qname.getLocalPart().equals(field.getFieldName())) {
364                     possibleMatch = field.getFieldName();
365                 }
366             }
367         }
368         
369         if (possibleMatch == null && canSearchParents) {
370             // check superclasses if they exist
371             // and we are allowed to look
372             if (parentDesc != null) {
373                 possibleMatch = parentDesc.getFieldNameForAttribute(qname);
374             }
375         }
376         
377         return possibleMatch;
378     }
379 
380     /**
381      * Get a FieldDesc by field name.
382      */
383     public FieldDesc getFieldByName(String name)
384     {
385         FieldDesc ret = (FieldDesc)fieldNameMap.get(name);
386         if (ret == null && canSearchParents) {
387             if (parentDesc != null) {
388                 ret = parentDesc.getFieldByName(name);
389             }
390         }
391         return ret;
392     }
393 
394     /**
395      * Do we have any FieldDescs marked as attributes?
396      */
397     public boolean hasAttributes() {
398         if (_hasAttributes)
399             return true;
400         
401         if (canSearchParents) {
402             if (parentDesc != null) {
403                 return parentDesc.hasAttributes();
404             }
405         }
406 
407         return false;
408     }
409 
410     public QName getXmlType() {
411         return xmlType;
412     }
413 
414     public void setXmlType(QName xmlType) {
415         this.xmlType = xmlType;
416     }
417 
418     /**
419      * Get/Cache the property descriptors
420      * @return PropertyDescriptor
421      */
422     public BeanPropertyDescriptor[] getPropertyDescriptors() {
423         // Return the propertyDescriptors if already set.
424         // If not set, use BeanUtils.getPd to get the property descriptions.
425         //
426         // Since javaClass is a generated class, there
427         // may be a faster way to set the property descriptions than
428         // using BeanUtils.getPd.  But for now calling getPd is sufficient.
429         if (propertyDescriptors == null) {
430             makePropertyDescriptors();
431         }
432         return propertyDescriptors;
433     }
434     
435     private synchronized void makePropertyDescriptors() {
436         if (propertyDescriptors != null)
437             return;
438 
439         propertyDescriptors = BeanUtils.getPd(javaClass, this);
440         if (!lookedForAny) {
441             anyDesc = BeanUtils.getAnyContentPD(javaClass);
442             lookedForAny = true;
443         }
444     }
445 
446     public BeanPropertyDescriptor getAnyContentDescriptor() {
447         if (!lookedForAny) {
448             anyDesc = BeanUtils.getAnyContentPD(javaClass);
449             lookedForAny = true;
450         }
451         return anyDesc;
452     }
453 
454     /**
455      * Get/Cache the property descriptor map
456      * @return Map with key=propertyName, value=descriptor
457      */
458     public Map getPropertyDescriptorMap() {
459         synchronized (this) {
460             // Return map if already set.
461             if (propertyMap != null) {
462                 return propertyMap;
463             }
464 
465             // Make sure properties exist
466             if (propertyDescriptors == null) {
467                 getPropertyDescriptors();  
468             }
469 
470             // Build the map
471             propertyMap = new HashMap();
472             for (int i = 0; i < propertyDescriptors.length; i++) {
473                 BeanPropertyDescriptor descriptor = propertyDescriptors[i];
474                 propertyMap.put(descriptor.getName(), descriptor);
475             }
476         }
477         return propertyMap;
478     }
479 }