1 /*
2  * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Distribution License v. 1.0, which is available at
6  * http://www.eclipse.org/org/documents/edl-v10.php.
7  *
8  * SPDX-License-Identifier: BSD-3-Clause
9  */

10
11 package com.sun.xml.bind.v2.runtime.reflect;
12
13 import java.lang.reflect.Field;
14 import java.lang.reflect.InvocationTargetException;
15 import java.lang.reflect.Method;
16 import java.lang.reflect.Modifier;
17 import java.lang.reflect.Type;
18 import java.util.Arrays;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.logging.Level;
23 import java.util.logging.Logger;
24
25 import javax.xml.bind.JAXBElement;
26 import javax.xml.bind.annotation.adapters.XmlAdapter;
27
28 import com.sun.istack.Nullable;
29 import com.sun.xml.bind.api.AccessorException;
30 import com.sun.xml.bind.api.JAXBRIContext;
31 import com.sun.xml.bind.v2.model.core.Adapter;
32 import com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder;
33 import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
34 import com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory;
35 import com.sun.xml.bind.v2.runtime.unmarshaller.Loader;
36 import com.sun.xml.bind.v2.runtime.unmarshaller.Receiver;
37 import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
38
39 import org.xml.sax.SAXException;
40
41 /**
42  * Accesses a particular property of a bean.
43  * <p>
44  * <p>
45  * This interface encapsulates the access to the actual data store.
46  * The intention is to generate implementations for a particular bean
47  * and a property to improve the performance.
48  * <p>
49  * <p>
50  * Accessor can be used as a receiver. Upon receiving an object
51  * it sets that to the field.
52  *
53  * @author Kohsuke Kawaguchi (kk@kohsuke.org)
54  * @see Accessor.FieldReflection
55  * @see TransducedAccessor
56  */

57 public abstract class Accessor<BeanT, ValueT> implements Receiver {
58
59     public final Class<ValueT> valueType;
60
61     public Class<ValueT> getValueType() {
62         return valueType;
63     }
64
65     protected Accessor(Class<ValueT> valueType) {
66         this.valueType = valueType;
67     }
68
69     /**
70      * Returns the optimized version of the same accessor.
71      *
72      * @param context The {@link JAXBContextImpl} that owns the whole thing.
73      *                (See {@link RuntimeModelBuilder#context}.)
74      * @return At least the implementation can return {@code this}.
75      */

76     public Accessor<BeanT, ValueT> optimize(@Nullable JAXBContextImpl context) {
77         return this;
78     }
79
80
81     /**
82      * Gets the value of the property of the given bean object.
83      *
84      * @param bean must not be null.
85      * @throws AccessorException if failed to set a value. For example, the getter method
86      *                           may throw an exception.
87      * @since 2.0 EA1
88      */

89     public abstract ValueT get(BeanT bean) throws AccessorException;
90
91     /**
92      * Sets the value of the property of the given bean object.
93      *
94      * @param bean  must not be null.
95      * @param value the value to be set. Setting value to null means resetting
96      *              to the VM default value (even for primitive properties.)
97      * @throws AccessorException if failed to set a value. For example, the setter method
98      *                           may throw an exception.
99      * @since 2.0 EA1
100      */

101     public abstract void set(BeanT bean, ValueT value) throws AccessorException;
102
103
104     /**
105      * Sets the value without adapting the value.
106      * <p>
107      * This ugly entry point is only used by JAX-WS.
108      * See {@link JAXBRIContext#getElementPropertyAccessor}
109      */

110     public Object getUnadapted(BeanT bean) throws AccessorException {
111         return get(bean);
112     }
113
114     /**
115      * Returns true if this accessor wraps an adapter.
116      * <p>
117      * This method needs to be used with care, but it helps some optimization.
118      */

119     public boolean isAdapted() {
120         return false;
121     }
122
123     /**
124      * Sets the value without adapting the value.
125      * <p>
126      * This ugly entry point is only used by JAX-WS.
127      * See {@link JAXBRIContext#getElementPropertyAccessor}
128      */

129     public void setUnadapted(BeanT bean, Object value) throws AccessorException {
130         set(bean, (ValueT) value);
131     }
132
133     public void receive(UnmarshallingContext.State state, Object o) throws SAXException {
134         try {
135             set((BeanT) state.getTarget(), (ValueT) o);
136         } catch (AccessorException e) {
137             Loader.handleGenericException(e, true);
138         } catch (IllegalAccessError iae) {
139             // throw UnmarshalException instead IllegalAccesssError | Issue 475
140             Loader.handleGenericError(iae);
141         }
142     }
143
144     private static List<Class> nonAbstractableClasses = Arrays.asList(new Class[]{
145             Object.class,
146             java.util.Calendar.class,
147             javax.xml.datatype.Duration.class,
148             javax.xml.datatype.XMLGregorianCalendar.class,
149             java.awt.Image.class,
150             javax.activation.DataHandler.class,
151             javax.xml.transform.Source.class,
152             java.util.Date.class,
153             java.io.File.class,
154             java.net.URI.class,
155             java.net.URL.class,
156             Class.class,
157             String.class,
158             javax.xml.transform.Source.class}
159     );
160
161     public boolean isValueTypeAbstractable() {
162         return !nonAbstractableClasses.contains(getValueType());
163     }
164
165     /**
166      * Checks if it is not builtin jaxb class
167      * @param clazz to be checked
168      * @return true if it is NOT builtin class
169      */

170     public boolean isAbstractable(Class clazz) {
171         return !nonAbstractableClasses.contains(clazz);
172     }
173
174     /**
175      * Wraps this  {@link Accessor} into another {@link Accessor}
176      * and performs the type adaption as necessary.
177      */

178     public final <T> Accessor<BeanT, T> adapt(Class<T> targetType, final Class<? extends XmlAdapter<T, ValueT>> adapter) {
179         return new AdaptedAccessor<BeanT, ValueT, T>(targetType, this, adapter);
180     }
181
182     public final <T> Accessor<BeanT, T> adapt(Adapter<Type, Class> adapter) {
183         return new AdaptedAccessor<BeanT, ValueT, T>(
184                 (Class<T>) Utils.REFLECTION_NAVIGATOR.erasure(adapter.defaultType),
185                 this,
186                 adapter.adapterType);
187     }
188
189     /**
190      * Flag that will be set to true after issueing a warning
191      * about the lack of permission to access non-public fields.
192      */

193     private static boolean accessWarned = false;
194
195
196     /**
197      * {@link Accessor} that uses Java reflection to access a field.
198      */

199     public static class FieldReflection<BeanT, ValueT> extends Accessor<BeanT, ValueT> {
200         public final Field f;
201
202         private static final Logger logger = com.sun.xml.bind.Utils.getClassLogger();
203
204         public FieldReflection(Field f) {
205             this(f, false);
206         }
207
208         public FieldReflection(Field f, boolean supressAccessorWarnings) {
209             super((Class<ValueT>) f.getType());
210             this.f = f;
211
212             int mod = f.getModifiers();
213             if (!Modifier.isPublic(mod) || Modifier.isFinal(mod) || !Modifier.isPublic(f.getDeclaringClass().getModifiers())) {
214                 try {
215                     // attempt to make it accessible, but do so in the security context of the calling application.
216                     // don't do this in the doPrivilege block
217                     f.setAccessible(true);
218                 } catch (SecurityException e) {
219                     if ((!accessWarned) && (!supressAccessorWarnings)) {
220                         // this happens when we don't have enough permission.
221                         logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format(
222                                 f.getDeclaringClass().getName(),
223                                 f.getName()),
224                                 e);
225                     }
226                     accessWarned = true;
227                 }
228             }
229         }
230
231         public ValueT get(BeanT bean) {
232             try {
233                 return (ValueT) f.get(bean);
234             } catch (IllegalAccessException e) {
235                 throw new IllegalAccessError(e.getMessage());
236             }
237         }
238
239         public void set(BeanT bean, ValueT value) {
240             try {
241                 if (value == null)
242                     value = (ValueT) uninitializedValues.get(valueType);
243                 f.set(bean, value);
244             } catch (IllegalAccessException e) {
245                 throw new IllegalAccessError(e.getMessage());
246             }
247         }
248
249         @Override
250         public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) {
251             if (context != null && context.fastBoot) {
252                 // let's not waste time on doing this for the sake of faster boot.
253                 return this;
254             }
255             Accessor<BeanT, ValueT> acc = OptimizedAccessorFactory.get(f);
256             if (acc != null) {
257                 return acc;
258             }
259             return this;
260         }
261     }
262
263     /**
264      * Read-only access to {@link Field}. Used to handle a static field.
265      */

