summaryrefslogtreecommitdiffstats
path: root/sca-java-2.x/tags/2.0.1-RC1/modules/databinding-jaxb/src/main/java/org/apache/tuscany/sca/databinding/jaxb/JAXBPropertyDescriptor.java
blob: 982d3c8aa343b96cd3b35ece36f13113c54ebb8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.tuscany.sca.databinding.jaxb;

import java.beans.IndexedPropertyDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;

import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

import org.oasisopen.sca.ServiceRuntimeException;

/**
 * A PropertyDescriptor provides access to a bean property.  Values can be queried/changed using the
 * read and writer methods of the PropertyDescriptor.
 * <p/>
 * A PropertyDescriptorPlus object wraps a PropertyDescriptor and supplies enhanced set/get methods
 * that match JAXB semantics.
 * <p/>
 * For example, the set(..) method is smart enough to add lists, arrays and atomic values on JAXB
 * beans.
 * <p/>
 * The PropertyDescriptorPlus object also stores the xmlName of the property.
 *
 * @See XMLRootElementUtil.createPropertyDescriptorMap , which creates the PropertyDescriptorPlus
 * objects
 */
public class JAXBPropertyDescriptor implements Comparable<JAXBPropertyDescriptor> {
    PropertyDescriptor descriptor;
    QName xmlName = null;
    int index;

    /**
     * Package protected constructor.  Only created by XMLRootElementUtil.createPropertyDescriptorMap
     * @param descriptor
     * @param index TODO
     * @param propertyName
     *
     * @see XMLRootElementUtil.createPropertyDescriptorMap
     */
    JAXBPropertyDescriptor(PropertyDescriptor descriptor, QName xmlName, int index) {
        super();
        this.descriptor = descriptor;
        this.xmlName = xmlName;
    }

    /**
     * Package protected constructor.  Only created by XMLRootElementUtil.createPropertyDescriptorMap
     * @param descriptor
     * @param index TODO
     * @param propertyName
     *
     * @see XMLRootElementUtil.createPropertyDescriptorMap
     */
    JAXBPropertyDescriptor(PropertyDescriptor descriptor, String xmlName, int index) {
        super();
        this.descriptor = descriptor;
        this.xmlName = new QName("", xmlName);
    }

    public int compareTo(JAXBPropertyDescriptor o) {
        return index - o.index;
    }

    /** @return xmlname */
    public String getXmlName() {
        return xmlName.getLocalPart();
    }

    public QName getXmlQName() {
        return xmlName;
    }

    /** @return property type */
    public Class<?> getPropertyType() {
        return descriptor.getPropertyType();
    }

    /** @return property name */
    public String getPropertyName() {
        return descriptor.getName();
    }

    /**
     * Get the object
     *
     * @param targetBean
     * @return Object for this property or null
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public Object get(Object targetBean) throws InvocationTargetException, IllegalAccessException {
        Method method = descriptor.getReadMethod();
        if (method == null && descriptor.getPropertyType() == Boolean.class) {
            String propertyName = descriptor.getName();
            if (propertyName != null) {
                String methodName = "is";
                methodName =
                    methodName + ((propertyName.length() > 0) ? propertyName.substring(0, 1).toUpperCase() : "");
                methodName = methodName + ((propertyName.length() > 1) ? propertyName.substring(1) : "");
                try {
                    method = targetBean.getClass().getMethod(methodName);
                } catch (NoSuchMethodException e) {
                }
            }
        }
        if (method == null) {
            throw new ServiceRuntimeException("No getter is found");
        }
        Object ret = method.invoke(targetBean);
        if (method.getReturnType() == JAXBElement.class) {
            ret = ((JAXBElement<?>)ret).getValue();
        }
        return ret;
    }

    /**
     * Set the object
     *
     * @param targetBean
     * @param propValue
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws JAXBWrapperException
     */
    public void set(Object targetBean, Object propValue) throws InvocationTargetException, IllegalAccessException,
        JAXBWrapperException {

        Method writeMethod = null;
        try {
            // No set occurs if the value is null
            if (propValue == null) {
                return;
            }

            // There are 3 different types of setters that can occur.
            // 1) Normal Atomic Setter : setFoo(type)
            // 2) Indexed Array Setter : setFoo(type[])
            // 3) No Setter case if the property is a List<T>.

            writeMethod = descriptor.getWriteMethod();
            if (descriptor instanceof IndexedPropertyDescriptor) {
                // Set for indexed  T[]
                setIndexedArray(targetBean, propValue, writeMethod);
            } else if (writeMethod == null) {
                // Set for List<T>
                setList(targetBean, propValue);
            } else if (descriptor.getPropertyType() == JAXBElement.class) {
                if (propValue != null) {
                    Class<?> clazz = propValue.getClass();
                    JAXBElement<?> element = new JAXBElement(xmlName, clazz, propValue);
                    setAtomic(targetBean, element, writeMethod);
                }
            } else {
                // Normal case
                setAtomic(targetBean, propValue, writeMethod);
            }
        } catch (RuntimeException e) {
            throw e;
        }
    }

