/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
* Portions Copyright 2013-2017 Philip Helger + contributors
*
* 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.helger.jcodemodel.meta;
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.AbstractJType;
import com.helger.jcodemodel.IJAnnotatable;
import com.helger.jcodemodel.JAnnotationArrayMember;
import com.helger.jcodemodel.JAnnotationUse;
/**
* @author Victor Nazarov <asviraspossible@gmail.com>
*/
class Annotator
{
private final DecidedErrorTypesModelsAdapter _modelsAdapter;
private final IJAnnotatable _annotatable;
private final TypeEnvironment _typeEnvironment;
public Annotator (final DecidedErrorTypesModelsAdapter modelsAdapter,
final IJAnnotatable annotatable,
final TypeEnvironment typeEnvironment)
{
this._modelsAdapter = modelsAdapter;
this._annotatable = annotatable;
this._typeEnvironment = typeEnvironment;
}
void annotate (final List <? extends AnnotationMirror> annotationMirrors) throws CodeModelBuildingException,
IllegalStateException,
ErrorTypeFound
{
for (final AnnotationMirror annotation : annotationMirrors)
{
annotate (annotation);
}
}
private void annotate (final AnnotationMirror annotation) throws CodeModelBuildingException,
IllegalStateException,
ErrorTypeFound
{
final JAnnotationUse annotationUse = _annotatable.annotate ((AbstractJClass) _modelsAdapter.toJType (annotation.getAnnotationType (),
_typeEnvironment));
final ArgumentAdder reader = new ArgumentAdder (annotationUse);
reader.addArguments (annotation);
}
class ArgumentAdder
{
private final JAnnotationUse _annotationUse;
public ArgumentAdder (final JAnnotationUse annotationUse)
{
this._annotationUse = annotationUse;
}
void addArguments (final AnnotationMirror annotation) throws CodeModelBuildingException,
IllegalStateException,
ErrorTypeFound
{
final Map <? extends ExecutableElement, ? extends AnnotationValue> annotationArguments = _modelsAdapter.getElementValuesWithDefaults (annotation);
for (final Map.Entry <? extends ExecutableElement, ? extends AnnotationValue> annotationValueAssignment : annotationArguments.entrySet ())
{
final String name = annotationValueAssignment.getKey ().getSimpleName ().toString ();
final Object value = annotationValueAssignment.getValue ().getValue ();
addArgument (name, value);
}
}
private void addArgument (final String name, final Object value) throws IllegalStateException,
CodeModelBuildingException,
ErrorTypeFound
{
if (value instanceof String)
_annotationUse.param (name, (String) value);
else
if (value instanceof Integer)
_annotationUse.param (name, ((Integer) value).intValue ());
else
if (value instanceof Long)
_annotationUse.param (name, ((Long) value).longValue ());
else
if (value instanceof Short)
_annotationUse.param (name, ((Short) value).shortValue ());
else
if (value instanceof Float)
_annotationUse.param (name, ((Float) value).floatValue ());
else
if (value instanceof Double)
_annotationUse.param (name, ((Double) value).doubleValue ());
else
if (value instanceof Byte)
_annotationUse.param (name, ((Byte) value).byteValue ());
else
if (value instanceof Character)
_annotationUse.param (name, ((Character) value).charValue ());
else
if (value instanceof Boolean)
_annotationUse.param (name, ((Boolean) value).booleanValue ());
else
if (value instanceof Class)
_annotationUse.param (name, (Class <?>) value);
else
if (value instanceof DeclaredType)
{
_annotationUse.param (name,
_modelsAdapter.toJType ((DeclaredType) value, _typeEnvironment));
}
else
if (value instanceof VariableElement)
{
try
{
_annotationUse.param (name, actualEnumConstantValue ((VariableElement) value));
}
catch (final ClassNotFoundException ex)
{
Logger.getLogger (Annotator.class.getName ()).log (Level.WARNING,
"Not processing annotation argument: {0}: {1}",
new Object [] { name, value });
}
}
else
if (value instanceof AnnotationMirror)
{
final AnnotationMirror annotation = (AnnotationMirror) value;
final AbstractJClass annotationClass = (AbstractJClass) _modelsAdapter.toJType (annotation.getAnnotationType (),
_typeEnvironment);
final JAnnotationUse annotationParam = _annotationUse.annotationParam (name,
annotationClass);
final ArgumentAdder adder = new ArgumentAdder (annotationParam);
adder.addArguments (annotation);
}
else
if (value instanceof List)
{
@SuppressWarnings (value = "unchecked")
final List <? extends AnnotationValue> list = (List <? extends AnnotationValue>) value;
final Iterator <? extends AnnotationValue> iterator = list.iterator ();
if (iterator.hasNext ())
{
final AnnotationValue firstElementValue = iterator.next ();
final Object element = firstElementValue.getValue ();
if (element instanceof String)
{
final String [] elements = new String [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = (String) elementValue.getValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Integer)
{
final int [] elements = new int [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Integer) elementValue.getValue ()).intValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Long)
{
final long [] elements = new long [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Long) elementValue.getValue ()).longValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Short)
{
final short [] elements = new short [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Short) elementValue.getValue ()).shortValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Float)
{
final float [] elements = new float [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Float) elementValue.getValue ()).floatValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Double)
{
final double [] elements = new double [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Double) elementValue.getValue ()).doubleValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Byte)
{
final byte [] elements = new byte [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Byte) elementValue.getValue ()).byteValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Character)
{
final char [] elements = new char [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Character) elementValue.getValue ()).charValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Boolean)
{
final boolean [] elements = new boolean [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = ((Boolean) elementValue.getValue ()).booleanValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof Class)
{
final Class <?> [] elements = new Class <?> [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = (Class <?>) elementValue.getValue ();
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof DeclaredType)
{
final AbstractJType [] elements = new AbstractJType [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = _modelsAdapter.toJType ((DeclaredType) elementValue.getValue (),
_typeEnvironment);
i++;
}
_annotationUse.paramArray (name, elements);
}
else
if (element instanceof VariableElement)
{
try
{
final Enum <?> [] elements = new Enum <?> [list.size ()];
int i = 0;
for (final AnnotationValue elementValue : list)
{
elements[i] = actualEnumConstantValue ((VariableElement) elementValue.getValue ());
i++;
}
_annotationUse.paramArray (name, elements);
}
catch (final ClassNotFoundException ex)
{
Logger.getLogger (Annotator.class.getName ())
.log (Level.WARNING,
"Not processing annotation argument: {0}: {1}",
new Object [] { name, list });
}
}
else
if (element instanceof AnnotationMirror)
{
final JAnnotationArrayMember paramArray = _annotationUse.paramArray (name);
for (final AnnotationValue elementValue : list)
{
final AnnotationMirror annotation = (AnnotationMirror) elementValue.getValue ();
final AbstractJClass annotationClass = (AbstractJClass) _modelsAdapter.toJType (annotation.getAnnotationType (),
_typeEnvironment);
final JAnnotationUse annotationParam = paramArray.annotate (annotationClass);
final ArgumentAdder adder = new ArgumentAdder (annotationParam);
adder.addArguments (annotation);
}
}
else
{
throw new IllegalStateException (MessageFormat.format ("Unknown annotation array argument: {0}: {1} ({2})",
name,
element,
element.getClass ()));
}
}
}
else
throw new IllegalStateException (MessageFormat.format ("Unknown annotation argument: {0}: {1} ({2})",
name,
value,
value.getClass ()));
}
private Enum <?> actualEnumConstantValue (final VariableElement variableElement) throws ClassNotFoundException
{
final TypeElement enumClassElement = (TypeElement) variableElement.getEnclosingElement ();
final Class <?> enumClass = Class.forName (enumClassElement.getQualifiedName ().toString ());
Field enumConstantField;
try
{
enumConstantField = enumClass.getField (variableElement.getSimpleName ().toString ());
}
catch (final NoSuchFieldException ex)
{
throw new IllegalStateException (MessageFormat.format ("Unable to load enum constant: {0}.{1}",
enumClassElement.getQualifiedName ().toString (),
variableElement.getSimpleName ().toString ()),
ex);
}
catch (final SecurityException ex)
{
throw new IllegalStateException (MessageFormat.format ("Unable to load enum constant: {0}.{1}",
enumClassElement.getQualifiedName ().toString (),
variableElement.getSimpleName ().toString ()),
ex);
}
Enum <?> enumValue;
try
{
enumValue = (Enum <?>) enumConstantField.get (null);
}
catch (final IllegalArgumentException ex)
{
throw new IllegalStateException (MessageFormat.format ("Unable to load enum constant actual value: {0}.{1}",
enumClassElement.getQualifiedName ().toString (),
variableElement.getSimpleName ().toString ()),
ex);
}
catch (final IllegalAccessException ex)
{
throw new IllegalStateException (MessageFormat.format ("Unable to load enum constant actual value: {0}.{1}",
enumClassElement.getQualifiedName ().toString (),
variableElement.getSimpleName ().toString ()),
ex);
}
return enumValue;
}
}
}