org.eclipse.persistence.jaxb.javamodel.AnnotationProxy Maven / Gradle / Ivy
Show all versions of eclipselink Show documentation
/*
* Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// dmccann - December 10/2009 - 2.0.1 - Initial implementation
package org.eclipse.persistence.jaxb.javamodel;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.persistence.internal.helper.ConversionManager;
/**
*
* Purpose:The purpose of this class is to act as a dynamic proxy that
* allows JDK Annotation method calls to be made on a non Annotation object.
*
*
* Responsibilities:
*
* - Create and return a dynamic proxy instance based on an Annotation class
* and a
Map
of components (method name to value pairs)
* - Allow JDK Annotation method calls to be invoked on the proxy object
*
*
* This class provides a means to invoke JDK Annotation method calls on a non
* Annotation instance.
*
* @see java.lang.reflect.Proxy
*/
public class AnnotationProxy implements InvocationHandler {
private Map components;
private ConversionManager conversionMgr;
private static final String ANNOTATION_TYPE_METHOD_NAME = "annotationType";
/**
* This constructor sets the Map
of components (method name to
* value pairs)and the ConversionManager to be used when converting values
* in the Map
based on the return type of the associated
* Method
Note that the preferred method of obtaining an
* instance of this proxy class is via
* {@code getProxy(Map, Class, ClassLoader, ConversionManager)}
*
* @param components
* Map
of method name to value pairs
* @param conversionMgr
* ConversionManager
instance for converting to the
* correct return type in the invoke
method
*/
private AnnotationProxy(Map components, ConversionManager conversionMgr) {
this.components = components;
this.conversionMgr = conversionMgr;
}
/**
* This is the preferred way to obtain an instance of a dynamic proxy.
*
* The method takes a ClassLoader
(which is used to load the
* target Annotation
), a Class
(which indicates
* the target Annotation
, i.e.
* jakarta.xml.bind.annotation.XmlElement.class)
, and a
* Map
of method name to value pairs, which represent the
* method names on the Annotation
and the values that are to be
* returned from each method call. For example, if this proxy is to be used
* for an @XmlElement
, the Map
should contain the
* following keys:
*
*
* - defaultValue
- name
- namespace
- nillable
* - required
- type
*
*
* Following are example key/value pairs :
*
*
* - "defaultValue", "##default"
- "name", "employee"
-
* "namespace", "www.example.org"
- "nillable", false
-
* "required", false
- "type",
* jakarta.xml.bind.annotation.XmlElement.DEFAULT.class
*
*
* @param components
* Map
of method name/value pairs for this proxy
* instance
* @param annoClass
* The interface for the proxy class to implement
* @param cl
* The ClassLoader
to define the proxy class
* @param conversionMgr
* ConversionManager
instance for converting to the
* correct return type in the invoke
method
* @return A dynamic proxy instance based on a Java model JavaAnnotation
*/
public static A getProxy(Map components, Class annoClass, ClassLoader cl, ConversionManager conversionMgr) {
// add the 'annotationType' method name/value pair to the components map
if (components == null) {
components = new HashMap<>();
}
components.put(ANNOTATION_TYPE_METHOD_NAME, annoClass.getName());
// Pass the classloader to the ConversionManager as well
// conversionMgr.setLoader(cl);
return (A) Proxy.newProxyInstance(cl, new Class>[] { annoClass }, new AnnotationProxy(components, conversionMgr));
}
/**
* Return the Map
of method name/value pairs for this proxy
* instance.
*
* @return Map
of method name/value pairs for this proxy
* instance
*/
public Map getComponents() {
return this.components;
}
/**
* Invoke a given Method
on this proxy. The component
* Map
will be accessed using the given Method
's
* name, and if an entry exists, the associated value is returned.
*
* @param proxy
* Satisfy the InvocationHandler
interface - not
* used
* @param method
* The Method
instance corresponding to the
* interface method invoked on the proxy instance
* @param args
* Satisfy the InvocationHandler
interface - not
* used
* @return The value from the method invocation on the proxy instance
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (components == null) {
return null;
}
Class> returnType = method.getReturnType();
Object value = getComponents().get(method.getName());
if (value == null && returnType == boolean.class) {
return false;
}
if (value == null && returnType == Boolean.class) {
return Boolean.FALSE;
}
if (returnType.isArray()) {
return handleArrayData(returnType, value);
}
// use the ConversionManager to ensure that the correct type is returned
return conversionMgr.convertObject(value, returnType);
}
private Object handleArrayData(Class> returnType, Object value) {
if (value == null) {
return null;
}
Object[] data = (Object[]) value;
Class> componentType = returnType.getComponentType();
Object[] convertedArray = (Object[]) Array.newInstance(componentType, data.length);
for (int i = 0; i < data.length; i++) {
convertedArray[i] = conversionMgr.convertObject(data[i], componentType);
}
return convertedArray;
}
}