ohair@286: /* aefimov@650: * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.bind.v2.runtime.reflect; ohair@286: ohair@286: import java.lang.reflect.Field; ohair@286: import java.lang.reflect.InvocationTargetException; ohair@286: import java.lang.reflect.Method; ohair@286: import java.lang.reflect.Modifier; ohair@286: import java.lang.reflect.Type; ohair@286: import java.util.Arrays; ohair@286: import java.util.HashMap; ohair@286: import java.util.List; ohair@286: import java.util.Map; ohair@286: import java.util.logging.Level; ohair@286: import java.util.logging.Logger; ohair@286: ohair@286: import javax.xml.bind.JAXBElement; ohair@286: import javax.xml.bind.annotation.adapters.XmlAdapter; ohair@286: ohair@286: import com.sun.istack.internal.Nullable; ohair@286: import com.sun.xml.internal.bind.Util; ohair@286: import com.sun.xml.internal.bind.api.AccessorException; ohair@286: import com.sun.xml.internal.bind.api.JAXBRIContext; ohair@286: import com.sun.xml.internal.bind.v2.model.core.Adapter; ohair@286: import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder; ohair@286: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; ohair@286: import com.sun.xml.internal.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver; ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; ohair@286: ohair@286: import org.xml.sax.SAXException; ohair@286: ohair@286: /** ohair@286: * Accesses a particular property of a bean. ohair@286: *

ohair@286: *

ohair@286: * This interface encapsulates the access to the actual data store. ohair@286: * The intention is to generate implementations for a particular bean ohair@286: * and a property to improve the performance. ohair@286: *

ohair@286: *

ohair@286: * Accessor can be used as a receiver. Upon receiving an object ohair@286: * it sets that to the field. ohair@286: * ohair@286: * @author Kohsuke Kawaguchi (kk@kohsuke.org) ohair@286: * @see Accessor.FieldReflection ohair@286: * @see TransducedAccessor ohair@286: */ ohair@286: public abstract class Accessor implements Receiver { ohair@286: ohair@286: public final Class valueType; ohair@286: ohair@286: public Class getValueType() { ohair@286: return valueType; ohair@286: } ohair@286: ohair@286: protected Accessor(Class valueType) { ohair@286: this.valueType = valueType; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns the optimized version of the same accessor. ohair@286: * ohair@286: * @param context The {@link JAXBContextImpl} that owns the whole thing. ohair@286: * (See {@link RuntimeModelBuilder#context}.) ohair@286: * @return At least the implementation can return this. ohair@286: */ ohair@286: public Accessor optimize(@Nullable JAXBContextImpl context) { ohair@286: return this; ohair@286: } ohair@286: ohair@286: ohair@286: /** ohair@286: * Gets the value of the property of the given bean object. ohair@286: * ohair@286: * @param bean must not be null. ohair@286: * @throws AccessorException if failed to set a value. For example, the getter method ohair@286: * may throw an exception. ohair@286: * @since 2.0 EA1 ohair@286: */ ohair@286: public abstract ValueT get(BeanT bean) throws AccessorException; ohair@286: ohair@286: /** ohair@286: * Sets the value of the property of the given bean object. ohair@286: * ohair@286: * @param bean must not be null. ohair@286: * @param value the value to be set. Setting value to null means resetting ohair@286: * to the VM default value (even for primitive properties.) ohair@286: * @throws AccessorException if failed to set a value. For example, the setter method ohair@286: * may throw an exception. ohair@286: * @since 2.0 EA1 ohair@286: */ ohair@286: public abstract void set(BeanT bean, ValueT value) throws AccessorException; ohair@286: ohair@286: ohair@286: /** ohair@286: * Sets the value without adapting the value. ohair@286: *

ohair@286: * This ugly entry point is only used by JAX-WS. ohair@286: * See {@link JAXBRIContext#getElementPropertyAccessor} ohair@286: */ ohair@286: public Object getUnadapted(BeanT bean) throws AccessorException { ohair@286: return get(bean); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Returns true if this accessor wraps an adapter. ohair@286: *

ohair@286: * This method needs to be used with care, but it helps some optimization. ohair@286: */ ohair@286: public boolean isAdapted() { ohair@286: return false; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Sets the value without adapting the value. ohair@286: *

ohair@286: * This ugly entry point is only used by JAX-WS. ohair@286: * See {@link JAXBRIContext#getElementPropertyAccessor} ohair@286: */ ohair@286: public void setUnadapted(BeanT bean, Object value) throws AccessorException { ohair@286: set(bean, (ValueT) value); ohair@286: } ohair@286: ohair@286: public void receive(UnmarshallingContext.State state, Object o) throws SAXException { ohair@286: try { aefimov@650: set((BeanT) state.getTarget(), (ValueT) o); ohair@286: } catch (AccessorException e) { ohair@286: Loader.handleGenericException(e, true); ohair@286: } catch (IllegalAccessError iae) { ohair@286: // throw UnmarshalException instead IllegalAccesssError | Issue 475 ohair@286: Loader.handleGenericError(iae); ohair@286: } ohair@286: } ohair@286: ohair@286: private static List nonAbstractableClasses = Arrays.asList(new Class[]{ ohair@286: Object.class, ohair@286: java.util.Calendar.class, ohair@286: javax.xml.datatype.Duration.class, ohair@286: javax.xml.datatype.XMLGregorianCalendar.class, ohair@286: java.awt.Image.class, ohair@286: javax.activation.DataHandler.class, ohair@286: javax.xml.transform.Source.class, ohair@286: java.util.Date.class, ohair@286: java.io.File.class, ohair@286: java.net.URI.class, ohair@286: java.net.URL.class, ohair@286: Class.class, ohair@286: String.class, ohair@286: javax.xml.transform.Source.class} ohair@286: ); ohair@286: ohair@286: public boolean isValueTypeAbstractable() { ohair@286: return !nonAbstractableClasses.contains(getValueType()); ohair@286: } ohair@286: ohair@286: /** alanb@368: * Checks if it is not builtin jaxb class alanb@368: * @param clazz to be checked alanb@368: * @return true if it is NOT builtin class alanb@368: */ alanb@368: public boolean isAbstractable(Class clazz) { alanb@368: return !nonAbstractableClasses.contains(clazz); alanb@368: } alanb@368: alanb@368: /** ohair@286: * Wraps this {@link Accessor} into another {@link Accessor} ohair@286: * and performs the type adaption as necessary. ohair@286: */ ohair@286: public final Accessor adapt(Class targetType, final Class> adapter) { ohair@286: return new AdaptedAccessor(targetType, this, adapter); ohair@286: } ohair@286: ohair@286: public final Accessor adapt(Adapter adapter) { ohair@286: return new AdaptedAccessor( mkos@450: (Class) Utils.REFLECTION_NAVIGATOR.erasure(adapter.defaultType), ohair@286: this, ohair@286: adapter.adapterType); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Flag that will be set to true after issueing a warning ohair@286: * about the lack of permission to access non-public fields. ohair@286: */ ohair@286: private static boolean accessWarned = false; ohair@286: ohair@286: ohair@286: /** ohair@286: * {@link Accessor} that uses Java reflection to access a field. ohair@286: */ ohair@286: public static class FieldReflection extends Accessor { ohair@286: public final Field f; ohair@286: ohair@286: private static final Logger logger = Util.getClassLogger(); ohair@286: ohair@286: public FieldReflection(Field f) { ohair@286: this(f, false); ohair@286: } ohair@286: ohair@286: public FieldReflection(Field f, boolean supressAccessorWarnings) { ohair@286: super((Class) f.getType()); ohair@286: this.f = f; ohair@286: ohair@286: int mod = f.getModifiers(); ohair@286: if (!Modifier.isPublic(mod) || Modifier.isFinal(mod) || !Modifier.isPublic(f.getDeclaringClass().getModifiers())) { ohair@286: try { ohair@286: // attempt to make it accessible, but do so in the security context of the calling application. ohair@286: // don't do this in the doPrivilege block, as that would create a security hole for anyone ohair@286: // to make any field accessible. ohair@286: f.setAccessible(true); ohair@286: } catch (SecurityException e) { ohair@286: if ((!accessWarned) && (!supressAccessorWarnings)) { ohair@286: // this happens when we don't have enough permission. ohair@286: logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format( ohair@286: f.getDeclaringClass().getName(), ohair@286: f.getName()), ohair@286: e); ohair@286: } ohair@286: accessWarned = true; ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: public ValueT get(BeanT bean) { ohair@286: try { ohair@286: return (ValueT) f.get(bean); ohair@286: } catch (IllegalAccessException e) { ohair@286: throw new IllegalAccessError(e.getMessage()); ohair@286: } ohair@286: } ohair@286: ohair@286: public void set(BeanT bean, ValueT value) { ohair@286: try { ohair@286: if (value == null) ohair@286: value = (ValueT) uninitializedValues.get(valueType); ohair@286: f.set(bean, value); ohair@286: } catch (IllegalAccessException e) { ohair@286: throw new IllegalAccessError(e.getMessage()); ohair@286: } ohair@286: } ohair@286: ohair@286: @Override ohair@286: public Accessor optimize(JAXBContextImpl context) { ohair@286: if (context != null && context.fastBoot) ohair@286: // let's not waste time on doing this for the sake of faster boot. ohair@286: return this; ohair@286: Accessor acc = OptimizedAccessorFactory.get(f); ohair@286: if (acc != null) ohair@286: return acc; ohair@286: else ohair@286: return this; ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Read-only access to {@link Field}. Used to handle a static field. ohair@286: */ ohair@286: public static final class ReadOnlyFieldReflection extends FieldReflection { ohair@286: public ReadOnlyFieldReflection(Field f, boolean supressAccessorWarnings) { ohair@286: super(f, supressAccessorWarnings); ohair@286: } ohair@286: public ReadOnlyFieldReflection(Field f) { ohair@286: super(f); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public void set(BeanT bean, ValueT value) { ohair@286: // noop ohair@286: } ohair@286: ohair@286: @Override ohair@286: public Accessor optimize(JAXBContextImpl context) { ohair@286: return this; ohair@286: } ohair@286: } ohair@286: ohair@286: ohair@286: /** ohair@286: * {@link Accessor} that uses Java reflection to access a getter and a setter. ohair@286: */ ohair@286: public static class GetterSetterReflection extends Accessor { ohair@286: public final Method getter; ohair@286: public final Method setter; ohair@286: ohair@286: private static final Logger logger = Util.getClassLogger(); ohair@286: ohair@286: public GetterSetterReflection(Method getter, Method setter) { ohair@286: super( ohair@286: (Class) (getter != null ? ohair@286: getter.getReturnType() : ohair@286: setter.getParameterTypes()[0])); ohair@286: this.getter = getter; ohair@286: this.setter = setter; ohair@286: ohair@286: if (getter != null) ohair@286: makeAccessible(getter); ohair@286: if (setter != null) ohair@286: makeAccessible(setter); ohair@286: } ohair@286: ohair@286: private void makeAccessible(Method m) { ohair@286: if (!Modifier.isPublic(m.getModifiers()) || !Modifier.isPublic(m.getDeclaringClass().getModifiers())) { ohair@286: try { ohair@286: m.setAccessible(true); ohair@286: } catch (SecurityException e) { ohair@286: if (!accessWarned) ohair@286: // this happens when we don't have enough permission. ohair@286: logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format( ohair@286: m.getDeclaringClass().getName(), ohair@286: m.getName()), ohair@286: e); ohair@286: accessWarned = true; ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: public ValueT get(BeanT bean) throws AccessorException { ohair@286: try { ohair@286: return (ValueT) getter.invoke(bean); ohair@286: } catch (IllegalAccessException e) { ohair@286: throw new IllegalAccessError(e.getMessage()); ohair@286: } catch (InvocationTargetException e) { ohair@286: throw handleInvocationTargetException(e); ohair@286: } ohair@286: } ohair@286: ohair@286: public void set(BeanT bean, ValueT value) throws AccessorException { ohair@286: try { ohair@286: if (value == null) ohair@286: value = (ValueT) uninitializedValues.get(valueType); ohair@286: setter.invoke(bean, value); ohair@286: } catch (IllegalAccessException e) { ohair@286: throw new IllegalAccessError(e.getMessage()); ohair@286: } catch (InvocationTargetException e) { ohair@286: throw handleInvocationTargetException(e); ohair@286: } ohair@286: } ohair@286: ohair@286: private AccessorException handleInvocationTargetException(InvocationTargetException e) { ohair@286: // don't block a problem in the user code ohair@286: Throwable t = e.getTargetException(); ohair@286: if (t instanceof RuntimeException) ohair@286: throw (RuntimeException) t; ohair@286: if (t instanceof Error) ohair@286: throw (Error) t; ohair@286: ohair@286: // otherwise it's a checked exception. ohair@286: // I'm not sure how to handle this. ohair@286: // we can throw a checked exception from here, ohair@286: // but because get/set would be called from so many different places, ohair@286: // the handling would be tedious. ohair@286: return new AccessorException(t); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public Accessor optimize(JAXBContextImpl context) { ohair@286: if (getter == null || setter == null) ohair@286: // if we aren't complete, OptimizedAccessor won't always work ohair@286: return this; ohair@286: if (context != null && context.fastBoot) ohair@286: // let's not waste time on doing this for the sake of faster boot. ohair@286: return this; ohair@286: ohair@286: Accessor acc = OptimizedAccessorFactory.get(getter, setter); ohair@286: if (acc != null) ohair@286: return acc; ohair@286: else ohair@286: return this; ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * A version of {@link GetterSetterReflection} that doesn't have any setter. ohair@286: *

ohair@286: *

ohair@286: * This provides a user-friendly error message. ohair@286: */ ohair@286: public static class GetterOnlyReflection extends GetterSetterReflection { ohair@286: public GetterOnlyReflection(Method getter) { ohair@286: super(getter, null); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public void set(BeanT bean, ValueT value) throws AccessorException { ohair@286: throw new AccessorException(Messages.NO_SETTER.format(getter.toString())); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * A version of {@link GetterSetterReflection} thaat doesn't have any getter. ohair@286: *

ohair@286: *

ohair@286: * This provides a user-friendly error message. ohair@286: */ ohair@286: public static class SetterOnlyReflection extends GetterSetterReflection { ohair@286: public SetterOnlyReflection(Method setter) { ohair@286: super(null, setter); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public ValueT get(BeanT bean) throws AccessorException { ohair@286: throw new AccessorException(Messages.NO_GETTER.format(setter.toString())); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the special {@link Accessor} used to recover from errors. ohair@286: */ ohair@286: @SuppressWarnings("unchecked") ohair@286: public static Accessor getErrorInstance() { ohair@286: return ERROR; ohair@286: } ohair@286: ohair@286: private static final Accessor ERROR = new Accessor(Object.class) { ohair@286: public Object get(Object o) { ohair@286: return null; ohair@286: } ohair@286: ohair@286: public void set(Object o, Object o1) { ohair@286: } ohair@286: }; ohair@286: ohair@286: /** ohair@286: * {@link Accessor} for {@link JAXBElement#getValue()}. ohair@286: */ ohair@286: public static final Accessor JAXB_ELEMENT_VALUE = new Accessor(Object.class) { ohair@286: public Object get(JAXBElement jaxbElement) { ohair@286: return jaxbElement.getValue(); ohair@286: } ohair@286: ohair@286: public void set(JAXBElement jaxbElement, Object o) { ohair@286: jaxbElement.setValue(o); ohair@286: } ohair@286: }; ohair@286: ohair@286: /** ohair@286: * Uninitialized map keyed by their classes. ohair@286: */ ohair@286: private static final Map uninitializedValues = new HashMap(); ohair@286: ohair@286: static { ohair@286: /* ohair@286: static byte default_value_byte = 0; ohair@286: static boolean default_value_boolean = false; ohair@286: static char default_value_char = 0; ohair@286: static float default_value_float = 0; ohair@286: static double default_value_double = 0; ohair@286: static int default_value_int = 0; ohair@286: static long default_value_long = 0; ohair@286: static short default_value_short = 0; ohair@286: */ ohair@286: uninitializedValues.put(byte.class, Byte.valueOf((byte) 0)); ohair@286: uninitializedValues.put(boolean.class, false); ohair@286: uninitializedValues.put(char.class, Character.valueOf((char) 0)); ohair@286: uninitializedValues.put(float.class, Float.valueOf(0)); ohair@286: uninitializedValues.put(double.class, Double.valueOf(0)); ohair@286: uninitializedValues.put(int.class, Integer.valueOf(0)); ohair@286: uninitializedValues.put(long.class, Long.valueOf(0)); ohair@286: uninitializedValues.put(short.class, Short.valueOf((short) 0)); ohair@286: } ohair@286: ohair@286: }