001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.aegis.type.basic;
019:
020: import java.beans.BeanInfo;
021: import java.beans.IntrospectionException;
022: import java.beans.Introspector;
023: import java.beans.PropertyDescriptor;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.Set;
031:
032: import javax.xml.namespace.QName;
033:
034: import org.apache.cxf.aegis.DatabindingException;
035: import org.apache.cxf.aegis.type.Type;
036: import org.apache.cxf.aegis.type.TypeCreator;
037: import org.apache.cxf.aegis.type.TypeMapping;
038:
039: public class BeanTypeInfo {
040: private Map<QName, QName> mappedName2typeName = new HashMap<QName, QName>();
041:
042: private Map<QName, String> mappedName2pdName = new HashMap<QName, String>();
043:
044: private Map<QName, Type> mappedName2type = new HashMap<QName, Type>();
045:
046: private Class<?> beanClass;
047:
048: private List<QName> attributes = new ArrayList<QName>();
049:
050: private List<QName> elements = new ArrayList<QName>();
051:
052: private PropertyDescriptor[] descriptors;
053:
054: private TypeMapping typeMapping;
055:
056: private volatile boolean initialized;
057:
058: private String defaultNamespace;
059:
060: private int minOccurs;
061:
062: private boolean nillable = true;
063:
064: private boolean isExtension;
065:
066: /**
067: * extensibleElements means adding xs:any to WSDL Complex Type Definition
068: */
069: private boolean extensibleElements = true;
070:
071: /**
072: * extensibleAttributes means adding xs:anyAttribute to WSDL Complex Type
073: * Definition
074: */
075: private boolean extensibleAttributes = true;
076:
077: public BeanTypeInfo(Class<?> typeClass, String defaultNamespace) {
078: this .beanClass = typeClass;
079: this .defaultNamespace = defaultNamespace;
080:
081: initializeProperties();
082: }
083:
084: /**
085: * Create a BeanTypeInfo class.
086: *
087: * @param typeClass
088: * @param defaultNamespace
089: * @param initiallize If true attempt default property/xml mappings.
090: */
091: public BeanTypeInfo(Class<?> typeClass, String defaultNamespace,
092: boolean initialize) {
093: this .beanClass = typeClass;
094: this .defaultNamespace = defaultNamespace;
095:
096: initializeProperties();
097: initialized = !initialize;
098: }
099:
100: public String getDefaultNamespace() {
101: return defaultNamespace;
102: }
103:
104: public void initialize() {
105: try {
106: if (!initialized) {
107: initializeSync();
108: }
109: } catch (Exception e) {
110: if (e instanceof DatabindingException) {
111: throw (DatabindingException) e;
112: }
113: throw new DatabindingException("Couldn't create TypeInfo.",
114: e);
115: }
116: }
117:
118: private synchronized void initializeSync() {
119: if (!initialized) {
120: for (int i = 0; i < descriptors.length; i++) {
121: // Don't map the property unless there is a read property
122: if (isMapped(descriptors[i])) {
123: mapProperty(descriptors[i]);
124: }
125: }
126: initialized = true;
127: }
128: }
129:
130: public boolean isMapped(PropertyDescriptor pd) {
131: if (pd.getReadMethod() == null) {
132: return false;
133: }
134:
135: return true;
136: }
137:
138: protected void mapProperty(PropertyDescriptor pd) {
139: String name = pd.getName();
140:
141: if (isAttribute(pd)) {
142: mapAttribute(name, createMappedName(pd));
143: } else if (isElement(pd)) {
144: mapElement(name, createMappedName(pd));
145: }
146: }
147:
148: protected PropertyDescriptor[] getPropertyDescriptors() {
149: return descriptors;
150: }
151:
152: protected PropertyDescriptor getPropertyDescriptor(String name) {
153: for (int i = 0; i < descriptors.length; i++) {
154: if (descriptors[i].getName().equals(name)) {
155: return descriptors[i];
156: }
157: }
158:
159: return null;
160: }
161:
162: /**
163: * Get the type class for the field with the specified QName.
164: */
165: public Type getType(QName name) {
166: // 1. Try a prexisting mapped type
167: Type type = mappedName2type.get(name);
168:
169: // 2. Try to get the type by its name, if there is one
170: if (type == null) {
171: QName typeName = getMappedTypeName(name);
172: if (typeName != null) {
173: type = getTypeMapping().getType(typeName);
174:
175: if (type != null) {
176: mapType(name, type);
177: }
178: }
179: }
180:
181: // 3. Create the type from the property descriptor and map it
182: if (type == null) {
183: PropertyDescriptor desc;
184: try {
185: desc = getPropertyDescriptorFromMappedName(name);
186: } catch (Exception e) {
187: if (e instanceof DatabindingException) {
188: throw (DatabindingException) e;
189: }
190: throw new DatabindingException(
191: "Couldn't get properties.", e);
192: }
193:
194: if (desc == null) {
195: return null;
196: }
197:
198: try {
199: TypeMapping tm = getTypeMapping();
200: TypeCreator tc = tm.getTypeCreator();
201: type = tc.createType(desc);
202: } catch (DatabindingException e) {
203: e.prepend("Couldn't create type for property "
204: + desc.getName() + " on " + getTypeClass());
205:
206: throw e;
207: }
208:
209: // second part is possible workaround for XFIRE-586
210: if (registerType(desc)) {
211: getTypeMapping().register(type);
212: }
213:
214: mapType(name, type);
215: }
216:
217: if (type == null) {
218: throw new DatabindingException(
219: "Couldn't find type for property " + name);
220: }
221:
222: return type;
223: }
224:
225: protected boolean registerType(PropertyDescriptor desc) {
226: return true;
227: }
228:
229: public void mapType(QName name, Type type) {
230: mappedName2type.put(name, type);
231: }
232:
233: private QName getMappedTypeName(QName name) {
234: return mappedName2typeName.get(name);
235: }
236:
237: public TypeMapping getTypeMapping() {
238: return typeMapping;
239: }
240:
241: public void setTypeMapping(TypeMapping typeMapping) {
242: this .typeMapping = typeMapping;
243: }
244:
245: /**
246: * Specifies the name of the property as it shows up in the xml schema. This
247: * method just returns <code>propertyDescriptor.getName();</code>
248: *
249: * @param desc
250: * @return
251: */
252: protected QName createMappedName(PropertyDescriptor desc) {
253: return new QName(getDefaultNamespace(), desc.getName());
254: }
255:
256: public void mapAttribute(String property, QName mappedName) {
257: mappedName2pdName.put(mappedName, property);
258: attributes.add(mappedName);
259: }
260:
261: public void mapElement(String property, QName mappedName) {
262: mappedName2pdName.put(mappedName, property);
263: elements.add(mappedName);
264: }
265:
266: /**
267: * Specifies the SchemaType for a particular class.
268: *
269: * @param mappedName
270: * @param type
271: */
272: public void mapTypeName(QName mappedName, QName type) {
273: mappedName2typeName.put(mappedName, type);
274: }
275:
276: private void initializeProperties() {
277: BeanInfo beanInfo = null;
278: try {
279: if (beanClass.isInterface() || beanClass.isPrimitive()) {
280: descriptors = getInterfacePropertyDescriptors(beanClass);
281: } else if (beanClass == Object.class
282: || beanClass == Throwable.class) {
283: // do nothing
284: } else if (beanClass == Throwable.class) {
285: // do nothing
286: } else if (Throwable.class.isAssignableFrom(beanClass)) {
287: beanInfo = Introspector.getBeanInfo(beanClass,
288: Throwable.class);
289: } else if (RuntimeException.class
290: .isAssignableFrom(beanClass)) {
291: beanInfo = Introspector.getBeanInfo(beanClass,
292: RuntimeException.class);
293: } else if (Throwable.class.isAssignableFrom(beanClass)) {
294: beanInfo = Introspector.getBeanInfo(beanClass,
295: Throwable.class);
296: } else {
297: beanInfo = Introspector.getBeanInfo(beanClass,
298: Object.class);
299: }
300: } catch (IntrospectionException e) {
301: throw new DatabindingException(
302: "Couldn't introspect interface.", e);
303: }
304:
305: if (beanInfo != null) {
306: descriptors = beanInfo.getPropertyDescriptors();
307: }
308:
309: if (descriptors == null) {
310: descriptors = new PropertyDescriptor[0];
311: }
312: }
313:
314: private PropertyDescriptor[] getInterfacePropertyDescriptors(
315: Class<?> clazz) {
316: List<PropertyDescriptor> pds = new ArrayList<PropertyDescriptor>();
317:
318: getInterfacePropertyDescriptors(clazz, pds,
319: new HashSet<Class<?>>());
320:
321: return pds.toArray(new PropertyDescriptor[pds.size()]);
322: }
323:
324: private void getInterfacePropertyDescriptors(Class<?> clazz,
325: List<PropertyDescriptor> pds, Set<Class<?>> classes) {
326: if (classes.contains(clazz)) {
327: return;
328: }
329:
330: classes.add(clazz);
331:
332: try {
333: Class[] interfaces = clazz.getInterfaces();
334:
335: /**
336: * add base interface information
337: */
338: BeanInfo info = Introspector.getBeanInfo(clazz);
339: for (int j = 0; j < info.getPropertyDescriptors().length; j++) {
340: PropertyDescriptor pd = info.getPropertyDescriptors()[j];
341: if (!containsPropertyName(pds, pd.getName())) {
342: pds.add(pd);
343: }
344: }
345:
346: /**
347: * add extended interface information
348: */
349: for (int i = 0; i < interfaces.length; i++) {
350: getInterfacePropertyDescriptors(interfaces[i], pds,
351: classes);
352: }
353: } catch (IntrospectionException e) {
354: // do nothing
355: }
356: }
357:
358: private boolean containsPropertyName(List<PropertyDescriptor> pds,
359: String name) {
360: for (Iterator<PropertyDescriptor> itr = pds.iterator(); itr
361: .hasNext();) {
362: PropertyDescriptor pd = itr.next();
363: if (pd.getName().equals(name)) {
364: return true;
365: }
366: }
367: return false;
368: }
369:
370: public PropertyDescriptor getPropertyDescriptorFromMappedName(
371: QName name) {
372: return getPropertyDescriptor(getPropertyNameFromMappedName(name));
373: }
374:
375: protected boolean isAttribute(PropertyDescriptor desc) {
376: return false;
377: }
378:
379: protected boolean isElement(PropertyDescriptor desc) {
380: return true;
381: }
382:
383: protected boolean isSerializable(PropertyDescriptor desc) {
384: return true;
385: }
386:
387: protected Class<?> getTypeClass() {
388: return beanClass;
389: }
390:
391: /**
392: * Nillable is only allowed if the actual property is Nullable
393: *
394: * @param name
395: * @return
396: */
397: public boolean isNillable(QName name) {
398: Type type = getType(name);
399: if (!type.isNillable()) {
400: return false;
401: }
402: return nillable;
403: }
404:
405: public int getMinOccurs(QName name) {
406: return minOccurs;
407: }
408:
409: public void setDefaultMinOccurs(int m) {
410: this .minOccurs = m;
411: }
412:
413: public void setDefaultNillable(boolean n) {
414: this .nillable = n;
415: }
416:
417: private String getPropertyNameFromMappedName(QName name) {
418: return mappedName2pdName.get(name);
419: }
420:
421: public Iterator<QName> getAttributes() {
422: return attributes.iterator();
423: }
424:
425: public Iterator<QName> getElements() {
426: return elements.iterator();
427: }
428:
429: public boolean isExtensibleElements() {
430: return extensibleElements;
431: }
432:
433: public void setExtensibleElements(boolean futureProof) {
434: this .extensibleElements = futureProof;
435: }
436:
437: public boolean isExtensibleAttributes() {
438: return extensibleAttributes;
439: }
440:
441: public void setExtensibleAttributes(boolean extensibleAttributes) {
442: this .extensibleAttributes = extensibleAttributes;
443: }
444:
445: public void setExtension(boolean extension) {
446: this .isExtension = extension;
447: }
448:
449: public boolean isExtension() {
450: return isExtension;
451: }
452:
453: }
|