You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by mb...@apache.org on 2010/09/13 23:38:02 UTC
svn commit: r996695 - in /commons/proper/lang/trunk/src:
main/java/org/apache/commons/lang3/AnnotationUtils.java
test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
Author: mbenson
Date: Mon Sep 13 21:38:02 2010
New Revision: 996695
URL: http://svn.apache.org/viewvc?rev=996695&view=rev
Log:
AnnotationUtils
Added:
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java (with props)
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java (with props)
Added: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java?rev=996695&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java (added)
+++ commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java Mon Sep 13 21:38:02 2010
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * Helper methods for working with {@link Annotation}s.
+ * @since 3.0
+ * @version $Id$
+ */
+public class AnnotationUtils {
+
+ /**
+ * <p><code>AnnotationUtils</code> instances should NOT be constructed in
+ * standard programming. Instead, the class should be used statically.</p>
+ *
+ * <p>This constructor is public to permit tools that require a JavaBean
+ * instance to operate.</p>
+ */
+ public AnnotationUtils() {
+ }
+
+ /**
+ * Learn whether two annotations are equivalent as defined by
+ * {@link Annotation#equals(Object)}. This method is useful because
+ * dynamically created {@link Annotation} instances are always proxy
+ * objects, which, though dependent upon implementation, very often cannot
+ * be depended upon to behave "normally" in terms of {@link #equals(Object)}
+ * implementation.
+ * @param a1 the first Annotation to compare
+ * @param a2 the second Annotation to compare
+ */
+ public static boolean equals(Annotation a1, Annotation a2) {
+ if (a1 == a2) {
+ return true;
+ }
+ if (a1 == null || a2 == null) {
+ return false;
+ }
+ Class<? extends Annotation> type = a1.annotationType();
+ Class<? extends Annotation> type2 = a2.annotationType();
+ Validate.notNull(type, "Annotation %s with null annotationType()", a1);
+ Validate.notNull(type2, "Annotation %s with null annotationType()", a2);
+ if (!type.equals(type2)) {
+ return false;
+ }
+ try {
+ for (Method m : type.getDeclaredMethods()) {
+ if (m.getParameterTypes().length == 0
+ && isValidAnnotationMemberType(m.getReturnType())) {
+ Object v1 = m.invoke(a1);
+ Object v2 = m.invoke(a2);
+ if (!memberEquals(m.getReturnType(), v1, v2)) {
+ return false;
+ }
+ }
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Learn whether the specified type is permitted as an annotation member.
+ * These include {@link String}, {@link Class}, primitive types,
+ * {@link Annotation}s, {@link Enum}s, and arrays of same.
+ * @param type to check
+ * @return boolean
+ */
+ public static boolean isValidAnnotationMemberType(Class<?> type) {
+ if (type == null) {
+ return false;
+ }
+ if (type.isArray()) {
+ type = type.getComponentType();
+ }
+ return type.isPrimitive() || type.isEnum() || type.isAnnotation()
+ || String.class.equals(type) || Class.class.equals(type);
+ }
+
+ private static boolean memberEquals(Class<?> type, Object o1, Object o2) {
+ if (o1 == o2) {
+ return true;
+ }
+ if (o1 == null || o2 == null) {
+ return false;
+ }
+ if (type.isArray()) {
+ return arrayMemberEquals(type.getComponentType(), o1, o2);
+ }
+ if (type.isAnnotation()) {
+ return equals((Annotation) o1, (Annotation) o2);
+ }
+ return o1.equals(o2);
+ }
+
+ private static boolean arrayMemberEquals(Class<?> componentType, Object o1, Object o2) {
+ if (componentType.isAnnotation()) {
+ return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
+ }
+ if (componentType.equals(Byte.TYPE)) {
+ return Arrays.equals((byte[]) o1, (byte[]) o2);
+ }
+ if (componentType.equals(Short.TYPE)) {
+ return Arrays.equals((short[]) o1, (short[]) o2);
+ }
+ if (componentType.equals(Integer.TYPE)) {
+ return Arrays.equals((int[]) o1, (int[]) o2);
+ }
+ if (componentType.equals(Character.TYPE)) {
+ return Arrays.equals((char[]) o1, (char[]) o2);
+ }
+ if (componentType.equals(Long.TYPE)) {
+ return Arrays.equals((long[]) o1, (long[]) o2);
+ }
+ if (componentType.equals(Float.TYPE)) {
+ return Arrays.equals((float[]) o1, (float[]) o2);
+ }
+ if (componentType.equals(Double.TYPE)) {
+ return Arrays.equals((double[]) o1, (double[]) o2);
+ }
+ if (componentType.equals(Boolean.TYPE)) {
+ return Arrays.equals((boolean[]) o1, (boolean[]) o2);
+ }
+ return Arrays.equals((Object[]) o1, (Object[]) o2);
+ }
+
+ private static boolean annotationArrayMemberEquals(Annotation[] a1, Annotation[] a2) {
+ if (a1.length != a2.length) {
+ return false;
+ }
+ for (int i = 0; i < a1.length; i++) {
+ if (!equals(a1[i], a2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
Propchange: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/AnnotationUtils.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java?rev=996695&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java (added)
+++ commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java Mon Sep 13 21:38:02 2010
@@ -0,0 +1,430 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.*;
+import static org.junit.Assert.*;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @version $Id$
+ */
+public class AnnotationUtilsTest {
+ @TestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ nest = @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object.class,
+ types = { Object.class }
+ ),
+ nests = {
+ @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object[].class,
+ types = { Object[].class }
+ )
+ },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = SHEMP,
+ stooges = { MOE, LARRY, CURLY },
+ string = "",
+ strings = { "" },
+ type = Object.class,
+ types = { Object.class }
+ )
+ public Object dummy1;
+
+ @TestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ nest = @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object.class,
+ types = { Object.class }
+ ),
+ nests = {
+ @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object[].class,
+ types = { Object[].class }
+ )
+ },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = SHEMP,
+ stooges = { MOE, LARRY, CURLY },
+ string = "",
+ strings = { "" },
+ type = Object.class,
+ types = { Object.class }
+ )
+ public Object dummy2;
+
+ @TestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ nest = @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object.class,
+ types = { Object.class }
+ ),
+ nests = {
+ @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object[].class,
+ types = { Object[].class }
+ ),
+ //add a second NestAnnotation to break equality:
+ @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object[].class,
+ types = { Object[].class }
+ )
+ },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = SHEMP,
+ stooges = { MOE, LARRY, CURLY },
+ string = "",
+ strings = { "" },
+ type = Object.class,
+ types = { Object.class }
+ )
+ public Object dummy3;
+
+ @NestAnnotation(
+ booleanValue = false,
+ booleanValues = { false },
+ byteValue = 0,
+ byteValues = { 0 },
+ charValue = 0,
+ charValues = { 0 },
+ doubleValue = 0,
+ doubleValues = { 0 },
+ floatValue = 0,
+ floatValues = { 0 },
+ intValue = 0,
+ intValues = { 0 },
+ longValue = 0,
+ longValues = { 0 },
+ shortValue = 0,
+ shortValues = { 0 },
+ stooge = CURLY,
+ stooges = { MOE, LARRY, SHEMP },
+ string = "",
+ strings = { "" },
+ type = Object[].class,
+ types = { Object[].class }
+ )
+
+ public Object dummy4;
+
+ @Target(FIELD)
+ @Retention(RUNTIME)
+ public @interface TestAnnotation {
+ String string();
+ String[] strings();
+ Class<?> type();
+ Class<?>[] types();
+ byte byteValue();
+ byte[] byteValues();
+ short shortValue();
+ short[] shortValues();
+ int intValue();
+ int[] intValues();
+ char charValue();
+ char[] charValues();
+ long longValue();
+ long[] longValues();
+ float floatValue();
+ float[] floatValues();
+ double doubleValue();
+ double[] doubleValues();
+ boolean booleanValue();
+ boolean[] booleanValues();
+ Stooge stooge();
+ Stooge[] stooges();
+ NestAnnotation nest();
+ NestAnnotation[] nests();
+ }
+
+ public @interface NestAnnotation {
+ String string();
+ String[] strings();
+ Class<?> type();
+ Class<?>[] types();
+ byte byteValue();
+ byte[] byteValues();
+ short shortValue();
+ short[] shortValues();
+ int intValue();
+ int[] intValues();
+ char charValue();
+ char[] charValues();
+ long longValue();
+ long[] longValues();
+ float floatValue();
+ float[] floatValues();
+ double doubleValue();
+ double[] doubleValues();
+ boolean booleanValue();
+ boolean[] booleanValues();
+ Stooge stooge();
+ Stooge[] stooges();
+ }
+
+ public static enum Stooge {
+ MOE, LARRY, CURLY, JOE, SHEMP;
+ }
+
+ private Field field1;
+ private Field field2;
+ private Field field3;
+ private Field field4;
+
+ @Before
+ public void setup() throws Exception {
+ field1 = getClass().getDeclaredField("dummy1");
+ field2 = getClass().getDeclaredField("dummy2");
+ field3 = getClass().getDeclaredField("dummy3");
+ field4 = getClass().getDeclaredField("dummy4");
+ }
+
+ @Test
+ public void testEquivalence() {
+ assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field2.getAnnotation(TestAnnotation.class)));
+ }
+
+ @Test
+ public void testSameInstance() {
+ assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class)));
+ }
+
+ @Test
+ public void testNonEquivalentAnnotationsOfSameType() {
+ assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field3.getAnnotation(TestAnnotation.class)));
+ }
+
+ @Test
+ public void testAnnotationsOfDifferingTypes() {
+ assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field4.getAnnotation(NestAnnotation.class)));
+ }
+
+ @Test
+ public void testOneArgNull() {
+ assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), null));
+ }
+
+ @Test
+ public void testBothArgsNull() {
+ assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), null));
+ }
+
+ @Test
+ public void testIsValidAnnotationMemberType() {
+ for (Class<?> type : new Class[] { byte.class, short.class, int.class, char.class,
+ long.class, float.class, double.class, boolean.class, String.class, Class.class,
+ NestAnnotation.class, TestAnnotation.class, Stooge.class, ElementType.class }) {
+ assertTrue(AnnotationUtils.isValidAnnotationMemberType(type));
+ assertTrue(AnnotationUtils.isValidAnnotationMemberType(Array.newInstance(type, 0)
+ .getClass()));
+ }
+ }
+}
Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/AnnotationUtilsTest.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL