Merge "Merge OpenJDK 8 java.net (part 2)"
diff --git a/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java
new file mode 100644
index 0000000..605df4d
--- /dev/null
+++ b/dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 dalvik.annotation.optimization;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An ART runtime built-in optimization for "native" methods to speed up JNI transitions.
+ *
+ * <p>
+ * This has the side-effect of disabling all garbage collections while executing a fast native
+ * method. Use with extreme caution. Any long-running methods must not be marked with
+ * {@code @FastNative} (including usually-fast but generally unbounded methods)!</p>
+ *
+ * <p><b>Deadlock Warning:</b>As a rule of thumb, do not acquire any locks during a fast native
+ * call if they aren't also locally released [before returning to managed code].</p>
+ *
+ * <p>
+ * Say some code does:
+ *
+ * <code>
+ * fast_jni_call_to_grab_a_lock();
+ * does_some_java_work();
+ * fast_jni_call_to_release_a_lock();
+ * </code>
+ *
+ * <p>
+ * This code can lead to deadlocks. Say thread 1 just finishes
+ * {@code fast_jni_call_to_grab_a_lock()} and is in {@code does_some_java_work()}.
+ * GC kicks in and suspends thread 1. Thread 2 now is in {@code fast_jni_call_to_grab_a_lock()}
+ * but is blocked on grabbing the native lock since it's held by thread 1.
+ * Now thread suspension can't finish since thread 2 can't be suspended since it's doing
+ * FastNative JNI.
+ * </p>
+ *
+ * <p>
+ * Normal JNI doesn't have the issue since once it's in native code,
+ * it is considered suspended from java's point of view.
+ * FastNative JNI however doesn't do the state transition done by JNI.
+ * </p>
+ *
+ * <p>
+ * Has no effect when used with non-native methods.
+ * </p>
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.CLASS) // Save memory, don't instantiate as an object at runtime.
+@Target(ElementType.METHOD)
+public @interface FastNative {}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java
index 8ff7956..e567504 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyCharBufferTest.java
@@ -140,8 +140,8 @@
}
try {
buf.put(buf);
- fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
- } catch (ReadOnlyBufferException e) {
+ fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+ } catch (IllegalArgumentException e) {
// expected
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java
index f2f1ea4..1673c16 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyDoubleBufferTest.java
@@ -137,8 +137,8 @@
}
try {
buf.put(buf);
- fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
- } catch (ReadOnlyBufferException e) {
+ fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+ } catch (IllegalArgumentException e) {
// expected
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java
index 56a14ba..3aec858 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyFloatBufferTest.java
@@ -138,8 +138,8 @@
}
try {
buf.put(buf);
- fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
- } catch (ReadOnlyBufferException e) {
+ fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+ } catch (IllegalArgumentException e) {
// expected
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java
index e618783..f0dcad0 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyIntBufferTest.java
@@ -138,8 +138,8 @@
}
try {
buf.put(buf);
- fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
- } catch (ReadOnlyBufferException e) {
+ fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+ } catch (IllegalArgumentException e) {
// expected
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java
index fd6438e..283f4f1 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyLongBufferTest.java
@@ -138,8 +138,8 @@
}
try {
buf.put(buf);
- fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
- } catch (ReadOnlyBufferException e) {
+ fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+ } catch (IllegalArgumentException e) {
// expected
}
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java
index aab913e..8885806 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/ReadOnlyShortBufferTest.java
@@ -138,8 +138,8 @@
}
try {
buf.put(buf);
- fail("Should throw ReadOnlyBufferException"); //$NON-NLS-1$
- } catch (ReadOnlyBufferException e) {
+ fail("Should throw IllegalArgumentException"); //$NON-NLS-1$
+ } catch (IllegalArgumentException e) {
// expected
}
}
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java
new file mode 100644
index 0000000..12b82b0
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandleInfoTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.invoke;
+
+import junit.framework.TestCase;
+
+import java.lang.invoke.MethodHandleInfo;
+import java.lang.invoke.MethodType;
+
+import static java.lang.invoke.MethodHandleInfo.*;
+
+public class MethodHandleInfoTest extends TestCase {
+ public void test_toString() {
+ final MethodType type = MethodType.methodType(String.class, String.class);
+ String string = MethodHandleInfo.toString(REF_invokeVirtual, String.class, "concat", type);
+ assertEquals("invokeVirtual java.lang.String.concat:(String)String", string);
+
+ try {
+ MethodHandleInfo.toString(-1, String.class, "concat", type);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ MethodHandleInfo.toString(REF_invokeVirtual, String.class, null, type);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ MethodHandleInfo.toString(REF_invokeVirtual, null, "concat", type);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ MethodHandleInfo.toString(REF_invokeVirtual, String.class, "concat", null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void test_referenceKindToString() {
+ assertEquals("getField", referenceKindToString(REF_getField));
+ assertEquals("getStatic", referenceKindToString(REF_getStatic));
+ assertEquals("putField", referenceKindToString(REF_putField));
+ assertEquals("putStatic", referenceKindToString(REF_putStatic));
+ assertEquals("invokeVirtual", referenceKindToString(REF_invokeVirtual));
+ assertEquals("invokeStatic", referenceKindToString(REF_invokeStatic));
+ assertEquals("invokeSpecial", referenceKindToString(REF_invokeSpecial));
+ assertEquals("newInvokeSpecial", referenceKindToString(REF_newInvokeSpecial));
+ assertEquals("invokeInterface", referenceKindToString(REF_invokeInterface));
+
+ try {
+ referenceKindToString(-1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ referenceKindToString(256);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java b/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java
deleted file mode 100644
index c9cd3d1..0000000
--- a/luni/src/test/java/libcore/java/lang/reflect/AnnotationsTest.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed 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 libcore.java.lang.reflect;
-
-import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Repeatable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Proxy;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import junit.framework.TestCase;
-
-public final class AnnotationsTest extends TestCase {
-
- public void testClassDirectAnnotations() {
- assertAnnotatedElement(Type.class,
- AnnotationA.class, AnnotationB.class, RepeatableAnnotation.class);
- assertAnnotatedElementDeclared(Type.class,
- AnnotationA.class, AnnotationB.class, RepeatableAnnotation.class);
- }
-
- public void testClassInheritedAnnotations() {
- assertAnnotatedElement(ExtendsType.class, AnnotationB.class);
- assertAnnotatedElementDeclared(ExtendsType.class);
- }
-
- public void testConstructorAnnotations() throws Exception {
- Constructor<Type> constructor = Type.class.getConstructor();
- assertAnnotatedElement(constructor, AnnotationA.class, AnnotationC.class);
- }
-
- public void testFieldAnnotations() throws Exception {
- Field field = Type.class.getField("field");
- assertAnnotatedElement(field, AnnotationA.class, AnnotationD.class);
- }
-
- public void testMethodAnnotations() throws Exception {
- Method method = Type.class.getMethod("method", String.class, String.class);
- assertAnnotatedElement(method, AnnotationB.class, AnnotationC.class);
- }
-
- public void testParameterAnnotations() throws Exception {
- Method method = Type.class.getMethod("method", String.class, String.class);
- Annotation[][] noParameterAnnotations = method.getParameterAnnotations();
- assertEquals(2, noParameterAnnotations.length);
- assertEquals(set(), annotationsToTypes(noParameterAnnotations[0]));
- assertEquals(set(), annotationsToTypes(noParameterAnnotations[1]));
-
- Method parameters = Type.class.getMethod("parameters", String.class, String.class);
- Annotation[][] parameterAnnotations = parameters.getParameterAnnotations();
- assertEquals(2, parameterAnnotations.length);
- assertEquals(set(AnnotationB.class, AnnotationD.class),
- annotationsToTypes(parameterAnnotations[0]));
- assertEquals(set(AnnotationC.class, AnnotationD.class),
- annotationsToTypes(parameterAnnotations[1]));
- }
-
- public void testAnnotationDefaults() throws Exception {
- assertEquals((byte) 5, defaultValue("a"));
- assertEquals((short) 6, defaultValue("b"));
- assertEquals(7, defaultValue("c"));
- assertEquals(8L, defaultValue("d"));
- assertEquals(9.0f, defaultValue("e"));
- assertEquals(10.0, defaultValue("f"));
- assertEquals('k', defaultValue("g"));
- assertEquals(true, defaultValue("h"));
- assertEquals(Breakfast.WAFFLES, defaultValue("i"));
- assertEquals("@" + AnnotationA.class.getName() + "()", defaultValue("j").toString());
- assertEquals("maple", defaultValue("k"));
- assertEquals(AnnotationB.class, defaultValue("l"));
- assertEquals("[1, 2, 3]", Arrays.toString((int[]) defaultValue("m")));
- assertEquals("[WAFFLES, PANCAKES]", Arrays.toString((Breakfast[]) defaultValue("n")));
- assertEquals(null, defaultValue("o"));
- assertEquals(null, defaultValue("p"));
- }
-
- private Object defaultValue(String name) throws NoSuchMethodException {
- return HasDefaultsAnnotation.class.getMethod(name).getDefaultValue();
- }
-
- public void testGetEnclosingClass() {
- assertNull(AnnotationsTest.class.getEnclosingClass());
- assertEquals(AnnotationsTest.class, Foo.class.getEnclosingClass());
- assertEquals(AnnotationsTest.class, HasMemberClassesInterface.class.getEnclosingClass());
- assertEquals(HasMemberClassesInterface.class,
- HasMemberClassesInterface.D.class.getEnclosingClass());
- assertEquals(AnnotationsTest.class, Foo.class.getEnclosingClass());
- }
-
- public void testGetDeclaringClass() {
- assertNull(AnnotationsTest.class.getDeclaringClass());
- assertEquals(AnnotationsTest.class, Foo.class.getDeclaringClass());
- assertEquals(AnnotationsTest.class, HasMemberClassesInterface.class.getDeclaringClass());
- assertEquals(HasMemberClassesInterface.class,
- HasMemberClassesInterface.D.class.getDeclaringClass());
- }
-
- public void testGetEnclosingClassIsTransitiveForClassesDefinedInAMethod() {
- class C {}
- assertEquals(AnnotationsTest.class, C.class.getEnclosingClass());
- }
-
- public void testGetDeclaringClassIsNotTransitiveForClassesDefinedInAMethod() {
- class C {}
- assertEquals(null, C.class.getDeclaringClass());
- }
-
- public void testGetEnclosingMethodIsNotTransitive() {
- class C {
- class D {}
- }
- assertEquals(null, C.D.class.getEnclosingMethod());
- }
-
- public void testStaticFieldAnonymousClass() {
- // The class declared in the <clinit> is enclosed by the <clinit>'s class.
- // http://b/11245138
- assertEquals(AnnotationsTest.class, staticAnonymous.getClass().getEnclosingClass());
- // However, because it is anonymous, it has no declaring class.
- // https://code.google.com/p/android/issues/detail?id=61003
- assertNull(staticAnonymous.getClass().getDeclaringClass());
- // Because the class is declared in <clinit> which is not exposed through reflection,
- // it has no enclosing method or constructor.
- assertNull(staticAnonymous.getClass().getEnclosingMethod());
- assertNull(staticAnonymous.getClass().getEnclosingConstructor());
- }
-
- public void testGetEnclosingMethodOfTopLevelClass() {
- assertNull(AnnotationsTest.class.getEnclosingMethod());
- }
-
- public void testGetEnclosingConstructorOfTopLevelClass() {
- assertNull(AnnotationsTest.class.getEnclosingConstructor());
- }
-
- public void testClassEnclosedByConstructor() throws Exception {
- Foo foo = new Foo("string");
- assertEquals(Foo.class, foo.c.getEnclosingClass());
- assertEquals(Foo.class.getDeclaredConstructor(String.class),
- foo.c.getEnclosingConstructor());
- assertNull(foo.c.getEnclosingMethod());
- assertNull(foo.c.getDeclaringClass());
- }
-
- public void testClassEnclosedByMethod() throws Exception {
- Foo foo = new Foo();
- foo.foo("string");
- assertEquals(Foo.class, foo.c.getEnclosingClass());
- assertNull(foo.c.getEnclosingConstructor());
- assertEquals(Foo.class.getDeclaredMethod("foo", String.class),
- foo.c.getEnclosingMethod());
- assertNull(foo.c.getDeclaringClass());
- }
-
- public void testGetClasses() throws Exception {
- // getClasses() doesn't include classes inherited from interfaces!
- assertSetEquals(HasMemberClasses.class.getClasses(),
- HasMemberClassesSuperclass.B.class, HasMemberClasses.H.class);
- }
-
- public void testGetDeclaredClasses() throws Exception {
- assertSetEquals(HasMemberClasses.class.getDeclaredClasses(),
- HasMemberClasses.G.class, HasMemberClasses.H.class, HasMemberClasses.I.class,
- HasMemberClasses.J.class, HasMemberClasses.K.class, HasMemberClasses.L.class);
- }
-
- public void testConstructorGetExceptions() throws Exception {
- assertSetEquals(HasThrows.class.getConstructor().getExceptionTypes(),
- IOException.class, InvocationTargetException.class, IllegalStateException.class);
- assertSetEquals(HasThrows.class.getConstructor(Void.class).getExceptionTypes());
- }
-
- public void testClassMethodGetExceptions() throws Exception {
- assertSetEquals(HasThrows.class.getMethod("foo").getExceptionTypes(),
- IOException.class, InvocationTargetException.class, IllegalStateException.class);
- assertSetEquals(HasThrows.class.getMethod("foo", Void.class).getExceptionTypes());
- }
-
- public void testProxyMethodGetExceptions() throws Exception {
- InvocationHandler emptyInvocationHandler = new InvocationHandler() {
- @Override public Object invoke(Object proxy, Method method, Object[] args) {
- return null;
- }
- };
-
- Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(),
- new Class[] { ThrowsInterface.class }, emptyInvocationHandler);
- assertSetEquals(proxy.getClass().getMethod("foo").getExceptionTypes(),
- IOException.class, InvocationTargetException.class, IllegalStateException.class);
- assertSetEquals(proxy.getClass().getMethod("foo", Void.class).getExceptionTypes());
- }
-
- public void testClassModifiers() {
- int modifiers = AnnotationsTest.class.getModifiers();
- assertTrue(Modifier.isPublic(modifiers));
- assertFalse(Modifier.isProtected(modifiers));
- assertFalse(Modifier.isPrivate(modifiers));
- assertFalse(Modifier.isAbstract(modifiers));
- assertFalse(Modifier.isStatic(modifiers));
- assertTrue(Modifier.isFinal(modifiers));
- assertFalse(Modifier.isStrict(modifiers));
- }
-
- public void testInnerClassModifiers() {
- int modifiers = Foo.class.getModifiers();
- assertFalse(Modifier.isPublic(modifiers));
- assertFalse(Modifier.isProtected(modifiers));
- assertTrue(Modifier.isPrivate(modifiers));
- assertFalse(Modifier.isAbstract(modifiers));
- assertTrue(Modifier.isStatic(modifiers));
- assertFalse(Modifier.isFinal(modifiers));
- assertFalse(Modifier.isStrict(modifiers));
- }
-
- public void testAnonymousClassModifiers() {
- int modifiers = staticAnonymous.getClass().getModifiers();
- assertFalse(Modifier.isPublic(modifiers));
- assertFalse(Modifier.isProtected(modifiers));
- assertFalse(Modifier.isPrivate(modifiers));
- assertFalse(Modifier.isAbstract(modifiers));
- assertTrue(Modifier.isStatic(modifiers));
- assertFalse(Modifier.isFinal(modifiers));
- assertFalse(Modifier.isStrict(modifiers));
- }
-
- public void testInnerClassName() {
- assertEquals("AnnotationsTest", AnnotationsTest.class.getSimpleName());
- assertEquals("Foo", Foo.class.getSimpleName());
- assertEquals("", staticAnonymous.getClass().getSimpleName());
- }
-
- public void testIsAnonymousClass() {
- assertFalse(AnnotationsTest.class.isAnonymousClass());
- assertFalse(Foo.class.isAnonymousClass());
- assertTrue(staticAnonymous.getClass().isAnonymousClass());
- }
-
- public void testRepeatableAnnotation() {
- RepeatableAnnotation[] annotations = TypeWithMultipleRepeatableAnnotations.class
- .getDeclaredAnnotationsByType(RepeatableAnnotation.class);
- assertNotNull(annotations);
- assertEquals(2, annotations.length);
-
- // The non-"WithType" methods will see the wrapper annotation
- assertAnnotatedElement(TypeWithMultipleRepeatableAnnotations.class,
- RepeatableAnnotations.class);
- assertAnnotatedElementDeclared(TypeWithMultipleRepeatableAnnotations.class,
- RepeatableAnnotations.class);
- }
-
- public void testRepeatableAnnotationExplicit() {
- RepeatableAnnotation[] annotations = TypeWithExplicitRepeatableAnnotations.class
- .getDeclaredAnnotationsByType(RepeatableAnnotation.class);
- assertNotNull(annotations);
- assertEquals(2, annotations.length);
-
- // The non-"WithType" methods will see the wrapper annotation
- assertAnnotatedElement(TypeWithExplicitRepeatableAnnotations.class,
- RepeatableAnnotations.class);
- assertAnnotatedElementDeclared(TypeWithExplicitRepeatableAnnotations.class,
- RepeatableAnnotations.class);
- }
-
- public void testRepeatableAnnotationOnPackage() {
- Package aPackage = AnnotationsTest.class.getPackage();
- RepeatableAnnotation[] annotations = aPackage
- .getDeclaredAnnotationsByType(RepeatableAnnotation.class);
- assertNotNull(annotations);
- assertEquals(2, annotations.length);
-
- // The non-"WithType" methods will see the wrapper annotation
- assertPresent(true, aPackage, RepeatableAnnotations.class);
- assertDeclared(true, aPackage, RepeatableAnnotations.class);
- }
-
- public void testRetentionPolicy() {
- assertNull(RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class));
- assertNotNull(RetentionAnnotations.class.getAnnotation(RuntimeRetentionAnnotation.class));
- assertNull(RetentionAnnotations.class.getAnnotation(SourceRetentionAnnotation.class));
- }
-
- private static final Object staticAnonymous = new Object() {};
-
- private static class Foo {
- Class<?> c;
- private Foo() {
- }
- private Foo(String s) {
- c = new Object() {}.getClass();
- }
- private Foo(int i) {
- c = new Object() {}.getClass();
- }
- private void foo(String s) {
- c = new Object() {}.getClass();
- }
- private void foo(int i) {
- c = new Object() {}.getClass();
- }
- }
-
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AnnotationA {}
-
- @Inherited
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AnnotationB {}
-
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AnnotationC {}
-
- @Retention(RetentionPolicy.RUNTIME)
- public @interface AnnotationD {}
-
- @Retention(RetentionPolicy.RUNTIME)
- @Repeatable(RepeatableAnnotations.class)
- public @interface RepeatableAnnotation {}
-
- @Retention(RetentionPolicy.RUNTIME)
- public @interface RepeatableAnnotations {
- RepeatableAnnotation[] value();
- }
-
- @Retention(RetentionPolicy.CLASS)
- public @interface ClassRetentionAnnotation {}
-
- @Retention(RetentionPolicy.RUNTIME)
- public @interface RuntimeRetentionAnnotation {}
-
- @Retention(RetentionPolicy.SOURCE)
- public @interface SourceRetentionAnnotation {}
-
- @AnnotationA @AnnotationB @RepeatableAnnotation
- public static class Type {
- @AnnotationA @AnnotationC public Type() {}
- @AnnotationA @AnnotationD public String field;
- @AnnotationB @AnnotationC public void method(String parameter1, String parameter2) {}
- @AnnotationB @AnnotationC public void parameters(@AnnotationB @AnnotationD String parameter1,
- @AnnotationC @AnnotationD String parameter2) {}
- }
-
- public static class ExtendsType extends Type {}
-
- @RepeatableAnnotation
- @RepeatableAnnotation
- public static class TypeWithMultipleRepeatableAnnotations {}
-
- @RepeatableAnnotations({ @RepeatableAnnotation, @RepeatableAnnotation})
- public static class TypeWithExplicitRepeatableAnnotations {}
-
- @ClassRetentionAnnotation @RuntimeRetentionAnnotation @SourceRetentionAnnotation
- public static class RetentionAnnotations {}
-
- static enum Breakfast { WAFFLES, PANCAKES }
-
- @Retention(RetentionPolicy.RUNTIME)
- public @interface HasDefaultsAnnotation {
- byte a() default 5;
- short b() default 6;
- int c() default 7;
- long d() default 8;
- float e() default 9.0f;
- double f() default 10.0;
- char g() default 'k';
- boolean h() default true;
- Breakfast i() default Breakfast.WAFFLES;
- AnnotationA j() default @AnnotationA();
- String k() default "maple";
- Class l() default AnnotationB.class;
- int[] m() default { 1, 2, 3 };
- Breakfast[] n() default { Breakfast.WAFFLES, Breakfast.PANCAKES };
- Breakfast o();
- int p();
- }
-
- static class HasMemberClassesSuperclass {
- class A {}
- public class B {}
- static class C {}
- }
-
- public interface HasMemberClassesInterface {
- class D {}
- public class E {}
- static class F {}
- }
-
- public static class HasMemberClasses extends HasMemberClassesSuperclass
- implements HasMemberClassesInterface {
- class G {}
- public class H {}
- static class I {}
- enum J {}
- interface K {}
- @interface L {}
- }
-
- public static class HasThrows {
- public HasThrows() throws IOException, InvocationTargetException, IllegalStateException {}
- public HasThrows(Void v) {}
- public void foo() throws IOException, InvocationTargetException, IllegalStateException {}
- public void foo(Void v) {}
- }
-
- public static interface ThrowsInterface {
- void foo() throws IOException, InvocationTargetException, IllegalStateException;
- void foo(Void v);
- }
-
- private void assertAnnotatedElement(
- AnnotatedElement element, Class<? extends Annotation>... expectedAnnotations) {
- Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getAnnotations());
- Set<Class<? extends Annotation>> expectedTypes = set(expectedAnnotations);
- assertEquals(expectedTypes, actualTypes);
-
- // getAnnotations() should be consistent with isAnnotationPresent() and getAnnotation()
- assertPresent(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
- assertPresent(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
- assertPresent(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
- assertPresent(expectedTypes.contains(RepeatableAnnotation.class),
- element, RepeatableAnnotation.class);
-
- try {
- element.isAnnotationPresent(null);
- fail();
- } catch (NullPointerException expected) {
- }
-
- try {
- element.getAnnotation(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- private void assertAnnotatedElementDeclared(
- AnnotatedElement element,
- Class<? extends Annotation>... expectedDeclaredAnnotations) {
- Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getDeclaredAnnotations());
- Set<Class<? extends Annotation>> expectedTypes = set(expectedDeclaredAnnotations);
- assertEquals(expectedTypes, actualTypes);
-
- assertDeclared(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
- assertDeclared(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
- assertDeclared(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
- assertDeclared(expectedTypes.contains(RepeatableAnnotation.class),
- element, RepeatableAnnotation.class);
-
- try {
- element.getDeclaredAnnotation(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- private Set<Class<? extends Annotation>> annotationsToTypes(Annotation[] annotations) {
- Set<Class<? extends Annotation>> result = new HashSet<Class<? extends Annotation>>();
- for (Annotation annotation : annotations) {
- result.add(annotation.annotationType());
- }
- return result;
- }
-
- private void assertPresent(boolean present, AnnotatedElement element,
- Class<? extends Annotation> annotation) {
- if (present) {
- assertNotNull(element.getAnnotation(annotation));
- assertTrue(element.isAnnotationPresent(annotation));
- } else {
- assertNull(element.getAnnotation(annotation));
- assertFalse(element.isAnnotationPresent(annotation));
- }
- }
-
- private void assertDeclared(boolean present, AnnotatedElement element,
- Class<? extends Annotation> annotation) {
- if (present) {
- assertNotNull(element.getDeclaredAnnotation(annotation));
- } else {
- assertNull(element.getDeclaredAnnotation(annotation));
- }
- }
-
- private <T> Set<T> set(T... instances) {
- return new HashSet<T>(Arrays.asList(instances));
- }
-
- private void assertSetEquals(Object[] actual, Object... expected) {
- Set<Object> actualSet = new HashSet<Object>(Arrays.asList(actual));
- Set<Object> expectedSet = new HashSet<Object>(Arrays.asList(expected));
- assertEquals(expectedSet, actualSet);
- }
-}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
index 1950bf3..f690295 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/ReflectionTest.java
@@ -16,22 +16,28 @@
package libcore.java.lang.reflect;
+import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.RandomAccess;
import java.util.Set;
-import junit.framework.Assert;
import junit.framework.TestCase;
public final class ReflectionTest extends TestCase {
@@ -430,4 +436,215 @@
void foobar() {}
}
}
+
+ public void testGetEnclosingClass() {
+ assertNull(ReflectionTest.class.getEnclosingClass());
+ assertEquals(ReflectionTest.class, Foo.class.getEnclosingClass());
+ assertEquals(ReflectionTest.class, HasMemberClassesInterface.class.getEnclosingClass());
+ assertEquals(HasMemberClassesInterface.class,
+ HasMemberClassesInterface.D.class.getEnclosingClass());
+ assertEquals(ReflectionTest.class, Foo.class.getEnclosingClass());
+ }
+
+ public void testGetDeclaringClass() {
+ assertNull(ReflectionTest.class.getDeclaringClass());
+ assertEquals(ReflectionTest.class, Foo.class.getDeclaringClass());
+ assertEquals(ReflectionTest.class, HasMemberClassesInterface.class.getDeclaringClass());
+ assertEquals(HasMemberClassesInterface.class,
+ HasMemberClassesInterface.D.class.getDeclaringClass());
+ }
+
+ public void testGetEnclosingClassIsTransitiveForClassesDefinedInAMethod() {
+ class C {}
+ assertEquals(ReflectionTest.class, C.class.getEnclosingClass());
+ }
+
+ public void testGetDeclaringClassIsNotTransitiveForClassesDefinedInAMethod() {
+ class C {}
+ assertEquals(null, C.class.getDeclaringClass());
+ }
+
+ public void testGetEnclosingMethodIsNotTransitive() {
+ class C {
+ class D {}
+ }
+ assertEquals(null, C.D.class.getEnclosingMethod());
+ }
+
+ private static final Object staticAnonymous = new Object() {};
+
+ public void testStaticFieldAnonymousClass() {
+ // The class declared in the <clinit> is enclosed by the <clinit>'s class.
+ // http://b/11245138
+ assertEquals(ReflectionTest.class, staticAnonymous.getClass().getEnclosingClass());
+ // However, because it is anonymous, it has no declaring class.
+ // https://code.google.com/p/android/issues/detail?id=61003
+ assertNull(staticAnonymous.getClass().getDeclaringClass());
+ // Because the class is declared in <clinit> which is not exposed through reflection,
+ // it has no enclosing method or constructor.
+ assertNull(staticAnonymous.getClass().getEnclosingMethod());
+ assertNull(staticAnonymous.getClass().getEnclosingConstructor());
+ }
+
+ public void testGetEnclosingMethodOfTopLevelClass() {
+ assertNull(ReflectionTest.class.getEnclosingMethod());
+ }
+
+ public void testGetEnclosingConstructorOfTopLevelClass() {
+ assertNull(ReflectionTest.class.getEnclosingConstructor());
+ }
+
+ public void testClassEnclosedByConstructor() throws Exception {
+ Foo foo = new Foo("string");
+ assertEquals(Foo.class, foo.c.getEnclosingClass());
+ assertEquals(Foo.class.getDeclaredConstructor(String.class),
+ foo.c.getEnclosingConstructor());
+ assertNull(foo.c.getEnclosingMethod());
+ assertNull(foo.c.getDeclaringClass());
+ }
+
+ public void testClassEnclosedByMethod() throws Exception {
+ Foo foo = new Foo();
+ foo.foo("string");
+ assertEquals(Foo.class, foo.c.getEnclosingClass());
+ assertNull(foo.c.getEnclosingConstructor());
+ assertEquals(Foo.class.getDeclaredMethod("foo", String.class),
+ foo.c.getEnclosingMethod());
+ assertNull(foo.c.getDeclaringClass());
+ }
+
+ public void testGetClasses() throws Exception {
+ // getClasses() doesn't include classes inherited from interfaces!
+ assertSetEquals(HasMemberClasses.class.getClasses(),
+ HasMemberClassesSuperclass.B.class, HasMemberClasses.H.class);
+ }
+
+ public void testGetDeclaredClasses() throws Exception {
+ assertSetEquals(HasMemberClasses.class.getDeclaredClasses(),
+ HasMemberClasses.G.class, HasMemberClasses.H.class, HasMemberClasses.I.class,
+ HasMemberClasses.J.class, HasMemberClasses.K.class, HasMemberClasses.L.class);
+ }
+
+ public void testConstructorGetExceptions() throws Exception {
+ assertSetEquals(HasThrows.class.getConstructor().getExceptionTypes(),
+ IOException.class, InvocationTargetException.class, IllegalStateException.class);
+ assertSetEquals(HasThrows.class.getConstructor(Void.class).getExceptionTypes());
+ }
+
+ public void testClassMethodGetExceptions() throws Exception {
+ assertSetEquals(HasThrows.class.getMethod("foo").getExceptionTypes(),
+ IOException.class, InvocationTargetException.class, IllegalStateException.class);
+ assertSetEquals(HasThrows.class.getMethod("foo", Void.class).getExceptionTypes());
+ }
+
+ public void testProxyMethodGetExceptions() throws Exception {
+ InvocationHandler emptyInvocationHandler = new InvocationHandler() {
+ @Override public Object invoke(Object proxy, Method method, Object[] args) {
+ return null;
+ }
+ };
+
+ Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class[] { ThrowsInterface.class }, emptyInvocationHandler);
+ assertSetEquals(proxy.getClass().getMethod("foo").getExceptionTypes(),
+ IOException.class, InvocationTargetException.class, IllegalStateException.class);
+ assertSetEquals(proxy.getClass().getMethod("foo", Void.class).getExceptionTypes());
+ }
+
+ public void testClassModifiers() {
+ int modifiers = ReflectionTest.class.getModifiers();
+ assertTrue(Modifier.isPublic(modifiers));
+ assertFalse(Modifier.isProtected(modifiers));
+ assertFalse(Modifier.isPrivate(modifiers));
+ assertFalse(Modifier.isAbstract(modifiers));
+ assertFalse(Modifier.isStatic(modifiers));
+ assertTrue(Modifier.isFinal(modifiers));
+ assertFalse(Modifier.isStrict(modifiers));
+ }
+
+ public void testInnerClassModifiers() {
+ int modifiers = Foo.class.getModifiers();
+ assertFalse(Modifier.isPublic(modifiers));
+ assertFalse(Modifier.isProtected(modifiers));
+ assertTrue(Modifier.isPrivate(modifiers));
+ assertFalse(Modifier.isAbstract(modifiers));
+ assertTrue(Modifier.isStatic(modifiers));
+ assertFalse(Modifier.isFinal(modifiers));
+ assertFalse(Modifier.isStrict(modifiers));
+ }
+
+ public void testAnonymousClassModifiers() {
+ int modifiers = staticAnonymous.getClass().getModifiers();
+ assertFalse(Modifier.isPublic(modifiers));
+ assertFalse(Modifier.isProtected(modifiers));
+ assertFalse(Modifier.isPrivate(modifiers));
+ assertFalse(Modifier.isAbstract(modifiers));
+ assertTrue(Modifier.isStatic(modifiers));
+ assertFalse(Modifier.isFinal(modifiers));
+ assertFalse(Modifier.isStrict(modifiers));
+ }
+
+ public void testInnerClassName() {
+ assertEquals("ReflectionTest", ReflectionTest.class.getSimpleName());
+ assertEquals("Foo", Foo.class.getSimpleName());
+ assertEquals("", staticAnonymous.getClass().getSimpleName());
+ }
+
+ private static class Foo {
+ Class<?> c;
+ private Foo() {
+ }
+ private Foo(String s) {
+ c = new Object() {}.getClass();
+ }
+ private Foo(int i) {
+ c = new Object() {}.getClass();
+ }
+ private void foo(String s) {
+ c = new Object() {}.getClass();
+ }
+ private void foo(int i) {
+ c = new Object() {}.getClass();
+ }
+ }
+
+ static class HasMemberClassesSuperclass {
+ class A {}
+ public class B {}
+ static class C {}
+ }
+
+ public interface HasMemberClassesInterface {
+ class D {}
+ public class E {}
+ static class F {}
+ }
+
+ public static class HasMemberClasses extends HasMemberClassesSuperclass
+ implements HasMemberClassesInterface {
+ class G {}
+ public class H {}
+ static class I {}
+ enum J {}
+ interface K {}
+ @interface L {}
+ }
+
+ public static class HasThrows {
+ public HasThrows() throws IOException, InvocationTargetException, IllegalStateException {}
+ public HasThrows(Void v) {}
+ public void foo() throws IOException, InvocationTargetException, IllegalStateException {}
+ public void foo(Void v) {}
+ }
+
+ public static interface ThrowsInterface {
+ void foo() throws IOException, InvocationTargetException, IllegalStateException;
+ void foo(Void v);
+ }
+
+ private void assertSetEquals(Object[] actual, Object... expected) {
+ Set<Object> actualSet = new HashSet<Object>(Arrays.asList(actual));
+ Set<Object> expectedSet = new HashSet<Object>(Arrays.asList(expected));
+ assertEquals(expectedSet, actualSet);
+ }
}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java
new file mode 100644
index 0000000..ec57469
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+/**
+ * Utility methods and annotation definitions for use when testing implementations of
+ * AnnotatedElement.
+ *
+ * <p>For compactness, the repeated annotation methods that take strings use a format based on Java
+ * syntax rather than the toString() of annotations. For example, "@Repeated(1)" rather than
+ * "@libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated(value=1)". Use
+ * {@link #EXPECT_EMPTY} to indicate "no annotationed expected".
+ */
+public class AnnotatedElementTestSupport {
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface AnnotationA {}
+
+ @Inherited
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface AnnotationB {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface AnnotationC {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface AnnotationD {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD,
+ ElementType.PARAMETER, ElementType.PACKAGE })
+ public @interface Container {
+ Repeated[] value();
+ }
+
+ @Repeatable(Container.class)
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD,
+ ElementType.PARAMETER, ElementType.PACKAGE })
+ public @interface Repeated {
+ int value();
+ }
+
+ /**
+ * A named constant that can be used with assert methods below that take
+ * "String[] expectedAnnotationStrings" as their final argument to indicate "none".
+ */
+ public static final String[] EXPECT_EMPTY = new String[0];
+
+ private AnnotatedElementTestSupport() {
+ }
+
+ /**
+ * Test the {@link AnnotatedElement} methods associated with "presence". i.e. methods that
+ * deal with annotations being "present" (i.e. "direct" or "inherited" annotations, not
+ * "indirect").
+ *
+ * <p>Asserts that calling {@link AnnotatedElement#getAnnotations()} on the supplied element
+ * returns annotations of the supplied expected classes.
+ *
+ * <p>Where the expected classes contains some subset from
+ * {@link AnnotationA}, {@link AnnotationB} and {@link AnnotationC}, this method also asserts
+ * that {@link AnnotatedElement#isAnnotationPresent(Class)} and
+ * {@link AnnotatedElement#getAnnotation(Class)} works as expected.
+ *
+ * <p>This method also confirms that {@link AnnotatedElement#isAnnotationPresent(Class)} and
+ * {@link AnnotatedElement#getAnnotation(Class)} work correctly with a {@code null} argument.
+ */
+ static void checkAnnotatedElementPresentMethods(
+ AnnotatedElement element, Class<? extends Annotation>... expectedAnnotations) {
+ Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getAnnotations());
+ Set<Class<? extends Annotation>> expectedTypes = set(expectedAnnotations);
+ assertEquals(expectedTypes, actualTypes);
+
+ // getAnnotations() should be consistent with isAnnotationPresent() and getAnnotation()
+ assertPresent(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
+ assertPresent(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
+ assertPresent(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
+
+ try {
+ element.isAnnotationPresent(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ element.getAnnotation(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ /**
+ * Test the {@link AnnotatedElement} methods associated with "direct" annotations.
+ *
+ * <p>Asserts that calling {@link AnnotatedElement#getDeclaredAnnotations()} on the supplied
+ * element returns annotations of the supplied expected classes.
+ *
+ * <p>Where the expected classes contains some subset from
+ * {@link AnnotationA}, {@link AnnotationB} and {@link AnnotationC}, this method also asserts
+ * that {@link AnnotatedElement#getDeclaredAnnotation(Class)} works as expected.
+ *
+ * <p>This method also confirms that {@link AnnotatedElement#isAnnotationPresent(Class)} and
+ * {@link AnnotatedElement#getAnnotation(Class)} work correctly with a {@code null} argument.
+ */
+ static void checkAnnotatedElementDirectMethods(
+ AnnotatedElement element,
+ Class<? extends Annotation>... expectedDeclaredAnnotations) {
+ Set<Class<? extends Annotation>> actualTypes = annotationsToTypes(element.getDeclaredAnnotations());
+ Set<Class<? extends Annotation>> expectedTypes = set(expectedDeclaredAnnotations);
+ assertEquals(expectedTypes, actualTypes);
+
+ assertDeclared(expectedTypes.contains(AnnotationA.class), element, AnnotationA.class);
+ assertDeclared(expectedTypes.contains(AnnotationB.class), element, AnnotationB.class);
+ assertDeclared(expectedTypes.contains(AnnotationC.class), element, AnnotationC.class);
+
+ try {
+ element.getDeclaredAnnotation(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ /**
+ * Extracts the annotation types ({@link Annotation#annotationType()} from the supplied
+ * annotations.
+ */
+ static Set<Class<? extends Annotation>> annotationsToTypes(Annotation[] annotations) {
+ Set<Class<? extends Annotation>> result = new HashSet<Class<? extends Annotation>>();
+ for (Annotation annotation : annotations) {
+ result.add(annotation.annotationType());
+ }
+ return result;
+ }
+
+ private static void assertPresent(boolean present, AnnotatedElement element,
+ Class<? extends Annotation> annotation) {
+ if (present) {
+ assertNotNull(element.getAnnotation(annotation));
+ assertTrue(element.isAnnotationPresent(annotation));
+ } else {
+ assertNull(element.getAnnotation(annotation));
+ assertFalse(element.isAnnotationPresent(annotation));
+ }
+ }
+
+ private static void assertDeclared(boolean present, AnnotatedElement element,
+ Class<? extends Annotation> annotation) {
+ if (present) {
+ assertNotNull(element.getDeclaredAnnotation(annotation));
+ } else {
+ assertNull(element.getDeclaredAnnotation(annotation));
+ }
+ }
+
+ @SafeVarargs
+ static <T> Set<T> set(T... instances) {
+ return new HashSet<>(Arrays.asList(instances));
+ }
+
+ /**
+ * Asserts that {@link AnnotatedElement#isAnnotationPresent(Class)} returns the expected result.
+ */
+ static void assertIsAnnotationPresent(
+ AnnotatedElement element, Class<? extends Annotation> annotationType,
+ boolean expected) {
+ assertEquals("element.isAnnotationPresent() for " + element + " and " + annotationType,
+ expected, element.isAnnotationPresent(annotationType));
+ }
+
+ /**
+ * Asserts that {@link AnnotatedElement#getDeclaredAnnotation(Class)} returns the expected
+ * result. The result is specified using a String. See {@link AnnotatedElementTestSupport} for
+ * the string syntax.
+ */
+ static void assertGetDeclaredAnnotation(AnnotatedElement annotatedElement,
+ Class<? extends Annotation> annotationType, String expectedAnnotationString) {
+ Annotation annotation = annotatedElement.getDeclaredAnnotation(annotationType);
+ assertAnnotationMatches(annotation, expectedAnnotationString);
+ }
+
+ /**
+ * Asserts that {@link AnnotatedElement#getDeclaredAnnotationsByType(Class)} returns the
+ * expected result. The result is specified using a String. See
+ * {@link AnnotatedElementTestSupport} for the string syntax.
+ */
+ static void assertGetDeclaredAnnotationsByType(
+ AnnotatedElement annotatedElement, Class<? extends Annotation> annotationType,
+ String[] expectedAnnotationStrings) {
+ Annotation[] annotations = annotatedElement.getDeclaredAnnotationsByType(annotationType);
+ assertAnnotationsMatch(annotations, expectedAnnotationStrings);
+ }
+
+ /**
+ * Asserts that {@link AnnotatedElement#getAnnotationsByType(Class)} returns the
+ * expected result. The result is specified using a String. See
+ * {@link AnnotatedElementTestSupport} for the string syntax.
+ */
+ static void assertGetAnnotationsByType(AnnotatedElement annotatedElement,
+ Class<? extends Annotation> annotationType, String[] expectedAnnotationStrings)
+ throws Exception {
+ Annotation[] annotations = annotatedElement.getAnnotationsByType(annotationType);
+ assertAnnotationsMatch(annotations, expectedAnnotationStrings);
+ }
+
+ private static void assertAnnotationMatches(
+ Annotation annotation, String expectedAnnotationString) {
+ if (expectedAnnotationString == null) {
+ assertNull(annotation);
+ } else {
+ assertNotNull(annotation);
+ assertEquals(expectedAnnotationString, createAnnotationTestString(annotation));
+ }
+ }
+
+ /**
+ * Asserts that the supplied annotations match the expectation Strings. See
+ * {@link AnnotatedElementTestSupport} for the string syntax.
+ */
+ static void assertAnnotationsMatch(Annotation[] annotations,
+ String[] expectedAnnotationStrings) {
+
+ // Due to Android's dex format insisting that Annotations are sorted by name the ordering of
+ // annotations is determined by the (simple?) name of the Annotation, not just the order
+ // that they are defined in the source. Tests have to be sensitive to that when handling
+ // mixed usage of "Container" and "Repeated" - the "Container" annotations will be
+ // discovered before "Repeated" due to their sort ordering.
+ //
+ // This code assumes that repeated annotations with the same name will be specified in the
+ // source their natural sort order when attributes are considered, just to make the testing
+ // simpler.
+ // e.g. @Repeated(1) @Repeated(2), never @Repeated(2) @Repeated(1)
+
+ // Sorting the expected and actual strings _should_ work providing the assumptions above
+ // hold. It may mask random ordering issues but it's harder to deal with that while the
+ // source ordering is no observed. Providing no developers are ascribing meaning to the
+ // relative order of annotations things should be ok.
+ Arrays.sort(expectedAnnotationStrings);
+
+ String[] actualAnnotationStrings = createAnnotationTestStrings(annotations);
+ Arrays.sort(actualAnnotationStrings);
+
+ assertEquals(
+ Arrays.asList(expectedAnnotationStrings),
+ Arrays.asList(actualAnnotationStrings));
+ }
+
+ private static String[] createAnnotationTestStrings(Annotation[] annotations) {
+ String[] annotationStrings = new String[annotations.length];
+ for (int i = 0; i < annotations.length; i++) {
+ annotationStrings[i] = createAnnotationTestString(annotations[i]);
+ }
+ return annotationStrings;
+ }
+
+ private static String createAnnotationTestString(Annotation annotation) {
+ return "@" + annotation.annotationType().getSimpleName() + createArgumentsTestString(
+ annotation);
+ }
+
+ private static String createArgumentsTestString(Annotation annotation) {
+ if (annotation instanceof Repeated) {
+ Repeated repeated = (Repeated) annotation;
+ return "(" + repeated.value() + ")";
+ } else if (annotation instanceof Container) {
+ Container container = (Container) annotation;
+ String[] repeatedValues = createAnnotationTestStrings(container.value());
+ StringJoiner joiner = new StringJoiner(", ", "{", "}");
+ for (String repeatedValue : repeatedValues) {
+ joiner.add(repeatedValue);
+ }
+ String repeatedValuesString = joiner.toString();
+ return "(" + repeatedValuesString + ")";
+ }
+ throw new AssertionError("Unknown annotation: " + annotation);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java
similarity index 99%
rename from luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java
rename to luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java
index 60e294b..94c265e 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/Annotations57649Test.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/Annotations57649Test.java
@@ -1,4 +1,4 @@
-package libcore.java.lang.reflect;
+package libcore.java.lang.reflect.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java
new file mode 100644
index 0000000..a694981
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotationsTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+
+import dalvik.system.VMRuntime;
+
+/**
+ * Tests for the behavior of Annotation instances at runtime.
+ */
+public class AnnotationsTest extends TestCase {
+
+ enum Breakfast { WAFFLES, PANCAKES }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface HasDefaultsAnnotation {
+ byte a() default 5;
+ short b() default 6;
+ int c() default 7;
+ long d() default 8;
+ float e() default 9.0f;
+ double f() default 10.0;
+ char g() default 'k';
+ boolean h() default true;
+ Breakfast i() default Breakfast.WAFFLES;
+ AnnotationA j() default @AnnotationA();
+ String k() default "maple";
+ Class l() default AnnotationB.class;
+ int[] m() default { 1, 2, 3 };
+ Breakfast[] n() default { Breakfast.WAFFLES, Breakfast.PANCAKES };
+ Breakfast o();
+ int p();
+ }
+
+ public void testAnnotationDefaults() throws Exception {
+ assertEquals((byte) 5, defaultValue("a"));
+ assertEquals((short) 6, defaultValue("b"));
+ assertEquals(7, defaultValue("c"));
+ assertEquals(8L, defaultValue("d"));
+ assertEquals(9.0f, defaultValue("e"));
+ assertEquals(10.0, defaultValue("f"));
+ assertEquals('k', defaultValue("g"));
+ assertEquals(true, defaultValue("h"));
+ assertEquals(Breakfast.WAFFLES, defaultValue("i"));
+ assertEquals("@" + AnnotationA.class.getName() + "()", defaultValue("j").toString());
+ assertEquals("maple", defaultValue("k"));
+ assertEquals(AnnotationB.class, defaultValue("l"));
+ assertEquals("[1, 2, 3]", Arrays.toString((int[]) defaultValue("m")));
+ assertEquals("[WAFFLES, PANCAKES]", Arrays.toString((Breakfast[]) defaultValue("n")));
+ assertEquals(null, defaultValue("o"));
+ assertEquals(null, defaultValue("p"));
+ }
+
+ private static Object defaultValue(String name) throws NoSuchMethodException {
+ return HasDefaultsAnnotation.class.getMethod(name).getDefaultValue();
+ }
+
+ @Retention(RetentionPolicy.CLASS)
+ public @interface ClassRetentionAnnotation {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface RuntimeRetentionAnnotation {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SourceRetentionAnnotation {}
+
+ @ClassRetentionAnnotation @RuntimeRetentionAnnotation @SourceRetentionAnnotation
+ public static class RetentionAnnotations {}
+
+ public void testRetentionPolicy() {
+ // b/29500035
+ int savedTargetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
+ try {
+ // Test N and later behavior
+ VMRuntime.getRuntime().setTargetSdkVersion(24);
+ Annotation classRetentionAnnotation =
+ RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class);
+ assertNull(classRetentionAnnotation);
+
+ // Test pre-N behavior
+ VMRuntime.getRuntime().setTargetSdkVersion(23);
+ classRetentionAnnotation =
+ RetentionAnnotations.class.getAnnotation(ClassRetentionAnnotation.class);
+ assertNotNull(classRetentionAnnotation);
+ } finally {
+ VMRuntime.getRuntime().setTargetSdkVersion(savedTargetSdkVersion);
+ }
+ assertNotNull(RetentionAnnotations.class.getAnnotation(RuntimeRetentionAnnotation.class));
+ assertNull(RetentionAnnotations.class.getAnnotation(SourceRetentionAnnotation.class));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java
new file mode 100644
index 0000000..5488ed1
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ClassTest.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Inherited;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementDirectMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class ClassTest extends TestCase {
+
+ public void setUp() throws Exception {
+ super.setUp();
+ // Required by all the tests.
+ Repeated.class.isAnnotationPresent(Inherited.class);
+ }
+
+ @AnnotationA
+ @AnnotationB
+ private static class Type {
+ }
+
+ public static class ExtendsType extends Type {}
+
+ public void testClassDirectAnnotations() {
+ checkAnnotatedElementPresentMethods(Type.class, AnnotationA.class, AnnotationB.class);
+ checkAnnotatedElementDirectMethods(Type.class, AnnotationA.class, AnnotationB.class);
+ }
+
+ public void testClassInheritedAnnotations() {
+ checkAnnotatedElementPresentMethods(ExtendsType.class, AnnotationB.class);
+ checkAnnotatedElementDirectMethods(ExtendsType.class);
+ }
+
+ @Repeated(1)
+ private static class SingleAnnotation {}
+
+ @Repeated(1)
+ @Repeated(2)
+ private static class MultipleAnnotation {}
+
+ @Container({@Repeated(1)})
+ private static class MultipleAnnotationExplicitSingle {}
+
+ @Repeated(1)
+ @Container({@Repeated(2), @Repeated(3)})
+ private static class MultipleAnnotationOddity {}
+
+ private static class NoAnnotation {}
+
+ private static class InheritedNoNewAnnotation extends SingleAnnotation {}
+
+ @Repeated(2)
+ private static class InheritedSingleWithNewSingleAnnotation extends SingleAnnotation {}
+
+ @Repeated(2)
+ @Repeated(3)
+ private static class InheritedSingleWithNewMultipleAnnotations extends SingleAnnotation {}
+
+ @Repeated(2)
+ private static class InheritedMultipleWithNewSingleAnnotation extends MultipleAnnotation {}
+
+ @Repeated(2)
+ @Repeated(3)
+ private static class InheritedMultipleWithNewMultipleAnnotations extends MultipleAnnotation {}
+
+ public void testIsAnnotationPresent() throws Exception {
+ Class<Repeated> repeated = Repeated.class;
+ assertIsAnnotationPresent(NoAnnotation.class, repeated, false);
+ assertIsAnnotationPresent(SingleAnnotation.class, repeated, true);
+ assertIsAnnotationPresent(MultipleAnnotation.class, repeated, false);
+ assertIsAnnotationPresent(MultipleAnnotationExplicitSingle.class, repeated, false);
+ assertIsAnnotationPresent(MultipleAnnotationOddity.class, repeated, true);
+ assertIsAnnotationPresent(InheritedNoNewAnnotation.class, repeated, true);
+ assertIsAnnotationPresent(InheritedSingleWithNewSingleAnnotation.class, repeated, true);
+ assertIsAnnotationPresent(InheritedSingleWithNewMultipleAnnotations.class, repeated, true);
+ assertIsAnnotationPresent(InheritedMultipleWithNewSingleAnnotation.class, repeated, true);
+ assertIsAnnotationPresent(InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+ false);
+
+ Class<Container> container = Container.class;
+ assertIsAnnotationPresent(NoAnnotation.class, repeated, false);
+ assertIsAnnotationPresent(SingleAnnotation.class, container, false);
+ assertIsAnnotationPresent(MultipleAnnotation.class, container, true);
+ assertIsAnnotationPresent(MultipleAnnotationExplicitSingle.class, container, true);
+ assertIsAnnotationPresent(MultipleAnnotationOddity.class, container, true);
+ assertIsAnnotationPresent(InheritedNoNewAnnotation.class, container, false);
+ assertIsAnnotationPresent(InheritedSingleWithNewSingleAnnotation.class, container, false);
+ assertIsAnnotationPresent(InheritedSingleWithNewMultipleAnnotations.class, container, true);
+ assertIsAnnotationPresent(InheritedMultipleWithNewSingleAnnotation.class, container, true);
+ assertIsAnnotationPresent(InheritedMultipleWithNewMultipleAnnotations.class, container,
+ true);
+ }
+
+ public void testGetDeclaredAnnotation() throws Exception {
+ Class<Repeated> repeated = Repeated.class;
+ assertGetDeclaredAnnotation(NoAnnotation.class, repeated, null);
+ assertGetDeclaredAnnotation(SingleAnnotation.class, repeated, "@Repeated(1)");
+ assertGetDeclaredAnnotation(MultipleAnnotation.class, repeated, null);
+ assertGetDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, repeated, null);
+ assertGetDeclaredAnnotation(MultipleAnnotationOddity.class, repeated, "@Repeated(1)");
+ assertGetDeclaredAnnotation(InheritedNoNewAnnotation.class, repeated, null);
+ assertGetDeclaredAnnotation(InheritedSingleWithNewSingleAnnotation.class, repeated,
+ "@Repeated(2)");
+ assertGetDeclaredAnnotation(InheritedSingleWithNewMultipleAnnotations.class, repeated,
+ null);
+ assertGetDeclaredAnnotation(InheritedMultipleWithNewSingleAnnotation.class, repeated,
+ "@Repeated(2)");
+ assertGetDeclaredAnnotation(InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+ null);
+
+ Class<Container> container = Container.class;
+ assertGetDeclaredAnnotation(NoAnnotation.class, container, null);
+ assertGetDeclaredAnnotation(SingleAnnotation.class, container, null);
+ assertGetDeclaredAnnotation(MultipleAnnotation.class, container,
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, container,
+ "@Container({@Repeated(1)})");
+ assertGetDeclaredAnnotation(MultipleAnnotationOddity.class, container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetDeclaredAnnotation(InheritedNoNewAnnotation.class, container, null);
+ assertGetDeclaredAnnotation(InheritedSingleWithNewSingleAnnotation.class, container, null);
+ assertGetDeclaredAnnotation(InheritedSingleWithNewMultipleAnnotations.class, container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetDeclaredAnnotation(InheritedMultipleWithNewSingleAnnotation.class, container,
+ null);
+ assertGetDeclaredAnnotation(InheritedMultipleWithNewMultipleAnnotations.class, container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ }
+
+ public void testGetDeclaredAnnotationsByType() throws Exception {
+ Class<Repeated> repeated = Repeated.class;
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ NoAnnotation.class, repeated, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ SingleAnnotation.class, repeated, new String[] { "@Repeated(1)" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ MultipleAnnotation.class, repeated, new String[] { "@Repeated(1)", "@Repeated(2)" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ MultipleAnnotationExplicitSingle.class, repeated, new String[] { "@Repeated(1)" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ MultipleAnnotationOddity.class, repeated,
+ new String[] { "@Repeated(1)", "@Repeated(2)", "@Repeated(3)" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedNoNewAnnotation.class, repeated, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedSingleWithNewSingleAnnotation.class, repeated,
+ new String[] { "@Repeated(2)" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedSingleWithNewMultipleAnnotations.class, repeated,
+ new String[] { "@Repeated(2)", "@Repeated(3)" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedMultipleWithNewSingleAnnotation.class, repeated,
+ new String[] { "@Repeated(2)" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+ new String[] { "@Repeated(2)", "@Repeated(3)" });
+
+ Class<Container> container = Container.class;
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ NoAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ SingleAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ MultipleAnnotation.class, container,
+ new String[] { "@Container({@Repeated(1), @Repeated(2)})" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ MultipleAnnotationExplicitSingle.class, container,
+ new String[] { "@Container({@Repeated(1)})" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ MultipleAnnotationOddity.class, container,
+ new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedNoNewAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedSingleWithNewSingleAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedSingleWithNewMultipleAnnotations.class, container,
+ new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedMultipleWithNewSingleAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ InheritedMultipleWithNewMultipleAnnotations.class, container,
+ new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+ }
+
+ public void testGetAnnotationsByType() throws Exception {
+ Class<Repeated> repeated = Repeated.class;
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ NoAnnotation.class, repeated, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ SingleAnnotation.class, repeated, new String[] { "@Repeated(1)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ MultipleAnnotation.class, repeated, new String[] { "@Repeated(1)", "@Repeated(2)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ MultipleAnnotationExplicitSingle.class, repeated, new String[] { "@Repeated(1)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ MultipleAnnotationOddity.class, repeated,
+ new String[] { "@Repeated(1)", "@Repeated(2)", "@Repeated(3)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedNoNewAnnotation.class, repeated, new String[] { "@Repeated(1)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedSingleWithNewSingleAnnotation.class, repeated,
+ new String[] { "@Repeated(2)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedSingleWithNewMultipleAnnotations.class, repeated,
+ new String[] { "@Repeated(2)", "@Repeated(3)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedMultipleWithNewSingleAnnotation.class, repeated,
+ new String[] { "@Repeated(2)" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedMultipleWithNewMultipleAnnotations.class, repeated,
+ new String[] { "@Repeated(2)", "@Repeated(3)" });
+
+ Class<Container> container = Container.class;
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ NoAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ SingleAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ MultipleAnnotation.class, container,
+ new String[] { "@Container({@Repeated(1), @Repeated(2)})" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ MultipleAnnotationExplicitSingle.class, container,
+ new String[] { "@Container({@Repeated(1)})" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ MultipleAnnotationOddity.class, container,
+ new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedNoNewAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedSingleWithNewSingleAnnotation.class, container, EXPECT_EMPTY);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedSingleWithNewMultipleAnnotations.class, container,
+ new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedMultipleWithNewSingleAnnotation.class, container,
+ new String[] { "@Container({@Repeated(1), @Repeated(2)})" });
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ InheritedMultipleWithNewMultipleAnnotations.class, container,
+ new String[] { "@Container({@Repeated(2), @Repeated(3)})" });
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java
new file mode 100644
index 0000000..828b601
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ConstructorTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class ConstructorTest extends TestCase {
+
+ private static class Type {
+ @AnnotationA
+ @AnnotationC
+ public Type() {}
+ }
+
+ public void testConstructorAnnotations() throws Exception {
+ Constructor<Type> constructor = Type.class.getConstructor();
+ checkAnnotatedElementPresentMethods(constructor, AnnotationA.class, AnnotationC.class);
+ }
+
+ // A class with multiple constructors that differ by their argument count.
+ private static class AnnotatedClass {
+ @Repeated(1)
+ public AnnotatedClass() {}
+
+ @Repeated(1)
+ @Repeated(2)
+ public AnnotatedClass(int a) {}
+
+ @Container({@Repeated(1)})
+ public AnnotatedClass(int a, int b) {}
+
+ @Repeated(1)
+ @Container({@Repeated(2), @Repeated(3)})
+ public AnnotatedClass(int a, int b, int c) {}
+
+ public AnnotatedClass(int a, int b, int c, int d) {}
+ }
+
+ // Tests for isAnnotationPresent and getDeclaredAnnotation.
+ public void testDeclaredAnnotation() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ checkDeclaredAnnotation(c, 4, repeated, null);
+ checkDeclaredAnnotation(c, 3, repeated, "@Repeated(1)");
+ checkDeclaredAnnotation(c, 2, repeated, null);
+ checkDeclaredAnnotation(c, 1, repeated, null);
+ checkDeclaredAnnotation(c, 0, repeated, "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ checkDeclaredAnnotation(c, 4, container, null);
+ checkDeclaredAnnotation(c, 3, container, "@Container({@Repeated(2), @Repeated(3)})");
+ checkDeclaredAnnotation(c, 2, container, "@Container({@Repeated(1)})");
+ checkDeclaredAnnotation(c, 1, container, "@Container({@Repeated(1), @Repeated(2)})");
+ checkDeclaredAnnotation(c, 0, container, null);
+ }
+
+ private static void checkDeclaredAnnotation(Class<?> c, int constructorArgCount,
+ Class<? extends Annotation> annotationType,
+ String expectedAnnotationString) throws Exception {
+ Constructor constructor = getConstructor(c, constructorArgCount);
+
+ // isAnnotationPresent
+ assertIsAnnotationPresent(constructor, annotationType,
+ expectedAnnotationString != null);
+
+ // getDeclaredAnnotation
+ assertGetDeclaredAnnotation(constructor, annotationType, expectedAnnotationString);
+ }
+
+ public void testGetDeclaredAnnotationsByType() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ assertGetDeclaredAnnotationsByType(c, 4, repeated, EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(c, 3, repeated,
+ "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+ assertGetDeclaredAnnotationsByType(c, 2, repeated, "@Repeated(1)");
+ assertGetDeclaredAnnotationsByType(c, 1, repeated, "@Repeated(1)", "@Repeated(2)");
+ assertGetDeclaredAnnotationsByType(c, 0, repeated, "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ assertGetDeclaredAnnotationsByType(c, 4, container, EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(c, 3, container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetDeclaredAnnotationsByType(c, 2, container, "@Container({@Repeated(1)})");
+ assertGetDeclaredAnnotationsByType(c, 1, container,
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetDeclaredAnnotationsByType(c, 0, container, EXPECT_EMPTY);
+ }
+
+ private static void assertGetDeclaredAnnotationsByType(Class<?> c, int constructorArgCount,
+ Class<? extends Annotation> annotationType,
+ String... expectedAnnotationStrings) throws Exception {
+ Constructor<?> constructor = getConstructor(c, constructorArgCount);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ constructor, annotationType, expectedAnnotationStrings);
+ }
+
+ public void testGetAnnotationsByType() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ assertGetAnnotationsByType(c, 4, repeated, EXPECT_EMPTY);
+ assertGetAnnotationsByType(c, 3, repeated, "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+ assertGetAnnotationsByType(c, 2, repeated, "@Repeated(1)");
+ assertGetAnnotationsByType(c, 1, repeated, "@Repeated(1)", "@Repeated(2)");
+ assertGetAnnotationsByType(c, 0, repeated, "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ assertGetAnnotationsByType(c, 4, container, EXPECT_EMPTY);
+ assertGetAnnotationsByType(c, 3, container, "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetAnnotationsByType(c, 2, container, "@Container({@Repeated(1)})");
+ assertGetAnnotationsByType(c, 1, container, "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetAnnotationsByType(c, 0, container, EXPECT_EMPTY);
+ }
+
+ private static void assertGetAnnotationsByType(Class<?> c, int constructorArgCount,
+ Class<? extends Annotation> annotationType,
+ String... expectedAnnotationStrings) throws Exception {
+ Constructor<?> constructor = getConstructor(c, constructorArgCount);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ constructor, annotationType, expectedAnnotationStrings);
+ }
+
+ private static Constructor<?> getConstructor(Class<?> c, int constructorArgCount)
+ throws NoSuchMethodException {
+
+ Class<?>[] args = new Class[constructorArgCount];
+ for (int i = 0; i < constructorArgCount; i++) {
+ args[i] = Integer.TYPE;
+ }
+ return c.getDeclaredConstructor(args);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java
new file mode 100644
index 0000000..8ab7d8e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/FieldTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class FieldTest extends TestCase {
+
+ private static class Type {
+ @AnnotationA
+ @AnnotationD
+ public String field;
+ }
+
+ public void testFieldAnnotations() throws Exception {
+ Field field = Type.class.getField("field");
+ checkAnnotatedElementPresentMethods(field, AnnotationA.class, AnnotationD.class);
+ }
+
+ private static class AnnotatedClass {
+ @Repeated(1)
+ private Object singleAnnotation;
+
+ @Repeated(1)
+ @Repeated(2)
+ private Object multipleAnnotation;
+
+ @Container({@Repeated(1)})
+ private Object multipleAnnotationExplicitSingle;
+
+ @Repeated(1)
+ @Container({@Repeated(2), @Repeated(3)})
+ private Object multipleAnnotationOddity;
+
+ private Object noAnnotation;
+ }
+
+ // Tests for isAnnotationPresent and getDeclaredAnnotation.
+ public void testDeclaredAnnotation() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ checkDeclaredAnnotation(c, "noAnnotation", repeated, null);
+ checkDeclaredAnnotation(c, "multipleAnnotationOddity", repeated, "@Repeated(1)");
+ checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", repeated, null);
+ checkDeclaredAnnotation(c, "multipleAnnotation", repeated, null);
+ checkDeclaredAnnotation(c, "singleAnnotation", repeated, "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ checkDeclaredAnnotation(c, "noAnnotation", container, null);
+ checkDeclaredAnnotation(c, "multipleAnnotationOddity", container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", container,
+ "@Container({@Repeated(1)})");
+ checkDeclaredAnnotation(c, "multipleAnnotation", container,
+ "@Container({@Repeated(1), @Repeated(2)})");
+ checkDeclaredAnnotation(c, "singleAnnotation", container, null);
+ }
+
+ private static void checkDeclaredAnnotation(
+ Class<?> c, String fieldName, Class<? extends Annotation> annotationType,
+ String expectedAnnotationString) throws Exception {
+ Field field = c.getDeclaredField(fieldName);
+
+ // isAnnotationPresent
+ assertIsAnnotationPresent(field, annotationType, expectedAnnotationString != null);
+
+ // getDeclaredAnnotation
+ assertGetDeclaredAnnotation(field, annotationType, expectedAnnotationString);
+ }
+
+ public void testGetDeclaredAnnotationsByType() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ assertGetDeclaredAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+ "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+ assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+ "@Repeated(1)");
+ assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotation",
+ "@Repeated(1)", "@Repeated(2)");
+ assertGetDeclaredAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ assertGetDeclaredAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationOddity",
+ "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+ "@Container({@Repeated(1)})");
+ assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotation",
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetDeclaredAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+ }
+
+ private static void assertGetDeclaredAnnotationsByType(
+ Class<?> c, Class<? extends Annotation> annotationType, String fieldName,
+ String... expectedAnnotationStrings) throws Exception {
+ Field field = c.getDeclaredField(fieldName);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ field, annotationType, expectedAnnotationStrings);
+ }
+
+ public void testGetAnnotationsByType() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ assertGetAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+ assertGetAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+ "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+ assertGetAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+ "@Repeated(1)");
+ assertGetAnnotationsByType(c, repeated, "multipleAnnotation",
+ "@Repeated(1)", "@Repeated(2)");
+ assertGetAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ assertGetAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+ assertGetAnnotationsByType(c, container, "multipleAnnotationOddity",
+ "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+ "@Container({@Repeated(1)})");
+ assertGetAnnotationsByType(c, container, "multipleAnnotation",
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+ }
+
+ private static void assertGetAnnotationsByType(
+ Class<?> c, Class<? extends Annotation> annotationType,
+ String fieldName, String... expectedAnnotationStrings) throws Exception {
+ Field field = c.getDeclaredField(fieldName);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ field, annotationType, expectedAnnotationStrings);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java
new file mode 100644
index 0000000..c732a9d
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/MethodTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.checkAnnotatedElementPresentMethods;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class MethodTest extends TestCase {
+
+ private static class Type {
+ @AnnotationB
+ @AnnotationC
+ public void method(String parameter1, String parameter2) {}
+ }
+
+ public void testMethodAnnotations() throws Exception {
+ Method method = Type.class.getMethod("method", String.class, String.class);
+ checkAnnotatedElementPresentMethods(method, AnnotationB.class, AnnotationC.class);
+ }
+
+ private static class AnnotatedClass {
+ @Repeated(1)
+ public void singleAnnotation() {}
+
+ @Repeated(1)
+ @Repeated(2)
+ public void multipleAnnotation() {}
+
+ @Container({@Repeated(1)})
+ public void multipleAnnotationExplicitSingle() {}
+
+ @Repeated(1)
+ @Container({@Repeated(2), @Repeated(3)})
+ public void multipleAnnotationOddity() {}
+
+ public void noAnnotation() {}
+ }
+
+ // Tests for isAnnotationPresent and getDeclaredAnnotation.
+ public void testDeclaredAnnotation() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ checkDeclaredAnnotation(c, "noAnnotation", repeated, null);
+ checkDeclaredAnnotation(c, "multipleAnnotationOddity", repeated, "@Repeated(1)");
+ checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", repeated, null);
+ checkDeclaredAnnotation(c, "multipleAnnotation", repeated, null);
+ checkDeclaredAnnotation(c, "singleAnnotation", repeated, "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ checkDeclaredAnnotation(c, "noAnnotation", container, null);
+ checkDeclaredAnnotation(c, "multipleAnnotationOddity", container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ checkDeclaredAnnotation(c, "multipleAnnotationExplicitSingle", container,
+ "@Container({@Repeated(1)})");
+ checkDeclaredAnnotation(c, "multipleAnnotation", container,
+ "@Container({@Repeated(1), @Repeated(2)})");
+ checkDeclaredAnnotation(c, "singleAnnotation", container, null);
+ }
+
+ private static void checkDeclaredAnnotation(
+ Class<?> c, String methodName, Class<? extends Annotation> annotationType,
+ String expectedAnnotationString) throws Exception {
+ Method method = c.getDeclaredMethod(methodName);
+
+ // isAnnotationPresent
+ assertIsAnnotationPresent(method, annotationType, expectedAnnotationString != null);
+
+ // getDeclaredAnnotation
+ assertGetDeclaredAnnotation(method, annotationType, expectedAnnotationString);
+ }
+
+ public void testGetDeclaredAnnotationsByType() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ assertGetDeclaredAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+ "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+ assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+ "@Repeated(1)");
+ assertGetDeclaredAnnotationsByType(c, repeated, "multipleAnnotation",
+ "@Repeated(1)", "@Repeated(2)");
+ assertGetDeclaredAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ assertGetDeclaredAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationOddity",
+ "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+ "@Container({@Repeated(1)})");
+ assertGetDeclaredAnnotationsByType(c, container, "multipleAnnotation",
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetDeclaredAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+ }
+
+ private static void assertGetDeclaredAnnotationsByType(
+ Class<?> c, Class<? extends Annotation> annotationType, String methodName,
+ String... expectedAnnotationStrings) throws Exception {
+ Method method = c.getDeclaredMethod(methodName);
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ method, annotationType, expectedAnnotationStrings);
+ }
+
+ public void testGetAnnotationsByType() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ Class<? extends Annotation> repeated = Repeated.class;
+ assertGetAnnotationsByType(c, repeated, "noAnnotation", EXPECT_EMPTY);
+ assertGetAnnotationsByType(c, repeated, "multipleAnnotationOddity",
+ "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+ assertGetAnnotationsByType(c, repeated, "multipleAnnotationExplicitSingle",
+ "@Repeated(1)");
+ assertGetAnnotationsByType(c, repeated, "multipleAnnotation",
+ "@Repeated(1)", "@Repeated(2)");
+ assertGetAnnotationsByType(c, repeated, "singleAnnotation", "@Repeated(1)");
+
+ Class<? extends Annotation> container = Container.class;
+ assertGetAnnotationsByType(c, container, "noAnnotation", EXPECT_EMPTY);
+ assertGetAnnotationsByType(c, container, "multipleAnnotationOddity",
+ "@Container({@Repeated(2), @Repeated(3)})");
+ assertGetAnnotationsByType(c, container, "multipleAnnotationExplicitSingle",
+ "@Container({@Repeated(1)})");
+ assertGetAnnotationsByType(c, container, "multipleAnnotation",
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetAnnotationsByType(c, container, "singleAnnotation", EXPECT_EMPTY);
+ }
+
+ private static void assertGetAnnotationsByType(
+ Class<?> c, Class<? extends Annotation> annotationType,
+ String methodName, String... expectedAnnotationStrings) throws Exception {
+ Method method = c.getDeclaredMethod(methodName);
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ method, annotationType, expectedAnnotationStrings);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java
new file mode 100644
index 0000000..f54a64e
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/PackageTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+import libcore.java.lang.reflect.annotations.multipleannotation.MultipleAnnotation;
+import libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle.MultipleAnnotationExplicitSingle;
+import libcore.java.lang.reflect.annotations.multipleannotationoddity.MultipleAnnotationOddity;
+import libcore.java.lang.reflect.annotations.noannotation.NoAnnotation;
+import libcore.java.lang.reflect.annotations.singleannotation.SingleAnnotation;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertGetDeclaredAnnotation;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertIsAnnotationPresent;
+
+public class PackageTest extends TestCase {
+
+ // Tests for isAnnotationPresent and getDeclaredAnnotation.
+ public void testDeclaredAnnotation() throws Exception {
+ Class<Repeated> repeated = Repeated.class;
+ checkDeclaredAnnotation(NoAnnotation.class, repeated, null);
+ checkDeclaredAnnotation(SingleAnnotation.class, repeated, "@Repeated(1)");
+ checkDeclaredAnnotation(MultipleAnnotation.class, repeated, null);
+ checkDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, repeated, null);
+ checkDeclaredAnnotation(MultipleAnnotationOddity.class, repeated, "@Repeated(1)");
+
+ Class<Container> container = Container.class;
+ checkDeclaredAnnotation(NoAnnotation.class, container, null);
+ checkDeclaredAnnotation(SingleAnnotation.class, container, null);
+ checkDeclaredAnnotation(MultipleAnnotation.class, container,
+ "@Container({@Repeated(1), @Repeated(2)})");
+ checkDeclaredAnnotation(MultipleAnnotationExplicitSingle.class, container,
+ "@Container({@Repeated(1)})");
+ checkDeclaredAnnotation(MultipleAnnotationOddity.class, container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ }
+
+ private static void checkDeclaredAnnotation(
+ Class<?> classInPackage, Class<? extends Annotation> annotationType,
+ String expectedAnnotationString) throws Exception {
+
+ Package aPackage = classInPackage.getPackage();
+ // isAnnotationPresent
+ assertIsAnnotationPresent(aPackage, annotationType, expectedAnnotationString != null);
+
+ // getDeclaredAnnotation
+ assertGetDeclaredAnnotation(aPackage, annotationType, expectedAnnotationString);
+ }
+
+ public void testGetDeclaredAnnotationsByType() throws Exception {
+ Class<Repeated> repeated = Repeated.class;
+
+ assertGetDeclaredAnnotationsByType(NoAnnotation.class, repeated, EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(SingleAnnotation.class, repeated,
+ "@Repeated(1)");
+ assertGetDeclaredAnnotationsByType(MultipleAnnotation.class, repeated,
+ "@Repeated(1)", "@Repeated(2)");
+ assertGetDeclaredAnnotationsByType(MultipleAnnotationExplicitSingle.class, repeated,
+ "@Repeated(1)");
+ assertGetDeclaredAnnotationsByType(MultipleAnnotationOddity.class, repeated,
+ "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+
+ Class<Container> container = Container.class;
+ assertGetDeclaredAnnotationsByType(NoAnnotation.class, container, EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(SingleAnnotation.class, container, EXPECT_EMPTY);
+ assertGetDeclaredAnnotationsByType(MultipleAnnotation.class, container,
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetDeclaredAnnotationsByType(MultipleAnnotationExplicitSingle.class, container,
+ "@Container({@Repeated(1)})");
+ assertGetDeclaredAnnotationsByType(MultipleAnnotationOddity.class, container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ }
+
+ private static void assertGetDeclaredAnnotationsByType(
+ Class<?> classInPackage, Class<? extends Annotation> annotationType,
+ String... expectedAnnotationStrings) throws Exception {
+ Package aPackage = classInPackage.getPackage();
+ AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+ aPackage, annotationType, expectedAnnotationStrings);
+ }
+
+ public void testGetAnnotationsByType() throws Exception {
+ Class<Repeated> repeated = Repeated.class;
+ assertGetAnnotationsByType(NoAnnotation.class, repeated, EXPECT_EMPTY);
+ assertGetAnnotationsByType(SingleAnnotation.class, repeated, "@Repeated(1)");
+ assertGetAnnotationsByType(MultipleAnnotation.class, repeated,
+ "@Repeated(1)", "@Repeated(2)");
+ assertGetAnnotationsByType(MultipleAnnotationExplicitSingle.class, repeated,
+ "@Repeated(1)");
+ assertGetAnnotationsByType(MultipleAnnotationOddity.class, repeated,
+ "@Repeated(1)", "@Repeated(2)", "@Repeated(3)");
+
+ Class<Container> container = Container.class;
+ assertGetAnnotationsByType(NoAnnotation.class, container, EXPECT_EMPTY);
+ assertGetAnnotationsByType(SingleAnnotation.class, container, EXPECT_EMPTY);
+ assertGetAnnotationsByType(MultipleAnnotation.class, container,
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertGetAnnotationsByType(MultipleAnnotationExplicitSingle.class, container,
+ "@Container({@Repeated(1)})");
+ assertGetAnnotationsByType(MultipleAnnotationOddity.class, container,
+ "@Container({@Repeated(2), @Repeated(3)})");
+ }
+
+ private static void assertGetAnnotationsByType(Class<?> classInPackage,
+ Class<? extends Annotation> annotationType,
+ String... expectedAnnotationStrings) throws Exception {
+ Package aPackage = classInPackage.getPackage();
+ AnnotatedElementTestSupport.assertGetAnnotationsByType(
+ aPackage, annotationType, expectedAnnotationStrings);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ParameterTest.java
new file mode 100644
index 0000000..f40ac9f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ParameterTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed 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 libcore.java.lang.reflect.annotations;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.EXPECT_EMPTY;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.annotationsToTypes;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.assertAnnotationsMatch;
+import static libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.set;
+
+public class ParameterTest extends TestCase {
+ private static class Type {
+ @AnnotationB
+ @AnnotationC
+ public void method(String parameter1, String parameter2) {}
+
+ @AnnotationB
+ @AnnotationC
+ public void parameters(@AnnotationB @AnnotationD String parameter1,
+ @AnnotationC @AnnotationD String parameter2) {}
+ }
+
+ public void testParameterAnnotations() throws Exception {
+ Method method = Type.class.getMethod("method", String.class, String.class);
+ Annotation[][] noParameterAnnotations = method.getParameterAnnotations();
+ assertEquals(2, noParameterAnnotations.length);
+ assertEquals(set(), annotationsToTypes(noParameterAnnotations[0]));
+ assertEquals(set(), annotationsToTypes(noParameterAnnotations[1]));
+
+ Method parameters = Type.class.getMethod("parameters", String.class, String.class);
+ Annotation[][] parameterAnnotations = parameters.getParameterAnnotations();
+ assertEquals(2, parameterAnnotations.length);
+ assertEquals(set(AnnotationB.class, AnnotationD.class),
+ annotationsToTypes(parameterAnnotations[0]));
+ assertEquals(set(AnnotationC.class, AnnotationD.class),
+ annotationsToTypes(parameterAnnotations[1]));
+ }
+
+ private static class AnnotatedClass {
+ public void singleAnnotation(@Repeated(1) String p0) {}
+
+ public void multipleAnnotation(@Repeated(1) @Repeated(2) String p0) {}
+
+ public void multipleAnnotationExplicitSingle(@Container({@Repeated(1)}) String p0) {}
+
+ public void multipleAnnotationOddity(
+ @Repeated(1) @Container({@Repeated(2), @Repeated(3)}) String p0) {}
+
+ public void noAnnotation(String p0) {}
+ }
+
+ public void testGetParameterAnnotations() throws Exception {
+ Class<?> c = AnnotatedClass.class;
+
+ assertParameter0Annotations(c, "noAnnotation", EXPECT_EMPTY);
+ assertParameter0Annotations(c, "multipleAnnotationOddity",
+ "@Repeated(1)", "@Container({@Repeated(2), @Repeated(3)})");
+ assertParameter0Annotations(c, "multipleAnnotationExplicitSingle",
+ "@Container({@Repeated(1)})");
+ assertParameter0Annotations(c, "multipleAnnotation",
+ "@Container({@Repeated(1), @Repeated(2)})");
+ assertParameter0Annotations(c, "singleAnnotation",
+ "@Repeated(1)");
+ }
+
+ private static void assertParameter0Annotations(
+ Class<?> c, String methodName, String... expectedAnnotationStrings) throws Exception {
+ Annotation[][] allAnnotations =
+ c.getDeclaredMethod(methodName, String.class).getParameterAnnotations();
+ final int expectedParameterCount = 1;
+ assertEquals(expectedParameterCount, allAnnotations.length);
+
+ Annotation[] p0Annotations = allAnnotations[0];
+ assertAnnotationsMatch(p0Annotations, expectedAnnotationStrings);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java
similarity index 64%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java
index 1a84c2e..d3e77ae 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/MultipleAnnotation.java
@@ -13,8 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package libcore.java.lang.reflect.annotations.multipleannotation;
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class MultipleAnnotation {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java
similarity index 71%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java
index 1a84c2e..1ab457e 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotation/package-info.java
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Repeated(1)
+@Repeated(2)
+package libcore.java.lang.reflect.annotations.multipleannotation;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java
similarity index 62%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java
index 1a84c2e..d14ff19 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/MultipleAnnotationExplicitSingle.java
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class MultipleAnnotationExplicitSingle {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java
similarity index 64%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java
index 1a84c2e..cf41d87 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationexplicitsingle/package-info.java
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Container({@Repeated(1)})
+package libcore.java.lang.reflect.annotations.multipleannotationexplicitsingle;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java
similarity index 63%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java
index 1a84c2e..b85f50f 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/MultipleAnnotationOddity.java
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.multipleannotationoddity;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class MultipleAnnotationOddity {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java
similarity index 62%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java
index 1a84c2e..afbd99e 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/multipleannotationoddity/package-info.java
@@ -14,7 +14,12 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Repeated(1)
+@Container({@Repeated(2), @Repeated(3)})
+package libcore.java.lang.reflect.annotations.multipleannotationoddity;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Container;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java
similarity index 65%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java
index 1a84c2e..e7fd957 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/NoAnnotation.java
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.noannotation;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class NoAnnotation {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java
similarity index 81%
rename from luni/src/test/java/libcore/java/lang/reflect/package-info.java
rename to luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java
index 1a84c2e..2942f1a 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/noannotation/package-info.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+package libcore.java.lang.reflect.annotations.noannotation;
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java
similarity index 64%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java
index 1a84c2e..453b299 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/SingleAnnotation.java
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+package libcore.java.lang.reflect.annotations.singleannotation;
+
+import libcore.java.lang.reflect.annotations.PackageTest;
+
+/**
+ * This class exists so that (after it is loaded) the ClassLoader will be aware of the associated
+ * package and return a {@link Package} object. See {@link PackageTest}.
+ */
+public class SingleAnnotation {
+}
diff --git a/luni/src/test/java/libcore/java/lang/reflect/package-info.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java
similarity index 72%
copy from luni/src/test/java/libcore/java/lang/reflect/package-info.java
copy to luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java
index 1a84c2e..3686f80 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/package-info.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/singleannotation/package-info.java
@@ -14,7 +14,11 @@
* limitations under the License.
*/
-//Used by AnnotationsTest.
-@AnnotationsTest.RepeatableAnnotation
-@AnnotationsTest.RepeatableAnnotation
-package libcore.java.lang.reflect;
\ No newline at end of file
+/**
+ * See {@link libcore.java.lang.reflect.annotations.PackageTest}.
+ */
+@Repeated(1)
+package libcore.java.lang.reflect.annotations.singleannotation;
+
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.Repeated;
+
diff --git a/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
index 40c2cac..43cc26d 100644
--- a/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
@@ -24,14 +24,12 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
-import java.net.SocketException;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.MembershipKey;
-import java.util.Enumeration;
public class MembershipKeyTest extends TestCase {
@@ -43,7 +41,7 @@
private final static InetAddress MULTICAST_ADDRESS = getMulticastAddress();
private final static NetworkInterface NETWORK_INTERFACE = getNetworkInterface();
- private void setup(boolean withSource) throws Exception {
+ private void init(boolean withSource) throws Exception {
client = DatagramChannel.open(StandardProtocolFamily.INET)
.bind(new InetSocketAddress(Inet4Address.ANY, PORT));
client.configureBlocking(false);
@@ -62,48 +60,48 @@
}
public void test_isValid_OnChannelCloseWithJoinWithoutSource() throws Exception {
- setup(false);
- test_isValid();
+ init(false);
+ check_isValid();
}
public void test_isValid_OnChannelCloseWithJoinWithSource() throws Exception {
- setup(true);
- test_isValid();
+ init(true);
+ check_isValid();
}
- private void test_isValid() throws IOException {
+ private void check_isValid() throws IOException {
assertTrue(key.isValid());
client.close();
assertFalse(key.isValid());
}
public void test_isValid_OnDropJoinWithoutSource() throws Exception {
- setup(false);
- test_isValid_OnDrop();
+ init(false);
+ check_isValid_OnDrop();
}
public void test_isValid_OnDropJoinWithSource() throws Exception {
- setup(true);
- test_isValid_OnDrop();
+ init(true);
+ check_isValid_OnDrop();
}
- private void test_isValid_OnDrop() {
+ private void check_isValid_OnDrop() {
assertTrue(key.isValid());
key.drop();
assertFalse(key.isValid());
}
public void test_dropWithJoinWithoutSource() throws Exception {
- setup(false);
- test_drop();
+ init(false);
+ check_drop();
}
public void test_dropWithJoinWithSource() throws Exception {
- setup(true);
- test_drop();
+ init(true);
+ check_drop();
}
- private void test_drop() throws IOException {
+ private void check_drop() throws IOException {
key.drop();
try(DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
assertEquals(TEST_MESSAGE.length(), dc
@@ -119,55 +117,55 @@
}
public void test_networkInterface() throws Exception {
- setup(false);
+ init(false);
assertEquals(NETWORK_INTERFACE, key.networkInterface());
client.close();
assertEquals(NETWORK_INTERFACE, key.networkInterface());
}
public void test_sourceAddressWithJoinWithSource() throws Exception {
- setup(true);
+ init(true);
assertEquals(sourceAddress, key.sourceAddress());
}
public void test_sourceAddressWithJoinWithoutSource() throws Exception {
- setup(false);
+ init(false);
assertNull(key.sourceAddress());
}
public void test_groupWithJoinWithSource() throws Exception {
- setup(true);
+ init(true);
assertEquals(MULTICAST_ADDRESS, key.group());
}
public void test_groupWithoutJoinWIthSource() throws Exception {
- setup(false);
+ init(false);
assertEquals(MULTICAST_ADDRESS, key.group());
}
public void test_channelWithJoinWithSource() throws Exception {
- setup(true);
+ init(true);
assertEquals(client, key.channel());
key.drop();
assertEquals(client, key.channel());
}
public void test_channelWithJoinWithoutSource() throws Exception {
- setup(false);
+ init(false);
assertEquals(client, key.channel());
key.drop();
assertEquals(client, key.channel());
}
public void test_blockWithJoinWithSource() throws Exception {
- setup(true);
+ init(true);
try {
key.block(sourceAddress);
} catch (IllegalStateException expected) {}
}
public void test_blockWithJoinWithoutSource() throws Exception {
- setup(false);
+ init(false);
key.block(sourceAddress);
try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
@@ -184,7 +182,7 @@
}
public void test_block_Exception () throws Exception {
- setup(false);
+ init(false);
// Blocking a multicast channel
try {
@@ -206,7 +204,7 @@
}
public void test_unblockWithJoinWithSource() throws Exception {
- setup(true);
+ init(true);
try {
key.unblock(Inet4Address.getByName("127.0.0.2"));
fail();
@@ -214,7 +212,7 @@
}
public void test_unblockWithJoinWithoutSource() throws Exception {
- setup(false);
+ init(false);
key.block(sourceAddress);
key.unblock(sourceAddress);
@@ -238,7 +236,7 @@
}
public void test_unblock_Exception() throws Exception {
- setup(false);
+ init(false);
try {
key.unblock(sourceAddress);
fail();
diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java
index e5ba6af..c15aa31 100644
--- a/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileStoreTest.java
@@ -35,7 +35,6 @@
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
-import static libcore.java.nio.file.FilesSetup.TEST_DIR;
import static libcore.java.nio.file.FilesSetup.execCmdAndWaitForTermination;
import static libcore.java.nio.file.FilesSetup.readFromInputStream;
import static org.junit.Assert.assertEquals;
@@ -48,7 +47,7 @@
@Test
public void test_name() throws IOException, InterruptedException {
- Path path = Paths.get(TEST_DIR, "dir");
+ Path path = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectory(path);
Process p = execCmdAndWaitForTermination("df", path.toAbsolutePath().toString());
String shellOutput = readFromInputStream(p.getInputStream()).split("\n")[1];
@@ -58,7 +57,7 @@
@Test
public void test_type() throws IOException, InterruptedException {
- Path path = Paths.get(TEST_DIR, "dir");
+ Path path = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectory(path);
String fileStore = Files.getFileStore(path).name();
Process p = execCmdAndWaitForTermination("mount");
@@ -104,11 +103,14 @@
@Test
public void test_supportsFileAttributeView$Class() throws IOException {
- Path path = Paths.get(TEST_DIR, "dir");
+ Path path = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectories(path);
- assertTrue(Files.getFileStore(path).supportsFileAttributeView(BasicFileAttributeView.class));
- assertTrue(Files.getFileStore(path).supportsFileAttributeView(FileOwnerAttributeView.class));
- assertTrue(Files.getFileStore(path).supportsFileAttributeView(PosixFileAttributeView.class));
+ assertTrue(Files.getFileStore(path).supportsFileAttributeView(
+ BasicFileAttributeView.class));
+ assertTrue(Files.getFileStore(path).supportsFileAttributeView(
+ FileOwnerAttributeView.class));
+ assertTrue(Files.getFileStore(path).supportsFileAttributeView(
+ PosixFileAttributeView.class));
assertFalse(Files.getFileStore(path).supportsFileAttributeView(DosFileAttributeView.class));
assertFalse(Files.getFileStore(path).
supportsFileAttributeView(UserDefinedFileAttributeView.class));
@@ -118,7 +120,7 @@
@Test
public void test_supportsFileAttributeView$String() throws IOException {
- Path path = Paths.get(TEST_DIR, "dir");
+ Path path = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectories(path);
assertTrue(Files.getFileStore(path).supportsFileAttributeView("basic"));
assertTrue(Files.getFileStore(path).supportsFileAttributeView("unix"));
@@ -131,9 +133,10 @@
@Test
public void test_getFileStoreAttributeView() throws IOException {
- Path path = Paths.get(TEST_DIR, "dir");
+ Path path = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectories(path);
- assertNull(Files.getFileStore(path).getFileStoreAttributeView(FileStoreAttributeView.class));
+ assertNull(Files.getFileStore(path).getFileStoreAttributeView(
+ FileStoreAttributeView.class));
try {
Files.getFileStore(path).getFileStoreAttributeView(null);
fail();
@@ -142,7 +145,7 @@
@Test
public void test_getAttribute() throws IOException {
- Path p = Paths.get(TEST_DIR, "dir");
+ Path p = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectories(p);
FileStore store = Files.getFileStore(p);
assertEquals(store.getTotalSpace(), store.getAttribute("totalSpace"));
diff --git a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java
index 97f73fd..3b1d65a 100644
--- a/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/DefaultFileSystemProviderTest.java
@@ -19,8 +19,6 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
import java.io.IOException;
import java.io.InputStream;
@@ -65,22 +63,17 @@
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import static libcore.java.nio.file.FilesSetup.DATA_FILE;
-import static libcore.java.nio.file.FilesSetup.DATA_FILE_PATH;
import static libcore.java.nio.file.FilesSetup.NonStandardOption;
-import static libcore.java.nio.file.FilesSetup.TEST_DIR;
import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA;
import static libcore.java.nio.file.FilesSetup.TEST_FILE_DATA_2;
-import static libcore.java.nio.file.FilesSetup.TEST_PATH;
import static libcore.java.nio.file.FilesSetup.readFromFile;
import static libcore.java.nio.file.FilesSetup.readFromInputStream;
-import static libcore.java.nio.file.FilesSetup.reset;
import static libcore.java.nio.file.FilesSetup.writeToFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-@RunWith(JUnit4.class)
public class DefaultFileSystemProviderTest {
@Rule
@@ -90,12 +83,12 @@
@Before
public void setUp() throws Exception {
- provider = DATA_FILE_PATH.getFileSystem().provider();
+ provider = filesSetup.getDataFilePath().getFileSystem().provider();
}
@Test
public void test_newInputStream() throws IOException {
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH, READ)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), READ)) {
assertEquals(TEST_FILE_DATA, readFromInputStream(is));
}
}
@@ -103,33 +96,34 @@
@Test
public void test_newInputStream_openOption() throws IOException {
// Write and Append are not supported.
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH, WRITE)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), WRITE)) {
fail();
} catch (UnsupportedOperationException expected) {
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH, APPEND)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), APPEND)) {
fail();
} catch (UnsupportedOperationException expected) {
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH, NonStandardOption.OPTION1)){
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(),
+ NonStandardOption.OPTION1)){
fail();
} catch (UnsupportedOperationException expected) {
}
// Supported options.
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH, DELETE_ON_CLOSE, CREATE_NEW,
- TRUNCATE_EXISTING, SPARSE, SYNC, DSYNC)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), DELETE_ON_CLOSE,
+ CREATE_NEW, TRUNCATE_EXISTING, SPARSE, SYNC, DSYNC)) {
assertEquals(TEST_FILE_DATA, readFromInputStream(is));
}
}
@Test
public void test_newInputStream_twice() throws IOException {
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH, READ);
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(), READ);
// Open the same file again.
- InputStream is2 = provider.newInputStream(DATA_FILE_PATH, READ)) {
+ InputStream is2 = provider.newInputStream(filesSetup.getDataFilePath(), READ)) {
assertEquals(TEST_FILE_DATA, readFromInputStream(is));
assertEquals(TEST_FILE_DATA, readFromInputStream(is2));
@@ -142,25 +136,26 @@
fail();
} catch (NullPointerException expected) {}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH, (OpenOption[]) null)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath(),
+ (OpenOption[]) null)) {
fail();
} catch (NullPointerException expected) {}
}
@Test
public void test_newOutputStream() throws IOException {
- try (OutputStream os = provider.newOutputStream(TEST_PATH)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath())) {
os.write(TEST_FILE_DATA.getBytes());
}
- try (InputStream is = provider.newInputStream(TEST_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) {
assertEquals(TEST_FILE_DATA, readFromInputStream(is));
}
}
@Test
public void test_newOutputStream_openOption_READ() throws IOException {
- try (OutputStream os = provider.newOutputStream(TEST_PATH, READ)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), READ)) {
fail();
} catch (IllegalArgumentException expected) {
}
@@ -169,82 +164,83 @@
@Test
public void test_newOutputStream_openOption_APPEND() throws IOException {
// When file exists and it contains data.
- try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, APPEND)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), APPEND)) {
os.write(TEST_FILE_DATA.getBytes());
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
assertEquals(TEST_FILE_DATA + TEST_FILE_DATA, readFromInputStream(is));
}
// When file doesn't exist.
- try (OutputStream os = provider.newOutputStream(TEST_PATH, APPEND)){
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), APPEND)) {
fail();
} catch (NoSuchFileException expected) {
- assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+ assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
}
}
@Test
public void test_newOutputStream_openOption_TRUNCATE() throws IOException {
// When file exists.
- try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, TRUNCATE_EXISTING)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(),
+ TRUNCATE_EXISTING)) {
os.write(TEST_FILE_DATA_2.getBytes());
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
assertEquals(TEST_FILE_DATA_2, readFromInputStream(is));
}
// When file doesn't exist.
- try (OutputStream os = provider.newOutputStream(TEST_PATH,
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(),
TRUNCATE_EXISTING)) {
fail();
} catch (NoSuchFileException expected) {
- assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+ assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
}
}
@Test
public void test_newOutputStream_openOption_WRITE() throws IOException {
// When file exists.
- try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, WRITE)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), WRITE)) {
os.write(TEST_FILE_DATA_2.getBytes());
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
String expectedFileData = TEST_FILE_DATA_2 +
TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length());
assertEquals(expectedFileData, readFromInputStream(is));
}
// When file doesn't exist.
- try (OutputStream os = provider.newOutputStream(TEST_PATH, WRITE)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), WRITE)) {
fail();
} catch (NoSuchFileException expected) {
- assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+ assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
}
}
@Test
public void test_newOutputStream_openOption_CREATE() throws IOException {
// When file exists.
- try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, CREATE)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), CREATE)) {
os.write(TEST_FILE_DATA_2.getBytes());
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
String expectedFileData = TEST_FILE_DATA_2 +
TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length());
assertEquals(expectedFileData, readFromInputStream(is));
}
// When file doesn't exist.
- try (OutputStream os = provider.newOutputStream(TEST_PATH, CREATE)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE)) {
os.write(TEST_FILE_DATA.getBytes());
}
- try (InputStream is = provider.newInputStream(TEST_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) {
assertEquals(TEST_FILE_DATA, readFromInputStream(is));
}
}
@@ -252,17 +248,17 @@
@Test
public void test_newOutputStream_openOption_CREATE_NEW() throws IOException {
// When file exists.
- try (OutputStream os = provider.newOutputStream(DATA_FILE_PATH, CREATE_NEW)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getDataFilePath(), CREATE_NEW)) {
fail();
} catch (FileAlreadyExistsException expected) {
}
// When file doesn't exist.
- try (OutputStream os = provider.newOutputStream(TEST_PATH, CREATE_NEW)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE_NEW)) {
os.write(TEST_FILE_DATA.getBytes());
}
- try (InputStream is = provider.newInputStream(TEST_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getTestPath())) {
assertEquals(TEST_FILE_DATA, readFromInputStream(is));
}
}
@@ -270,8 +266,8 @@
@Test
public void test_newOutputStream_openOption_SYNC() throws IOException {
// The data should be written to the file
- try (OutputStream os = provider.newOutputStream(TEST_PATH, CREATE, SYNC);
- InputStream is = provider.newInputStream(TEST_PATH, SYNC)) {
+ try (OutputStream os = provider.newOutputStream(filesSetup.getTestPath(), CREATE, SYNC);
+ InputStream is = provider.newInputStream(filesSetup.getTestPath(), SYNC)) {
os.write(TEST_FILE_DATA.getBytes());
assertEquals(TEST_FILE_DATA, readFromInputStream(is));
}
@@ -283,7 +279,8 @@
fail();
} catch (NullPointerException expected) {}
- try (OutputStream os = provider.newOutputStream(TEST_PATH, (OpenOption[]) null)) {
+ try (OutputStream os = provider
+ .newOutputStream(filesSetup.getTestPath(), (OpenOption[]) null)) {
fail();
} catch (NullPointerException expected) {}
}
@@ -293,23 +290,23 @@
Set<OpenOption> set = new HashSet<OpenOption>();
// When file doesn't exist
- try (SeekableByteChannel sbc = provider.newByteChannel(TEST_PATH, set)) {
+ try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getTestPath(), set)) {
fail();
} catch (NoSuchFileException expected) {
- assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+ assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
}
// When file exists.
// File opens in READ mode by default. The channel is non writable by default.
- try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+ try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
sbc.write(ByteBuffer.allocate(10));
fail();
} catch (NonWritableChannelException expected) {
}
// Read a file.
- try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+ try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
ByteBuffer readBuffer = ByteBuffer.allocate(10);
int bytesReadCount = sbc.read(readBuffer);
@@ -329,26 +326,26 @@
set.add(WRITE);
// When file doesn't exist
- try (SeekableByteChannel sbc = provider.newByteChannel(TEST_PATH, set)) {
+ try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getTestPath(), set)) {
fail();
} catch (NoSuchFileException expected) {
- assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+ assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
}
// When file exists.
- try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+ try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
sbc.read(ByteBuffer.allocate(10));
fail();
} catch (NonReadableChannelException expected) {
}
// Write in file.
- try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+ try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
String expectedFileData = TEST_FILE_DATA_2 +
TEST_FILE_DATA.substring(TEST_FILE_DATA_2.length());
assertEquals(expectedFileData, readFromInputStream(is));
@@ -366,7 +363,7 @@
set.add(READ);
set.add(SYNC);
- try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, set)) {
+ try (SeekableByteChannel sbc = provider.newByteChannel(filesSetup.getDataFilePath(), set)) {
ByteBuffer readBuffer = ByteBuffer.allocate(10);
int bytesReadCount = sbc.read(readBuffer);
@@ -379,7 +376,7 @@
sbc.write(ByteBuffer.wrap(TEST_FILE_DATA_2.getBytes()));
}
- try (InputStream is = provider.newInputStream(DATA_FILE_PATH)) {
+ try (InputStream is = provider.newInputStream(filesSetup.getDataFilePath())) {
String expectedFileData = TEST_FILE_DATA + TEST_FILE_DATA_2;
assertEquals(expectedFileData, readFromInputStream(is));
}
@@ -392,7 +389,8 @@
fail();
} catch(NullPointerException expected) {}
- try (SeekableByteChannel sbc = provider.newByteChannel(DATA_FILE_PATH, null)) {
+ try (SeekableByteChannel sbc = provider
+ .newByteChannel(filesSetup.getDataFilePath(), null)) {
fail();
} catch(NullPointerException expected) {}
}
@@ -400,7 +398,7 @@
@Test
public void test_createDirectory() throws IOException {
// Check if createDirectory is actually creating a directory.
- Path newDirectory = Paths.get(TEST_DIR, "newDir");
+ Path newDirectory = Paths.get(filesSetup.getTestDir(), "newDir");
assertFalse(Files.exists(newDirectory));
assertFalse(Files.isDirectory(newDirectory));
@@ -417,7 +415,7 @@
}
// File with unicode name.
- Path unicodeFilePath = Paths.get(TEST_DIR, "टेस्ट डायरेक्टरी");
+ Path unicodeFilePath = Paths.get(filesSetup.getTestDir(), "टेस्ट डायरेक्टरी");
provider.createDirectory(unicodeFilePath);
assertTrue(Files.exists(unicodeFilePath));
}
@@ -426,13 +424,13 @@
public void test_createDirectory$String$FileAttr() throws IOException {
Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
- provider.createDirectory(TEST_PATH, attr);
- assertEquals(attr.value(), Files.getAttribute(TEST_PATH, attr.name()));
+ provider.createDirectory(filesSetup.getTestPath(), attr);
+ assertEquals(attr.value(), Files.getAttribute(filesSetup.getTestPath(), attr.name()));
// Creating a new file and passing multiple attribute of the same name.
perm = PosixFilePermissions.fromString("rw-------");
FileAttribute<Set<PosixFilePermission>> attr1 = PosixFilePermissions.asFileAttribute(perm);
- Path dirPath2 = Paths.get(TEST_DIR, "new_file");
+ Path dirPath2 = Paths.get(filesSetup.getTestDir(), "new_file");
provider.createDirectory(dirPath2, attr, attr1);
// Value should be equal to the last attribute passed.
assertEquals(attr1.value(), Files.getAttribute(dirPath2, attr.name()));
@@ -448,7 +446,7 @@
} catch(NullPointerException expected) {}
try {
- provider.createDirectory(TEST_PATH, (FileAttribute<?>[]) null);
+ provider.createDirectory(filesSetup.getTestPath(), (FileAttribute<?>[]) null);
fail();
} catch(NullPointerException expected) {}
}
@@ -463,34 +461,37 @@
@Test
public void test_createSymbolicLink() throws IOException {
- provider.createSymbolicLink(/* Path of the symbolic link */ TEST_PATH,
- /* Path of the target of the symbolic link */ DATA_FILE_PATH.toAbsolutePath());
- assertTrue(Files.isSymbolicLink(TEST_PATH));
+ provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(),
+ /* Path of the target of the symbolic link */
+ filesSetup.getDataFilePath().toAbsolutePath());
+ assertTrue(Files.isSymbolicLink(filesSetup.getTestPath()));
// When file exists at the sym link location.
try {
- provider.createSymbolicLink(/* Path of the symbolic link */ TEST_PATH,
- /* Path of the target of the symbolic link */ DATA_FILE_PATH.toAbsolutePath());
+ provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(),
+ /* Path of the target of the symbolic link */
+ filesSetup.getDataFilePath().toAbsolutePath());
fail();
} catch (FileAlreadyExistsException expected) {} finally {
- Files.deleteIfExists(TEST_PATH);
+ Files.deleteIfExists(filesSetup.getTestPath());
}
// Sym link to itself
- provider.createSymbolicLink(/* Path of the symbolic link */ TEST_PATH,
- /* Path of the target of the symbolic link */ TEST_PATH.toAbsolutePath());
- assertTrue(Files.isSymbolicLink(TEST_PATH.toAbsolutePath()));
+ provider.createSymbolicLink(/* Path of the symbolic link */ filesSetup.getTestPath(),
+ /* Path of the target of the symbolic link */
+ filesSetup.getTestPath().toAbsolutePath());
+ assertTrue(Files.isSymbolicLink(filesSetup.getTestPath().toAbsolutePath()));
}
@Test
public void test_createSymbolicLink_NPE() throws IOException {
try {
- provider.createSymbolicLink(null, DATA_FILE_PATH.toAbsolutePath());
+ provider.createSymbolicLink(null, filesSetup.getDataFilePath().toAbsolutePath());
fail();
} catch (NullPointerException expected) {}
try {
- provider.createSymbolicLink(TEST_PATH, null);
+ provider.createSymbolicLink(filesSetup.getTestPath(), null);
fail();
} catch (NullPointerException expected) {}
}
@@ -501,8 +502,8 @@
Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions
.asFileAttribute(perm);
- provider.createSymbolicLink(TEST_PATH, DATA_FILE_PATH.toAbsolutePath()
- , attr);
+ provider.createSymbolicLink(filesSetup.getTestPath(),
+ filesSetup.getDataFilePath().toAbsolutePath(), attr);
fail();
} catch (UnsupportedOperationException expected) {}
}
@@ -514,18 +515,18 @@
.asFileAttribute(perm);
try {
- provider.createSymbolicLink(null, DATA_FILE_PATH.toAbsolutePath(), attr);
+ provider.createSymbolicLink(null, filesSetup.getDataFilePath().toAbsolutePath(), attr);
fail();
} catch (NullPointerException expected) {}
try {
- provider.createSymbolicLink(TEST_PATH, null, attr);
+ provider.createSymbolicLink(filesSetup.getTestPath(), null, attr);
fail();
} catch (NullPointerException expected) {}
try {
- provider.createSymbolicLink(TEST_PATH, DATA_FILE_PATH,
+ provider.createSymbolicLink(filesSetup.getTestPath(), filesSetup.getDataFilePath(),
(FileAttribute<?>[]) null);
fail();
} catch (NullPointerException expected) {}
@@ -534,19 +535,19 @@
@Test
public void test_delete() throws IOException {
// Delete existing file.
- provider.delete(DATA_FILE_PATH);
- assertFalse(Files.exists(DATA_FILE_PATH));
+ provider.delete(filesSetup.getDataFilePath());
+ assertFalse(Files.exists(filesSetup.getDataFilePath()));
// Delete non existing files.
try {
- provider.delete(TEST_PATH);
+ provider.delete(filesSetup.getTestPath());
fail();
} catch (NoSuchFileException expected) {
- assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+ assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
}
// Delete a directory.
- Path dirPath = Paths.get(TEST_DIR, "dir");
+ Path dirPath = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectory(dirPath);
provider.delete(dirPath);
assertFalse(Files.exists(dirPath));
@@ -554,7 +555,7 @@
// Delete a non empty directory.
Files.createDirectory(dirPath);
- Files.createFile(Paths.get(TEST_DIR, "dir/file"));
+ Files.createFile(Paths.get(filesSetup.getTestDir(), "dir/file"));
try {
provider.delete(dirPath);
fail();
@@ -572,21 +573,21 @@
@Test
public void test_deleteIfExist() throws IOException {
// Delete existing file.
- assertTrue(Files.deleteIfExists(DATA_FILE_PATH));
- assertFalse(Files.exists(DATA_FILE_PATH));
+ assertTrue(Files.deleteIfExists(filesSetup.getDataFilePath()));
+ assertFalse(Files.exists(filesSetup.getDataFilePath()));
// Delete non existing files.
- assertFalse(Files.deleteIfExists(TEST_PATH));
+ assertFalse(Files.deleteIfExists(filesSetup.getTestPath()));
// Delete a directory.
- Path dirPath = Paths.get(TEST_DIR, "dir");
+ Path dirPath = Paths.get(filesSetup.getTestDir(), "dir");
Files.createDirectory(dirPath);
assertTrue(Files.deleteIfExists(dirPath));
assertFalse(Files.exists(dirPath));
// Delete a non empty directory.
Files.createDirectory(dirPath);
- Files.createFile(Paths.get(TEST_DIR, "dir/file"));
+ Files.createFile(Paths.get(filesSetup.getTestDir(), "dir/file"));
try {
provider.deleteIfExists(dirPath);
fail();
@@ -603,63 +604,64 @@
@Test
public void test_copy() throws IOException {
- provider.copy(DATA_FILE_PATH, TEST_PATH);
- assertEquals(TEST_FILE_DATA, readFromFile(TEST_PATH));
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath());
+ assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath()));
// The original file should also exists.
- assertEquals(TEST_FILE_DATA, readFromFile(DATA_FILE_PATH));
+ assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getDataFilePath()));
// When target file exists.
try {
- provider.copy(DATA_FILE_PATH, TEST_PATH);
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath());
fail();
} catch (FileAlreadyExistsException expected) {}
// Copy to existing target file with REPLACE_EXISTING copy option.
- writeToFile(DATA_FILE_PATH, TEST_FILE_DATA_2);
- provider.copy(DATA_FILE_PATH, TEST_PATH, REPLACE_EXISTING);
- assertEquals(TEST_FILE_DATA_2, readFromFile(TEST_PATH));
+ writeToFile(filesSetup.getDataFilePath(), TEST_FILE_DATA_2);
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), REPLACE_EXISTING);
+ assertEquals(TEST_FILE_DATA_2, readFromFile(filesSetup.getTestPath()));
// Copy to the same file. Should not fail.
- reset();
- provider.copy(DATA_FILE_PATH, DATA_FILE_PATH);
- assertEquals(TEST_FILE_DATA, readFromFile(DATA_FILE_PATH));
+ filesSetup.reset();
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getDataFilePath());
+ assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getDataFilePath()));
// With target is a symbolic link file.
try {
- reset();
- Path symlink = Paths.get(TEST_DIR, "symlink");
- Path newFile = Paths.get(TEST_DIR, "newDir");
+ filesSetup.reset();
+ Path symlink = Paths.get(filesSetup.getTestDir(), "symlink");
+ Path newFile = Paths.get(filesSetup.getTestDir(), "newDir");
Files.createFile(newFile);
assertTrue(Files.exists(newFile));
- Files.createSymbolicLink(symlink, DATA_FILE_PATH);
- provider.copy(DATA_FILE_PATH, symlink);
+ Files.createSymbolicLink(symlink, filesSetup.getDataFilePath());
+ provider.copy(filesSetup.getDataFilePath(), symlink);
fail();
} catch (FileAlreadyExistsException expected) {}
- reset();
+ filesSetup.reset();
try {
- provider.copy(TEST_PATH, DATA_FILE_PATH, REPLACE_EXISTING);
+ provider.copy(filesSetup.getTestPath(), filesSetup.getDataFilePath(), REPLACE_EXISTING);
fail();
} catch (NoSuchFileException expected) {
- assertTrue(expected.getMessage().contains(TEST_PATH.toString()));
+ assertTrue(expected.getMessage().contains(filesSetup.getTestPath().toString()));
}
}
@Test
public void test_copy_NPE() throws IOException {
try {
- provider.copy((Path)null, TEST_PATH);
+ provider.copy((Path) null, filesSetup.getTestPath());
fail();
} catch(NullPointerException expected) {}
try {
- provider.copy(DATA_FILE_PATH, (Path)null);
+ provider.copy(filesSetup.getDataFilePath(), (Path) null);
fail();
} catch(NullPointerException expected) {}
try {
- provider.copy(DATA_FILE_PATH, TEST_PATH, (CopyOption[]) null);
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(),
+ (CopyOption[]) null);
fail();
} catch(NullPointerException expected) {}
}
@@ -668,39 +670,41 @@
public void test_copy_CopyOption() throws IOException {
// COPY_ATTRIBUTES
FileTime fileTime = FileTime.fromMillis(System.currentTimeMillis() - 10000);
- Files.setAttribute(DATA_FILE_PATH, "basic:lastModifiedTime", fileTime);
- provider.copy(DATA_FILE_PATH, TEST_PATH, COPY_ATTRIBUTES);
+ Files.setAttribute(filesSetup.getDataFilePath(), "basic:lastModifiedTime", fileTime);
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), COPY_ATTRIBUTES);
assertEquals(fileTime.to(TimeUnit.SECONDS),
- ((FileTime)Files.getAttribute(TEST_PATH,
+ ((FileTime) Files.getAttribute(filesSetup.getTestPath(),
"basic:lastModifiedTime")).to(TimeUnit.SECONDS));
- assertEquals(TEST_FILE_DATA, readFromFile(TEST_PATH));
+ assertEquals(TEST_FILE_DATA, readFromFile(filesSetup.getTestPath()));
// ATOMIC_MOVE
- Files.deleteIfExists(TEST_PATH);
+ Files.deleteIfExists(filesSetup.getTestPath());
try {
- provider.copy(DATA_FILE_PATH, TEST_PATH, ATOMIC_MOVE);
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(), ATOMIC_MOVE);
fail();
} catch (UnsupportedOperationException expected) {}
- Files.deleteIfExists(TEST_PATH);
+ Files.deleteIfExists(filesSetup.getTestPath());
try {
- provider.copy(DATA_FILE_PATH, TEST_PATH, NonStandardOption.OPTION1);
+ provider.copy(filesSetup.getDataFilePath(), filesSetup.getTestPath(),
+ NonStandardOption.OPTION1);
fail();
} catch (UnsupportedOperationException expected) {}
}
@Test
public void test_copy_directory() throws IOException {
- final Path dirPath = Paths.get(TEST_DIR, "dir1");
- final Path dirPath2 = Paths.get(TEST_DIR, "dir2");
+ final Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1");
+ final Path dirPath2 = Paths.get(filesSetup.getTestDir(), "dir2");
// Nested directory.
- final Path dirPath3 = Paths.get(TEST_DIR, "dir1/dir");
+ final Path dirPath3 = Paths.get(filesSetup.getTestDir(), "dir1/dir");
// Create dir1 and dir1/dir, and copying dir1/dir to dir2. Copy will create dir2, however,
// it will not copy the content of the source directory.
Files.createDirectory(dirPath);
Files.createDirectory(dirPath3);
- provider.copy(DATA_FILE_PATH, Paths.get(TEST_DIR, "dir1/" + DATA_FILE));
+ provider.copy(filesSetup.getDataFilePath(),
+ Paths.get(filesSetup.getTestDir(), "dir1/" + DATA_FILE));
provider.copy(dirPath, dirPath2);
assertTrue(Files.exists(dirPath2));
@@ -714,7 +718,7 @@
// When the target directory is not empty.
- Path dirPath4 = Paths.get(TEST_DIR, "dir4");
+ Path dirPath4 = Paths.get(filesSetup.getTestDir(), "dir4");
Files.createDirectories(dirPath4);
Path file = Paths.get("file");
Files.createFile(Paths.get(dirPath.toString(), file.toString()));
@@ -730,14 +734,14 @@
public void test_newDirectoryStream$Path$Filter() throws IOException {
// Initial setup of directory.
- Path path_root = Paths.get(TEST_DIR, "dir");
- Path path_dir1 = Paths.get(TEST_DIR, "dir/dir1");
- Path path_dir2 = Paths.get(TEST_DIR, "dir/dir2");
- Path path_dir3 = Paths.get(TEST_DIR, "dir/dir3");
+ Path path_root = Paths.get(filesSetup.getTestDir(), "dir");
+ Path path_dir1 = Paths.get(filesSetup.getTestDir(), "dir/dir1");
+ Path path_dir2 = Paths.get(filesSetup.getTestDir(), "dir/dir2");
+ Path path_dir3 = Paths.get(filesSetup.getTestDir(), "dir/dir3");
- Path path_f1 = Paths.get(TEST_DIR, "dir/f1");
- Path path_f2 = Paths.get(TEST_DIR, "dir/f2");
- Path path_f3 = Paths.get(TEST_DIR, "dir/f3");
+ Path path_f1 = Paths.get(filesSetup.getTestDir(), "dir/f1");
+ Path path_f2 = Paths.get(filesSetup.getTestDir(), "dir/f2");
+ Path path_f3 = Paths.get(filesSetup.getTestDir(), "dir/f3");
Files.createDirectory(path_root);
Files.createDirectory(path_dir1);
@@ -772,7 +776,7 @@
@Test
public void test_newDirectoryStream$Filter_Exception() throws IOException {
// Non existent directory.
- Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+ Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
DirectoryStream.Filter<Path> fileFilter = new DirectoryStream.Filter<Path>() {
@Override
public boolean accept(Path entry) throws IOException {
@@ -788,7 +792,7 @@
}
// File instead of directory.
- Path path_file1 = Paths.get(TEST_DIR, "newFile1");
+ Path path_file1 = Paths.get(filesSetup.getTestDir(), "newFile1");
Files.createFile(path_file1);
try (DirectoryStream<Path> directoryStream = provider.newDirectoryStream(path_file1,
fileFilter)) {
@@ -812,7 +816,7 @@
}
// Non existent directory.
- Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+ Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
try (DirectoryStream<Path> directoryStream = provider.newDirectoryStream(path_dir1,
(DirectoryStream.Filter<? super Path>) null)) {
fail();
diff --git a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
index c845cc3..6931978 100644
--- a/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
+++ b/luni/src/test/java/libcore/java/nio/file/FilesSetup.java
@@ -35,9 +35,7 @@
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
-public class FilesSetup implements TestRule {
-
- final static String TEST_DIR = "testDir";
+class FilesSetup implements TestRule {
final static String DATA_FILE = "dataFile";
@@ -47,19 +45,28 @@
final static String TEST_FILE_DATA_2 = "test";
- final static Path DATA_FILE_PATH = Paths.get(TEST_DIR, DATA_FILE);
+ private String testDir;
- final static Path TEST_PATH = Paths.get(TEST_DIR, NON_EXISTENT_FILE);
+ private Path dataFilePath;
- final static Path TEST_DIR_PATH = Paths.get(TEST_DIR);
+ private Path testPath;
- public void setUp() throws Exception {
+ private boolean filesInitialized = false;
+
+ void setUp() throws Exception {
initializeFiles();
}
- private static void initializeFiles() throws IOException {
- Files.createDirectory(TEST_DIR_PATH);
- File testInputFile = new File(TEST_DIR, DATA_FILE);
+ void tearDown() throws Exception {
+ filesInitialized = false;
+ clearAll();
+ }
+
+ private void initializeFiles() throws IOException {
+ testDir = Files.createTempDirectory("testDir").toString();
+ dataFilePath = Paths.get(testDir, DATA_FILE);
+ testPath = Paths.get(testDir, NON_EXISTENT_FILE);
+ File testInputFile = new File(testDir, DATA_FILE);
if (!testInputFile.exists()) {
testInputFile.createNewFile();
}
@@ -67,18 +74,36 @@
BufferedWriter bw = new BufferedWriter(fw);
bw.write(TEST_FILE_DATA);
bw.close();
+ filesInitialized = true;
}
- static public void tearDown() throws Exception {
- clearAll();
+ Path getTestPath() {
+ checkState();
+ return testPath;
}
- static void clearAll() throws IOException {
- Path root = Paths.get(TEST_DIR);
+ Path getDataFilePath() {
+ checkState();
+ return dataFilePath;
+ }
+
+ String getTestDir() {
+ checkState();
+ return testDir;
+ }
+
+ private void checkState() {
+ if (!filesInitialized) {
+ throw new IllegalStateException("Files are not setup.");
+ }
+ }
+
+ void clearAll() throws IOException {
+ Path root = Paths.get(testDir);
delete(root);
}
- static void reset() throws IOException {
+ void reset() throws IOException {
clearAll();
initializeFiles();
}
diff --git a/luni/src/test/java/libcore/java/nio/file/FilesTest.java b/luni/src/test/java/libcore/java/nio/file/FilesTest.java
index 6a7740a..3f967c9 100644
--- a/luni/src/test/java/libcore/java/nio/file/FilesTest.java
+++ b/luni/src/test/java/libcore/java/nio/file/FilesTest.java
@@ -19,13 +19,9 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.Statement;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import org.mockito.runners.MockitoJUnitRunner;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -53,9 +49,6 @@
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.READ;
-import static libcore.java.nio.file.FilesSetup.DATA_FILE_PATH;
-import static libcore.java.nio.file.FilesSetup.TEST_DIR;
-import static libcore.java.nio.file.FilesSetup.TEST_PATH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
@@ -115,7 +108,7 @@
@Test
public void test_newByteChannel() throws IOException {
- try (FileChannel sfc = FileChannel.open(DATA_FILE_PATH)) {
+ try (FileChannel sfc = FileChannel.open(filesSetup.getDataFilePath())) {
HashSet<OpenOption> openOptions = new HashSet<>();
openOptions.add(READ);
@@ -129,18 +122,18 @@
@Test
public void test_createFile() throws IOException {
- assertFalse(Files.exists(TEST_PATH));
- Files.createFile(TEST_PATH);
- assertTrue(Files.exists(TEST_PATH));
+ assertFalse(Files.exists(filesSetup.getTestPath()));
+ Files.createFile(filesSetup.getTestPath());
+ assertTrue(Files.exists(filesSetup.getTestPath()));
// File with unicode name.
- Path unicodeFilePath = Paths.get(TEST_DIR, "परीक्षण फ़ाइल");
+ Path unicodeFilePath = Paths.get(filesSetup.getTestDir(), "परीक्षण फ़ाइल");
Files.createFile(unicodeFilePath);
Files.exists(unicodeFilePath);
// When file exists.
try {
- Files.createFile(DATA_FILE_PATH);
+ Files.createFile(filesSetup.getDataFilePath());
fail();
} catch(FileAlreadyExistsException expected) {}
}
@@ -157,20 +150,20 @@
public void test_createFile$String$Attr() throws IOException {
Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
- Files.createFile(TEST_PATH, attr);
- assertEquals(attr.value(), Files.getAttribute(TEST_PATH, attr.name()));
+ Files.createFile(filesSetup.getTestPath(), attr);
+ assertEquals(attr.value(), Files.getAttribute(filesSetup.getTestPath(), attr.name()));
// Creating a new file and passing multiple attribute of the same name.
perm = PosixFilePermissions.fromString("rw-------");
FileAttribute<Set<PosixFilePermission>> attr1 = PosixFilePermissions.asFileAttribute(perm);
- Path filePath2 = Paths.get(TEST_DIR, "new_file");
+ Path filePath2 = Paths.get(filesSetup.getTestDir(), "new_file");
Files.createFile(filePath2, attr, attr1);
// Value should be equal to the last attribute passed.
assertEquals(attr1.value(), Files.getAttribute(filePath2, attr.name()));
// When file exists.
try {
- Files.createFile(DATA_FILE_PATH, attr);
+ Files.createFile(filesSetup.getDataFilePath(), attr);
fail();
} catch(FileAlreadyExistsException expected) {}
}
@@ -186,7 +179,7 @@
@Test
public void test_createDirectories() throws IOException {
// Should be able to create parent directories.
- Path dirPath = Paths.get(TEST_DIR, "dir1/dir2/dir3");
+ Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir3");
assertFalse(Files.exists(dirPath));
Files.createDirectories(dirPath);
assertTrue(Files.isDirectory(dirPath));
@@ -205,7 +198,7 @@
@Test
public void test_createDirectories$Path$Attr() throws IOException {
- Path dirPath = Paths.get(TEST_DIR, "dir1/dir2/dir3");
+ Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir3");
Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
assertFalse(Files.exists(dirPath));
@@ -221,7 +214,7 @@
assertEquals(attr.value(), Files.getAttribute(dirPath, attr.name()));
// Creating a new directory and passing multiple attribute of the same name.
- Path dirPath2 = Paths.get(TEST_DIR, "dir1/dir2/dir4");
+ Path dirPath2 = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir4");
Files.createDirectories(dirPath2, attr, attr1);
// Value should be equal to the last attribute passed.
assertEquals(attr1.value(), Files.getAttribute(dirPath2, attr.name()));
@@ -229,7 +222,7 @@
@Test
public void test_createDirectories$Path$Attr_NPE() throws IOException {
- Path dirPath = Paths.get(TEST_DIR, "dir1/dir2/dir3");
+ Path dirPath = Paths.get(filesSetup.getTestDir(), "dir1/dir2/dir3");
Set<PosixFilePermission> perm = PosixFilePermissions.fromString("rwx------");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perm);
try {
@@ -246,12 +239,12 @@
@Test
public void test_newDirectoryStream() throws IOException {
// Directory setup.
- Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
- Path path_dir2 = Paths.get(TEST_DIR, "newDir1/newDir2");
- Path path_dir3 = Paths.get(TEST_DIR, "newDir1/newDir3");
- Path path_file1 = Paths.get(TEST_DIR, "newDir1/newFile1");
- Path path_file2 = Paths.get(TEST_DIR, "newDir1/newFile2");
- Path path_file3 = Paths.get(TEST_DIR, "newDir1/newDir2/newFile3");
+ Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
+ Path path_dir2 = Paths.get(filesSetup.getTestDir(), "newDir1/newDir2");
+ Path path_dir3 = Paths.get(filesSetup.getTestDir(), "newDir1/newDir3");
+ Path path_file1 = Paths.get(filesSetup.getTestDir(), "newDir1/newFile1");
+ Path path_file2 = Paths.get(filesSetup.getTestDir(), "newDir1/newFile2");
+ Path path_file3 = Paths.get(filesSetup.getTestDir(), "newDir1/newDir2/newFile3");
Files.createDirectory(path_dir1);
Files.createDirectory(path_dir2);
@@ -277,14 +270,14 @@
public void test_newDirectoryStream_Exception() throws IOException {
// Non existent directory.
- Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+ Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_dir1)) {
fail();
} catch (NoSuchFileException expected) {
}
// File instead of directory.
- Path path_file1 = Paths.get(TEST_DIR, "newFile1");
+ Path path_file1 = Paths.get(filesSetup.getTestDir(), "newFile1");
Files.createFile(path_file1);
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_file1)) {
fail();
@@ -300,14 +293,14 @@
@Test
public void test_newDirectoryStream$Path$String() throws IOException {
// Directory setup.
- Path path_root = Paths.get(TEST_DIR, "dir");
- Path path_java1 = Paths.get(TEST_DIR, "dir/f1.java");
- Path path_java2 = Paths.get(TEST_DIR, "dir/f2.java");
- Path path_java3 = Paths.get(TEST_DIR, "dir/f3.java");
+ Path path_root = Paths.get(filesSetup.getTestDir(), "dir");
+ Path path_java1 = Paths.get(filesSetup.getTestDir(), "dir/f1.java");
+ Path path_java2 = Paths.get(filesSetup.getTestDir(), "dir/f2.java");
+ Path path_java3 = Paths.get(filesSetup.getTestDir(), "dir/f3.java");
- Path path_txt1 = Paths.get(TEST_DIR, "dir/f1.txt");
- Path path_txt2 = Paths.get(TEST_DIR, "dir/f2.txt");
- Path path_txt3 = Paths.get(TEST_DIR, "dir/f3.txt");
+ Path path_txt1 = Paths.get(filesSetup.getTestDir(), "dir/f1.txt");
+ Path path_txt2 = Paths.get(filesSetup.getTestDir(), "dir/f2.txt");
+ Path path_txt3 = Paths.get(filesSetup.getTestDir(), "dir/f3.txt");
Files.createDirectory(path_root);
// A directory with .java extension.
@@ -335,14 +328,14 @@
public void test_newDirectoryStream$Path$String_Exception() throws IOException {
// Non existent directory.
- Path path_dir1 = Paths.get(TEST_DIR, "newDir1");
+ Path path_dir1 = Paths.get(filesSetup.getTestDir(), "newDir1");
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_dir1, "*.c")) {
fail();
} catch (NoSuchFileException expected) {
}
// File instead of directory.
- Path path_file1 = Paths.get(TEST_DIR, "newFile1");
+ Path path_file1 = Paths.get(filesSetup.getTestDir(), "newFile1");
Files.createFile(path_file1);
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path_file1, "*.c")) {
fail();
diff --git a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java b/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
index 74d8473..397108c 100644
--- a/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
+++ b/luni/src/test/java/libcore/java/util/ConcurrentHashMapTest.java
@@ -22,7 +22,8 @@
public void test_getOrDefault() {
MapDefaultMethodTester.test_getOrDefault(new ConcurrentHashMap<>(),
- false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
+ false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/,
+ true /*getAcceptsAnyObject*/);
}
public void test_forEach() {
diff --git a/luni/src/test/java/libcore/java/util/HashMapTest.java b/luni/src/test/java/libcore/java/util/HashMapTest.java
index 12bf59b..d4585b7 100644
--- a/luni/src/test/java/libcore/java/util/HashMapTest.java
+++ b/luni/src/test/java/libcore/java/util/HashMapTest.java
@@ -16,14 +16,25 @@
package libcore.java.util;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.TreeMap;
public class HashMapTest extends junit.framework.TestCase {
public void test_getOrDefault() {
MapDefaultMethodTester.test_getOrDefault(new HashMap<>(), true /*acceptsNullKey*/,
- true /*acceptsNullValue*/);
+ true /*acceptsNullValue*/, true /*getAcceptsAnyObject*/);
}
public void test_forEach() {
@@ -70,6 +81,146 @@
.test_merge(new HashMap<>(), true /*acceptsNullKey*/);
}
+ public void test_spliterator_keySet() {
+ Map<String, Integer> m = new HashMap<>();
+ m.put("a", 1);
+ m.put("b", 2);
+ m.put("c", 3);
+ m.put("d", 4);
+ m.put("e", 5);
+ m.put("f", 6);
+ m.put("g", 7);
+ m.put("h", 8);
+ m.put("i", 9);
+ m.put("j", 10);
+ ArrayList<String> expectedKeys = new ArrayList<>(
+ Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"));
+ Set<String> keys = m.keySet();
+ SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys);
+ SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+ SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+ SpliteratorTester.runSizedTests(keys.spliterator(), 10);
+ assertEquals(Spliterator.DISTINCT | Spliterator.SIZED,
+ keys.spliterator().characteristics());
+ }
+
+ public void test_spliterator_values() {
+ Map<String, Integer> m = new HashMap<>();
+ m.put("a", 1);
+ m.put("b", 2);
+ m.put("c", 3);
+ m.put("d", 4);
+ m.put("e", 5);
+ m.put("f", 6);
+ m.put("g", 7);
+ m.put("h", 8);
+ m.put("i", 9);
+ m.put("j", 10);
+ ArrayList<Integer> expectedValues = new ArrayList<>(
+ Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ );
+ Collection<Integer> values = m.values();
+ SpliteratorTester.runBasicIterationTests(values.spliterator(), expectedValues);
+ SpliteratorTester.runBasicSplitTests(values, expectedValues);
+ SpliteratorTester.testSpliteratorNPE(values.spliterator());
+ SpliteratorTester.runSizedTests(values, 10);
+ assertEquals(Spliterator.SIZED, values.spliterator().characteristics());
+ }
+
+ public void test_spliterator_entrySet() {
+ MapDefaultMethodTester.test_entrySet_spliterator_unordered(new HashMap<>());
+
+ Map<String, Integer> m = new HashMap<>(Collections.singletonMap("key", 42));
+ assertEquals(Spliterator.DISTINCT | Spliterator.SIZED,
+ m.entrySet().spliterator().characteristics());
+ }
+
+ /**
+ * Checks that {@code HashMap.entrySet().spliterator().trySplit()}
+ * estimates half of the parents' estimate (rounded down, which
+ * can be an underestimate) but is not itself SIZED.
+ *
+ * These assertions are still stronger than what the documentation
+ * guarantees since un-SIZED Spliterators' size estimates may be off by
+ * an arbitrary amount.
+ */
+ public void test_entrySet_subsizeEstimates() {
+ Map<String, String> m = new HashMap<>();
+ assertNull(m.entrySet().spliterator().trySplit());
+ // For the empty map, the estimates are exact
+ assertEquals(0, m.entrySet().spliterator().estimateSize());
+ assertEquals(0, m.entrySet().spliterator().getExactSizeIfKnown());
+
+ m.put("key1", "value1");
+ assertSubsizeEstimate(m.entrySet().spliterator(), 0);
+ m.put("key2", "value2");
+ assertSubsizeEstimate(m.entrySet().spliterator(), 1);
+ m.put("key3", "value3");
+ m.put("key4", "value4");
+ m.put("key5", "value5");
+ m.put("key6", "value6");
+ m.put("key7", "value7");
+ m.put("key8", "value8");
+ assertSubsizeEstimate(m.entrySet().spliterator(), 4);
+
+ m.put("key9", "value9");
+ assertSubsizeEstimate(m.entrySet().spliterator(), 4);
+ assertFalse(m.entrySet().spliterator().trySplit().hasCharacteristics(Spliterator.SIZED));
+ }
+
+ /**
+ * Checks that HashMap.entrySet()'s spliterator halfs its estimate (rounding down)
+ * for each split, even though this estimate may be inaccurate.
+ */
+ public void test_entrySet_subsizeEstimates_recursive() {
+ Map<Integer, String> m = new HashMap<>();
+ for (int i = 0; i < 100; i++) {
+ m.put(i, "value");
+ }
+ Set<Map.Entry<Integer, String>> entries = m.entrySet();
+ // Recursive splitting - HashMap will estimate the size halving each split, rounding down.
+ assertSubsizeEstimate(entries.spliterator(), 50);
+ assertSubsizeEstimate(entries.spliterator().trySplit(), 25);
+ assertSubsizeEstimate(entries.spliterator().trySplit().trySplit(), 12);
+ assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit(), 6);
+ assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit().trySplit(), 3);
+ assertSubsizeEstimate(
+ entries.spliterator().trySplit().trySplit().trySplit().trySplit().trySplit(), 1);
+ assertSubsizeEstimate(entries.spliterator().trySplit().trySplit().trySplit().trySplit()
+ .trySplit().trySplit(), 0);
+ }
+
+ /**
+ * Checks that HashMap.EntryIterator is SIZED but not SUBSIZED.
+ */
+ public void test_entrySet_spliterator_sizedButNotSubsized() {
+ Map<String, String> m = new HashMap<>();
+ assertTrue(m.entrySet().spliterator().hasCharacteristics(Spliterator.SIZED));
+ assertFalse(m.entrySet().spliterator().hasCharacteristics(Spliterator.SUBSIZED));
+ m.put("key1", "value1");
+ m.put("key2", "value2");
+ assertTrue(m.entrySet().spliterator().hasCharacteristics(Spliterator.SIZED));
+ assertFalse(m.entrySet().spliterator().hasCharacteristics(Spliterator.SUBSIZED));
+ Spliterator<Map.Entry<String, String>> parent = m.entrySet().spliterator();
+ Spliterator<Map.Entry<String, String>> child = parent.trySplit();
+ assertFalse(parent.hasCharacteristics(Spliterator.SIZED));
+ assertFalse(child.hasCharacteristics(Spliterator.SIZED));
+ assertFalse(parent.hasCharacteristics(Spliterator.SUBSIZED));
+ assertFalse(child.hasCharacteristics(Spliterator.SUBSIZED));
+ }
+
+ /**
+ * Tests that the given spliterator can be trySplit(), resulting in children that each
+ * estimate the specified size.
+ */
+ private static<T> void assertSubsizeEstimate(Spliterator<T> spliterator,
+ long expectedEstimate) {
+ Spliterator<T> child = spliterator.trySplit();
+ assertNotNull(child);
+ assertEquals(expectedEstimate, spliterator.estimateSize());
+ assertEquals(expectedEstimate, child.estimateSize());
+ }
+
public void test_replaceAll() throws Exception {
HashMap<String, String> map = new HashMap<>();
map.put("one", "1");
@@ -83,12 +234,9 @@
assertEquals(3, map.size());
try {
- map.replaceAll(new java.util.function.BiFunction<String, String, String>() {
- @Override
- public String apply(String k, String v) {
- map.put("foo1", v);
- return v;
- }
+ map.replaceAll((k, v) -> {
+ map.put("foo1", v);
+ return v;
});
fail();
} catch(ConcurrentModificationException expected) {}
diff --git a/luni/src/test/java/libcore/java/util/HashtableTest.java b/luni/src/test/java/libcore/java/util/HashtableTest.java
index 40fbf25..a7650e2 100644
--- a/luni/src/test/java/libcore/java/util/HashtableTest.java
+++ b/luni/src/test/java/libcore/java/util/HashtableTest.java
@@ -24,7 +24,7 @@
public void test_getOrDefault() {
MapDefaultMethodTester.test_getOrDefault(new Hashtable<>(), false /*doesNotAcceptNullKey*/,
- false /*doesNotAcceptNullValue*/);
+ false /*doesNotAcceptNullValue*/, true /*getAcceptsAnyObject*/);
}
public void test_forEach() {
diff --git a/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java b/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java
index fec95b1..e25f8fe 100644
--- a/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java
+++ b/luni/src/test/java/libcore/java/util/LinkedHashMapTest.java
@@ -16,9 +16,22 @@
package libcore.java.util;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.lang.Iterable;
+import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
+import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -27,7 +40,7 @@
public void test_getOrDefault() {
MapDefaultMethodTester
.test_getOrDefault(new LinkedHashMap<>(), true /*acceptsNullKey*/,
- true /*acceptsNullValue*/);
+ true /*acceptsNullValue*/, true /*getAcceptsAnyObject*/);
// Test for access order
Map<String, String> m = new LinkedHashMap<String, String>(8, .75f, true);
@@ -209,33 +222,46 @@
assertEquals("value3", newest.getValue());
}
- // http://b/27929722
- // This tests the behaviour is consistent with earlier Android releases.
- // This behaviour is NOT consistent with the RI. Future Android releases
- // might change this.
+ // This tests the behaviour is consistent with the RI.
+ // This behaviour is NOT consistent with earlier Android releases up to
+ // and including Android N, see http://b/27929722
public void test_removeEldestEntry() {
final AtomicBoolean removeEldestEntryReturnValue = new AtomicBoolean(false);
final AtomicInteger removeEldestEntryCallCount = new AtomicInteger(0);
LinkedHashMap<String, String> m = new LinkedHashMap<String, String>() {
@Override
protected boolean removeEldestEntry(Entry eldest) {
+ int size = size();
+ assertEquals(size, iterableSize(entrySet()));
+ assertEquals(size, iterableSize(keySet()));
+ assertEquals(size, iterableSize(values()));
+ assertEquals(size, removeEldestEntryCallCount.get() + 1);
removeEldestEntryCallCount.incrementAndGet();
return removeEldestEntryReturnValue.get();
}
};
- m.put("foo", "bar");
assertEquals(0, removeEldestEntryCallCount.get());
- m.put("baz", "quux");
+ m.put("foo", "bar");
assertEquals(1, removeEldestEntryCallCount.get());
+ m.put("baz", "quux");
+ assertEquals(2, removeEldestEntryCallCount.get());
removeEldestEntryReturnValue.set(true);
m.put("foob", "faab");
- assertEquals(2, removeEldestEntryCallCount.get());
+ assertEquals(3, removeEldestEntryCallCount.get());
assertEquals(2, m.size());
assertFalse(m.containsKey("foo"));
}
+ private static<E> int iterableSize(Iterable<E> iterable) {
+ int result = 0;
+ for (E element : iterable) {
+ result++;
+ }
+ return result;
+ }
+
public void test_replaceAll() {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("one", "1");
@@ -249,12 +275,9 @@
assertEquals(3, map.size());
try {
- map.replaceAll(new java.util.function.BiFunction<String, String, String>() {
- @Override
- public String apply(String k, String v) {
- map.put("foo1", v);
- return v;
- }
+ map.replaceAll((k, v) -> {
+ map.put("foo1", v);
+ return v;
});
fail();
} catch(ConcurrentModificationException expected) {}
@@ -264,4 +287,162 @@
fail();
} catch(NullPointerException expected) {}
}
+
+ public void test_eldest_empty() {
+ LinkedHashMap<String, String> emptyMap = createMap();
+ assertNull(eldest(emptyMap));
+ }
+
+ public void test_eldest_nonempty() {
+ assertEntry("key", "value", eldest(createMap("key", "value")));
+ assertEntry("A", "1", eldest(createMap("A", "1", "B", "2", "C", "3")));
+ assertEntry("A", "4", eldest(createMap("A", "1", "B", "2", "C", "3", "A", "4")));
+ assertEntry("A", "4", eldest(createMap("A", "1", "B", "2", "C", "3", "A", "4", "D", "5")));
+ }
+
+ public void test_eldest_compatibleWithIterationOrder() {
+ check_eldest_comparibleWithIterationOrder(createMap());
+ check_eldest_comparibleWithIterationOrder(createMap("key", "value"));
+ check_eldest_comparibleWithIterationOrder(createMap("A", "1", "B", "2"));
+ check_eldest_comparibleWithIterationOrder(createMap("A", "1", "B", "2", "A", "3"));
+ check_eldest_comparibleWithIterationOrder(createMap("A", "1", "A", "2", "A", "3"));
+
+ Random random = new Random(31337); // arbitrary
+ LinkedHashMap<String, String> m = new LinkedHashMap<>();
+ for (int i = 0; i < 8000; i++) {
+ m.put(String.valueOf(random.nextInt(4000)), String.valueOf(random.nextDouble()));
+ }
+ check_eldest_comparibleWithIterationOrder(m);
+ }
+
+ private void check_eldest_comparibleWithIterationOrder(LinkedHashMap<?, ?> map) {
+ Iterator<? extends Map.Entry<?, ?>> it = map.entrySet().iterator();
+ if (it.hasNext()) {
+ Map.Entry<?, ?> expected = it.next();
+ Object expectedKey = expected.getKey();
+ Object expectedValue = expected.getValue();
+ assertEntry(expectedKey, expectedValue, eldest(map));
+ } else {
+ assertNull(eldest(map));
+ }
+ }
+
+ /**
+ * Check that {@code LinkedHashMap.Entry} compiles and refers to
+ * {@link java.util.Map.Entry}, which is required for source
+ * compatibility with earlier versions of Android.
+ * If this test fails to compile, the code under test is broken.
+ */
+ public void test_entryCompatibility_compiletime() {
+ Class c = LinkedHashMap.Entry.class;
+ assertEquals("java.util.Map$Entry", c.getName());
+ assertEquals(Map.Entry.class, c);
+ }
+
+ /**
+ * Checks that there is no nested class named 'Entry' in LinkedHashMap.
+ * If {@link #test_entryCompatibility_compiletime()} passes but
+ * this test fails, then the test was probably compiled against a
+ * version of LinkedHashMap that does not have a nested Entry class,
+ * but run against a version that does.
+ */
+ public void test_entryCompatibility_runtime() {
+ String forbiddenClassName = "java.util.LinkedHashMap$Entry";
+ try {
+ Class.forName(forbiddenClassName);
+ fail("Class " + forbiddenClassName + " should not exist");
+ } catch (ClassNotFoundException expected) {
+ // pass
+ }
+ }
+
+ public void test_spliterator_keySet() {
+ Map<String, Integer> m = new LinkedHashMap<>();
+ m.put("a", 1);
+ m.put("b", 2);
+ m.put("c", 3);
+ m.put("d", 4);
+ m.put("e", 5);
+ m.put("f", 6);
+ m.put("g", 7);
+ m.put("h", 8);
+ m.put("i", 9);
+ m.put("j", 10);
+ ArrayList<String> expectedKeys = new ArrayList<>(
+ Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"));
+ Set<String> keys = m.keySet();
+ SpliteratorTester.runBasicIterationTests(keys.spliterator(), expectedKeys);
+ SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
+ SpliteratorTester.testSpliteratorNPE(keys.spliterator());
+ SpliteratorTester.runOrderedTests(keys);
+ SpliteratorTester.runSizedTests(keys.spliterator(), 10);
+ SpliteratorTester.runSubSizedTests(keys.spliterator(), 10);
+ assertEquals(
+ Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED
+ | Spliterator.SUBSIZED,
+ keys.spliterator().characteristics());
+ }
+
+ public void test_spliterator_values() {
+ Map<String, Integer> m = new LinkedHashMap<>();
+ m.put("a", 1);
+ m.put("b", 2);
+ m.put("c", 3);
+ m.put("d", 4);
+ m.put("e", 5);
+ m.put("f", 6);
+ m.put("g", 7);
+ m.put("h", 8);
+ m.put("i", 9);
+ m.put("j", 10);
+ ArrayList<Integer> expectedValues = new ArrayList<>(
+ Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ );
+ Collection<Integer> values = m.values();
+ SpliteratorTester.runBasicIterationTests(
+ values.spliterator(), expectedValues);
+ SpliteratorTester.runBasicSplitTests(values, expectedValues);
+ SpliteratorTester.testSpliteratorNPE(values.spliterator());
+ SpliteratorTester.runOrderedTests(values);
+ SpliteratorTester.runSizedTests(values, 10);
+ SpliteratorTester.runSubSizedTests(values, 10);
+ assertEquals(Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED,
+ values.spliterator().characteristics());
+ }
+
+ public void test_spliterator_entrySet() {
+ MapDefaultMethodTester
+ .test_entrySet_spliterator_unordered(new LinkedHashMap<>());
+
+ Map<String, Integer> m = new LinkedHashMap<>(Collections.singletonMap("key", 23));
+ assertEquals(
+ Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED |
+ Spliterator.SUBSIZED,
+ m.entrySet().spliterator().characteristics());
+ }
+
+ private static Map.Entry<?, ?> eldest(LinkedHashMap<?,?> map) {
+ // Should be the same as: return (map.isEmpty()) ? null : map.entrySet().iterator().next();
+ return map.eldest();
+ }
+
+ private static void assertEntry(Object key, Object value, Map.Entry<?, ?> entry) {
+ String msg = String.format(Locale.US, "Expected (%s, %s), got (%s, %s)",
+ key, value, entry.getKey(), entry.getValue());
+ boolean equal = Objects.equals(key, entry.getKey())
+ && Objects.equals(value, entry.getValue());
+ if (!equal) {
+ fail(msg);
+ }
+ }
+
+ private static<T> LinkedHashMap<T, T> createMap(T... keysAndValues) {
+ assertEquals(0, keysAndValues.length % 2);
+ LinkedHashMap<T, T> result = new LinkedHashMap<>();
+ for (int i = 0; i < keysAndValues.length; i += 2) {
+ result.put(keysAndValues[i], keysAndValues[i+1]);
+ }
+ return result;
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java b/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java
index e3c4ad1..098801b 100644
--- a/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java
+++ b/luni/src/test/java/libcore/java/util/MapDefaultMethodTester.java
@@ -16,39 +16,119 @@
package libcore.java.util;
+import java.util.AbstractList;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.Spliterator;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertSame;
public class MapDefaultMethodTester {
private MapDefaultMethodTester() {}
- public static void test_getOrDefault(Map<Integer, Double> m, boolean acceptsNullKey,
- boolean acceptsNullValue) {
- // Unmapped key
- assertEquals(-1.0, m.getOrDefault(1, -1.0));
-
- // Mapped key
- m.put(1, 11.0);
- assertEquals(11.0, m.getOrDefault(1, -1.0));
-
- // Check for null value
- if (acceptsNullValue) {
- m.put(1, null);
- assertEquals(null, m.getOrDefault(1, -1.0));
- }
-
- // Check for null key
+ /**
+ * @param getAcceptsAnyObject whether get() and getOrDefault() allow any
+ * nonnull key Object, returning false rather than throwing
+ * ClassCastException if the key is inappropriate for the map.
+ */
+ public static void test_getOrDefault(Map<String, String> m, boolean acceptsNullKey,
+ boolean acceptsNullValue, boolean getAcceptsAnyObject) {
+ // absent key
if (acceptsNullKey) {
- m.put(null, 1.0);
- assertEquals(1.0, m.getOrDefault(null, -1.0));
+ checkGetOrDefault("default", m, null, "default");
+ if (acceptsNullValue) {
+ checkGetOrDefault(null, m, null, null);
+ }
}
+ m.put("key", "value");
+ if (acceptsNullValue) {
+ checkGetOrDefault(null, m, "absentkey", null);
+ if (acceptsNullKey) {
+ checkGetOrDefault(null, m, null, null);
+ }
+ }
+ checkGetOrDefault("default", m, "absentkey", "default");
+ m.put("anotherkey", "anothervalue");
+ checkGetOrDefault("default", m, "absentkey", "default");
+
+ // absent key - inappropriate type
+ boolean getAcceptedObject;
+ try {
+ assertSame("default", m.getOrDefault(new Object(), "default"));
+ getAcceptedObject = true;
+ } catch (ClassCastException e) {
+ getAcceptedObject = false;
+ }
+ assertEquals(getAcceptsAnyObject, getAcceptedObject);
+
+ // present key
+ checkGetOrDefault("value", m, "key", "default");
+ checkGetOrDefault("value", m, "key", new String("value"));
+
+ // null value
+ if (acceptsNullValue) {
+ m.put("keyWithNullValue", null);
+ checkGetOrDefault(null, m, "keyWithNullValue", "default");
+ }
+
+ // null key
+ if (acceptsNullKey) {
+ m.put(null, "valueForNullKey");
+ checkGetOrDefault("valueForNullKey", m, null, "valueForNullKey");
+ }
+ }
+
+ /**
+ * Checks that the value returned by {@link LinkedHashMap#getOrDefault(Object, Object)}
+ * is consistent with various other ways getOrDefault() could be computed.
+ */
+ private static<K, V> void checkGetOrDefault(
+ V expected, Map<K, V> map, K key, V defaultValue) {
+ V actual = map.getOrDefault(key, defaultValue);
+ assertSame(expected, actual);
+
+ assertSame(expected, getOrDefault_hashMap(map, key, defaultValue));
+ assertSame(expected, getOrDefault_optimizeForPresent(map, key, defaultValue));
+ assertSame(expected, getOrDefault_optimizeForAbsent(map, key, defaultValue));
+ }
+
+ /** Implementation of getOrDefault() on top of HashMap.getOrDefault(). */
+ private static<K, V> V getOrDefault_hashMap(Map<K, V> map, K key, V defaultValue) {
+ return new HashMap<>(map).getOrDefault(key, defaultValue);
+ }
+
+ /**
+ * Implementation of Map.getOrDefault() that only needs one lookup if the key is
+ * absent.
+ */
+ private static<K, V> V getOrDefault_optimizeForAbsent(Map<K, V> map, K key, V defaultValue) {
+ return map.containsKey(key) ? map.get(key) : defaultValue;
+ }
+
+ /**
+ * Implementation of getOrDefault() that only needs one lookup if the key is
+ * present and not mapped to null.
+ */
+ private static<K, V> V getOrDefault_optimizeForPresent(Map<K, V> map, K key, V defaultValue) {
+ V result = map.get(key);
+ if (result == null && !map.containsKey(key)) {
+ result = defaultValue;
+ }
+ return result;
}
public static void test_forEach(Map<Integer, Double> m) {
@@ -63,6 +143,7 @@
// Null pointer exception for empty function
try {
m.forEach(null);
+ fail();
} catch (NullPointerException expected) {
}
}
@@ -275,6 +356,7 @@
// If the remapping function is null
try {
m.computeIfPresent(1, null);
+ fail();
} catch (NullPointerException expected) {}
if (acceptsNullKey) {
@@ -283,6 +365,7 @@
} else {
try {
m.computeIfPresent(null, (k, v) -> 5.0);
+ fail();
} catch (NullPointerException expected) {}
}
}
@@ -351,4 +434,79 @@
} catch (NullPointerException expected) {}
}
}
+
+ public static void test_entrySet_spliterator_unordered(Map<String, String> m) {
+ checkEntrySpliterator(m);
+ m.put("key", "value");
+ checkEntrySpliterator(m, "key", "value");
+ m.put("key2", "value2");
+ checkEntrySpliterator(m, "key", "value", "key2", "value2");
+ m.put("key", "newValue");
+ checkEntrySpliterator(m, "key", "newValue", "key2", "value2");
+ m.remove("key2");
+ checkEntrySpliterator(m, "key", "newValue");
+ m.clear();
+
+ // Check 100 entries in random order
+ Random random = new Random(1000); // arbitrary
+
+ final List<Integer> order = new ArrayList<>(new AbstractList<Integer>() {
+ @Override public Integer get(int index) { return index; }
+ @Override public int size() { return 100; }
+ });
+ List<Map.Entry<String, String>> entries = new AbstractList<Map.Entry<String, String>>() {
+ @Override
+ public Map.Entry<String, String> get(int index) {
+ int i = order.get(index);
+ return new AbstractMap.SimpleEntry<>("key" + i, "value" + i);
+ }
+ @Override public int size() { return order.size(); }
+ };
+ Collections.shuffle(order, random); // Pick a random put() order of the entries
+ for (Map.Entry<String, String> entry : entries) {
+ m.put(entry.getKey(), entry.getValue());
+ }
+ Collections.shuffle(order, random); // Pick a different random order for the assertion
+ checkEntrySpliterator(m, new ArrayList<>(entries));
+ }
+
+ private static void checkEntrySpliterator(Map<String, String> m,
+ String... expectedKeysAndValues) {
+ checkEntrySpliterator(m, makeEntries(expectedKeysAndValues));
+ }
+
+ private static void checkEntrySpliterator(Map<String, String> m,
+ ArrayList<Map.Entry<String, String>> expectedEntries) {
+ Set<Map.Entry<String, String>> entrySet = m.entrySet();
+ Comparator<Map.Entry<String, String>> keysThenValuesComparator =
+ Map.Entry.<String, String>comparingByKey()
+ .thenComparing(Map.Entry.comparingByValue());
+
+ assertTrue(entrySet.spliterator().hasCharacteristics(Spliterator.DISTINCT));
+
+ SpliteratorTester.runBasicIterationTests_unordered(entrySet.spliterator(),
+ expectedEntries, keysThenValuesComparator);
+ SpliteratorTester.runBasicSplitTests(entrySet.spliterator(),
+ expectedEntries, keysThenValuesComparator);
+ SpliteratorTester.testSpliteratorNPE(entrySet.spliterator());
+
+ boolean isSized = entrySet.spliterator().hasCharacteristics(Spliterator.SIZED);
+ if (isSized) {
+ SpliteratorTester.runSizedTests(entrySet.spliterator(), entrySet.size());
+ }
+ Spliterator<?> subSpliterator = entrySet.spliterator().trySplit();
+ if (subSpliterator != null && subSpliterator.hasCharacteristics(Spliterator.SIZED)) {
+ SpliteratorTester.runSubSizedTests(entrySet.spliterator(), entrySet.size());
+ }
+ }
+
+ private static<T> ArrayList<Map.Entry<T, T>> makeEntries(T... keysAndValues) {
+ assertEquals(0, keysAndValues.length % 2);
+ ArrayList<Map.Entry<T, T>> result = new ArrayList<>();
+ for (int i = 0; i < keysAndValues.length; i += 2) {
+ result.add(new AbstractMap.SimpleEntry<>(keysAndValues[i], keysAndValues[i+1]));
+ }
+ return result;
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/util/SpliteratorTester.java b/luni/src/test/java/libcore/java/util/SpliteratorTester.java
index a5b076b..9538aef 100644
--- a/luni/src/test/java/libcore/java/util/SpliteratorTester.java
+++ b/luni/src/test/java/libcore/java/util/SpliteratorTester.java
@@ -21,6 +21,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
+import java.util.Map;
import java.util.Spliterator;
import java.util.function.Consumer;
@@ -55,8 +56,12 @@
Consumer<T> consumer = (T value) -> recorder.add(value);
// tryAdvance.
- assertTrue(spliterator.tryAdvance(consumer));
- assertTrue(expectedElements.contains(recorder.get(0)));
+ if (expectedElements.isEmpty()) {
+ assertFalse(spliterator.tryAdvance(consumer));
+ } else {
+ assertTrue(spliterator.tryAdvance(consumer));
+ assertTrue(expectedElements.contains(recorder.get(0)));
+ }
// forEachRemaining.
spliterator.forEachRemaining(consumer);
@@ -106,7 +111,8 @@
ArrayList<T> recorder = new ArrayList<>();
// Advance the original spliterator by one element.
- assertTrue(spliterator.tryAdvance(value -> recorder.add(value)));
+ boolean didAdvance = spliterator.tryAdvance(value -> recorder.add(value));
+ assertEquals(!expectedElements.isEmpty(), didAdvance);
// Try splitting it.
Spliterator<T> split1 = spliterator.trySplit();
@@ -168,7 +174,12 @@
assertEquals(iteration1, iteration2);
}
+ /**
+ * Checks that the specified SIZED Spliterator reports containing the
+ * specified number of elements.
+ */
public static <T> void runSizedTests(Spliterator<T> spliterator, int expectedSize) {
+ assertTrue(spliterator.hasCharacteristics(Spliterator.SIZED));
assertEquals(expectedSize, spliterator.estimateSize());
assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
}
@@ -177,14 +188,28 @@
runSizedTests(spliterable.spliterator(), expectedSize);
}
+ /**
+ * Checks that the specified Spliterator and its {@link Spliterator#trySplit()
+ * children} are SIZED and SUBSIZED and report containing the specified number
+ * of elements.
+ */
public static <T> void runSubSizedTests(Spliterator<T> spliterator, int expectedSize) {
+ assertTrue(spliterator.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
assertEquals(expectedSize, spliterator.estimateSize());
assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
-
- Spliterator<T> split1 = spliterator.trySplit();
- assertEquals(expectedSize, spliterator.estimateSize() + split1.estimateSize());
- assertEquals(expectedSize, spliterator.getExactSizeIfKnown() + split1.getExactSizeIfKnown());
+ Spliterator<T> child = spliterator.trySplit();
+ assertTrue(spliterator.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
+ if (expectedSize == 0) {
+ assertNull(child);
+ assertEquals(expectedSize, spliterator.estimateSize());
+ assertEquals(expectedSize, spliterator.getExactSizeIfKnown());
+ } else {
+ assertTrue(child.hasCharacteristics(Spliterator.SIZED | Spliterator.SUBSIZED));
+ assertEquals(expectedSize, spliterator.estimateSize() + child.estimateSize());
+ assertEquals(expectedSize,
+ spliterator.getExactSizeIfKnown() + child.getExactSizeIfKnown());
+ }
}
public static <T> void runSubSizedTests(Iterable<T> spliterable, int expectedSize) {
diff --git a/luni/src/test/java/libcore/java/util/TreeMapTest.java b/luni/src/test/java/libcore/java/util/TreeMapTest.java
index 355d29d..749ffa1 100644
--- a/luni/src/test/java/libcore/java/util/TreeMapTest.java
+++ b/luni/src/test/java/libcore/java/util/TreeMapTest.java
@@ -495,13 +495,14 @@
String::compareTo);
SpliteratorTester.runBasicSplitTests(keys, expectedKeys);
SpliteratorTester.testSpliteratorNPE(keys.spliterator());
-
- assertTrue(keys.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED));
+ assertEquals(
+ Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED,
+ keys.spliterator().characteristics());
SpliteratorTester.runSortedTests(keys);
SpliteratorTester.runOrderedTests(keys);
}
- public void test_spliterator_valueSet() {
+ public void test_spliterator_values() {
TreeMap<String, String> treeMap = new TreeMap<>();
treeMap.put("a", "1");
treeMap.put("b", "2");
@@ -528,7 +529,8 @@
SpliteratorTester.runBasicSplitTests(values, expectedValues);
SpliteratorTester.testSpliteratorNPE(values.spliterator());
- assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SIZED));
+ assertEquals(Spliterator.ORDERED | Spliterator.SIZED,
+ values.spliterator().characteristics());
SpliteratorTester.runSizedTests(values, 16);
SpliteratorTester.runOrderedTests(values);
}
@@ -552,20 +554,22 @@
treeMap.put("o", "15");
treeMap.put("p", "16");
- Set<Map.Entry<String, String>> values = treeMap.entrySet();
- ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(values);
+ Set<Map.Entry<String, String>> entries = treeMap.entrySet();
+ ArrayList<Map.Entry<String, String>> expectedValues = new ArrayList<>(entries);
Comparator<Map.Entry<String, String>> comparator =
(a, b) -> (a.getKey().compareTo(b.getKey()));
- SpliteratorTester.runBasicIterationTests_unordered(values.spliterator(), expectedValues,
+ SpliteratorTester.runBasicIterationTests_unordered(entries.spliterator(), expectedValues,
(a, b) -> (a.getKey().compareTo(b.getKey())));
- SpliteratorTester.runBasicSplitTests(values, expectedValues, comparator);
- SpliteratorTester.testSpliteratorNPE(values.spliterator());
+ SpliteratorTester.runBasicSplitTests(entries, expectedValues, comparator);
+ SpliteratorTester.testSpliteratorNPE(entries.spliterator());
- assertTrue(values.spliterator().hasCharacteristics(Spliterator.ORDERED | Spliterator.SORTED));
- SpliteratorTester.runSortedTests(values, (a, b) -> (a.getKey().compareTo(b.getKey())));
- SpliteratorTester.runOrderedTests(values);
+ assertEquals(
+ Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SORTED,
+ entries.spliterator().characteristics());
+ SpliteratorTester.runSortedTests(entries, (a, b) -> (a.getKey().compareTo(b.getKey())));
+ SpliteratorTester.runOrderedTests(entries);
}
public void test_replaceAll() throws Exception {
@@ -586,12 +590,9 @@
} catch(NullPointerException expected) {}
try {
- map.replaceAll(new java.util.function.BiFunction<String, String, String>() {
- @Override
- public String apply(String k, String v) {
- map.put("foo", v);
- return v;
- }
+ map.replaceAll((k, v) -> {
+ map.put("foo", v);
+ return v;
});
fail();
} catch(ConcurrentModificationException expected) {}
diff --git a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
index 5cbaa8f..54b354e 100644
--- a/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
+++ b/luni/src/test/java/libcore/java/util/concurrent/ConcurrentSkipListMapTest.java
@@ -24,7 +24,8 @@
public void test_getOrDefault() {
MapDefaultMethodTester.test_getOrDefault(new ConcurrentSkipListMap<>(),
- false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/);
+ false /*doesNotAcceptNullKey*/, false /*doesNotAcceptNullValue*/,
+ false /*getAcceptsAnyObject*/);
}
public void test_forEach() {
diff --git a/non_openjdk_java_files.mk b/non_openjdk_java_files.mk
index 0719fdf..14fd77b 100644
--- a/non_openjdk_java_files.mk
+++ b/non_openjdk_java_files.mk
@@ -36,6 +36,7 @@
dalvik/src/main/java/dalvik/annotation/TestTarget.java \
dalvik/src/main/java/dalvik/annotation/TestTargetClass.java \
dalvik/src/main/java/dalvik/annotation/Throws.java \
+ dalvik/src/main/java/dalvik/annotation/optimization/FastNative.java \
dalvik/src/main/java/dalvik/bytecode/OpcodeInfo.java \
dalvik/src/main/java/dalvik/bytecode/Opcodes.java \
dalvik/src/main/java/dalvik/system/AllocationLimitError.java \
diff --git a/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java b/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java
deleted file mode 100644
index 68a736c..0000000
--- a/ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.lang.invoke;
-
-import java.lang.invoke.MethodHandles.Lookup;
-import java.lang.reflect.Member;
-
-public
-interface MethodHandleInfo {
- public static final int REF_getField = 0;
- public static final int REF_getStatic = 0;
- public static final int REF_putField = 0;
- public static final int REF_putStatic = 0;
- public static final int REF_invokeVirtual = 0;
- public static final int REF_invokeStatic = 0;
- public static final int REF_invokeSpecial = 0;
- public static final int REF_newInvokeSpecial = 0;
- public static final int REF_invokeInterface = 0;
-
- public int getReferenceKind();
-
- public Class<?> getDeclaringClass();
-
- public String getName();
-
- public MethodType getMethodType();
-
- public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
-
- public int getModifiers();
-
- public default boolean isVarArgs() { return false; }
-
- public static String referenceKindToString(int referenceKind) { return null; }
-
- public static String toString(int kind, Class<?> defc, String name, MethodType type) {
- return null;
- }
-}
diff --git a/ojluni/src/main/java/java/lang/Class.java b/ojluni/src/main/java/java/lang/Class.java
index 66a1917..44eadce 100644
--- a/ojluni/src/main/java/java/lang/Class.java
+++ b/ojluni/src/main/java/java/lang/Class.java
@@ -28,42 +28,24 @@
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
-import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
-import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.ref.SoftReference;
import java.io.InputStream;
-import java.io.ObjectStreamField;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.LinkedList;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.Map;
import java.util.HashMap;
-import sun.misc.Unsafe;
import sun.reflect.CallerSensitive;
import java.lang.annotation.Inherited;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Proxy;
-import sun.reflect.annotation.*;
-import sun.reflect.misc.ReflectUtil;
import java.io.Serializable;
-import java.lang.reflect.AccessibleObject;
import com.android.dex.Dex;
import dalvik.system.VMStack;
import libcore.reflect.InternalNames;
@@ -72,7 +54,6 @@
import libcore.util.BasicLruCache;
import libcore.util.CollectionUtils;
import libcore.util.EmptyArray;
-import libcore.util.SneakyThrow;
import java.util.Collections;
/**
@@ -258,8 +239,6 @@
/** Offset of the first virtual method defined in this class in the methods array. */
private transient short virtualMethodsOffset;
- private AnnotationType annotationType;
-
private static final int ANNOTATION = 0x00002000;
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;
@@ -1760,7 +1739,6 @@
* resolution checks first. If no fields exist, the list is not modified.
*
* @param publicOnly Whether to return only public fields.
- * @param fields A list to populate with declared fields.
* @hide
*/
public native Field[] getDeclaredFieldsUnchecked(boolean publicOnly);
@@ -1819,7 +1797,6 @@
* resolution checks first. If no methods exist, the list is not modified.
*
* @param publicOnly Whether to return only public methods.
- * @param methods A list to populate with declared methods.
* @hide
*/
public native Method[] getDeclaredMethodsUnchecked(boolean publicOnly);
@@ -1867,8 +1844,6 @@
/**
* Returns the constructor with the given parameters if it is defined by this class;
* {@code null} otherwise. This may return a non-public member.
- *
- * @param args the types of the parameters to the constructor.
*/
private native Constructor<?>[] getDeclaredConstructorsInternal(boolean publicOnly);
@@ -2477,18 +2452,6 @@
*/
private native boolean isDeclaredAnnotationPresent(Class<? extends Annotation> annotationClass);
- // Annotation types cache their internal (AnnotationType) form
-
- /** @hide */
- public void setAnnotationType(AnnotationType type) {
- annotationType = type;
- }
-
- /** @hide */
- public AnnotationType getAnnotationType() {
- return annotationType;
- }
-
private String getSignatureAttribute() {
String[] annotation = getSignatureAnnotation();
if (annotation == null) {
diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java b/ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java
new file mode 100644
index 0000000..08ce6f0
--- /dev/null
+++ b/ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.lang.invoke;
+
+import java.lang.reflect.*;
+import java.util.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+
+/**
+ * A symbolic reference obtained by cracking a direct method handle
+ * into its consitutent symbolic parts.
+ * To crack a direct method handle, call {@code Lookup#revealDirect Lookup.revealDirect}.
+ * <h1><a name="directmh"></a>Direct Method Handles</h1>
+ * A <em>direct method handle</em> represents a method, constructor, or field without
+ * any intervening argument bindings or other transformations.
+ * The method, constructor, or field referred to by a direct method handle is called
+ * its <em>underlying member</em>.
+ * Direct method handles may be obtained in any of these ways:
+ * <ul>
+ * <li>By executing an {@code ldc} instruction on a {@code CONSTANT_MethodHandle} constant.
+ * (See the Java Virtual Machine Specification, sections 4.4.8 and 5.4.3.)
+ * <li>By calling one of the <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>,
+ * such as {@code Lookup#findVirtual Lookup.findVirtual},
+ * to resolve a symbolic reference into a method handle.
+ * A symbolic reference consists of a class, name string, and type.
+ * <li>By calling the factory method {@code Lookup#unreflect Lookup.unreflect}
+ * or {@code Lookup#unreflectSpecial Lookup.unreflectSpecial}
+ * to convert a {@link Method} into a method handle.
+ * <li>By calling the factory method {@code Lookup#unreflectConstructor Lookup.unreflectConstructor}
+ * to convert a {@link Constructor} into a method handle.
+ * <li>By calling the factory method {@code Lookup#unreflectGetter Lookup.unreflectGetter}
+ * or {@code Lookup#unreflectSetter Lookup.unreflectSetter}
+ * to convert a {@link Field} into a method handle.
+ * </ul>
+ *
+ * <h1>Restrictions on Cracking</h1>
+ * Given a suitable {@code Lookup} object, it is possible to crack any direct method handle
+ * to recover a symbolic reference for the underlying method, constructor, or field.
+ * Cracking must be done via a {@code Lookup} object equivalent to that which created
+ * the target method handle, or which has enough access permissions to recreate
+ * an equivalent method handle.
+ * <p>
+ * If the underlying method is <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a>,
+ * the direct method handle will have been "bound" to a particular caller class, the
+ * {@code java.lang.invoke.MethodHandles.Lookup#lookupClass() lookup class}
+ * of the lookup object used to create it.
+ * Cracking this method handle with a different lookup class will fail
+ * even if the underlying method is public (like {@code Class.forName}).
+ * <p>
+ * The requirement of lookup object matching provides a "fast fail" behavior
+ * for programs which may otherwise trust erroneous revelation of a method
+ * handle with symbolic information (or caller binding) from an unexpected scope.
+ * Use {@code java.lang.invoke.MethodHandles#reflectAs} to override this limitation.
+ *
+ * <h1><a name="refkinds"></a>Reference kinds</h1>
+ * The <a href="MethodHandles.Lookup.html#lookups">Lookup Factory Methods</a>
+ * correspond to all major use cases for methods, constructors, and fields.
+ * These use cases may be distinguished using small integers as follows:
+ * <table border=1 cellpadding=5 summary="reference kinds">
+ * <tr><th>reference kind</th><th>descriptive name</th><th>scope</th><th>member</th><th>behavior</th></tr>
+ * <tr>
+ * <td>{@code 1}</td><td>{@code REF_getField}</td><td>{@code class}</td>
+ * <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 2}</td><td>{@code REF_getStatic}</td><td>{@code class} or {@code interface}</td>
+ * <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 3}</td><td>{@code REF_putField}</td><td>{@code class}</td>
+ * <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 4}</td><td>{@code REF_putStatic}</td><td>{@code class}</td>
+ * <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 5}</td><td>{@code REF_invokeVirtual}</td><td>{@code class}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 6}</td><td>{@code REF_invokeStatic}</td><td>{@code class} or {@code interface}</td>
+ * <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 7}</td><td>{@code REF_invokeSpecial}</td><td>{@code class} or {@code interface}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 8}</td><td>{@code REF_newInvokeSpecial}</td><td>{@code class}</td>
+ * <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
+ * </tr>
+ * <tr>
+ * <td>{@code 9}</td><td>{@code REF_invokeInterface}</td><td>{@code interface}</td>
+ * <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
+ * </tr>
+ * </table>
+ * @since 1.8
+ */
+// TODO(narayan) : Change @code back to @link once MethodHandles has been imported.
+public
+interface MethodHandleInfo {
+ /**
+ * A direct method handle reference kind,
+ * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
+ */
+ // Android-changed: Inlined the values of these constants from MethodHandleNatives.Constants.
+ public static final int
+ REF_getField = 1,
+ REF_getStatic = 2,
+ REF_putField = 3,
+ REF_putStatic = 4,
+ REF_invokeVirtual = 5,
+ REF_invokeStatic = 6,
+ REF_invokeSpecial = 7,
+ REF_newInvokeSpecial = 8,
+ REF_invokeInterface = 9;
+
+ /**
+ * Returns the reference kind of the cracked method handle, which in turn
+ * determines whether the method handle's underlying member was a constructor, method, or field.
+ * See the <a href="MethodHandleInfo.html#refkinds">table above</a> for definitions.
+ * @return the integer code for the kind of reference used to access the underlying member
+ */
+ public int getReferenceKind();
+
+ /**
+ * Returns the class in which the cracked method handle's underlying member was defined.
+ * @return the declaring class of the underlying member
+ */
+ public Class<?> getDeclaringClass();
+
+ /**
+ * Returns the name of the cracked method handle's underlying member.
+ * This is {@code "<init>"} if the underlying member was a constructor,
+ * else it is a simple method name or field name.
+ * @return the simple name of the underlying member
+ */
+ public String getName();
+
+ /**
+ * Returns the nominal type of the cracked symbolic reference, expressed as a method type.
+ * If the reference is to a constructor, the return type will be {@code void}.
+ * If it is to a non-static method, the method type will not mention the {@code this} parameter.
+ * If it is to a field and the requested access is to read the field,
+ * the method type will have no parameters and return the field type.
+ * If it is to a field and the requested access is to write the field,
+ * the method type will have one parameter of the field type and return {@code void}.
+ * <p>
+ * Note that original direct method handle may include a leading {@code this} parameter,
+ * or (in the case of a constructor) will replace the {@code void} return type
+ * with the constructed class.
+ * The nominal type does not include any {@code this} parameter,
+ * and (in the case of a constructor) will return {@code void}.
+ * @return the type of the underlying member, expressed as a method type
+ */
+ public MethodType getMethodType();
+
+ // Utility methods.
+ // NOTE: class/name/type and reference kind constitute a symbolic reference
+ // member and modifiers are an add-on, derived from Core Reflection (or the equivalent)
+
+ /**
+ * Reflects the underlying member as a method, constructor, or field object.
+ * If the underlying member is public, it is reflected as if by
+ * {@code getMethod}, {@code getConstructor}, or {@code getField}.
+ * Otherwise, it is reflected as if by
+ * {@code getDeclaredMethod}, {@code getDeclaredConstructor}, or {@code getDeclaredField}.
+ * The underlying member must be accessible to the given lookup object.
+ * @param <T> the desired type of the result, either {@link Member} or a subtype
+ * @param expected a class object representing the desired result type {@code T}
+ * @param lookup the lookup object that created this MethodHandleInfo, or one with equivalent access privileges
+ * @return a reference to the method, constructor, or field object
+ * @exception ClassCastException if the member is not of the expected type
+ * @exception NullPointerException if either argument is {@code null}
+ * @exception IllegalArgumentException if the underlying member is not accessible to the given lookup object
+ */
+ // TODO(narayan): change the second argument back to MethodHandles.Lookup once that
+ // class has been introduced.
+ //
+ // public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
+ //
+ // We need to temporarily keep the existing version because of a circular dependency between
+ // the two classes.
+ public <T extends Member> T reflectAs(Class<T> expected, Object lookup);
+
+ /**
+ * Returns the access modifiers of the underlying member.
+ * @return the Java language modifiers for underlying member,
+ * or -1 if the member cannot be accessed
+ * @see Modifier
+ * @see #reflectAs
+ */
+ public int getModifiers();
+
+ /**
+ * Determines if the underlying member was a variable arity method or constructor.
+ * Such members are represented by method handles that are varargs collectors.
+ * @implSpec
+ * This produces a result equivalent to:
+ * <pre>{@code
+ * getReferenceKind() >= REF_invokeVirtual && Modifier.isTransient(getModifiers())
+ * }</pre>
+ *
+ *
+ * @return {@code true} if and only if the underlying member was declared with variable arity.
+ */
+ // spelling derived from java.lang.reflect.Executable, not MethodHandle.isVarargsCollector
+ public default boolean isVarArgs() {
+ // fields are never varargs:
+ if (refKindIsField(getReferenceKind()))
+ return false;
+ // not in the public API: Modifier.VARARGS
+ final int ACC_VARARGS = 0x00000080; // from JVMS 4.6 (Table 4.20)
+
+ // Android-changed: Removed assert() due to http://b/30862573
+ // assert(ACC_VARARGS == Modifier.TRANSIENT);
+ return Modifier.isTransient(getModifiers());
+ }
+
+ /**
+ * Returns the descriptive name of the given reference kind,
+ * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
+ * The conventional prefix "REF_" is omitted.
+ * @param referenceKind an integer code for a kind of reference used to access a class member
+ * @return a mixed-case string such as {@code "getField"}
+ * @exception IllegalArgumentException if the argument is not a valid
+ * <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+ */
+ public static String referenceKindToString(int referenceKind) {
+ if (!refKindIsValid(referenceKind))
+ throw newIllegalArgumentException("invalid reference kind", referenceKind);
+ return refKindName(referenceKind);
+ }
+
+ /**
+ * Returns a string representation for a {@code MethodHandleInfo},
+ * given the four parts of its symbolic reference.
+ * This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
+ * {@linkplain #referenceKindToString reference kind string} for {@code kind},
+ * {@code C} is the {@linkplain java.lang.Class#getName name} of {@code defc}
+ * {@code N} is the {@code name}, and
+ * {@code MT} is the {@code type}.
+ * These four values may be obtained from the
+ * {@linkplain #getReferenceKind reference kind},
+ * {@linkplain #getDeclaringClass declaring class},
+ * {@linkplain #getName member name},
+ * and {@linkplain #getMethodType method type}
+ * of a {@code MethodHandleInfo} object.
+ *
+ * @implSpec
+ * This produces a result equivalent to:
+ * <pre>{@code
+ * String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type)
+ * }</pre>
+ *
+ * @param kind the {@linkplain #getReferenceKind reference kind} part of the symbolic reference
+ * @param defc the {@linkplain #getDeclaringClass declaring class} part of the symbolic reference
+ * @param name the {@linkplain #getName member name} part of the symbolic reference
+ * @param type the {@linkplain #getMethodType method type} part of the symbolic reference
+ * @return a string of the form {@code "RK C.N:MT"}
+ * @exception IllegalArgumentException if the first argument is not a valid
+ * <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
+ * @exception NullPointerException if any reference argument is {@code null}
+ */
+ public static String toString(int kind, Class<?> defc, String name, MethodType type) {
+ Objects.requireNonNull(name); Objects.requireNonNull(type);
+ return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
+ }
+
+ // Android-changed: Inlined from MethodHandleNatives and changed to avoid references to
+ // REF_NONE and REF_LIMITED
+ static boolean refKindIsValid(int refKind) {
+ return (refKind >= REF_getField && refKind <= REF_invokeInterface);
+ }
+
+ // Android-changed: Inlined from MethodHandleNatives.
+ static boolean refKindIsField(int refKind) {
+ return (refKind <= REF_putStatic);
+ }
+
+ // Android-changed: Inlined from MethodHandleNatives and replaced 'byte' argument with
+ // 'int'.
+ static String refKindName(int refKind) {
+ switch (refKind) {
+ case REF_getField: return "getField";
+ case REF_getStatic: return "getStatic";
+ case REF_putField: return "putField";
+ case REF_putStatic: return "putStatic";
+ case REF_invokeVirtual: return "invokeVirtual";
+ case REF_invokeStatic: return "invokeStatic";
+ case REF_invokeSpecial: return "invokeSpecial";
+ case REF_newInvokeSpecial: return "newInvokeSpecial";
+ case REF_invokeInterface: return "invokeInterface";
+ default: return "REF_???";
+ }
+ }
+}
diff --git a/ojluni/src/main/java/java/nio/ByteBuffer.java b/ojluni/src/main/java/java/nio/ByteBuffer.java
index 523bbda..c78447f 100644
--- a/ojluni/src/main/java/java/nio/ByteBuffer.java
+++ b/ojluni/src/main/java/java/nio/ByteBuffer.java
@@ -533,12 +533,12 @@
if (!isAccessible()) {
throw new IllegalStateException("buffer is inaccessible");
}
- if (isReadOnly) {
- throw new ReadOnlyBufferException();
- }
if (src == this) {
throw new IllegalArgumentException();
}
+ if (isReadOnly) {
+ throw new ReadOnlyBufferException();
+ }
int n = src.remaining();
if (n > remaining()) {
throw new BufferOverflowException();
diff --git a/ojluni/src/main/java/java/nio/HeapCharBuffer.java b/ojluni/src/main/java/java/nio/HeapCharBuffer.java
index f67c91f..fad4fa6 100644
--- a/ojluni/src/main/java/java/nio/HeapCharBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapCharBuffer.java
@@ -165,12 +165,13 @@
}
public CharBuffer put(CharBuffer src) {
+ if (src == this) {
+ throw new IllegalArgumentException();
+ }
if (isReadOnly) {
throw new ReadOnlyBufferException();
}
if (src instanceof HeapCharBuffer) {
- if (src == this)
- throw new IllegalArgumentException();
HeapCharBuffer sb = (HeapCharBuffer) src;
int n = sb.remaining();
if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java b/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java
index e3d9634..02634dd 100644
--- a/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapDoubleBuffer.java
@@ -160,12 +160,13 @@
}
public DoubleBuffer put(DoubleBuffer src) {
+ if (src == this) {
+ throw new IllegalArgumentException();
+ }
if (isReadOnly) {
throw new ReadOnlyBufferException();
}
if (src instanceof HeapDoubleBuffer) {
- if (src == this)
- throw new IllegalArgumentException();
HeapDoubleBuffer sb = (HeapDoubleBuffer)src;
int n = sb.remaining();
if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapFloatBuffer.java b/ojluni/src/main/java/java/nio/HeapFloatBuffer.java
index ca341a0..42dd8ce 100644
--- a/ojluni/src/main/java/java/nio/HeapFloatBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapFloatBuffer.java
@@ -159,12 +159,13 @@
}
public FloatBuffer put(FloatBuffer src) {
+ if (src == this) {
+ throw new IllegalArgumentException();
+ }
if (isReadOnly) {
throw new ReadOnlyBufferException();
}
if (src instanceof HeapFloatBuffer) {
- if (src == this)
- throw new IllegalArgumentException();
HeapFloatBuffer sb = (HeapFloatBuffer) src;
int n = sb.remaining();
if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapIntBuffer.java b/ojluni/src/main/java/java/nio/HeapIntBuffer.java
index 642e782..b4f3bf7 100644
--- a/ojluni/src/main/java/java/nio/HeapIntBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapIntBuffer.java
@@ -160,12 +160,13 @@
}
public IntBuffer put(IntBuffer src) {
+ if (src == this) {
+ throw new IllegalArgumentException();
+ }
if (isReadOnly) {
throw new ReadOnlyBufferException();
}
if (src instanceof HeapIntBuffer) {
- if (src == this)
- throw new IllegalArgumentException();
HeapIntBuffer sb = (HeapIntBuffer) src;
int n = sb.remaining();
if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapLongBuffer.java b/ojluni/src/main/java/java/nio/HeapLongBuffer.java
index 60596b0..c0678b3 100644
--- a/ojluni/src/main/java/java/nio/HeapLongBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapLongBuffer.java
@@ -161,12 +161,13 @@
}
public LongBuffer put(LongBuffer src) {
+ if (src == this) {
+ throw new IllegalArgumentException();
+ }
if (isReadOnly) {
throw new ReadOnlyBufferException();
}
if (src instanceof HeapLongBuffer) {
- if (src == this)
- throw new IllegalArgumentException();
HeapLongBuffer sb = (HeapLongBuffer) src;
int n = sb.remaining();
if (n > remaining())
diff --git a/ojluni/src/main/java/java/nio/HeapShortBuffer.java b/ojluni/src/main/java/java/nio/HeapShortBuffer.java
index f78f241..af39261 100644
--- a/ojluni/src/main/java/java/nio/HeapShortBuffer.java
+++ b/ojluni/src/main/java/java/nio/HeapShortBuffer.java
@@ -160,12 +160,13 @@
}
public ShortBuffer put(ShortBuffer src) {
+ if (src == this) {
+ throw new IllegalArgumentException();
+ }
if (isReadOnly) {
throw new ReadOnlyBufferException();
}
if (src instanceof HeapShortBuffer) {
- if (src == this)
- throw new IllegalArgumentException();
HeapShortBuffer sb = (HeapShortBuffer)src;
int n = sb.remaining();
if (n > remaining())
diff --git a/ojluni/src/main/java/java/util/HashMap.java b/ojluni/src/main/java/java/util/HashMap.java
index f47d681..f2cedf8 100644
--- a/ojluni/src/main/java/java/util/HashMap.java
+++ b/ojluni/src/main/java/java/util/HashMap.java
@@ -1,5 +1,4 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -26,10 +25,15 @@
package java.util;
-import java.io.*;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.Serializable;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
-import java.util.function.BiConsumer;
+import java.util.function.Function;
/**
* Hash table based implementation of the <tt>Map</tt> interface. This
@@ -60,20 +64,25 @@
* structures are rebuilt) so that the hash table has approximately twice the
* number of buckets.
*
- * <p>As a general rule, the default load factor (.75) offers a good tradeoff
- * between time and space costs. Higher values decrease the space overhead
- * but increase the lookup cost (reflected in most of the operations of the
- * <tt>HashMap</tt> class, including <tt>get</tt> and <tt>put</tt>). The
- * expected number of entries in the map and its load factor should be taken
- * into account when setting its initial capacity, so as to minimize the
- * number of rehash operations. If the initial capacity is greater
- * than the maximum number of entries divided by the load factor, no
- * rehash operations will ever occur.
+ * <p>As a general rule, the default load factor (.75) offers a good
+ * tradeoff between time and space costs. Higher values decrease the
+ * space overhead but increase the lookup cost (reflected in most of
+ * the operations of the <tt>HashMap</tt> class, including
+ * <tt>get</tt> and <tt>put</tt>). The expected number of entries in
+ * the map and its load factor should be taken into account when
+ * setting its initial capacity, so as to minimize the number of
+ * rehash operations. If the initial capacity is greater than the
+ * maximum number of entries divided by the load factor, no rehash
+ * operations will ever occur.
*
- * <p>If many mappings are to be stored in a <tt>HashMap</tt> instance,
- * creating it with a sufficiently large capacity will allow the mappings to
- * be stored more efficiently than letting it perform automatic rehashing as
- * needed to grow the table.
+ * <p>If many mappings are to be stored in a <tt>HashMap</tt>
+ * instance, creating it with a sufficiently large capacity will allow
+ * the mappings to be stored more efficiently than letting it perform
+ * automatic rehashing as needed to grow the table. Note that using
+ * many keys with the same {@code hashCode()} is a sure way to slow
+ * down performance of any hash table. To ameliorate impact, when keys
+ * are {@link Comparable}, this class may use comparison order among
+ * keys to help break ties.
*
* <p><strong>Note that this implementation is not synchronized.</strong>
* If multiple threads access a hash map concurrently, and at least one of
@@ -125,16 +134,105 @@
* @see Hashtable
* @since 1.2
*/
+public class HashMap<K,V> extends AbstractMap<K,V>
+ implements Map<K,V>, Cloneable, Serializable {
-public class HashMap<K,V>
- extends AbstractMap<K,V>
- implements Map<K,V>, Cloneable, Serializable
-{
+ private static final long serialVersionUID = 362498820763181265L;
+
+ /*
+ * Implementation notes.
+ *
+ * This map usually acts as a binned (bucketed) hash table, but
+ * when bins get too large, they are transformed into bins of
+ * TreeNodes, each structured similarly to those in
+ * java.util.TreeMap. Most methods try to use normal bins, but
+ * relay to TreeNode methods when applicable (simply by checking
+ * instanceof a node). Bins of TreeNodes may be traversed and
+ * used like any others, but additionally support faster lookup
+ * when overpopulated. However, since the vast majority of bins in
+ * normal use are not overpopulated, checking for existence of
+ * tree bins may be delayed in the course of table methods.
+ *
+ * Tree bins (i.e., bins whose elements are all TreeNodes) are
+ * ordered primarily by hashCode, but in the case of ties, if two
+ * elements are of the same "class C implements Comparable<C>",
+ * type then their compareTo method is used for ordering. (We
+ * conservatively check generic types via reflection to validate
+ * this -- see method comparableClassFor). The added complexity
+ * of tree bins is worthwhile in providing worst-case O(log n)
+ * operations when keys either have distinct hashes or are
+ * orderable, Thus, performance degrades gracefully under
+ * accidental or malicious usages in which hashCode() methods
+ * return values that are poorly distributed, as well as those in
+ * which many keys share a hashCode, so long as they are also
+ * Comparable. (If neither of these apply, we may waste about a
+ * factor of two in time and space compared to taking no
+ * precautions. But the only known cases stem from poor user
+ * programming practices that are already so slow that this makes
+ * little difference.)
+ *
+ * Because TreeNodes are about twice the size of regular nodes, we
+ * use them only when bins contain enough nodes to warrant use
+ * (see TREEIFY_THRESHOLD). And when they become too small (due to
+ * removal or resizing) they are converted back to plain bins. In
+ * usages with well-distributed user hashCodes, tree bins are
+ * rarely used. Ideally, under random hashCodes, the frequency of
+ * nodes in bins follows a Poisson distribution
+ * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+ * parameter of about 0.5 on average for the default resizing
+ * threshold of 0.75, although with a large variance because of
+ * resizing granularity. Ignoring variance, the expected
+ * occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
+ * factorial(k)). The first values are:
+ *
+ * 0: 0.60653066
+ * 1: 0.30326533
+ * 2: 0.07581633
+ * 3: 0.01263606
+ * 4: 0.00157952
+ * 5: 0.00015795
+ * 6: 0.00001316
+ * 7: 0.00000094
+ * 8: 0.00000006
+ * more: less than 1 in ten million
+ *
+ * The root of a tree bin is normally its first node. However,
+ * sometimes (currently only upon Iterator.remove), the root might
+ * be elsewhere, but can be recovered following parent links
+ * (method TreeNode.root()).
+ *
+ * All applicable internal methods accept a hash code as an
+ * argument (as normally supplied from a public method), allowing
+ * them to call each other without recomputing user hashCodes.
+ * Most internal methods also accept a "tab" argument, that is
+ * normally the current table, but may be a new or old one when
+ * resizing or converting.
+ *
+ * When bin lists are treeified, split, or untreeified, we keep
+ * them in the same relative access/traversal order (i.e., field
+ * Node.next) to better preserve locality, and to slightly
+ * simplify handling of splits and traversals that invoke
+ * iterator.remove. When using comparators on insertion, to keep a
+ * total ordering (or as close as is required here) across
+ * rebalancings, we compare classes and identityHashCodes as
+ * tie-breakers.
+ *
+ * The use and transitions among plain vs tree modes is
+ * complicated by the existence of subclass LinkedHashMap. See
+ * below for hook methods defined to be invoked upon insertion,
+ * removal and access that allow LinkedHashMap internals to
+ * otherwise remain independent of these mechanics. (This also
+ * requires that a map instance be passed to some utility methods
+ * that may create new nodes.)
+ *
+ * The concurrent-programming-like SSA-based coding style helps
+ * avoid aliasing errors amid all of the twisty pointer operations.
+ */
/**
* The default initial capacity - MUST be a power of two.
*/
- static final int DEFAULT_INITIAL_CAPACITY = 4;
+ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
@@ -149,14 +247,158 @@
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
- * An empty table instance to share when the table is not inflated.
+ * The bin count threshold for using a tree rather than list for a
+ * bin. Bins are converted to trees when adding an element to a
+ * bin with at least this many nodes. The value must be greater
+ * than 2 and should be at least 8 to mesh with assumptions in
+ * tree removal about conversion back to plain bins upon
+ * shrinkage.
*/
- static final HashMapEntry<?,?>[] EMPTY_TABLE = {};
+ static final int TREEIFY_THRESHOLD = 8;
/**
- * The table, resized as necessary. Length MUST Always be a power of two.
+ * The bin count threshold for untreeifying a (split) bin during a
+ * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+ * most 6 to mesh with shrinkage detection under removal.
*/
- transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
+ static final int UNTREEIFY_THRESHOLD = 6;
+
+ /**
+ * The smallest table capacity for which bins may be treeified.
+ * (Otherwise the table is resized if too many nodes in a bin.)
+ * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
+ * between resizing and treeification thresholds.
+ */
+ static final int MIN_TREEIFY_CAPACITY = 64;
+
+ /**
+ * Basic hash bin node, used for most entries. (See below for
+ * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
+ */
+ static class Node<K,V> implements Map.Entry<K,V> {
+ final int hash;
+ final K key;
+ V value;
+ Node<K,V> next;
+
+ Node(int hash, K key, V value, Node<K,V> next) {
+ this.hash = hash;
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+
+ public final K getKey() { return key; }
+ public final V getValue() { return value; }
+ public final String toString() { return key + "=" + value; }
+
+ public final int hashCode() {
+ return Objects.hashCode(key) ^ Objects.hashCode(value);
+ }
+
+ public final V setValue(V newValue) {
+ V oldValue = value;
+ value = newValue;
+ return oldValue;
+ }
+
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (o instanceof Map.Entry) {
+ Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+ if (Objects.equals(key, e.getKey()) &&
+ Objects.equals(value, e.getValue()))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /* ---------------- Static utilities -------------- */
+
+ /**
+ * Computes key.hashCode() and spreads (XORs) higher bits of hash
+ * to lower. Because the table uses power-of-two masking, sets of
+ * hashes that vary only in bits above the current mask will
+ * always collide. (Among known examples are sets of Float keys
+ * holding consecutive whole numbers in small tables.) So we
+ * apply a transform that spreads the impact of higher bits
+ * downward. There is a tradeoff between speed, utility, and
+ * quality of bit-spreading. Because many common sets of hashes
+ * are already reasonably distributed (so don't benefit from
+ * spreading), and because we use trees to handle large sets of
+ * collisions in bins, we just XOR some shifted bits in the
+ * cheapest possible way to reduce systematic lossage, as well as
+ * to incorporate impact of the highest bits that would otherwise
+ * never be used in index calculations because of table bounds.
+ */
+ static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+ }
+
+ /**
+ * Returns x's Class if it is of the form "class C implements
+ * Comparable<C>", else null.
+ */
+ static Class<?> comparableClassFor(Object x) {
+ if (x instanceof Comparable) {
+ Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
+ if ((c = x.getClass()) == String.class) // bypass checks
+ return c;
+ if ((ts = c.getGenericInterfaces()) != null) {
+ for (int i = 0; i < ts.length; ++i) {
+ if (((t = ts[i]) instanceof ParameterizedType) &&
+ ((p = (ParameterizedType)t).getRawType() ==
+ Comparable.class) &&
+ (as = p.getActualTypeArguments()) != null &&
+ as.length == 1 && as[0] == c) // type arg is c
+ return c;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns k.compareTo(x) if x matches kc (k's screened comparable
+ * class), else 0.
+ */
+ @SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
+ static int compareComparables(Class<?> kc, Object k, Object x) {
+ return (x == null || x.getClass() != kc ? 0 :
+ ((Comparable)k).compareTo(x));
+ }
+
+ /**
+ * Returns a power of two size for the given target capacity.
+ */
+ static final int tableSizeFor(int cap) {
+ int n = cap - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ /* ---------------- Fields -------------- */
+
+ /**
+ * The table, initialized on first use, and resized as
+ * necessary. When allocated, length is always a power of two.
+ * (We also tolerate length zero in some operations to allow
+ * bootstrapping mechanics that are currently not needed.)
+ */
+ transient Node<K,V>[] table;
+
+ /**
+ * Holds cached entrySet(). Note that AbstractMap fields are used
+ * for keySet() and values().
+ */
+ transient Set<Map.Entry<K,V>> entrySet;
/**
* The number of key-value mappings contained in this map.
@@ -164,23 +406,6 @@
transient int size;
/**
- * The next size value at which to resize (capacity * load factor).
- * @serial
- */
- // If table == EMPTY_TABLE then this is the initial capacity at which the
- // table will be created when inflated.
- int threshold;
-
- /**
- * The load factor for the hash table.
- *
- * @serial
- */
- // Android-Note: We always use a load factor of 0.75 and ignore any explicitly
- // selected values.
- final float loadFactor = DEFAULT_LOAD_FACTOR;
-
- /**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
@@ -190,6 +415,26 @@
transient int modCount;
/**
+ * The next size value at which to resize (capacity * load factor).
+ *
+ * @serial
+ */
+ // (The javadoc description is true upon serialization.
+ // Additionally, if the table array has not been allocated, this
+ // field holds the initial array capacity, or zero signifying
+ // DEFAULT_INITIAL_CAPACITY.)
+ int threshold;
+
+ /**
+ * The load factor for the hash table.
+ *
+ * @serial
+ */
+ final float loadFactor;
+
+ /* ---------------- Public operations -------------- */
+
+ /**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
@@ -202,23 +447,13 @@
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
- if (initialCapacity > MAXIMUM_CAPACITY) {
+ if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
- } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
- initialCapacity = DEFAULT_INITIAL_CAPACITY;
- }
-
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
- // Android-Note: We always use the default load factor of 0.75f.
-
- // This might appear wrong but it's just awkward design. We always call
- // inflateTable() when table == EMPTY_TABLE. That method will take "threshold"
- // to mean "capacity" and then replace it with the real threshold (i.e, multiplied with
- // the load factor).
- threshold = initialCapacity;
- init();
+ this.loadFactor = loadFactor;
+ this.threshold = tableSizeFor(initialCapacity);
}
/**
@@ -237,7 +472,7 @@
* (16) and the default load factor (0.75).
*/
public HashMap() {
- this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
@@ -250,61 +485,35 @@
* @throws NullPointerException if the specified map is null
*/
public HashMap(Map<? extends K, ? extends V> m) {
- this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
- DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
- inflateTable(threshold);
-
- putAllForCreate(m);
- }
-
- private static int roundUpToPowerOf2(int number) {
- // assert number >= 0 : "number must be non-negative";
- int rounded = number >= MAXIMUM_CAPACITY
- ? MAXIMUM_CAPACITY
- : (rounded = Integer.highestOneBit(number)) != 0
- ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded
- : 1;
-
- return rounded;
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ putMapEntries(m, false);
}
/**
- * Inflates the table.
+ * Implements Map.putAll and Map constructor
+ *
+ * @param m the map
+ * @param evict false when initially constructing this map, else
+ * true (relayed to method afterNodeInsertion).
*/
- private void inflateTable(int toSize) {
- // Find a power of 2 >= toSize
- int capacity = roundUpToPowerOf2(toSize);
-
- // Android-changed: Replace usage of Math.min() here because this method is
- // called from the <clinit> of runtime, at which point the native libraries
- // needed by Float.* might not be loaded.
- float thresholdFloat = capacity * loadFactor;
- if (thresholdFloat > MAXIMUM_CAPACITY + 1) {
- thresholdFloat = MAXIMUM_CAPACITY + 1;
+ final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
+ int s = m.size();
+ if (s > 0) {
+ if (table == null) { // pre-size
+ float ft = ((float)s / loadFactor) + 1.0F;
+ int t = ((ft < (float)MAXIMUM_CAPACITY) ?
+ (int)ft : MAXIMUM_CAPACITY);
+ if (t > threshold)
+ threshold = tableSizeFor(t);
+ }
+ else if (s > threshold)
+ resize();
+ for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
+ K key = e.getKey();
+ V value = e.getValue();
+ putVal(hash(key), key, value, false, evict);
+ }
}
-
- threshold = (int) thresholdFloat;
- table = new HashMapEntry[capacity];
- }
-
- // internal utilities
-
- /**
- * Initialization hook for subclasses. This method is called
- * in all constructors and pseudo-constructors (clone, readObject)
- * after HashMap has been initialized but before any entries have
- * been inserted. (In the absence of this method, readObject would
- * require explicit knowledge of subclasses.)
- */
- void init() {
- }
-
- /**
- * Returns index for hash code h.
- */
- static int indexFor(int h, int length) {
- // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
- return h & (length-1);
}
/**
@@ -343,27 +552,33 @@
* @see #put(Object, Object)
*/
public V get(Object key) {
- if (key == null)
- return getForNullKey();
- Entry<K,V> entry = getEntry(key);
-
- return null == entry ? null : entry.getValue();
+ Node<K,V> e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
- * Offloaded version of get() to look up null keys. Null keys map
- * to index 0. This null case is split out into separate methods
- * for the sake of performance in the two most commonly used
- * operations (get and put), but incorporated with conditionals in
- * others.
+ * Implements Map.get and related methods
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @return the node, or null if none
*/
- private V getForNullKey() {
- if (size == 0) {
- return null;
- }
- for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
- if (e.key == null)
- return e.value;
+ final Node<K,V> getNode(int hash, Object key) {
+ Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ return first;
+ if ((e = first.next) != null) {
+ if (first instanceof TreeNode)
+ return ((TreeNode<K,V>)first).getTreeNode(hash, key);
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
}
return null;
}
@@ -377,29 +592,7 @@
* key.
*/
public boolean containsKey(Object key) {
- return getEntry(key) != null;
- }
-
- /**
- * Returns the entry associated with the specified key in the
- * HashMap. Returns null if the HashMap contains no mapping
- * for the key.
- */
- final Entry<K,V> getEntry(Object key) {
- if (size == 0) {
- return null;
- }
-
- int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
- for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)];
- e != null;
- e = e.next) {
- Object k;
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- return e;
- }
- return null;
+ return getNode(hash(key), key) != null;
}
/**
@@ -415,118 +608,167 @@
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
- if (table == EMPTY_TABLE) {
- inflateTable(threshold);
- }
- if (key == null)
- return putForNullKey(value);
- int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
- int i = indexFor(hash, table.length);
- for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
+ return putVal(hash(key), key, value, false, true);
+ }
+
+ /**
+ * Implements Map.put and related methods
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @param value the value to put
+ * @param onlyIfAbsent if true, don't change existing value
+ * @param evict if false, the table is in creation mode.
+ * @return previous value, or null if none
+ */
+ final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node<K,V>[] tab; Node<K,V> p; int n, i;
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ Node<K,V> e; K k;
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ else if (p instanceof TreeNode)
+ e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ p.next = newNode(hash, key, value, null);
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ if (e != null) { // existing mapping for key
V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
return oldValue;
}
}
-
- modCount++;
- addEntry(hash, key, value, i);
+ ++modCount;
+ if (++size > threshold)
+ resize();
+ afterNodeInsertion(evict);
return null;
}
/**
- * Offloaded version of put for null keys
- */
- private V putForNullKey(V value) {
- for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
- if (e.key == null) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- modCount++;
- addEntry(0, null, value, 0);
- return null;
- }
-
- /**
- * This method is used instead of put by constructors and
- * pseudoconstructors (clone, readObject). It does not resize the table,
- * check for comodification, etc. It calls createEntry rather than
- * addEntry.
- */
- private void putForCreate(K key, V value) {
- int hash = null == key ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
- int i = indexFor(hash, table.length);
-
- /**
- * Look for preexisting entry for key. This will never happen for
- * clone or deserialize. It will only happen for construction if the
- * input Map is a sorted map whose ordering is inconsistent w/ equals.
- */
- for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k)))) {
- e.value = value;
- return;
- }
- }
-
- createEntry(hash, key, value, i);
- }
-
- private void putAllForCreate(Map<? extends K, ? extends V> m) {
- for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
- putForCreate(e.getKey(), e.getValue());
- }
-
- /**
- * Rehashes the contents of this map into a new array with a
- * larger capacity. This method is called automatically when the
- * number of keys in this map reaches its threshold.
+ * Initializes or doubles table size. If null, allocates in
+ * accord with initial capacity target held in field threshold.
+ * Otherwise, because we are using power-of-two expansion, the
+ * elements from each bin must either stay at same index, or move
+ * with a power of two offset in the new table.
*
- * If current capacity is MAXIMUM_CAPACITY, this method does not
- * resize the map, but sets threshold to Integer.MAX_VALUE.
- * This has the effect of preventing future calls.
- *
- * @param newCapacity the new capacity, MUST be a power of two;
- * must be greater than current capacity unless current
- * capacity is MAXIMUM_CAPACITY (in which case value
- * is irrelevant).
+ * @return the table
*/
- void resize(int newCapacity) {
- HashMapEntry[] oldTable = table;
- int oldCapacity = oldTable.length;
- if (oldCapacity == MAXIMUM_CAPACITY) {
- threshold = Integer.MAX_VALUE;
- return;
+ final Node<K,V>[] resize() {
+ Node<K,V>[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr << 1; // double threshold
}
-
- HashMapEntry[] newTable = new HashMapEntry[newCapacity];
- transfer(newTable);
- table = newTable;
- threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
+ table = newTab;
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node<K,V> e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node<K,V> loHead = null, loTail = null;
+ Node<K,V> hiHead = null, hiTail = null;
+ Node<K,V> next;
+ do {
+ next = e.next;
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
}
/**
- * Transfers all entries from current table to newTable.
+ * Replaces all linked nodes in bin at index for given hash unless
+ * table is too small, in which case resizes instead.
*/
- void transfer(HashMapEntry[] newTable) {
- int newCapacity = newTable.length;
- for (HashMapEntry<K,V> e : table) {
- while(null != e) {
- HashMapEntry<K,V> next = e.next;
- int i = indexFor(e.hash, newCapacity);
- e.next = newTable[i];
- newTable[i] = e;
- e = next;
- }
+ final void treeifyBin(Node<K,V>[] tab, int hash) {
+ int n, index; Node<K,V> e;
+ if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+ resize();
+ else if ((e = tab[index = (n - 1) & hash]) != null) {
+ TreeNode<K,V> hd = null, tl = null;
+ do {
+ TreeNode<K,V> p = replacementTreeNode(e, null);
+ if (tl == null)
+ hd = p;
+ else {
+ p.prev = tl;
+ tl.next = p;
+ }
+ tl = p;
+ } while ((e = e.next) != null);
+ if ((tab[index] = hd) != null)
+ hd.treeify(tab);
}
}
@@ -539,36 +781,7 @@
* @throws NullPointerException if the specified map is null
*/
public void putAll(Map<? extends K, ? extends V> m) {
- int numKeysToBeAdded = m.size();
- if (numKeysToBeAdded == 0)
- return;
-
- if (table == EMPTY_TABLE) {
- inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
- }
-
- /*
- * Expand the map if the map if the number of mappings to be added
- * is greater than or equal to threshold. This is conservative; the
- * obvious condition is (m.size() + size) >= threshold, but this
- * condition could result in a map with twice the appropriate capacity,
- * if the keys to be added overlap with the keys already in this map.
- * By using the conservative calculation, we subject ourself
- * to at most one extra resize.
- */
- if (numKeysToBeAdded > threshold) {
- int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
- if (targetCapacity > MAXIMUM_CAPACITY)
- targetCapacity = MAXIMUM_CAPACITY;
- int newCapacity = table.length;
- while (newCapacity < targetCapacity)
- newCapacity <<= 1;
- if (newCapacity > table.length)
- resize(newCapacity);
- }
-
- for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
- put(e.getKey(), e.getValue());
+ putMapEntries(m, true);
}
/**
@@ -581,77 +794,60 @@
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V remove(Object key) {
- Entry<K,V> e = removeEntryForKey(key);
- return (e == null ? null : e.getValue());
+ Node<K,V> e;
+ return (e = removeNode(hash(key), key, null, false, true)) == null ?
+ null : e.value;
}
/**
- * Removes and returns the entry associated with the specified key
- * in the HashMap. Returns null if the HashMap contains no mapping
- * for this key.
+ * Implements Map.remove and related methods
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @param value the value to match if matchValue, else ignored
+ * @param matchValue if true only remove if value is equal
+ * @param movable if false do not move other nodes while removing
+ * @return the node, or null if none
*/
- final Entry<K,V> removeEntryForKey(Object key) {
- if (size == 0) {
- return null;
- }
- int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
- int i = indexFor(hash, table.length);
- HashMapEntry<K,V> prev = table[i];
- HashMapEntry<K,V> e = prev;
-
- while (e != null) {
- HashMapEntry<K,V> next = e.next;
- Object k;
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k)))) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
- else
- prev.next = next;
- e.recordRemoval(this);
- return e;
+ final Node<K,V> removeNode(int hash, Object key, Object value,
+ boolean matchValue, boolean movable) {
+ Node<K,V>[] tab; Node<K,V> p; int n, index;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (p = tab[index = (n - 1) & hash]) != null) {
+ Node<K,V> node = null, e; K k; V v;
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ node = p;
+ else if ((e = p.next) != null) {
+ if (p instanceof TreeNode)
+ node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
+ else {
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key ||
+ (key != null && key.equals(k)))) {
+ node = e;
+ break;
+ }
+ p = e;
+ } while ((e = e.next) != null);
+ }
}
- prev = e;
- e = next;
- }
-
- return e;
- }
-
- /**
- * Special version of remove for EntrySet using {@code Map.Entry.equals()}
- * for matching.
- */
- final Entry<K,V> removeMapping(Object o) {
- if (size == 0 || !(o instanceof Map.Entry))
- return null;
-
- Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
- Object key = entry.getKey();
- int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
- int i = indexFor(hash, table.length);
- HashMapEntry<K,V> prev = table[i];
- HashMapEntry<K,V> e = prev;
-
- while (e != null) {
- HashMapEntry<K,V> next = e.next;
- if (e.hash == hash && e.equals(entry)) {
- modCount++;
- size--;
- if (prev == e)
- table[i] = next;
+ if (node != null && (!matchValue || (v = node.value) == value ||
+ (value != null && value.equals(v)))) {
+ if (node instanceof TreeNode)
+ ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
+ else if (node == p)
+ tab[index] = node.next;
else
- prev.next = next;
- e.recordRemoval(this);
- return e;
+ p.next = node.next;
+ ++modCount;
+ --size;
+ afterNodeRemoval(node);
+ return node;
}
- prev = e;
- e = next;
}
-
- return e;
+ return null;
}
/**
@@ -659,9 +855,13 @@
* The map will be empty after this call returns.
*/
public void clear() {
+ Node<K,V>[] tab;
modCount++;
- Arrays.fill(table, null);
- size = 0;
+ if ((tab = table) != null && size > 0) {
+ size = 0;
+ for (int i = 0; i < tab.length; ++i)
+ tab[i] = null;
+ }
}
/**
@@ -673,172 +873,552 @@
* specified value
*/
public boolean containsValue(Object value) {
- if (value == null)
- return containsNullValue();
-
- HashMapEntry[] tab = table;
- for (int i = 0; i < tab.length ; i++)
- for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
- if (value.equals(e.value))
- return true;
+ Node<K,V>[] tab; V v;
+ if ((tab = table) != null && size > 0) {
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+ if ((v = e.value) == value ||
+ (value != null && value.equals(v)))
+ return true;
+ }
+ }
+ }
return false;
}
/**
- * Special-case code for containsValue with null argument
+ * Returns a {@link Set} view of the keys contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. If the map is modified
+ * while an iteration over the set is in progress (except through
+ * the iterator's own <tt>remove</tt> operation), the results of
+ * the iteration are undefined. The set supports element removal,
+ * which removes the corresponding mapping from the map, via the
+ * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+ * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
+ * operations.
+ *
+ * @return a set view of the keys contained in this map
*/
- private boolean containsNullValue() {
- HashMapEntry[] tab = table;
- for (int i = 0; i < tab.length ; i++)
- for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
- if (e.value == null)
- return true;
+ public Set<K> keySet() {
+ Set<K> ks;
+ return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
+ }
+
+ final class KeySet extends AbstractSet<K> {
+ public final int size() { return size; }
+ public final void clear() { HashMap.this.clear(); }
+ public final Iterator<K> iterator() { return new KeyIterator(); }
+ public final boolean contains(Object o) { return containsKey(o); }
+ public final boolean remove(Object key) {
+ return removeNode(hash(key), key, null, false, true) != null;
+ }
+ public final Spliterator<K> spliterator() {
+ return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
+ }
+ public final void forEach(Consumer<? super K> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ // Android-changed: Detect changes to modCount early.
+ for (int i = 0; (i < tab.length && modCount == mc); ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e.key);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
+ /**
+ * Returns a {@link Collection} view of the values contained in this map.
+ * The collection is backed by the map, so changes to the map are
+ * reflected in the collection, and vice-versa. If the map is
+ * modified while an iteration over the collection is in progress
+ * (except through the iterator's own <tt>remove</tt> operation),
+ * the results of the iteration are undefined. The collection
+ * supports element removal, which removes the corresponding
+ * mapping from the map, via the <tt>Iterator.remove</tt>,
+ * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
+ * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
+ * support the <tt>add</tt> or <tt>addAll</tt> operations.
+ *
+ * @return a view of the values contained in this map
+ */
+ public Collection<V> values() {
+ Collection<V> vs;
+ return (vs = values) == null ? (values = new Values()) : vs;
+ }
+
+ final class Values extends AbstractCollection<V> {
+ public final int size() { return size; }
+ public final void clear() { HashMap.this.clear(); }
+ public final Iterator<V> iterator() { return new ValueIterator(); }
+ public final boolean contains(Object o) { return containsValue(o); }
+ public final Spliterator<V> spliterator() {
+ return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
+ }
+ public final void forEach(Consumer<? super V> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ // Android-changed: Detect changes to modCount early.
+ for (int i = 0; (i < tab.length && modCount == mc); ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e.value);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
+ /**
+ * Returns a {@link Set} view of the mappings contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. If the map is modified
+ * while an iteration over the set is in progress (except through
+ * the iterator's own <tt>remove</tt> operation, or through the
+ * <tt>setValue</tt> operation on a map entry returned by the
+ * iterator) the results of the iteration are undefined. The set
+ * supports element removal, which removes the corresponding
+ * mapping from the map, via the <tt>Iterator.remove</tt>,
+ * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
+ * <tt>clear</tt> operations. It does not support the
+ * <tt>add</tt> or <tt>addAll</tt> operations.
+ *
+ * @return a set view of the mappings contained in this map
+ */
+ public Set<Map.Entry<K,V>> entrySet() {
+ Set<Map.Entry<K,V>> es;
+ return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
+ }
+
+ final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
+ public final int size() { return size; }
+ public final void clear() { HashMap.this.clear(); }
+ public final Iterator<Map.Entry<K,V>> iterator() {
+ return new EntryIterator();
+ }
+ public final boolean contains(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+ Object key = e.getKey();
+ Node<K,V> candidate = getNode(hash(key), key);
+ return candidate != null && candidate.equals(e);
+ }
+ public final boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+ Object key = e.getKey();
+ Object value = e.getValue();
+ return removeNode(hash(key), key, value, true, true) != null;
+ }
+ return false;
+ }
+ public final Spliterator<Map.Entry<K,V>> spliterator() {
+ return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
+ }
+ public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ // Android-changed: Detect changes to modCount early.
+ for (int i = 0; (i < tab.length && modCount == mc); ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
+ // Overrides of JDK8 Map extension methods
+
+ @Override
+ public V getOrDefault(Object key, V defaultValue) {
+ Node<K,V> e;
+ return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
+ }
+
+ @Override
+ public V putIfAbsent(K key, V value) {
+ return putVal(hash(key), key, value, true, true);
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ return removeNode(hash(key), key, value, true, true) != null;
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ Node<K,V> e; V v;
+ if ((e = getNode(hash(key), key)) != null &&
+ ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
+ e.value = newValue;
+ afterNodeAccess(e);
+ return true;
+ }
return false;
}
+ @Override
+ public V replace(K key, V value) {
+ Node<K,V> e;
+ if ((e = getNode(hash(key), key)) != null) {
+ V oldValue = e.value;
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
+ }
+ return null;
+ }
+
+ @Override
+ public V computeIfAbsent(K key,
+ Function<? super K, ? extends V> mappingFunction) {
+ if (mappingFunction == null)
+ throw new NullPointerException();
+ int hash = hash(key);
+ Node<K,V>[] tab; Node<K,V> first; int n, i;
+ int binCount = 0;
+ TreeNode<K,V> t = null;
+ Node<K,V> old = null;
+ if (size > threshold || (tab = table) == null ||
+ (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((first = tab[i = (n - 1) & hash]) != null) {
+ if (first instanceof TreeNode)
+ old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+ else {
+ Node<K,V> e = first; K k;
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ old = e;
+ break;
+ }
+ ++binCount;
+ } while ((e = e.next) != null);
+ }
+ V oldValue;
+ if (old != null && (oldValue = old.value) != null) {
+ afterNodeAccess(old);
+ return oldValue;
+ }
+ }
+ V v = mappingFunction.apply(key);
+ if (v == null) {
+ return null;
+ } else if (old != null) {
+ old.value = v;
+ afterNodeAccess(old);
+ return v;
+ }
+ else if (t != null)
+ t.putTreeVal(this, tab, hash, key, v);
+ else {
+ tab[i] = newNode(hash, key, v, first);
+ if (binCount >= TREEIFY_THRESHOLD - 1)
+ treeifyBin(tab, hash);
+ }
+ ++modCount;
+ ++size;
+ afterNodeInsertion(true);
+ return v;
+ }
+
+ public V computeIfPresent(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (remappingFunction == null)
+ throw new NullPointerException();
+ Node<K,V> e; V oldValue;
+ int hash = hash(key);
+ if ((e = getNode(hash, key)) != null &&
+ (oldValue = e.value) != null) {
+ V v = remappingFunction.apply(key, oldValue);
+ if (v != null) {
+ e.value = v;
+ afterNodeAccess(e);
+ return v;
+ }
+ else
+ removeNode(hash, key, null, false, true);
+ }
+ return null;
+ }
+
+ @Override
+ public V compute(K key,
+ BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ if (remappingFunction == null)
+ throw new NullPointerException();
+ int hash = hash(key);
+ Node<K,V>[] tab; Node<K,V> first; int n, i;
+ int binCount = 0;
+ TreeNode<K,V> t = null;
+ Node<K,V> old = null;
+ if (size > threshold || (tab = table) == null ||
+ (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((first = tab[i = (n - 1) & hash]) != null) {
+ if (first instanceof TreeNode)
+ old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+ else {
+ Node<K,V> e = first; K k;
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ old = e;
+ break;
+ }
+ ++binCount;
+ } while ((e = e.next) != null);
+ }
+ }
+ V oldValue = (old == null) ? null : old.value;
+ V v = remappingFunction.apply(key, oldValue);
+ if (old != null) {
+ if (v != null) {
+ old.value = v;
+ afterNodeAccess(old);
+ }
+ else
+ removeNode(hash, key, null, false, true);
+ }
+ else if (v != null) {
+ if (t != null)
+ t.putTreeVal(this, tab, hash, key, v);
+ else {
+ tab[i] = newNode(hash, key, v, first);
+ if (binCount >= TREEIFY_THRESHOLD - 1)
+ treeifyBin(tab, hash);
+ }
+ ++modCount;
+ ++size;
+ afterNodeInsertion(true);
+ }
+ return v;
+ }
+
+ @Override
+ public V merge(K key, V value,
+ BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ if (value == null)
+ throw new NullPointerException();
+ if (remappingFunction == null)
+ throw new NullPointerException();
+ int hash = hash(key);
+ Node<K,V>[] tab; Node<K,V> first; int n, i;
+ int binCount = 0;
+ TreeNode<K,V> t = null;
+ Node<K,V> old = null;
+ if (size > threshold || (tab = table) == null ||
+ (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((first = tab[i = (n - 1) & hash]) != null) {
+ if (first instanceof TreeNode)
+ old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
+ else {
+ Node<K,V> e = first; K k;
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ old = e;
+ break;
+ }
+ ++binCount;
+ } while ((e = e.next) != null);
+ }
+ }
+ if (old != null) {
+ V v;
+ if (old.value != null)
+ v = remappingFunction.apply(old.value, value);
+ else
+ v = value;
+ if (v != null) {
+ old.value = v;
+ afterNodeAccess(old);
+ }
+ else
+ removeNode(hash, key, null, false, true);
+ return v;
+ }
+ if (value != null) {
+ if (t != null)
+ t.putTreeVal(this, tab, hash, key, value);
+ else {
+ tab[i] = newNode(hash, key, value, first);
+ if (binCount >= TREEIFY_THRESHOLD - 1)
+ treeifyBin(tab, hash);
+ }
+ ++modCount;
+ ++size;
+ afterNodeInsertion(true);
+ }
+ return value;
+ }
+
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ Node<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ // Android-changed: Detect changes to modCount early.
+ for (int i = 0; (i < tab.length && mc == modCount); ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next)
+ action.accept(e.key, e.value);
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
+ Node<K,V>[] tab;
+ if (function == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+ e.value = function.apply(e.key, e.value);
+ }
+ }
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ // Cloning and serialization
+
/**
* Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
* values themselves are not cloned.
*
* @return a shallow copy of this map
*/
+ @SuppressWarnings("unchecked")
+ @Override
public Object clone() {
- HashMap<K,V> result = null;
+ HashMap<K,V> result;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
- // assert false;
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError(e);
}
- if (result.table != EMPTY_TABLE) {
- result.inflateTable(Math.min(
- (int) Math.min(
- size * Math.min(1 / loadFactor, 4.0f),
- // we have limits...
- HashMap.MAXIMUM_CAPACITY),
- table.length));
- }
- result.entrySet = null;
- result.modCount = 0;
- result.size = 0;
- result.init();
- result.putAllForCreate(this);
-
+ result.reinitialize();
+ result.putMapEntries(this, false);
return result;
}
- /** @hide */ // Android added.
- static class HashMapEntry<K,V> implements Map.Entry<K,V> {
- final K key;
- V value;
- HashMapEntry<K,V> next;
- int hash;
+ // These methods are also used when serializing HashSets
+ final float loadFactor() { return loadFactor; }
+ final int capacity() {
+ return (table != null) ? table.length :
+ (threshold > 0) ? threshold :
+ DEFAULT_INITIAL_CAPACITY;
+ }
- /**
- * Creates new entry.
- */
- HashMapEntry(int h, K k, V v, HashMapEntry<K,V> n) {
- value = v;
- next = n;
- key = k;
- hash = h;
- }
+ /**
+ * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
+ * serialize it).
+ *
+ * @serialData The <i>capacity</i> of the HashMap (the length of the
+ * bucket array) is emitted (int), followed by the
+ * <i>size</i> (an int, the number of key-value
+ * mappings), followed by the key (Object) and value (Object)
+ * for each key-value mapping. The key-value mappings are
+ * emitted in no particular order.
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws IOException {
+ int buckets = capacity();
+ // Write out the threshold, loadfactor, and any hidden stuff
+ s.defaultWriteObject();
+ s.writeInt(buckets);
+ s.writeInt(size);
+ internalWriteEntries(s);
+ }
- public final K getKey() {
- return key;
- }
+ /**
+ * Reconstitute the {@code HashMap} instance from a stream (i.e.,
+ * deserialize it).
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ // Read in the threshold (ignored), loadfactor, and any hidden stuff
+ s.defaultReadObject();
+ reinitialize();
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new InvalidObjectException("Illegal load factor: " +
+ loadFactor);
+ s.readInt(); // Read and ignore number of buckets
+ int mappings = s.readInt(); // Read number of mappings (size)
+ if (mappings < 0)
+ throw new InvalidObjectException("Illegal mappings count: " +
+ mappings);
+ else if (mappings > 0) { // (if zero, use defaults)
+ // Size the table using given load factor only if within
+ // range of 0.25...4.0
+ float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
+ float fc = (float)mappings / lf + 1.0f;
+ int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
+ DEFAULT_INITIAL_CAPACITY :
+ (fc >= MAXIMUM_CAPACITY) ?
+ MAXIMUM_CAPACITY :
+ tableSizeFor((int)fc));
+ float ft = (float)cap * lf;
+ threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
+ (int)ft : Integer.MAX_VALUE);
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
+ table = tab;
- public final V getValue() {
- return value;
- }
-
- public final V setValue(V newValue) {
- V oldValue = value;
- value = newValue;
- return oldValue;
- }
-
- public final boolean equals(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry e = (Map.Entry)o;
- Object k1 = getKey();
- Object k2 = e.getKey();
- if (k1 == k2 || (k1 != null && k1.equals(k2))) {
- Object v1 = getValue();
- Object v2 = e.getValue();
- if (v1 == v2 || (v1 != null && v1.equals(v2)))
- return true;
+ // Read the keys and values, and put the mappings in the HashMap
+ for (int i = 0; i < mappings; i++) {
+ @SuppressWarnings("unchecked")
+ K key = (K) s.readObject();
+ @SuppressWarnings("unchecked")
+ V value = (V) s.readObject();
+ putVal(hash(key), key, value, false, false);
}
- return false;
- }
-
- public final int hashCode() {
- return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
- }
-
- public final String toString() {
- return getKey() + "=" + getValue();
- }
-
- /**
- * This method is invoked whenever the value in an entry is
- * overwritten by an invocation of put(k,v) for a key k that's already
- * in the HashMap.
- */
- void recordAccess(HashMap<K,V> m) {
- }
-
- /**
- * This method is invoked whenever the entry is
- * removed from the table.
- */
- void recordRemoval(HashMap<K,V> m) {
}
}
- /**
- * Adds a new entry with the specified key, value and hash code to
- * the specified bucket. It is the responsibility of this
- * method to resize the table if appropriate.
- *
- * Subclass overrides this to alter the behavior of put method.
- */
- void addEntry(int hash, K key, V value, int bucketIndex) {
- if ((size >= threshold) && (null != table[bucketIndex])) {
- resize(2 * table.length);
- hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
- bucketIndex = indexFor(hash, table.length);
- }
+ /* ------------------------------------------------------------ */
+ // iterators
- createEntry(hash, key, value, bucketIndex);
- }
-
- /**
- * Like addEntry except that this version is used when creating entries
- * as part of Map construction or "pseudo-construction" (cloning,
- * deserialization). This version needn't worry about resizing the table.
- *
- * Subclass overrides this to alter the behavior of HashMap(Map),
- * clone, and readObject.
- */
- void createEntry(int hash, K key, V value, int bucketIndex) {
- HashMapEntry<K,V> e = table[bucketIndex];
- table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
- size++;
- }
-
- private abstract class HashIterator<E> implements Iterator<E> {
- HashMapEntry<K,V> next; // next entry to return
- int expectedModCount; // For fast-fail
- int index; // current slot
- HashMapEntry<K,V> current; // current entry
+ abstract class HashIterator {
+ Node<K,V> next; // next entry to return
+ Node<K,V> current; // current entry
+ int expectedModCount; // for fast-fail
+ int index; // current slot
HashIterator() {
expectedModCount = modCount;
- if (size > 0) { // advance to first entry
- HashMapEntry[] t = table;
- while (index < t.length && (next = t[index++]) == null)
- ;
+ Node<K,V>[] t = table;
+ current = next = null;
+ index = 0;
+ if (t != null && size > 0) { // advance to first entry
+ do {} while (index < t.length && (next = t[index++]) == null);
}
}
@@ -846,58 +1426,53 @@
return next != null;
}
- final Entry<K,V> nextEntry() {
+ final Node<K,V> nextNode() {
+ Node<K,V>[] t;
+ Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
- HashMapEntry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
-
- if ((next = e.next) == null) {
- HashMapEntry[] t = table;
- while (index < t.length && (next = t[index++]) == null)
- ;
+ if ((next = (current = e).next) == null && (t = table) != null) {
+ do {} while (index < t.length && (next = t[index++]) == null);
}
- current = e;
return e;
}
- public void remove() {
- if (current == null)
+ public final void remove() {
+ Node<K,V> p = current;
+ if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
- Object k = current.key;
current = null;
- HashMap.this.removeEntryForKey(k);
+ K key = p.key;
+ removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
- private final class ValueIterator extends HashIterator<V> {
- public V next() {
- return nextEntry().getValue();
- }
+ final class KeyIterator extends HashIterator
+ implements Iterator<K> {
+ public final K next() { return nextNode().key; }
}
- private final class KeyIterator extends HashIterator<K> {
- public K next() {
- return nextEntry().getKey();
- }
+ final class ValueIterator extends HashIterator
+ implements Iterator<V> {
+ public final V next() { return nextNode().value; }
}
- private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
- public Map.Entry<K,V> next() {
- return nextEntry();
- }
+ final class EntryIterator extends HashIterator
+ implements Iterator<Map.Entry<K,V>> {
+ public final Map.Entry<K,V> next() { return nextNode(); }
}
- /* ------------------------------------------------------------ */
+ /* ------------------------------------------------------------ */
// spliterators
static class HashMapSpliterator<K,V> {
final HashMap<K,V> map;
- HashMapEntry<K,V> current; // current entry
+ Node<K,V> current; // current node
int index; // current index, modified on advance/split
int fence; // one past last index
int est; // size estimate
@@ -919,7 +1494,7 @@
HashMap<K,V> m = map;
est = m.size;
expectedModCount = m.modCount;
- HashMapEntry<K,V>[] tab = m.table;
+ Node<K,V>[] tab = m.table;
hi = fence = (tab == null) ? 0 : tab.length;
}
return hi;
@@ -932,8 +1507,8 @@
}
static final class KeySpliterator<K,V>
- extends HashMapSpliterator<K,V>
- implements Spliterator<K> {
+ extends HashMapSpliterator<K,V>
+ implements Spliterator<K> {
KeySpliterator(HashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
@@ -942,8 +1517,8 @@
public KeySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid || current != null) ? null :
- new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
- expectedModCount);
+ new KeySpliterator<>(map, lo, index = mid, est >>>= 1,
+ expectedModCount);
}
public void forEachRemaining(Consumer<? super K> action) {
@@ -951,7 +1526,7 @@
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
- HashMapEntry<K,V>[] tab = m.table;
+ Node<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = (tab == null) ? 0 : tab.length;
@@ -959,8 +1534,8 @@
else
mc = expectedModCount;
if (tab != null && tab.length >= hi &&
- (i = index) >= 0 && (i < (index = hi) || current != null)) {
- HashMapEntry<K,V> p = current;
+ (i = index) >= 0 && (i < (index = hi) || current != null)) {
+ Node<K,V> p = current;
current = null;
do {
if (p == null)
@@ -979,7 +1554,7 @@
int hi;
if (action == null)
throw new NullPointerException();
- HashMapEntry<K,V>[] tab = map.table;
+ Node<K,V>[] tab = map.table;
if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
@@ -999,14 +1574,13 @@
public int characteristics() {
return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
- Spliterator.DISTINCT |
- ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+ Spliterator.DISTINCT;
}
}
static final class ValueSpliterator<K,V>
- extends HashMapSpliterator<K,V>
- implements Spliterator<V> {
+ extends HashMapSpliterator<K,V>
+ implements Spliterator<V> {
ValueSpliterator(HashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
@@ -1015,8 +1589,8 @@
public ValueSpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid || current != null) ? null :
- new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
- expectedModCount);
+ new ValueSpliterator<>(map, lo, index = mid, est >>>= 1,
+ expectedModCount);
}
public void forEachRemaining(Consumer<? super V> action) {
@@ -1024,7 +1598,7 @@
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
- HashMapEntry<K,V>[] tab = m.table;
+ Node<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = (tab == null) ? 0 : tab.length;
@@ -1032,8 +1606,8 @@
else
mc = expectedModCount;
if (tab != null && tab.length >= hi &&
- (i = index) >= 0 && (i < (index = hi) || current != null)) {
- HashMapEntry<K,V> p = current;
+ (i = index) >= 0 && (i < (index = hi) || current != null)) {
+ Node<K,V> p = current;
current = null;
do {
if (p == null)
@@ -1052,7 +1626,7 @@
int hi;
if (action == null)
throw new NullPointerException();
- HashMapEntry<K,V>[] tab = map.table;
+ Node<K,V>[] tab = map.table;
if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
@@ -1071,14 +1645,13 @@
}
public int characteristics() {
- return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
- ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+ return (fence < 0 || est == map.size ? Spliterator.SIZED : 0);
}
}
static final class EntrySpliterator<K,V>
- extends HashMapSpliterator<K,V>
- implements Spliterator<Map.Entry<K,V>> {
+ extends HashMapSpliterator<K,V>
+ implements Spliterator<Map.Entry<K,V>> {
EntrySpliterator(HashMap<K,V> m, int origin, int fence, int est,
int expectedModCount) {
super(m, origin, fence, est, expectedModCount);
@@ -1087,8 +1660,8 @@
public EntrySpliterator<K,V> trySplit() {
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid || current != null) ? null :
- new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
- expectedModCount);
+ new EntrySpliterator<>(map, lo, index = mid, est >>>= 1,
+ expectedModCount);
}
public void forEachRemaining(Consumer<? super Map.Entry<K,V>> action) {
@@ -1096,7 +1669,7 @@
if (action == null)
throw new NullPointerException();
HashMap<K,V> m = map;
- HashMapEntry<K,V>[] tab = m.table;
+ Node<K,V>[] tab = m.table;
if ((hi = fence) < 0) {
mc = expectedModCount = m.modCount;
hi = fence = (tab == null) ? 0 : tab.length;
@@ -1104,8 +1677,8 @@
else
mc = expectedModCount;
if (tab != null && tab.length >= hi &&
- (i = index) >= 0 && (i < (index = hi) || current != null)) {
- HashMapEntry<K,V> p = current;
+ (i = index) >= 0 && (i < (index = hi) || current != null)) {
+ Node<K,V> p = current;
current = null;
do {
if (p == null)
@@ -1124,13 +1697,13 @@
int hi;
if (action == null)
throw new NullPointerException();
- HashMapEntry<K,V>[] tab = map.table;
+ Node<K,V>[] tab = map.table;
if (tab != null && tab.length >= (hi = getFence()) && index >= 0) {
while (current != null || index < hi) {
if (current == null)
current = tab[index++];
else {
- HashMapEntry<K,V> e = current;
+ Node<K,V> e = current;
current = current.next;
action.accept(e);
if (map.modCount != expectedModCount)
@@ -1144,346 +1717,667 @@
public int characteristics() {
return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) |
- Spliterator.DISTINCT |
- ((map instanceof LinkedHashMap) ? Spliterator.ORDERED : 0);
+ Spliterator.DISTINCT;
}
}
- // Subclass overrides these to alter behavior of views' iterator() method
- Iterator<K> newKeyIterator() {
- return new KeyIterator();
- }
- Iterator<V> newValueIterator() {
- return new ValueIterator();
- }
- Iterator<Map.Entry<K,V>> newEntryIterator() {
- return new EntryIterator();
- }
+ /* ------------------------------------------------------------ */
+ // LinkedHashMap support
- // Views
-
- private transient Set<Map.Entry<K,V>> entrySet = null;
-
- /**
- * Returns a {@link Set} view of the keys contained in this map.
- * The set is backed by the map, so changes to the map are
- * reflected in the set, and vice-versa. If the map is modified
- * while an iteration over the set is in progress (except through
- * the iterator's own <tt>remove</tt> operation), the results of
- * the iteration are undefined. The set supports element removal,
- * which removes the corresponding mapping from the map, via the
- * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
- * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
- * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
- * operations.
+ /*
+ * The following package-protected methods are designed to be
+ * overridden by LinkedHashMap, but not by any other subclass.
+ * Nearly all other internal methods are also package-protected
+ * but are declared final, so can be used by LinkedHashMap, view
+ * classes, and HashSet.
*/
- public Set<K> keySet() {
- Set<K> ks = keySet;
- return (ks != null ? ks : (keySet = new KeySet()));
+
+ // Create a regular (non-tree) node
+ Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
+ return new Node<>(hash, key, value, next);
}
- private final class KeySet extends AbstractSet<K> {
- public Iterator<K> iterator() {
- return newKeyIterator();
- }
- public int size() {
- return size;
- }
- public boolean contains(Object o) {
- return containsKey(o);
- }
- public boolean remove(Object o) {
- return HashMap.this.removeEntryForKey(o) != null;
- }
- public void clear() {
- HashMap.this.clear();
- }
- public final Spliterator<K> spliterator() {
- return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
- }
- public final void forEach(Consumer<? super K> action) {
- HashMapEntry<K,V>[] tab;
- if (action == null)
- throw new NullPointerException();
- if (size > 0 && (tab = table) != null) {
- int mc = modCount;
- for (int i = 0; i < tab.length; ++i) {
- for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
- action.accept(e.key);
- // Android-modified - this was outside of the loop, inconsistent with other
- // collections
- if (modCount != mc) {
- throw new ConcurrentModificationException();
- }
- }
- }
+ // For conversion from TreeNodes to plain nodes
+ Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
+ return new Node<>(p.hash, p.key, p.value, next);
+ }
- }
- }
+ // Create a tree bin node
+ TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
+ return new TreeNode<>(hash, key, value, next);
+ }
+
+ // For treeifyBin
+ TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
+ return new TreeNode<>(p.hash, p.key, p.value, next);
}
/**
- * Returns a {@link Collection} view of the values contained in this map.
- * The collection is backed by the map, so changes to the map are
- * reflected in the collection, and vice-versa. If the map is
- * modified while an iteration over the collection is in progress
- * (except through the iterator's own <tt>remove</tt> operation),
- * the results of the iteration are undefined. The collection
- * supports element removal, which removes the corresponding
- * mapping from the map, via the <tt>Iterator.remove</tt>,
- * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
- * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
- * support the <tt>add</tt> or <tt>addAll</tt> operations.
+ * Reset to initial default state. Called by clone and readObject.
*/
- public Collection<V> values() {
- Collection<V> vs = values;
- return (vs != null ? vs : (values = new Values()));
+ void reinitialize() {
+ table = null;
+ entrySet = null;
+ keySet = null;
+ values = null;
+ modCount = 0;
+ threshold = 0;
+ size = 0;
}
- private final class Values extends AbstractCollection<V> {
- public Iterator<V> iterator() {
- return newValueIterator();
- }
- public int size() {
- return size;
- }
- public boolean contains(Object o) {
- return containsValue(o);
- }
- public void clear() {
- HashMap.this.clear();
- }
- public final Spliterator<V> spliterator() {
- return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
- }
- public final void forEach(Consumer<? super V> action) {
- HashMapEntry<K,V>[] tab;
- if (action == null)
- throw new NullPointerException();
- if (size > 0 && (tab = table) != null) {
- int mc = modCount;
- for (int i = 0; i < tab.length; ++i) {
- for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
- action.accept(e.value);
- // Android-modified - this was outside of the loop, inconsistent with other
- // collections
- if (modCount != mc) {
- throw new ConcurrentModificationException();
- }
- }
+ // Callbacks to allow LinkedHashMap post-actions
+ void afterNodeAccess(Node<K,V> p) { }
+ void afterNodeInsertion(boolean evict) { }
+ void afterNodeRemoval(Node<K,V> p) { }
+
+ // Called only from writeObject, to ensure compatible ordering.
+ void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+ Node<K,V>[] tab;
+ if (size > 0 && (tab = table) != null) {
+ for (int i = 0; i < tab.length; ++i) {
+ for (Node<K,V> e = tab[i]; e != null; e = e.next) {
+ s.writeObject(e.key);
+ s.writeObject(e.value);
}
}
}
}
+ /* ------------------------------------------------------------ */
+ // Tree bins
+
/**
- * Returns a {@link Set} view of the mappings contained in this map.
- * The set is backed by the map, so changes to the map are
- * reflected in the set, and vice-versa. If the map is modified
- * while an iteration over the set is in progress (except through
- * the iterator's own <tt>remove</tt> operation, or through the
- * <tt>setValue</tt> operation on a map entry returned by the
- * iterator) the results of the iteration are undefined. The set
- * supports element removal, which removes the corresponding
- * mapping from the map, via the <tt>Iterator.remove</tt>,
- * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
- * <tt>clear</tt> operations. It does not support the
- * <tt>add</tt> or <tt>addAll</tt> operations.
- *
- * @return a set view of the mappings contained in this map
+ * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
+ * extends Node) so can be used as extension of either regular or
+ * linked node.
*/
- // Android-changed: Changed type parameter from <? extends Entry<K, V>
- // to a Map.Entry<K, V>.
- public Set<Map.Entry<K,V>> entrySet() {
- return entrySet0();
- }
-
- private Set<Map.Entry<K,V>> entrySet0() {
- Set<Map.Entry<K,V>> es = entrySet;
- return es != null ? es : (entrySet = new EntrySet());
- }
-
- private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
- public Iterator<Map.Entry<K,V>> iterator() {
- return newEntryIterator();
+ static final class TreeNode<K,V> extends LinkedHashMap.LinkedHashMapEntry<K,V> {
+ TreeNode<K,V> parent; // red-black tree links
+ TreeNode<K,V> left;
+ TreeNode<K,V> right;
+ TreeNode<K,V> prev; // needed to unlink next upon deletion
+ boolean red;
+ TreeNode(int hash, K key, V val, Node<K,V> next) {
+ super(hash, key, val, next);
}
- public boolean contains(Object o) {
- if (!(o instanceof Map.Entry))
+
+ /**
+ * Returns root of tree containing this node.
+ */
+ final TreeNode<K,V> root() {
+ for (TreeNode<K,V> r = this, p;;) {
+ if ((p = r.parent) == null)
+ return r;
+ r = p;
+ }
+ }
+
+ /**
+ * Ensures that the given root is the first node of its bin.
+ */
+ static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
+ int n;
+ if (root != null && tab != null && (n = tab.length) > 0) {
+ int index = (n - 1) & root.hash;
+ TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
+ if (root != first) {
+ Node<K,V> rn;
+ tab[index] = root;
+ TreeNode<K,V> rp = root.prev;
+ if ((rn = root.next) != null)
+ ((TreeNode<K,V>)rn).prev = rp;
+ if (rp != null)
+ rp.next = rn;
+ if (first != null)
+ first.prev = root;
+ root.next = first;
+ root.prev = null;
+ }
+ assert checkInvariants(root);
+ }
+ }
+
+ /**
+ * Finds the node starting at root p with the given hash and key.
+ * The kc argument caches comparableClassFor(key) upon first use
+ * comparing keys.
+ */
+ final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
+ TreeNode<K,V> p = this;
+ do {
+ int ph, dir; K pk;
+ TreeNode<K,V> pl = p.left, pr = p.right, q;
+ if ((ph = p.hash) > h)
+ p = pl;
+ else if (ph < h)
+ p = pr;
+ else if ((pk = p.key) == k || (k != null && k.equals(pk)))
+ return p;
+ else if (pl == null)
+ p = pr;
+ else if (pr == null)
+ p = pl;
+ else if ((kc != null ||
+ (kc = comparableClassFor(k)) != null) &&
+ (dir = compareComparables(kc, k, pk)) != 0)
+ p = (dir < 0) ? pl : pr;
+ else if ((q = pr.find(h, k, kc)) != null)
+ return q;
+ else
+ p = pl;
+ } while (p != null);
+ return null;
+ }
+
+ /**
+ * Calls find for root node.
+ */
+ final TreeNode<K,V> getTreeNode(int h, Object k) {
+ return ((parent != null) ? root() : this).find(h, k, null);
+ }
+
+ /**
+ * Tie-breaking utility for ordering insertions when equal
+ * hashCodes and non-comparable. We don't require a total
+ * order, just a consistent insertion rule to maintain
+ * equivalence across rebalancings. Tie-breaking further than
+ * necessary simplifies testing a bit.
+ */
+ static int tieBreakOrder(Object a, Object b) {
+ int d;
+ if (a == null || b == null ||
+ (d = a.getClass().getName().
+ compareTo(b.getClass().getName())) == 0)
+ d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
+ -1 : 1);
+ return d;
+ }
+
+ /**
+ * Forms tree of the nodes linked from this node.
+ * @return root of tree
+ */
+ final void treeify(Node<K,V>[] tab) {
+ TreeNode<K,V> root = null;
+ for (TreeNode<K,V> x = this, next; x != null; x = next) {
+ next = (TreeNode<K,V>)x.next;
+ x.left = x.right = null;
+ if (root == null) {
+ x.parent = null;
+ x.red = false;
+ root = x;
+ }
+ else {
+ K k = x.key;
+ int h = x.hash;
+ Class<?> kc = null;
+ for (TreeNode<K,V> p = root;;) {
+ int dir, ph;
+ K pk = p.key;
+ if ((ph = p.hash) > h)
+ dir = -1;
+ else if (ph < h)
+ dir = 1;
+ else if ((kc == null &&
+ (kc = comparableClassFor(k)) == null) ||
+ (dir = compareComparables(kc, k, pk)) == 0)
+ dir = tieBreakOrder(k, pk);
+
+ TreeNode<K,V> xp = p;
+ if ((p = (dir <= 0) ? p.left : p.right) == null) {
+ x.parent = xp;
+ if (dir <= 0)
+ xp.left = x;
+ else
+ xp.right = x;
+ root = balanceInsertion(root, x);
+ break;
+ }
+ }
+ }
+ }
+ moveRootToFront(tab, root);
+ }
+
+ /**
+ * Returns a list of non-TreeNodes replacing those linked from
+ * this node.
+ */
+ final Node<K,V> untreeify(HashMap<K,V> map) {
+ Node<K,V> hd = null, tl = null;
+ for (Node<K,V> q = this; q != null; q = q.next) {
+ Node<K,V> p = map.replacementNode(q, null);
+ if (tl == null)
+ hd = p;
+ else
+ tl.next = p;
+ tl = p;
+ }
+ return hd;
+ }
+
+ /**
+ * Tree version of putVal.
+ */
+ final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
+ int h, K k, V v) {
+ Class<?> kc = null;
+ boolean searched = false;
+ TreeNode<K,V> root = (parent != null) ? root() : this;
+ for (TreeNode<K,V> p = root;;) {
+ int dir, ph; K pk;
+ if ((ph = p.hash) > h)
+ dir = -1;
+ else if (ph < h)
+ dir = 1;
+ else if ((pk = p.key) == k || (k != null && k.equals(pk)))
+ return p;
+ else if ((kc == null &&
+ (kc = comparableClassFor(k)) == null) ||
+ (dir = compareComparables(kc, k, pk)) == 0) {
+ if (!searched) {
+ TreeNode<K,V> q, ch;
+ searched = true;
+ if (((ch = p.left) != null &&
+ (q = ch.find(h, k, kc)) != null) ||
+ ((ch = p.right) != null &&
+ (q = ch.find(h, k, kc)) != null))
+ return q;
+ }
+ dir = tieBreakOrder(k, pk);
+ }
+
+ TreeNode<K,V> xp = p;
+ if ((p = (dir <= 0) ? p.left : p.right) == null) {
+ Node<K,V> xpn = xp.next;
+ TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
+ if (dir <= 0)
+ xp.left = x;
+ else
+ xp.right = x;
+ xp.next = x;
+ x.parent = x.prev = xp;
+ if (xpn != null)
+ ((TreeNode<K,V>)xpn).prev = x;
+ moveRootToFront(tab, balanceInsertion(root, x));
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Removes the given node, that must be present before this call.
+ * This is messier than typical red-black deletion code because we
+ * cannot swap the contents of an interior node with a leaf
+ * successor that is pinned by "next" pointers that are accessible
+ * independently during traversal. So instead we swap the tree
+ * linkages. If the current tree appears to have too few nodes,
+ * the bin is converted back to a plain bin. (The test triggers
+ * somewhere between 2 and 6 nodes, depending on tree structure).
+ */
+ final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
+ boolean movable) {
+ int n;
+ if (tab == null || (n = tab.length) == 0)
+ return;
+ int index = (n - 1) & hash;
+ TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
+ TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
+ if (pred == null)
+ tab[index] = first = succ;
+ else
+ pred.next = succ;
+ if (succ != null)
+ succ.prev = pred;
+ if (first == null)
+ return;
+ if (root.parent != null)
+ root = root.root();
+ if (root == null || root.right == null ||
+ (rl = root.left) == null || rl.left == null) {
+ tab[index] = first.untreeify(map); // too small
+ return;
+ }
+ TreeNode<K,V> p = this, pl = left, pr = right, replacement;
+ if (pl != null && pr != null) {
+ TreeNode<K,V> s = pr, sl;
+ while ((sl = s.left) != null) // find successor
+ s = sl;
+ boolean c = s.red; s.red = p.red; p.red = c; // swap colors
+ TreeNode<K,V> sr = s.right;
+ TreeNode<K,V> pp = p.parent;
+ if (s == pr) { // p was s's direct parent
+ p.parent = s;
+ s.right = p;
+ }
+ else {
+ TreeNode<K,V> sp = s.parent;
+ if ((p.parent = sp) != null) {
+ if (s == sp.left)
+ sp.left = p;
+ else
+ sp.right = p;
+ }
+ if ((s.right = pr) != null)
+ pr.parent = s;
+ }
+ p.left = null;
+ if ((p.right = sr) != null)
+ sr.parent = p;
+ if ((s.left = pl) != null)
+ pl.parent = s;
+ if ((s.parent = pp) == null)
+ root = s;
+ else if (p == pp.left)
+ pp.left = s;
+ else
+ pp.right = s;
+ if (sr != null)
+ replacement = sr;
+ else
+ replacement = p;
+ }
+ else if (pl != null)
+ replacement = pl;
+ else if (pr != null)
+ replacement = pr;
+ else
+ replacement = p;
+ if (replacement != p) {
+ TreeNode<K,V> pp = replacement.parent = p.parent;
+ if (pp == null)
+ root = replacement;
+ else if (p == pp.left)
+ pp.left = replacement;
+ else
+ pp.right = replacement;
+ p.left = p.right = p.parent = null;
+ }
+
+ TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
+
+ if (replacement == p) { // detach
+ TreeNode<K,V> pp = p.parent;
+ p.parent = null;
+ if (pp != null) {
+ if (p == pp.left)
+ pp.left = null;
+ else if (p == pp.right)
+ pp.right = null;
+ }
+ }
+ if (movable)
+ moveRootToFront(tab, r);
+ }
+
+ /**
+ * Splits nodes in a tree bin into lower and upper tree bins,
+ * or untreeifies if now too small. Called only from resize;
+ * see above discussion about split bits and indices.
+ *
+ * @param map the map
+ * @param tab the table for recording bin heads
+ * @param index the index of the table being split
+ * @param bit the bit of hash to split on
+ */
+ final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
+ TreeNode<K,V> b = this;
+ // Relink into lo and hi lists, preserving order
+ TreeNode<K,V> loHead = null, loTail = null;
+ TreeNode<K,V> hiHead = null, hiTail = null;
+ int lc = 0, hc = 0;
+ for (TreeNode<K,V> e = b, next; e != null; e = next) {
+ next = (TreeNode<K,V>)e.next;
+ e.next = null;
+ if ((e.hash & bit) == 0) {
+ if ((e.prev = loTail) == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ ++lc;
+ }
+ else {
+ if ((e.prev = hiTail) == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ ++hc;
+ }
+ }
+
+ if (loHead != null) {
+ if (lc <= UNTREEIFY_THRESHOLD)
+ tab[index] = loHead.untreeify(map);
+ else {
+ tab[index] = loHead;
+ if (hiHead != null) // (else is already treeified)
+ loHead.treeify(tab);
+ }
+ }
+ if (hiHead != null) {
+ if (hc <= UNTREEIFY_THRESHOLD)
+ tab[index + bit] = hiHead.untreeify(map);
+ else {
+ tab[index + bit] = hiHead;
+ if (loHead != null)
+ hiHead.treeify(tab);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ // Red-black tree methods, all adapted from CLR
+
+ static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
+ TreeNode<K,V> p) {
+ TreeNode<K,V> r, pp, rl;
+ if (p != null && (r = p.right) != null) {
+ if ((rl = p.right = r.left) != null)
+ rl.parent = p;
+ if ((pp = r.parent = p.parent) == null)
+ (root = r).red = false;
+ else if (pp.left == p)
+ pp.left = r;
+ else
+ pp.right = r;
+ r.left = p;
+ p.parent = r;
+ }
+ return root;
+ }
+
+ static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
+ TreeNode<K,V> p) {
+ TreeNode<K,V> l, pp, lr;
+ if (p != null && (l = p.left) != null) {
+ if ((lr = p.left = l.right) != null)
+ lr.parent = p;
+ if ((pp = l.parent = p.parent) == null)
+ (root = l).red = false;
+ else if (pp.right == p)
+ pp.right = l;
+ else
+ pp.left = l;
+ l.right = p;
+ p.parent = l;
+ }
+ return root;
+ }
+
+ static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
+ TreeNode<K,V> x) {
+ x.red = true;
+ for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
+ if ((xp = x.parent) == null) {
+ x.red = false;
+ return x;
+ }
+ else if (!xp.red || (xpp = xp.parent) == null)
+ return root;
+ if (xp == (xppl = xpp.left)) {
+ if ((xppr = xpp.right) != null && xppr.red) {
+ xppr.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.right) {
+ root = rotateLeft(root, x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ root = rotateRight(root, xpp);
+ }
+ }
+ }
+ }
+ else {
+ if (xppl != null && xppl.red) {
+ xppl.red = false;
+ xp.red = false;
+ xpp.red = true;
+ x = xpp;
+ }
+ else {
+ if (x == xp.left) {
+ root = rotateRight(root, x = xp);
+ xpp = (xp = x.parent) == null ? null : xp.parent;
+ }
+ if (xp != null) {
+ xp.red = false;
+ if (xpp != null) {
+ xpp.red = true;
+ root = rotateLeft(root, xpp);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
+ TreeNode<K,V> x) {
+ for (TreeNode<K,V> xp, xpl, xpr;;) {
+ if (x == null || x == root)
+ return root;
+ else if ((xp = x.parent) == null) {
+ x.red = false;
+ return x;
+ }
+ else if (x.red) {
+ x.red = false;
+ return root;
+ }
+ else if ((xpl = xp.left) == x) {
+ if ((xpr = xp.right) != null && xpr.red) {
+ xpr.red = false;
+ xp.red = true;
+ root = rotateLeft(root, xp);
+ xpr = (xp = x.parent) == null ? null : xp.right;
+ }
+ if (xpr == null)
+ x = xp;
+ else {
+ TreeNode<K,V> sl = xpr.left, sr = xpr.right;
+ if ((sr == null || !sr.red) &&
+ (sl == null || !sl.red)) {
+ xpr.red = true;
+ x = xp;
+ }
+ else {
+ if (sr == null || !sr.red) {
+ if (sl != null)
+ sl.red = false;
+ xpr.red = true;
+ root = rotateRight(root, xpr);
+ xpr = (xp = x.parent) == null ?
+ null : xp.right;
+ }
+ if (xpr != null) {
+ xpr.red = (xp == null) ? false : xp.red;
+ if ((sr = xpr.right) != null)
+ sr.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ root = rotateLeft(root, xp);
+ }
+ x = root;
+ }
+ }
+ }
+ else { // symmetric
+ if (xpl != null && xpl.red) {
+ xpl.red = false;
+ xp.red = true;
+ root = rotateRight(root, xp);
+ xpl = (xp = x.parent) == null ? null : xp.left;
+ }
+ if (xpl == null)
+ x = xp;
+ else {
+ TreeNode<K,V> sl = xpl.left, sr = xpl.right;
+ if ((sl == null || !sl.red) &&
+ (sr == null || !sr.red)) {
+ xpl.red = true;
+ x = xp;
+ }
+ else {
+ if (sl == null || !sl.red) {
+ if (sr != null)
+ sr.red = false;
+ xpl.red = true;
+ root = rotateLeft(root, xpl);
+ xpl = (xp = x.parent) == null ?
+ null : xp.left;
+ }
+ if (xpl != null) {
+ xpl.red = (xp == null) ? false : xp.red;
+ if ((sl = xpl.left) != null)
+ sl.red = false;
+ }
+ if (xp != null) {
+ xp.red = false;
+ root = rotateRight(root, xp);
+ }
+ x = root;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Recursive invariant check
+ */
+ static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
+ TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
+ tb = t.prev, tn = (TreeNode<K,V>)t.next;
+ if (tb != null && tb.next != t)
return false;
- Map.Entry<K,V> e = (Map.Entry<K,V>) o;
- Entry<K,V> candidate = getEntry(e.getKey());
- return candidate != null && candidate.equals(e);
- }
- public boolean remove(Object o) {
- return removeMapping(o) != null;
- }
- public int size() {
- return size;
- }
- public void clear() {
- HashMap.this.clear();
- }
- public final Spliterator<Map.Entry<K,V>> spliterator() {
- return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
- }
- public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
- HashMapEntry<K,V>[] tab;
- if (action == null)
- throw new NullPointerException();
- if (size > 0 && (tab = table) != null) {
- int mc = modCount;
- for (int i = 0; i < tab.length; ++i) {
- for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
- action.accept(e);
- // Android-modified - this was outside of the loop, inconsistent with other
- // collections
- if (modCount != mc) {
- throw new ConcurrentModificationException();
- }
- }
- }
- }
- }
- }
-
- @Override
- public void forEach(BiConsumer<? super K, ? super V> action) {
- HashMapEntry<K,V>[] tab;
- if (action == null)
- throw new NullPointerException();
- if (size > 0 && (tab = table) != null) {
- int mc = modCount;
- for (int i = 0; i < tab.length; ++i) {
- for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
- action.accept(e.key, e.value);
- // Android-modified - this was outside of the loop, inconsistent with other
- // collections
- if (modCount != mc) {
- throw new ConcurrentModificationException();
- }
- }
- }
- }
- }
-
- @Override
- public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
- HashMapEntry<K,V>[] tab;
- if (function == null)
- throw new NullPointerException();
- if (size > 0 && (tab = table) != null) {
- int mc = modCount;
- for (int i = 0; i < tab.length; ++i) {
- for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
- e.value = function.apply(e.key, e.value);
- }
- }
- if (modCount != mc)
- throw new ConcurrentModificationException();
- }
- }
-
- /**
- * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
- * serialize it).
- *
- * @serialData The <i>capacity</i> of the HashMap (the length of the
- * bucket array) is emitted (int), followed by the
- * <i>size</i> (an int, the number of key-value
- * mappings), followed by the key (Object) and value (Object)
- * for each key-value mapping. The key-value mappings are
- * emitted in no particular order.
- */
- private void writeObject(java.io.ObjectOutputStream s)
- throws IOException
- {
- // Write out the threshold, loadfactor, and any hidden stuff
- s.defaultWriteObject();
-
- // Write out number of buckets
- if (table==EMPTY_TABLE) {
- s.writeInt(roundUpToPowerOf2(threshold));
- } else {
- s.writeInt(table.length);
- }
-
- // Write out size (number of Mappings)
- s.writeInt(size);
-
- // Write out keys and values (alternating)
- if (size > 0) {
- for(Map.Entry<K,V> e : entrySet0()) {
- s.writeObject(e.getKey());
- s.writeObject(e.getValue());
- }
- }
- }
-
- private static final long serialVersionUID = 362498820763181265L;
-
- /**
- * Reconstitute the {@code HashMap} instance from a stream (i.e.,
- * deserialize it).
- */
- private void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException
- {
- // Read in the threshold (ignored), loadfactor, and any hidden stuff
- s.defaultReadObject();
- if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
- throw new InvalidObjectException("Illegal load factor: " +
- loadFactor);
- }
-
- // set other fields that need values
- table = (HashMapEntry<K,V>[]) EMPTY_TABLE;
-
- // Read in number of buckets
- s.readInt(); // ignored.
-
- // Read number of mappings
- int mappings = s.readInt();
- if (mappings < 0)
- throw new InvalidObjectException("Illegal mappings count: " +
- mappings);
-
- // capacity chosen by number of mappings and desired load (if >= 0.25)
- int capacity = (int) Math.min(
- mappings * Math.min(1 / loadFactor, 4.0f),
- // we have limits...
- HashMap.MAXIMUM_CAPACITY);
-
- // allocate the bucket array;
- if (mappings > 0) {
- inflateTable(capacity);
- } else {
- threshold = capacity;
- }
-
- init(); // Give subclass a chance to do its thing.
-
- // Read the keys and values, and put the mappings in the HashMap
- for (int i = 0; i < mappings; i++) {
- K key = (K) s.readObject();
- V value = (V) s.readObject();
- putForCreate(key, value);
- }
- }
-
- @Override
- public boolean replace(K key, V oldValue, V newValue) {
- HashMapEntry<K,V> e; V v;
- if ((e = (HashMapEntry)getEntry(key)) != null &&
- ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
- e.value = newValue;
- e.recordAccess(this);
+ if (tn != null && tn.prev != t)
+ return false;
+ if (tp != null && t != tp.left && t != tp.right)
+ return false;
+ if (tl != null && (tl.parent != t || tl.hash > t.hash))
+ return false;
+ if (tr != null && (tr.parent != t || tr.hash < t.hash))
+ return false;
+ if (t.red && tl != null && tl.red && tr != null && tr.red)
+ return false;
+ if (tl != null && !checkInvariants(tl))
+ return false;
+ if (tr != null && !checkInvariants(tr))
+ return false;
return true;
}
- return false;
}
- // These methods are used when serializing HashSets
- int capacity() { return table.length; }
- float loadFactor() { return loadFactor; }
}
diff --git a/ojluni/src/main/java/java/util/HashSet.java b/ojluni/src/main/java/java/util/HashSet.java
index ab5fe4d..ebbe43c 100644
--- a/ojluni/src/main/java/java/util/HashSet.java
+++ b/ojluni/src/main/java/java/util/HashSet.java
@@ -25,6 +25,8 @@
package java.util;
+import java.io.InvalidObjectException;
+
/**
* This class implements the <tt>Set</tt> interface, backed by a hash table
* (actually a <tt>HashMap</tt> instance). It makes no guarantees as to the
@@ -247,13 +249,14 @@
*
* @return a shallow copy of this set
*/
+ @SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
- throw new InternalError();
+ throw new InternalError(e);
}
}
@@ -293,19 +296,41 @@
// Read in any hidden serialization magic
s.defaultReadObject();
- // Read in HashMap capacity and load factor and create backing HashMap
+ // Read capacity and verify non-negative.
int capacity = s.readInt();
+ if (capacity < 0) {
+ throw new InvalidObjectException("Illegal capacity: " +
+ capacity);
+ }
+
+ // Read load factor and verify positive and non NaN.
float loadFactor = s.readFloat();
- map = (((HashSet)this) instanceof LinkedHashSet ?
+ if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
+ throw new InvalidObjectException("Illegal load factor: " +
+ loadFactor);
+ }
+
+ // Read size and verify non-negative.
+ int size = s.readInt();
+ if (size < 0) {
+ throw new InvalidObjectException("Illegal size: " +
+ size);
+ }
+
+ // Set the capacity according to the size and load factor ensuring that
+ // the HashMap is at least 25% full but clamping to maximum capacity.
+ capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
+ HashMap.MAXIMUM_CAPACITY);
+
+ // Create backing HashMap
+ map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
- // Read in size
- int size = s.readInt();
-
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
- E e = (E) s.readObject();
+ @SuppressWarnings("unchecked")
+ E e = (E) s.readObject();
map.put(e, PRESENT);
}
}
diff --git a/ojluni/src/main/java/java/util/LinkedHashMap.java b/ojluni/src/main/java/java/util/LinkedHashMap.java
index 0396e8c..dd32423 100644
--- a/ojluni/src/main/java/java/util/LinkedHashMap.java
+++ b/ojluni/src/main/java/java/util/LinkedHashMap.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
- * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,12 +25,10 @@
package java.util;
-import sun.misc.Hashing;
-
-import java.io.*;
-import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.io.IOException;
/**
* <p>Hash table and linked list implementation of the <tt>Map</tt> interface,
@@ -75,7 +72,8 @@
* key-value mappings are provided by the specified map's entry set iterator.
* <i>No other methods generate entry accesses.</i> In particular, operations
* on collection-views do <i>not</i> affect the order of iteration of the
- * backing map. *
+ * backing map.
+ *
* <p>The {@link #removeEldestEntry(Map.Entry)} method may be overridden to
* impose a policy for removing stale mappings automatically when new mappings
* are added to the map.
@@ -116,8 +114,8 @@
* iteration order. In insertion-ordered linked hash maps, merely changing
* the value associated with a key that is already contained in the map is not
* a structural modification. <strong>In access-ordered linked hash maps,
- * merely querying the map with <tt>get</tt> is a structural
- * modification.</strong>)
+ * merely querying the map with <tt>get</tt> is a structural modification.
+ * </strong>)
*
* <p>The iterators returned by the <tt>iterator</tt> method of the collections
* returned by all of this class's collection view methods are
@@ -142,7 +140,7 @@
* <em>fail-fast</em>, and additionally report {@link Spliterator#ORDERED}.
*
* <p>This class is a member of the
- * <a href="{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/collections/index.html">
+ * <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @implNote
@@ -162,18 +160,59 @@
* @see Hashtable
* @since 1.4
*/
-
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
+ /*
+ * Implementation note. A previous version of this class was
+ * internally structured a little differently. Because superclass
+ * HashMap now uses trees for some of its nodes, class
+ * LinkedHashMap.Entry is now treated as intermediary node class
+ * that can also be converted to tree form.
+ *
+ * Android-changed BEGIN
+ * LinkedHashMapEntry should not be renamed. Specifically, for
+ * source compatibility with earlier versions of Android, this
+ * nested class must not be named "Entry". Otherwise, it would
+ * hide Map.Entry which would break compilation of code like:
+ *
+ * LinkedHashMap.Entry<K, V> entry = map.entrySet().iterator.next()
+ *
+ * To compile, that code snippet's "LinkedHashMap.Entry" must
+ * mean java.util.Map.Entry which is the compile time type of
+ * entrySet()'s elements.
+ * Android-changed END
+ *
+ * The changes in node classes also require using two fields
+ * (head, tail) rather than a pointer to a header node to maintain
+ * the doubly-linked before/after list. This class also
+ * previously used a different style of callback methods upon
+ * access, insertion, and removal.
+ */
+
+ /**
+ * HashMap.Node subclass for normal LinkedHashMap entries.
+ */
+ static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
+ LinkedHashMapEntry<K,V> before, after;
+ LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
+ super(hash, key, value, next);
+ }
+ }
+
private static final long serialVersionUID = 3801124242820219131L;
/**
- * The head of the doubly linked list.
+ * The head (eldest) of the doubly linked list.
*/
- private transient LinkedHashMapEntry<K,V> header;
+ transient LinkedHashMapEntry<K,V> head;
+
+ /**
+ * The tail (youngest) of the doubly linked list.
+ */
+ transient LinkedHashMapEntry<K,V> tail;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
@@ -181,7 +220,125 @@
*
* @serial
*/
- private final boolean accessOrder;
+ final boolean accessOrder;
+
+ // internal utilities
+
+ // link at the end of list
+ private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
+ LinkedHashMapEntry<K,V> last = tail;
+ tail = p;
+ if (last == null)
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
+ }
+
+ // apply src's links to dst
+ private void transferLinks(LinkedHashMapEntry<K,V> src,
+ LinkedHashMapEntry<K,V> dst) {
+ LinkedHashMapEntry<K,V> b = dst.before = src.before;
+ LinkedHashMapEntry<K,V> a = dst.after = src.after;
+ if (b == null)
+ head = dst;
+ else
+ b.after = dst;
+ if (a == null)
+ tail = dst;
+ else
+ a.before = dst;
+ }
+
+ // overrides of HashMap hook methods
+
+ void reinitialize() {
+ super.reinitialize();
+ head = tail = null;
+ }
+
+ Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
+ LinkedHashMapEntry<K,V> p =
+ new LinkedHashMapEntry<K,V>(hash, key, value, e);
+ linkNodeLast(p);
+ return p;
+ }
+
+ Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
+ LinkedHashMapEntry<K,V> q = (LinkedHashMapEntry<K,V>)p;
+ LinkedHashMapEntry<K,V> t =
+ new LinkedHashMapEntry<K,V>(q.hash, q.key, q.value, next);
+ transferLinks(q, t);
+ return t;
+ }
+
+ TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
+ TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
+ linkNodeLast(p);
+ return p;
+ }
+
+ TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
+ LinkedHashMapEntry<K,V> q = (LinkedHashMapEntry<K,V>)p;
+ TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
+ transferLinks(q, t);
+ return t;
+ }
+
+ void afterNodeRemoval(Node<K,V> e) { // unlink
+ LinkedHashMapEntry<K,V> p =
+ (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
+ p.before = p.after = null;
+ if (b == null)
+ head = a;
+ else
+ b.after = a;
+ if (a == null)
+ tail = b;
+ else
+ a.before = b;
+ }
+
+ void afterNodeInsertion(boolean evict) { // possibly remove eldest
+ LinkedHashMapEntry<K,V> first;
+ if (evict && (first = head) != null && removeEldestEntry(first)) {
+ K key = first.key;
+ removeNode(hash(key), key, null, false, true);
+ }
+ }
+
+ void afterNodeAccess(Node<K,V> e) { // move node to last
+ LinkedHashMapEntry<K,V> last;
+ if (accessOrder && (last = tail) != e) {
+ LinkedHashMapEntry<K,V> p =
+ (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
+ p.after = null;
+ if (b == null)
+ head = a;
+ else
+ b.after = a;
+ if (a != null)
+ a.before = b;
+ else
+ last = b;
+ if (last == null)
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
+ tail = p;
+ ++modCount;
+ }
+ }
+
+ void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
+ for (LinkedHashMapEntry<K,V> e = head; e != null; e = e.after) {
+ s.writeObject(e.key);
+ s.writeObject(e.value);
+ }
+ }
/**
* Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
@@ -228,8 +385,9 @@
* @throws NullPointerException if the specified map is null
*/
public LinkedHashMap(Map<? extends K, ? extends V> m) {
- super(m);
+ super();
accessOrder = false;
+ putMapEntries(m, false);
}
/**
@@ -250,32 +408,6 @@
this.accessOrder = accessOrder;
}
- /**
- * Called by superclass constructors and pseudoconstructors (clone,
- * readObject) before any entries are inserted into the map. Initializes
- * the chain.
- */
- @Override
- void init() {
- header = new LinkedHashMapEntry<>(-1, null, null, null);
- header.before = header.after = header;
- }
-
- /**
- * Transfers all entries to new table array. This method is called
- * by superclass resize. It is overridden for performance, as it is
- * faster to iterate using our linked list.
- */
- @Override
- void transfer(HashMapEntry[] newTable) {
- int newCapacity = newTable.length;
- for (LinkedHashMapEntry<K,V> e = header.after; e != header; e = e.after) {
- int index = indexFor(e.hash, newCapacity);
- e.next = newTable[index];
- newTable[index] = e;
- }
- }
-
/**
* Returns <tt>true</tt> if this map maps one or more keys to the
@@ -286,15 +418,10 @@
* specified value
*/
public boolean containsValue(Object value) {
- // Overridden to take advantage of faster iterator
- if (value==null) {
- for (LinkedHashMapEntry e = header.after; e != header; e = e.after)
- if (e.value==null)
- return true;
- } else {
- for (LinkedHashMapEntry e = header.after; e != header; e = e.after)
- if (value.equals(e.value))
- return true;
+ for (LinkedHashMapEntry<K,V> e = head; e != null; e = e.after) {
+ V v = e.value;
+ if (v == value || (value != null && value.equals(v)))
+ return true;
}
return false;
}
@@ -315,153 +442,32 @@
* distinguish these two cases.
*/
public V get(Object key) {
- LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
- if (e == null)
+ Node<K,V> e;
+ if ((e = getNode(hash(key), key)) == null)
return null;
- e.recordAccess(this);
+ if (accessOrder)
+ afterNodeAccess(e);
return e.value;
}
/**
- * Removes all of the mappings from this map.
- * The map will be empty after this call returns.
+ * {@inheritDoc}
+ */
+ public V getOrDefault(Object key, V defaultValue) {
+ Node<K,V> e;
+ if ((e = getNode(hash(key), key)) == null)
+ return defaultValue;
+ if (accessOrder)
+ afterNodeAccess(e);
+ return e.value;
+ }
+
+ /**
+ * {@inheritDoc}
*/
public void clear() {
super.clear();
- header.before = header.after = header;
- }
-
- /**
- * LinkedHashMap entry.
- */
- private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V> {
- // These fields comprise the doubly linked list used for iteration.
- LinkedHashMapEntry<K,V> before, after;
-
- LinkedHashMapEntry(int hash, K key, V value, HashMapEntry<K,V> next) {
- super(hash, key, value, next);
- }
-
- /**
- * Removes this entry from the linked list.
- */
- private void remove() {
- before.after = after;
- after.before = before;
- }
-
- /**
- * Inserts this entry before the specified existing entry in the list.
- */
- private void addBefore(LinkedHashMapEntry<K,V> existingEntry) {
- after = existingEntry;
- before = existingEntry.before;
- before.after = this;
- after.before = this;
- }
-
- /**
- * This method is invoked by the superclass whenever the value
- * of a pre-existing entry is read by Map.get or modified by Map.set.
- * If the enclosing Map is access-ordered, it moves the entry
- * to the end of the list; otherwise, it does nothing.
- */
- void recordAccess(HashMap<K,V> m) {
- LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
- if (lm.accessOrder) {
- lm.modCount++;
- remove();
- addBefore(lm.header);
- }
- }
-
- void recordRemoval(HashMap<K,V> m) {
- remove();
- }
- }
-
- private abstract class LinkedHashIterator<T> implements Iterator<T> {
- LinkedHashMapEntry<K,V> nextEntry = header.after;
- LinkedHashMapEntry<K,V> lastReturned = null;
-
- /**
- * The modCount value that the iterator believes that the backing
- * List should have. If this expectation is violated, the iterator
- * has detected concurrent modification.
- */
- int expectedModCount = modCount;
-
- public boolean hasNext() {
- return nextEntry != header;
- }
-
- public void remove() {
- if (lastReturned == null)
- throw new IllegalStateException();
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
-
- LinkedHashMap.this.remove(lastReturned.key);
- lastReturned = null;
- expectedModCount = modCount;
- }
-
- Entry<K,V> nextEntry() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- if (nextEntry == header)
- throw new NoSuchElementException();
-
- LinkedHashMapEntry<K,V> e = lastReturned = nextEntry;
- nextEntry = e.after;
- return e;
- }
- }
-
- private class KeyIterator extends LinkedHashIterator<K> {
- public K next() { return nextEntry().getKey(); }
- }
-
- private class ValueIterator extends LinkedHashIterator<V> {
- public V next() { return nextEntry().getValue(); }
- }
-
- private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
- public Map.Entry<K,V> next() { return nextEntry(); }
- }
-
- // These Overrides alter the behavior of superclass view iterator() methods
- Iterator<K> newKeyIterator() { return new KeyIterator(); }
- Iterator<V> newValueIterator() { return new ValueIterator(); }
- Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
-
- /**
- * This override alters behavior of superclass put method. It causes newly
- * allocated entry to get inserted at the end of the linked list and
- * removes the eldest entry if appropriate.
- */
- void addEntry(int hash, K key, V value, int bucketIndex) {
- // Previous Android releases called removeEldestEntry() before actually
- // inserting a value but after increasing the size.
- // The RI is documented to call it afterwards.
- // **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
-
- // Remove eldest entry if instructed
- LinkedHashMapEntry<K,V> eldest = header.after;
- if (eldest != header) {
- boolean removeEldest;
- size++;
- try {
- removeEldest = removeEldestEntry(eldest);
- } finally {
- size--;
- }
- if (removeEldest) {
- removeEntryForKey(eldest.key);
- }
- }
-
- super.addEntry(hash, key, value, bucketIndex);
+ head = tail = null;
}
/**
@@ -472,28 +478,10 @@
* @hide
*/
public Map.Entry<K, V> eldest() {
- Entry<K, V> eldest = header.after;
- return eldest != header ? eldest : null;
+ return head;
}
/**
- * This override differs from addEntry in that it doesn't resize the
- * table or remove the eldest entry.
- */
- void createEntry(int hash, K key, V value, int bucketIndex) {
- HashMapEntry<K,V> old = table[bucketIndex];
- LinkedHashMapEntry<K,V> e = new LinkedHashMapEntry<>(hash, key, value, old);
- table[bucketIndex] = e;
- e.addBefore(header);
- size++;
- }
-
- // Intentionally make this not JavaDoc, as the we don't conform to
- // the behaviour documented here (we call removeEldestEntry before
- // inserting the new value to be consistent with previous Android
- // releases).
- // **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
- /*
* Returns <tt>true</tt> if this map should remove its eldest entry.
* This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
* inserting a new entry into the map. It provides the implementor
@@ -508,7 +496,7 @@
* private static final int MAX_ENTRIES = 100;
*
* protected boolean removeEldestEntry(Map.Entry eldest) {
- * return size() > MAX_ENTRIES;
+ * return size() > MAX_ENTRIES;
* }
* </pre>
*
@@ -538,13 +526,174 @@
return false;
}
+ /**
+ * Returns a {@link Set} view of the keys contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. If the map is modified
+ * while an iteration over the set is in progress (except through
+ * the iterator's own <tt>remove</tt> operation), the results of
+ * the iteration are undefined. The set supports element removal,
+ * which removes the corresponding mapping from the map, via the
+ * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
+ * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
+ * operations. It does not support the <tt>add</tt> or <tt>addAll</tt>
+ * operations.
+ * Its {@link Spliterator} typically provides faster sequential
+ * performance but much poorer parallel performance than that of
+ * {@code HashMap}.
+ *
+ * @return a set view of the keys contained in this map
+ */
+ public Set<K> keySet() {
+ Set<K> ks;
+ return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
+ }
+
+ final class LinkedKeySet extends AbstractSet<K> {
+ public final int size() { return size; }
+ public final void clear() { LinkedHashMap.this.clear(); }
+ public final Iterator<K> iterator() {
+ return new LinkedKeyIterator();
+ }
+ public final boolean contains(Object o) { return containsKey(o); }
+ public final boolean remove(Object key) {
+ return removeNode(hash(key), key, null, false, true) != null;
+ }
+ public final Spliterator<K> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.SIZED |
+ Spliterator.ORDERED |
+ Spliterator.DISTINCT);
+ }
+ public final void forEach(Consumer<? super K> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ // Android-changed: Detect changes to modCount early.
+ for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after)
+ action.accept(e.key);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /**
+ * Returns a {@link Collection} view of the values contained in this map.
+ * The collection is backed by the map, so changes to the map are
+ * reflected in the collection, and vice-versa. If the map is
+ * modified while an iteration over the collection is in progress
+ * (except through the iterator's own <tt>remove</tt> operation),
+ * the results of the iteration are undefined. The collection
+ * supports element removal, which removes the corresponding
+ * mapping from the map, via the <tt>Iterator.remove</tt>,
+ * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
+ * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not
+ * support the <tt>add</tt> or <tt>addAll</tt> operations.
+ * Its {@link Spliterator} typically provides faster sequential
+ * performance but much poorer parallel performance than that of
+ * {@code HashMap}.
+ *
+ * @return a view of the values contained in this map
+ */
+ public Collection<V> values() {
+ Collection<V> vs;
+ return (vs = values) == null ? (values = new LinkedValues()) : vs;
+ }
+
+ final class LinkedValues extends AbstractCollection<V> {
+ public final int size() { return size; }
+ public final void clear() { LinkedHashMap.this.clear(); }
+ public final Iterator<V> iterator() {
+ return new LinkedValueIterator();
+ }
+ public final boolean contains(Object o) { return containsValue(o); }
+ public final Spliterator<V> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.SIZED |
+ Spliterator.ORDERED);
+ }
+ public final void forEach(Consumer<? super V> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ // Android-changed: Detect changes to modCount early.
+ for (LinkedHashMapEntry<K,V> e = head; (e != null && modCount == mc); e = e.after)
+ action.accept(e.value);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /**
+ * Returns a {@link Set} view of the mappings contained in this map.
+ * The set is backed by the map, so changes to the map are
+ * reflected in the set, and vice-versa. If the map is modified
+ * while an iteration over the set is in progress (except through
+ * the iterator's own <tt>remove</tt> operation, or through the
+ * <tt>setValue</tt> operation on a map entry returned by the
+ * iterator) the results of the iteration are undefined. The set
+ * supports element removal, which removes the corresponding
+ * mapping from the map, via the <tt>Iterator.remove</tt>,
+ * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
+ * <tt>clear</tt> operations. It does not support the
+ * <tt>add</tt> or <tt>addAll</tt> operations.
+ * Its {@link Spliterator} typically provides faster sequential
+ * performance but much poorer parallel performance than that of
+ * {@code HashMap}.
+ *
+ * @return a set view of the mappings contained in this map
+ */
+ public Set<Map.Entry<K,V>> entrySet() {
+ Set<Map.Entry<K,V>> es;
+ return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
+ }
+
+ final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
+ public final int size() { return size; }
+ public final void clear() { LinkedHashMap.this.clear(); }
+ public final Iterator<Map.Entry<K,V>> iterator() {
+ return new LinkedEntryIterator();
+ }
+ public final boolean contains(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+ Object key = e.getKey();
+ Node<K,V> candidate = getNode(hash(key), key);
+ return candidate != null && candidate.equals(e);
+ }
+ public final boolean remove(Object o) {
+ if (o instanceof Map.Entry) {
+ Map.Entry<?,?> e = (Map.Entry<?,?>) o;
+ Object key = e.getKey();
+ Object value = e.getValue();
+ return removeNode(hash(key), key, value, true, true) != null;
+ }
+ return false;
+ }
+ public final Spliterator<Map.Entry<K,V>> spliterator() {
+ return Spliterators.spliterator(this, Spliterator.SIZED |
+ Spliterator.ORDERED |
+ Spliterator.DISTINCT);
+ }
+ public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ // Android-changed: Detect changes to modCount early.
+ for (LinkedHashMapEntry<K,V> e = head; (e != null && mc == modCount); e = e.after)
+ action.accept(e);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
+ }
+
// Map overrides
+
public void forEach(BiConsumer<? super K, ? super V> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
- // Android modified - breaks from the loop when modCount != mc
- for (LinkedHashMapEntry<K,V> e = header.after; modCount == mc && e != header; e = e.after)
+ // Android-changed: Detect changes to modCount early.
+ for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after)
action.accept(e.key, e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
@@ -554,10 +703,68 @@
if (function == null)
throw new NullPointerException();
int mc = modCount;
- // Android modified - breaks from the loop when modCount != mc
- for (LinkedHashMapEntry<K,V> e = header.after; modCount == mc && e != header; e = e.after)
+ // Android-changed: Detect changes to modCount early.
+ for (LinkedHashMapEntry<K,V> e = head; modCount == mc && e != null; e = e.after)
e.value = function.apply(e.key, e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
}
+
+ // Iterators
+
+ abstract class LinkedHashIterator {
+ LinkedHashMapEntry<K,V> next;
+ LinkedHashMapEntry<K,V> current;
+ int expectedModCount;
+
+ LinkedHashIterator() {
+ next = head;
+ expectedModCount = modCount;
+ current = null;
+ }
+
+ public final boolean hasNext() {
+ return next != null;
+ }
+
+ final LinkedHashMapEntry<K,V> nextNode() {
+ LinkedHashMapEntry<K,V> e = next;
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (e == null)
+ throw new NoSuchElementException();
+ current = e;
+ next = e.after;
+ return e;
+ }
+
+ public final void remove() {
+ Node<K,V> p = current;
+ if (p == null)
+ throw new IllegalStateException();
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ current = null;
+ K key = p.key;
+ removeNode(hash(key), key, null, false, false);
+ expectedModCount = modCount;
+ }
+ }
+
+ final class LinkedKeyIterator extends LinkedHashIterator
+ implements Iterator<K> {
+ public final K next() { return nextNode().getKey(); }
+ }
+
+ final class LinkedValueIterator extends LinkedHashIterator
+ implements Iterator<V> {
+ public final V next() { return nextNode().value; }
+ }
+
+ final class LinkedEntryIterator extends LinkedHashIterator
+ implements Iterator<Map.Entry<K,V>> {
+ public final Map.Entry<K,V> next() { return nextNode(); }
+ }
+
+
}
diff --git a/ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java b/ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java
deleted file mode 100644
index b833dc0..0000000
--- a/ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.reflect.annotation;
-
-import java.lang.annotation.*;
-import java.lang.reflect.*;
-import java.util.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-
-/**
- * Represents an annotation type at run time. Used to type-check annotations
- * and apply member defaults.
- *
- * @author Josh Bloch
- * @since 1.5
- */
-public class AnnotationType {
- /**
- * Member name -> type mapping. Note that primitive types
- * are represented by the class objects for the corresponding wrapper
- * types. This matches the return value that must be used for a
- * dynamic proxy, allowing for a simple isInstance test.
- */
- private final Map<String, Class<?>> memberTypes = new HashMap<String,Class<?>>();
-
- /**
- * Member name -> default value mapping.
- */
- private final Map<String, Object> memberDefaults =
- new HashMap<String, Object>();
-
- /**
- * Member name -> Method object mapping. This (and its assoicated
- * accessor) are used only to generate AnnotationTypeMismatchExceptions.
- */
- private final Map<String, Method> members = new HashMap<String, Method>();
-
- /**
- * The retention policy for this annotation type.
- */
- private RetentionPolicy retention = RetentionPolicy.RUNTIME;;
-
- /**
- * Whether this annotation type is inherited.
- */
- private boolean inherited = false;
-
- /**
- * Returns an AnnotationType instance for the specified annotation type.
- *
- * @throw IllegalArgumentException if the specified class object for
- * does not represent a valid annotation type
- */
- public static synchronized AnnotationType getInstance(
- Class<? extends Annotation> annotationClass)
- {
- AnnotationType result = annotationClass.getAnnotationType();
- if (result == null)
- result = new AnnotationType((Class<? extends Annotation>) annotationClass);
-
- return result;
- }
-
- /**
- * Sole constructor.
- *
- * @param annotationClass the class object for the annotation type
- * @throw IllegalArgumentException if the specified class object for
- * does not represent a valid annotation type
- */
- private AnnotationType(final Class<? extends Annotation> annotationClass) {
- if (!annotationClass.isAnnotation())
- throw new IllegalArgumentException("Not an annotation type");
-
- Method[] methods =
- AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
- public Method[] run() {
- // Initialize memberTypes and defaultValues
- return annotationClass.getDeclaredMethods();
- }
- });
-
-
- for (Method method : methods) {
- if (method.getParameterTypes().length != 0)
- throw new IllegalArgumentException(method + " has params");
- String name = method.getName();
- Class<?> type = method.getReturnType();
- memberTypes.put(name, invocationHandlerReturnType(type));
- members.put(name, method);
-
- Object defaultValue = method.getDefaultValue();
- if (defaultValue != null)
- memberDefaults.put(name, defaultValue);
-
- members.put(name, method);
- }
-
- annotationClass.setAnnotationType(this);
-
- // Initialize retention, & inherited fields. Special treatment
- // of the corresponding annotation types breaks infinite recursion.
- if (annotationClass != Retention.class &&
- annotationClass != Inherited.class) {
- Retention ret = annotationClass.getAnnotation(Retention.class);
- retention = (ret == null ? RetentionPolicy.CLASS : ret.value());
- inherited = annotationClass.isAnnotationPresent(Inherited.class);
- }
- }
-
- /**
- * Returns the type that must be returned by the invocation handler
- * of a dynamic proxy in order to have the dynamic proxy return
- * the specified type (which is assumed to be a legal member type
- * for an annotation).
- */
- public static Class<?> invocationHandlerReturnType(Class<?> type) {
- // Translate primitives to wrappers
- if (type == byte.class)
- return Byte.class;
- if (type == char.class)
- return Character.class;
- if (type == double.class)
- return Double.class;
- if (type == float.class)
- return Float.class;
- if (type == int.class)
- return Integer.class;
- if (type == long.class)
- return Long.class;
- if (type == short.class)
- return Short.class;
- if (type == boolean.class)
- return Boolean.class;
-
- // Otherwise, just return declared type
- return type;
- }
-
- /**
- * Returns member types for this annotation type
- * (member name -> type mapping).
- */
- public Map<String, Class<?>> memberTypes() {
- return memberTypes;
- }
-
- /**
- * Returns members of this annotation type
- * (member name -> associated Method object mapping).
- */
- public Map<String, Method> members() {
- return members;
- }
-
- /**
- * Returns the default values for this annotation type
- * (Member name -> default value mapping).
- */
- public Map<String, Object> memberDefaults() {
- return memberDefaults;
- }
-
- /**
- * Returns the retention policy for this annotation type.
- */
- public RetentionPolicy retention() {
- return retention;
- }
-
- /**
- * Returns true if this this annotation type is inherited.
- */
- public boolean isInherited() {
- return inherited;
- }
-
- /**
- * For debugging.
- */
- public String toString() {
- StringBuffer s = new StringBuffer("Annotation Type:" + "\n");
- s.append(" Member types: " + memberTypes + "\n");
- s.append(" Member defaults: " + memberDefaults + "\n");
- s.append(" Retention policy: " + retention + "\n");
- s.append(" Inherited: " + inherited);
- return s.toString();
- }
-}
diff --git a/openjdk_java_files.mk b/openjdk_java_files.mk
index 145e344..fe4ad2d 100644
--- a/openjdk_java_files.mk
+++ b/openjdk_java_files.mk
@@ -1157,6 +1157,7 @@
ojluni/src/main/java/sun/misc/FloatingDecimal.java \
ojluni/src/main/java/java/lang/invoke/LambdaConversionException.java \
ojluni/src/main/java/java/lang/invoke/MethodHandle.java \
+ ojluni/src/main/java/java/lang/invoke/MethodHandleInfo.java \
ojluni/src/main/java/java/lang/invoke/MethodHandleStatics.java \
ojluni/src/main/java/java/lang/invoke/MethodType.java \
ojluni/src/main/java/java/lang/invoke/MethodTypeForm.java \
@@ -1414,7 +1415,6 @@
ojluni/src/main/java/sun/nio/fs/UnixUriUtils.java \
ojluni/src/main/java/sun/nio/fs/UnixUserPrincipals.java \
ojluni/src/main/java/sun/nio/fs/Util.java \
- ojluni/src/main/java/sun/reflect/annotation/AnnotationType.java \
ojluni/src/main/java/sun/reflect/ConstructorAccessor.java \
ojluni/src/main/java/sun/reflect/misc/ReflectUtil.java \
ojluni/src/main/java/sun/reflect/Reflection.java \
@@ -1618,7 +1618,6 @@
openjdk_lambda_stub_files := \
ojluni/src/lambda/java/java/lang/invoke/CallSite.java \
ojluni/src/lambda/java/java/lang/invoke/LambdaMetafactory.java \
- ojluni/src/lambda/java/java/lang/invoke/MethodHandleInfo.java \
ojluni/src/lambda/java/java/lang/invoke/MethodHandles.java \
ojluni/src/lambda/java/java/lang/invoke/SerializedLambda.java
openjdk_lambda_duplicate_stub_files := \