com.sun.xml.bind.v2.runtime.reflect.Accessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaxb-impl Show documentation
Show all versions of jaxb-impl Show documentation
Old JAXB Runtime module. Contains sources required for runtime processing.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.xml.bind.v2.runtime.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.List;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.sun.istack.Nullable;
import com.sun.xml.bind.Util;
import com.sun.xml.bind.api.AccessorException;
import com.sun.xml.bind.api.JAXBRIContext;
import com.sun.xml.bind.v2.model.core.Adapter;
import com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder;
import com.sun.xml.bind.v2.model.nav.Navigator;
import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
import com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory;
import com.sun.xml.bind.v2.runtime.unmarshaller.Loader;
import com.sun.xml.bind.v2.runtime.unmarshaller.Receiver;
import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
import com.sun.istack.Nullable;
import org.xml.sax.SAXException;
/**
* Accesses a particular property of a bean.
*
*
* This interface encapsulates the access to the actual data store.
* The intention is to generate implementations for a particular bean
* and a property to improve the performance.
*
*
* Accessor can be used as a receiver. Upon receiving an object
* it sets that to the field.
*
* @author Kohsuke Kawaguchi ([email protected])
* @see Accessor.FieldReflection
* @see TransducedAccessor
*/
public abstract class Accessor implements Receiver {
public final Class valueType;
public Class getValueType() {
return valueType;
}
protected Accessor(Class valueType) {
this.valueType = valueType;
}
/**
* Returns the optimized version of the same accessor.
*
* @param context The {@link JAXBContextImpl} that owns the whole thing.
* (See {@link RuntimeModelBuilder#context}.)
* @return At least the implementation can return this.
*/
public Accessor optimize(@Nullable JAXBContextImpl context) {
return this;
}
/**
* Gets the value of the property of the given bean object.
*
* @param bean must not be null.
* @throws AccessorException if failed to set a value. For example, the getter method
* may throw an exception.
* @since 2.0 EA1
*/
public abstract ValueT get(BeanT bean) throws AccessorException;
/**
* Sets the value of the property of the given bean object.
*
* @param bean must not be null.
* @param value the value to be set. Setting value to null means resetting
* to the VM default value (even for primitive properties.)
* @throws AccessorException if failed to set a value. For example, the setter method
* may throw an exception.
* @since 2.0 EA1
*/
public abstract void set(BeanT bean, ValueT value) throws AccessorException;
/**
* Sets the value without adapting the value.
*
* This ugly entry point is only used by JAX-WS.
* See {@link JAXBRIContext#getElementPropertyAccessor}
*/
public Object getUnadapted(BeanT bean) throws AccessorException {
return get(bean);
}
/**
* Returns true if this accessor wraps an adapter.
*
* This method needs to be used with care, but it helps some optimization.
*/
public boolean isAdapted() {
return false;
}
/**
* Sets the value without adapting the value.
*
* This ugly entry point is only used by JAX-WS.
* See {@link JAXBRIContext#getElementPropertyAccessor}
*/
public void setUnadapted(BeanT bean, Object value) throws AccessorException {
set(bean, (ValueT) value);
}
public void receive(UnmarshallingContext.State state, Object o) throws SAXException {
try {
set((BeanT) state.target, (ValueT) o);
} catch (AccessorException e) {
Loader.handleGenericException(e, true);
} catch (IllegalAccessError iae) {
// throw UnmarshalException instead IllegalAccesssError | Issue 475
Loader.handleGenericError(iae);
}
}
private static List nonAbstractableClasses = Arrays.asList(new Class[]{
Object.class,
java.util.Calendar.class,
javax.xml.datatype.Duration.class,
javax.xml.datatype.XMLGregorianCalendar.class,
java.awt.Image.class,
javax.activation.DataHandler.class,
javax.xml.transform.Source.class,
java.util.Date.class,
java.io.File.class,
java.net.URI.class,
java.net.URL.class,
Class.class,
String.class,
javax.xml.transform.Source.class}
);
public boolean isValueTypeAbstractable() {
return !nonAbstractableClasses.contains(getValueType());
}
/**
* Wraps this {@link Accessor} into another {@link Accessor}
* and performs the type adaption as necessary.
*/
public final Accessor adapt(Class targetType, final Class extends XmlAdapter> adapter) {
return new AdaptedAccessor(targetType, this, adapter);
}
public final Accessor adapt(Adapter adapter) {
return new AdaptedAccessor(
(Class) Navigator.REFLECTION.erasure(adapter.defaultType),
this,
adapter.adapterType);
}
/**
* Flag that will be set to true after issueing a warning
* about the lack of permission to access non-public fields.
*/
private static boolean accessWarned = false;
/**
* {@link Accessor} that uses Java reflection to access a field.
*/
public static class FieldReflection extends Accessor {
public final Field f;
private static final Logger logger = Util.getClassLogger();
public FieldReflection(Field f) {
this(f, false);
}
public FieldReflection(Field f, boolean supressAccessorWarnings) {
super((Class) f.getType());
this.f = f;
int mod = f.getModifiers();
if (!Modifier.isPublic(mod) || Modifier.isFinal(mod) || !Modifier.isPublic(f.getDeclaringClass().getModifiers())) {
try {
// attempt to make it accessible, but do so in the security context of the calling application.
// don't do this in the doPrivilege block, as that would create a security hole for anyone
// to make any field accessible.
f.setAccessible(true);
} catch (SecurityException e) {
if ((!accessWarned) && (!supressAccessorWarnings)) {
// this happens when we don't have enough permission.
logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format(
f.getDeclaringClass().getName(),
f.getName()),
e);
}
accessWarned = true;
}
}
}
public ValueT get(BeanT bean) {
try {
return (ValueT) f.get(bean);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
}
}
public void set(BeanT bean, ValueT value) {
try {
if (value == null)
value = (ValueT) uninitializedValues.get(valueType);
f.set(bean, value);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
}
}
@Override
public Accessor optimize(JAXBContextImpl context) {
if (context != null && context.fastBoot)
// let's not waste time on doing this for the sake of faster boot.
return this;
Accessor acc = OptimizedAccessorFactory.get(f);
if (acc != null)
return acc;
else
return this;
}
}
/**
* Read-only access to {@link Field}. Used to handle a static field.
*/
public static final class ReadOnlyFieldReflection extends FieldReflection {
public ReadOnlyFieldReflection(Field f, boolean supressAccessorWarnings) {
super(f, supressAccessorWarnings);
}
public ReadOnlyFieldReflection(Field f) {
super(f);
}
@Override
public void set(BeanT bean, ValueT value) {
// noop
}
@Override
public Accessor optimize(JAXBContextImpl context) {
return this;
}
}
/**
* {@link Accessor} that uses Java reflection to access a getter and a setter.
*/
public static class GetterSetterReflection extends Accessor {
public final Method getter;
public final Method setter;
private static final Logger logger = Util.getClassLogger();
public GetterSetterReflection(Method getter, Method setter) {
super(
(Class) (getter != null ?
getter.getReturnType() :
setter.getParameterTypes()[0]));
this.getter = getter;
this.setter = setter;
if (getter != null)
makeAccessible(getter);
if (setter != null)
makeAccessible(setter);
}
private void makeAccessible(Method m) {
if (!Modifier.isPublic(m.getModifiers()) || !Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
try {
m.setAccessible(true);
} catch (SecurityException e) {
if (!accessWarned)
// this happens when we don't have enough permission.
logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format(
m.getDeclaringClass().getName(),
m.getName()),
e);
accessWarned = true;
}
}
}
public ValueT get(BeanT bean) throws AccessorException {
try {
return (ValueT) getter.invoke(bean);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
} catch (InvocationTargetException e) {
throw handleInvocationTargetException(e);
}
}
public void set(BeanT bean, ValueT value) throws AccessorException {
try {
if (value == null)
value = (ValueT) uninitializedValues.get(valueType);
setter.invoke(bean, value);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
} catch (InvocationTargetException e) {
throw handleInvocationTargetException(e);
}
}
private AccessorException handleInvocationTargetException(InvocationTargetException e) {
// don't block a problem in the user code
Throwable t = e.getTargetException();
if (t instanceof RuntimeException)
throw (RuntimeException) t;
if (t instanceof Error)
throw (Error) t;
// otherwise it's a checked exception.
// I'm not sure how to handle this.
// we can throw a checked exception from here,
// but because get/set would be called from so many different places,
// the handling would be tedious.
return new AccessorException(t);
}
@Override
public Accessor optimize(JAXBContextImpl context) {
if (getter == null || setter == null)
// if we aren't complete, OptimizedAccessor won't always work
return this;
if (context != null && context.fastBoot)
// let's not waste time on doing this for the sake of faster boot.
return this;
Accessor acc = OptimizedAccessorFactory.get(getter, setter);
if (acc != null)
return acc;
else
return this;
}
}
/**
* A version of {@link GetterSetterReflection} that doesn't have any setter.
*
*
* This provides a user-friendly error message.
*/
public static class GetterOnlyReflection extends GetterSetterReflection {
public GetterOnlyReflection(Method getter) {
super(getter, null);
}
@Override
public void set(BeanT bean, ValueT value) throws AccessorException {
throw new AccessorException(Messages.NO_SETTER.format(getter.toString()));
}
}
/**
* A version of {@link GetterSetterReflection} thaat doesn't have any getter.
*
*
* This provides a user-friendly error message.
*/
public static class SetterOnlyReflection extends GetterSetterReflection {
public SetterOnlyReflection(Method setter) {
super(null, setter);
}
@Override
public ValueT get(BeanT bean) throws AccessorException {
throw new AccessorException(Messages.NO_GETTER.format(setter.toString()));
}
}
/**
* Gets the special {@link Accessor} used to recover from errors.
*/
@SuppressWarnings("unchecked")
public static Accessor getErrorInstance() {
return ERROR;
}
private static final Accessor ERROR = new Accessor