/**
* 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;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.helger.jcodemodel.util.JCValueEnforcer;
/**
* Represents an annotation on a program element.
*
* @author Bhakti Mehta (bhakti.mehta@sun.com)
*/
public class JAnnotationUse extends AbstractJAnnotationValueOwned
{
/**
* The special parameter name that can be optimized away if used without any
* other parameter
*/
public static final String SPECIAL_KEY_VALUE = "value";
/**
* The {@link Annotation} class
*/
private final AbstractJClass m_aAnnotationClass;
/**
* Map of member values.
*/
private Map <String, AbstractJAnnotationValue> m_aMemberValues;
public JAnnotationUse (@Nonnull final AbstractJClass aAnnotationClass)
{
m_aAnnotationClass = JCValueEnforcer.notNull (aAnnotationClass, "AnnotationClass");
}
@Nonnull
public AbstractJClass getAnnotationClass ()
{
return m_aAnnotationClass;
}
@Nonnull
public JCodeModel owner ()
{
return m_aAnnotationClass.owner ();
}
@Nonnull
public Map <String, AbstractJAnnotationValue> getAnnotationMembers ()
{
return m_aMemberValues == null ? new HashMap <> () : Collections.unmodifiableMap (m_aMemberValues);
}
public boolean hasAnnotationMembers ()
{
return m_aMemberValues != null && !m_aMemberValues.isEmpty ();
}
@Nullable
public AbstractJAnnotationValue getParam (@Nullable final String sName)
{
return m_aMemberValues == null ? null : m_aMemberValues.get (sName);
}
@SuppressWarnings ("unchecked")
private static <T> T _castAnnotationArgument (@Nullable final AbstractJAnnotationValue value,
@Nonnull final Class <T> klass) throws ClassCastException
{
if (!klass.isArray ())
{
if (value == null)
throw new ClassCastException ("Can't cast null annotation value to " + klass + " class");
if (JAnnotationUse.class.isAssignableFrom (klass))
return klass.cast (value);
if (!(value instanceof JAnnotationStringValue))
throw new ClassCastException ("Can't cast " + value + " annotation value to " + klass + " class");
final JAnnotationStringValue aStringValue = (JAnnotationStringValue) value;
return (T) aStringValue.nativeValue ();
}
// It's any array
if (value == null)
return (T) Array.newInstance (klass.getComponentType (), 0);
final JAnnotationArrayMember jarray = (JAnnotationArrayMember) value;
final Collection <AbstractJAnnotationValue> interfaceJArray = jarray.getAllAnnotations ();
final Object [] result = (Object []) Array.newInstance (klass.getComponentType (), interfaceJArray.size ());
final Iterator <AbstractJAnnotationValue> iterator = interfaceJArray.iterator ();
for (int i = 0; iterator.hasNext (); i++)
{
result[i] = _castAnnotationArgument (iterator.next (), klass.getComponentType ());
}
return (T) result;
}
/**
* Return annotation argument represented as required type. Return type is
* chosen to conform to given klass argument.
* <p>
* For example, you can have annotation parameter named 'value' of type
* String.
* <p>
* You can write {@code getParam("value", String.class)} to get raw
* string-value. You can write
* {@code getParam("value", AbstractJAnnotationValue.class)} to get
* AbstractJAnnotationValue for this argument.
* <p>
* Arrays are supported as a result type.
*
* @param <T>
* return type
* @param sName
* annotation parameter name
* @param klass
* type to use as a return type
* @return annotation argument represented as required type
*/
@Nullable
public <T> T getParam (@Nonnull final String sName, @Nonnull final Class <T> klass) throws ClassCastException
{
final AbstractJAnnotationValue value = getParam (sName);
return _castAnnotationArgument (value, klass);
}
@Nullable
public JAnnotationStringValue getConstantParam (@Nullable final String sName)
{
final AbstractJAnnotationValue aParam = getParam (sName);
return aParam instanceof JAnnotationStringValue ? (JAnnotationStringValue) aParam : null;
}
@Nullable
public IJExpression getConstantParamValue (@Nullable final String sName)
{
final JAnnotationStringValue aParam = getConstantParam (sName);
return aParam != null ? aParam.value () : null;
}
@Nonnull
private JAnnotationUse _addValue (@Nonnull final String sName,
@Nonnull final AbstractJAnnotationValue aAnnotationValue)
{
JCValueEnforcer.notEmpty (sName, "Name");
JCValueEnforcer.notNull (aAnnotationValue, "AnnotationValue");
// Use ordered map to keep the code generation the same on any JVM.
// Lazily created.
if (m_aMemberValues == null)
m_aMemberValues = new LinkedHashMap <> ();
m_aMemberValues.put (sName, aAnnotationValue);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param sName
* The simple name for this annotation
* @param value
* The boolean value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String sName, final boolean value)
{
return _addValue (sName, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final boolean... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The byte member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final byte value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final byte... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The char member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final char value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final char... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The double member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final double value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final double... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The float member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final float value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final float... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The long member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final long value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final long... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The short member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final short value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final short... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The int member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final int value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final int... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The String member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, final String value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final String... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The enum class which is member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, @Nonnull final Enum <?> value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final Enum <?>... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation
*
* @param name
* The simple name for this annotation
* @param value
* The {@link JEnumConstant} which is member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, @Nonnull final JEnumConstant value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final JEnumConstant... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation This can be used for e.g to
* specify
*
* <pre>
* @XmlCollectionItem(type=Integer.class);
* </pre>
*
* For adding a value of Class<? extends Annotation>
* {@link #annotationParam(String, Class)}
*
* @param name
* The simple name for this annotation param
* @param value
* The class type of the param
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, @Nonnull final Class <?> value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final Class <?>... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation based on the type represented
* by the given {@link AbstractJType}
*
* @param name
* The simple name for this annotation param
* @param type
* the {@link AbstractJType} representing the actual type
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, @Nonnull final AbstractJType type)
{
return _addValue (name, wrap (type));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final AbstractJType... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair to this annotation.
*
* @param name
* The simple name for this annotation
* @param value
* The {@link IJExpression} which provides the content value for this
* annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse param (@Nonnull final String name, @Nonnull final IJExpression value)
{
return _addValue (name, wrap (value));
}
@Nonnull
public JAnnotationUse paramArray (@Nonnull final String name, @Nonnull final IJExpression... values)
{
paramArray (name).params (values);
return this;
}
/**
* Adds a member value pair which is of type array to this annotation
*
* @param sName
* The simple name for this annotation
* @return The {@link JAnnotationArrayMember}. For adding array values
* @see JAnnotationArrayMember
*/
@Nonnull
public JAnnotationArrayMember paramArray (@Nonnull final String sName)
{
final JAnnotationArrayMember aArrayMember = new JAnnotationArrayMember (owner ());
_addValue (sName, aArrayMember);
return aArrayMember;
}
/**
* Adds a member value pair to this annotation For adding class values as
* param
*
* @see #param(String, Class)
* @param name
* The simple name for this annotation
* @param value
* The annotation class which is member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse annotationParam (@Nonnull final String name, @Nonnull final Class <? extends Annotation> value)
{
return annotationParam (name, owner ().ref (value));
}
/**
* Adds a member value pair to this annotation For adding class values as
* param
*
* @see #param(String, Class)
* @param name
* The simple name for this annotation
* @param value
* The annotation class which is member value for this annotation
* @return The {@link JAnnotationUse}. More member value pairs can be added to
* it using the same or the overloaded methods.
*/
@Nonnull
public JAnnotationUse annotationParam (@Nonnull final String name, @Nonnull final AbstractJClass value)
{
final JAnnotationUse annotationUse = new JAnnotationUse (value);
_addValue (name, annotationUse);
return annotationUse;
}
@Nonnegative
public int size ()
{
return m_aMemberValues.size ();
}
private boolean _isOptimizable ()
{
return m_aMemberValues.size () == 1 && m_aMemberValues.containsKey (SPECIAL_KEY_VALUE);
}
public void generate (final JFormatter f)
{
f.print ('@').generable (m_aAnnotationClass);
if (m_aMemberValues != null && !m_aMemberValues.isEmpty ())
{
f.print ('(');
if (_isOptimizable ())
{
// short form
f.generable (m_aMemberValues.get (SPECIAL_KEY_VALUE));
}
else
{
boolean bFirst = true;
for (final Map.Entry <String, AbstractJAnnotationValue> mapEntry : m_aMemberValues.entrySet ())
{
if (!bFirst)
f.print (',');
f.print (mapEntry.getKey ()).print ('=').generable (mapEntry.getValue ());
bFirst = false;
}
}
f.print (')');
}
}
}