266     public static final class ReadOnlyFieldReflection<BeanT, ValueT> extends FieldReflection<BeanT, ValueT> {
267         public ReadOnlyFieldReflection(Field f, boolean supressAccessorWarnings) {
268             super(f, supressAccessorWarnings);
269         }
270         public ReadOnlyFieldReflection(Field f) {
271             super(f);
272         }
273
274         @Override
275         public void set(BeanT bean, ValueT value) {
276             // noop
277         }
278
279         @Override
280         public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) {
281             return this;
282         }
283     }
284
285
286     /**
287      * {@link Accessor} that uses Java reflection to access a getter and a setter.
288      */

289     public static class GetterSetterReflection<BeanT, ValueT> extends Accessor<BeanT, ValueT> {
290         public final Method getter;
291         public final Method setter;
292
293         private static final Logger logger = com.sun.xml.bind.Utils.getClassLogger();
294
295         public GetterSetterReflection(Method getter, Method setter) {
296             super(
297                     (Class<ValueT>) (getter != null ?
298                             getter.getReturnType() :
299                             setter.getParameterTypes()[0]));
300             this.getter = getter;
301             this.setter = setter;
302
303             if (getter != null)
304                 makeAccessible(getter);
305             if (setter != null)
306                 makeAccessible(setter);
307         }
308
309         private void makeAccessible(Method m) {
310             if (!Modifier.isPublic(m.getModifiers()) || !Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
311                 try {
312                     m.setAccessible(true);
313                 } catch (SecurityException e) {
314                     if (!accessWarned)
315                         // this happens when we don't have enough permission.
316                         logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format(
317                                 m.getDeclaringClass().getName(),
318                                 m.getName()),
319                                 e);
320                     accessWarned = true;
321                 }
322             }
323         }
324
325         public ValueT get(BeanT bean) throws AccessorException {
326             try {
327                 return (ValueT) getter.invoke(bean);
328             } catch (IllegalAccessException e) {
329                 throw new IllegalAccessError(e.getMessage());
330             } catch (InvocationTargetException e) {
331                 throw handleInvocationTargetException(e);
332             }
333         }
334
335         public void set(BeanT bean, ValueT value) throws AccessorException {
336             try {
337                 if (value == null)
338                     value = (ValueT) uninitializedValues.get(valueType);
339                 setter.invoke(bean, value);
340             } catch (IllegalAccessException e) {
341                 throw new IllegalAccessError(e.getMessage());
342             } catch (InvocationTargetException e) {
343                 throw handleInvocationTargetException(e);
344             }
345         }
346
347         private AccessorException handleInvocationTargetException(InvocationTargetException e) {
348             // don't block a problem in the user code
349             Throwable t = e.getTargetException();
350             if (t instanceof RuntimeException)
351                 throw (RuntimeException) t;
352             if (t instanceof Error)
353                 throw (Error) t;
354
355             // otherwise it's a checked exception.
356             // I'm not sure how to handle this.
357             // we can throw a checked exception from here,
358             // but because get/set would be called from so many different places,
359             // the handling would be tedious.
360             return new AccessorException(t);
361         }
362
363         @Override
364         public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) {
365             if (getter == null || setter == null) {
366                 // if we aren't complete, OptimizedAccessor won't always work
367                 return this;
368             }
369             if (context != null && context.fastBoot) {
370                 // let's not waste time on doing this for the sake of faster boot.
371                 return this;
372             }
373
374             Accessor<BeanT, ValueT> acc = OptimizedAccessorFactory.get(getter, setter);
375             if (acc != null) {
376                 return acc;
377             }
378
379             return this;
380         }
381     }
382
383     /**
384      * A version of {@link GetterSetterReflection} that doesn't have any setter.
385      * <p>
386      * <p>
387      * This provides a user-friendly error message.
388      */

389     public static class GetterOnlyReflection<BeanT, ValueT> extends GetterSetterReflection<BeanT, ValueT> {
390         public GetterOnlyReflection(Method getter) {
391             super(getter, null);
392         }
393
394         @Override
395         public void set(BeanT bean, ValueT value) throws AccessorException {
396             throw new AccessorException(Messages.NO_SETTER.format(getter.toString()));
397         }
398     }
399
400     /**
401      * A version of {@link GetterSetterReflection} thaat doesn't have any getter.
402      * <p>
403      * <p>
404      * This provides a user-friendly error message.
405      */

406     public static class SetterOnlyReflection<BeanT, ValueT> extends GetterSetterReflection<BeanT, ValueT> {
407         public SetterOnlyReflection(Method setter) {
408             super(null, setter);
409         }
410
411         @Override
412         public ValueT get(BeanT bean) throws AccessorException {
413             throw new AccessorException(Messages.NO_GETTER.format(setter.toString()));
414         }
415     }
416
417     /**
418      * Gets the special {@link Accessor} used to recover from errors.
419      */

420     @SuppressWarnings("unchecked")
421     public static <A, B> Accessor<A, B> getErrorInstance() {
422         return ERROR;
423     }
424
425     private static final Accessor ERROR = new Accessor<Object, Object>(Object.class) {
426         public Object get(Object o) {
427             return null;
428         }
429
430         public void set(Object o, Object o1) {
431         }
432     };
433
434     /**
435      * {@link Accessor} for {@link JAXBElement#getValue()}.
436      */

437     public static final Accessor<JAXBElement, Object> JAXB_ELEMENT_VALUE = new Accessor<JAXBElement, Object>(Object.class) {
438         public Object get(JAXBElement jaxbElement) {
439             return jaxbElement.getValue();
440         }
441
442         public void set(JAXBElement jaxbElement, Object o) {
443             jaxbElement.setValue(o);
444         }
445     };
446
447     /**
448      * Uninitialized map keyed by their classes.
449      */

450     private static final Map<Class, Object> uninitializedValues = new HashMap<Class, Object>();
451
452     static {
453 /*
454     static byte default_value_byte = 0;
455     static boolean default_value_boolean = false;
456     static char default_value_char = 0;
457     static float default_value_float = 0;
458     static double default_value_double = 0;
459     static int default_value_int = 0;
460     static long default_value_long = 0;
461     static short default_value_short = 0;
462 */

463         uninitializedValues.put(byte.class, Byte.valueOf((byte) 0));
464         uninitializedValues.put(boolean.classfalse);
465         uninitializedValues.put(char.class, Character.valueOf((char) 0));
466         uninitializedValues.put(float.class, Float.valueOf(0));
467         uninitializedValues.put(double.class, Double.valueOf(0));
468         uninitializedValues.put(int.class, Integer.valueOf(0));
469         uninitializedValues.put(long.class, Long.valueOf(0));
470         uninitializedValues.put(short.class, Short.valueOf((short) 0));
471     }
472
473 }
474