    /**
     * Set the property value onto targetBean using the writeMethod
     *
     * @param targetBean
     * @param propValue
     * @param writeMethod (set(T))
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws JAXBWrapperException
     */
    private void setAtomic(Object targetBean, Object propValue, Method writeMethod) throws InvocationTargetException,
        IllegalAccessException, JAXBWrapperException {
        // JAXB provides setters for atomic value.

        if (propValue != null) {
            // Normal case
            Object[] SINGLE_PARAM = new Object[1];
            SINGLE_PARAM[0] = propValue;
            writeMethod.invoke(targetBean, SINGLE_PARAM);
        } else {
            Class<?>[] paramTypes = writeMethod.getParameterTypes();

            if (paramTypes != null && paramTypes.length == 1) {
                Class<?> paramType = paramTypes[0];
                if (paramType.isPrimitive() && propValue == null) {
                    //Ignoring null value for primitive type, this could potentially be the way of a customer indicating to set
                    //default values defined in JAXBObject/xmlSchema.
                    return;
                }
            }
        }

    }

    /**
     * Set the property value using the indexed array setter
     *
     * @param targetBean
     * @param propValue
     * @param writeMethod set(T[])
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws JAXBWrapperException
     */
    private void setIndexedArray(Object targetBean, Object propValue, Method writeMethod)
        throws InvocationTargetException, IllegalAccessException, JAXBWrapperException {

        Class<?> paramType = writeMethod.getParameterTypes()[0];
        Object value = asArray(propValue, paramType);
        // JAXB provides setters for atomic value.
        Object[] SINGLE_PARAM = new Object[1];
        SINGLE_PARAM[0] = value;

        writeMethod.invoke(targetBean, SINGLE_PARAM);
    }

    /**
     * Set the property value for the collection case.
     *
     * @param targetBean
     * @param propValue
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws JAXBWrapperException
     */
    private void setList(Object targetBean, Object propValue) throws InvocationTargetException, IllegalAccessException,
        JAXBWrapperException {
        // For the List<T> case, there is no setter. 
        // You are supposed to use the getter to obtain access to the collection and then add the collection

        Collection value = asCollection(propValue, descriptor.getPropertyType());
        Collection collection = (Collection)get(targetBean);

        // Now add our our object to the collection
        collection.clear();
        if (propValue != null) {
            collection.addAll(value);
        }
    }

    /**
     * @param propValue
     * @param destType
     * @return propValue as a Collection
     */
    private static Collection asCollection(Object propValue, Class<?> destType) {
        // TODO Missing function
        // Convert the object into an equivalent object that is a collection
        if (DataConverter.isConvertable(propValue, destType)) {
            return (Collection)DataConverter.convert(propValue, destType);
        } else {
            String objectClass = (propValue == null) ? "null" : propValue.getClass().getName();
            throw new ServiceRuntimeException("Cannot convert " + objectClass);
        }
    }

    /**
     * @param propValue
     * @param destType  T[]
     * @return array of component type
     */
    private static Object asArray(Object propValue, Class<?> destType) {
        if (DataConverter.isConvertable(propValue, destType)) {
            return DataConverter.convert(propValue, destType);
        } else {
            String objectClass = (propValue == null) ? "null" : propValue.getClass().getName();
            throw new ServiceRuntimeException("Cannot convert " + objectClass);

        }
    }

    @Override
    public String toString() {
        String value = "PropertyDescriptorPlus[";
        value += " name=" + this.getPropertyName();
        value += " type=" + this.getPropertyType().getName();
        value += " propertyDecriptor=" + this.descriptor;
        return value + "]";
    }
}