Merge "Print errors encountered while writing XML reports"
diff --git a/luni/src/main/java/java/lang/Double.java b/luni/src/main/java/java/lang/Double.java
index d6ad58e..95c7b81 100644
--- a/luni/src/main/java/java/lang/Double.java
+++ b/luni/src/main/java/java/lang/Double.java
@@ -174,11 +174,13 @@
}
/**
- * Compares this object with the specified object and indicates if they are
- * equal. In order to be equal, {@code object} must be an instance of
- * {@code Double} and the bit pattern of its double value is the same as
- * this object's.
- *
+ * Tests this double for equality with {@code object}.
+ * To be equal, {@code object} must be an instance of {@code Double} and
+ * {@code doubleToLongBits} must give the same value for both objects.
+ *
+ * <p>Note that, unlike {@code ==}, {@code -0.0} and {@code +0.0} compare
+ * unequal, and {@code NaN}s compare equal by this method.
+ *
* @param object
* the object to compare this double with.
* @return {@code true} if the specified object is equal to this
diff --git a/luni/src/main/java/java/lang/Float.java b/luni/src/main/java/java/lang/Float.java
index 92b1731..54cc246 100644
--- a/luni/src/main/java/java/lang/Float.java
+++ b/luni/src/main/java/java/lang/Float.java
@@ -144,10 +144,13 @@
}
/**
- * Compares this instance with the specified object and indicates if they
- * are equal. In order to be equal, {@code object} must be an instance of
- * {@code Float} and have the same float value as this object.
- *
+ * Tests this double for equality with {@code object}.
+ * To be equal, {@code object} must be an instance of {@code Float} and
+ * {@code floatToIntBits} must give the same value for both objects.
+ *
+ * <p>Note that, unlike {@code ==}, {@code -0.0} and {@code +0.0} compare
+ * unequal, and {@code NaN}s compare equal by this method.
+ *
* @param object
* the object to compare this float with.
* @return {@code true} if the specified object is equal to this
diff --git a/luni/src/main/java/java/lang/Integer.java b/luni/src/main/java/java/lang/Integer.java
index 34b9c16..63cd615 100644
--- a/luni/src/main/java/java/lang/Integer.java
+++ b/luni/src/main/java/java/lang/Integer.java
@@ -610,15 +610,20 @@
}
/**
- * Converts the specified integer into a string representation based on the
+ * Converts the specified signed integer into a string representation based on the
* specified radix. The returned string is a concatenation of a minus sign
* if the number is negative and characters from '0' to '9' and 'a' to 'z',
* depending on the radix. If {@code radix} is not in the interval defined
* by {@code Character.MIN_RADIX} and {@code Character.MAX_RADIX} then 10 is
* used as the base for the conversion.
+ *
+ * <p>This method treats its argument as signed. If you want to convert an
+ * unsigned value to one of the common non-decimal bases, you may find
+ * {@link #toBinaryString}, {@code #toHexString}, or {@link #toOctalString}
+ * more convenient.
*
* @param i
- * the integer to convert.
+ * the signed integer to convert.
* @param radix
* the base to use for the conversion.
* @return the string representation of {@code i}.
diff --git a/luni/src/main/java/java/lang/Iterable.java b/luni/src/main/java/java/lang/Iterable.java
index 31883fb0..193fb7b 100644
--- a/luni/src/main/java/java/lang/Iterable.java
+++ b/luni/src/main/java/java/lang/Iterable.java
@@ -19,8 +19,8 @@
import java.util.Iterator;
/**
- * Objects of classes that implement this interface can be used within a
- * {@code foreach} statement.
+ * Instances of classes that implement this interface can be used with
+ * the enhanced for loop.
*
* @since 1.5
*/
diff --git a/luni/src/main/java/java/lang/Long.java b/luni/src/main/java/java/lang/Long.java
index 1c52896..2cc4cd7 100644
--- a/luni/src/main/java/java/lang/Long.java
+++ b/luni/src/main/java/java/lang/Long.java
@@ -583,15 +583,20 @@
}
/**
- * Converts the specified long value into a string representation based on
+ * Converts the specified signed long value into a string representation based on
* the specified radix. The returned string is a concatenation of a minus
* sign if the number is negative and characters from '0' to '9' and 'a' to
* 'z', depending on the radix. If {@code radix} is not in the interval
* defined by {@code Character.MIN_RADIX} and {@code Character.MAX_RADIX}
* then 10 is used as the base for the conversion.
+ *
+ * <p>This method treats its argument as signed. If you want to convert an
+ * unsigned value to one of the common non-decimal bases, you may find
+ * {@link #toBinaryString}, {@code #toHexString}, or {@link #toOctalString}
+ * more convenient.
*
* @param v
- * the long to convert.
+ * the signed long to convert.
* @param radix
* the base to use for the conversion.
* @return the string representation of {@code v}.
diff --git a/luni/src/main/java/java/lang/String.java b/luni/src/main/java/java/lang/String.java
index 1556389..8e4c9a8 100644
--- a/luni/src/main/java/java/lang/String.java
+++ b/luni/src/main/java/java/lang/String.java
@@ -2192,8 +2192,16 @@
}
/**
- * Returns a formatted string, using the supplied format and arguments,
- * using the default locale.
+ * Returns a localized formatted string, using the supplied format and arguments,
+ * using the user's default locale.
+ *
+ * <p>Note that this method can be dangerous: the user's default locale may
+ * not be the locale you tested in, and this may have unexpected effects on
+ * the output. In particular, floating point numbers may be output with
+ * ',' instead of '.' as the decimal separator if that's what the user's
+ * locale dictates. If you're formatting a string other than for human
+ * consumption, you should use {@link #format(Locale,String,Object...}) and
+ * supply {@code Locale.US}.
*
* @param format
* a format string.
diff --git a/luni/src/main/java/java/lang/StringBuffer.java b/luni/src/main/java/java/lang/StringBuffer.java
index 3ec43dc..766b942 100644
--- a/luni/src/main/java/java/lang/StringBuffer.java
+++ b/luni/src/main/java/java/lang/StringBuffer.java
@@ -24,21 +24,22 @@
import java.io.Serializable;
/**
- * StringBuffer is a variable size contiguous indexable array of characters. The
- * length of the StringBuffer is the number of characters it contains. The
- * capacity of the StringBuffer is the number of characters it can hold.
- * <p>
- * Characters may be inserted at any position up to the length of the
- * StringBuffer, increasing the length of the StringBuffer. Characters at any
- * position in the StringBuffer may be replaced, which does not affect the
- * StringBuffer length.
- * <p>
- * The capacity of a StringBuffer may be specified when the StringBuffer is
- * created. If the capacity of the StringBuffer is exceeded, the capacity is
- * increased.
+ * A modifiable {@link CharSequence sequence of characters} for use in creating
+ * strings, where all accesses are synchronized. This class has mostly been replaced
+ * by {@link StringBuilder} because this synchronization is rarely useful. This
+ * class is mainly used to interact with legacy APIs that expose it.
*
- * @see String
+ * <p>For particularly complex string-building needs, consider {@link java.util.Formatter}.
+ *
+ * <p>The majority of the modification methods on this class return {@code
+ * this} so that method calls can be chained together. For example:
+ * {@code new StringBuffer("a").append("b").append("c").toString()}.
+ *
+ * @see CharSequence
+ * @see Appendable
* @see StringBuilder
+ * @see String
+ * @see String.format
* @since 1.0
*/
public final class StringBuffer extends AbstractStringBuilder implements
diff --git a/luni/src/main/java/java/lang/StringBuilder.java b/luni/src/main/java/java/lang/StringBuilder.java
index 12d401f..775864e 100644
--- a/luni/src/main/java/java/lang/StringBuilder.java
+++ b/luni/src/main/java/java/lang/StringBuilder.java
@@ -24,20 +24,21 @@
/**
* A modifiable {@link CharSequence sequence of characters} for use in creating
- * and modifying Strings. This class is intended as a direct replacement of
+ * strings. This class is intended as a direct replacement of
* {@link StringBuffer} for non-concurrent use; unlike {@code StringBuffer} this
- * class is not synchronized for thread safety.
- * <p>
- * The majority of the modification methods on this class return {@code
- * StringBuilder}, so that, like {@code StringBuffer}s, they can be used in
- * chaining method calls together. For example, {@code new StringBuilder("One
- * should ").append("always strive ").append("to achieve Harmony")}.
+ * class is not synchronized.
+ *
+ * <p>For particularly complex string-building needs, consider {@link java.util.Formatter}.
+ *
+ * <p>The majority of the modification methods on this class return {@code
+ * this} so that method calls can be chained together. For example:
+ * {@code new StringBuilder("a").append("b").append("c").toString()}.
*
* @see CharSequence
* @see Appendable
* @see StringBuffer
* @see String
- *
+ * @see String.format
* @since 1.5
*/
public final class StringBuilder extends AbstractStringBuilder implements
diff --git a/luni/src/main/java/java/util/ArrayList.java b/luni/src/main/java/java/util/ArrayList.java
index 7c46e89..ba96593 100644
--- a/luni/src/main/java/java/util/ArrayList.java
+++ b/luni/src/main/java/java/util/ArrayList.java
@@ -29,10 +29,18 @@
import java.lang.reflect.Array;
/**
- * ArrayList is an implementation of {@link List}, backed by an array. All
- * optional operations adding, removing, and replacing are supported. The
- * elements can be any objects.
- *
+ * ArrayList is an implementation of {@link List}, backed by an array.
+ * All optional operations including adding, removing, and replacing elements are supported.
+ *
+ * <p>All elements are permitted, including null.
+ *
+ * <p>This class is a good choice as your default {@code List} implementation.
+ * {@link Vector} synchronizes all operations, but not necessarily in a way that's
+ * meaningful to your application: synchronizing each call to {@code get}, for example, is not
+ * equivalent to synchronizing the list and iterating over it (which is probably what you intended).
+ * {@link java.util.concurrent.CopyOnWriteArrayList} is intended for the special case of very high
+ * concurrency, frequent traversals, and very rare mutations.
+ *
* @param <E> The element type of this list.
* @since 1.2
*/
diff --git a/luni/src/main/java/java/util/Enumeration.java b/luni/src/main/java/java/util/Enumeration.java
index 8b8f7bd..5944a75 100644
--- a/luni/src/main/java/java/util/Enumeration.java
+++ b/luni/src/main/java/java/util/Enumeration.java
@@ -18,15 +18,19 @@
package java.util;
/**
- * An Enumeration is used to sequence over a collection of objects.
- * <p>
- * Preferably an {@link Iterator} should be used. {@code Iterator} replaces the
+ * A legacy iteration interface.
+ *
+ * <p>New code should use {@link Iterator} instead. {@code Iterator} replaces the
* enumeration interface and adds a way to remove elements from a collection.
- *
- * @see Hashtable
- * @see Properties
- * @see Vector
+ *
+ * <p>If you <i>have</i> an {@code Enumeration} and want a {@code Collection}, you
+ * can use {@link Collections#list} to get a {@code List}.
+ *
+ * <p>If you <i>need</i> an {@code Enumeration} for a legacy API and have a
+ * {@code Collection}, you can use {@link Collections#enumeration}.
+ *
* @version 1.0
+ * @see Iterator
*/
public interface Enumeration<E> {
diff --git a/luni/src/main/java/java/util/HashMap.java b/luni/src/main/java/java/util/HashMap.java
index f79601f..19aa2b8 100644
--- a/luni/src/main/java/java/util/HashMap.java
+++ b/luni/src/main/java/java/util/HashMap.java
@@ -29,14 +29,31 @@
import java.io.Serializable;
/**
- * HashMap is an implementation of Map. All optional operations (adding and
- * removing) are supported. Keys and values can be any objects.
- *
+ * HashMap is an implementation of {@link Map}. All optional operations are supported.
+ *
+ * <p>All elements are permitted as keys or values, including null.
+ *
+ * <p>Note that the iteration order for HashMap is non-deterministic. If you want
+ * deterministic iteration, use {@link LinkedHashMap}.
+ *
+ * <p>Note: the implementation of {@code HashMap} is not synchronized.
+ * If one thread of several threads accessing an instance modifies the map
+ * structurally, access to the map needs to be synchronized. A structural
+ * modification is an operation that adds or removes an entry. Changes in
+ * the value of an entry are not structural changes.
+ *
+ * <p>The {@code Iterator} created by calling the {@code iterator} method
+ * may throw a {@code ConcurrentModificationException} if the map is structurally
+ * changed while an iterator is used to iterate over the elements. Only the
+ * {@code remove} method that is provided by the iterator allows for removal of
+ * elements during iteration. It is not possible to guarantee that this
+ * mechanism works in all cases of unsynchronized concurrent modification. It
+ * should only be used for debugging purposes.
+ *
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*/
-public class HashMap<K, V> extends AbstractMap<K, V>
- implements Cloneable, Serializable {
+public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable {
/**
* Min capacity (other than zero) for a HashMap. Must be a power of two
* greater than 1 (and less than 1 << 30).
@@ -60,7 +77,7 @@
/**
* The default load factor. Note that this implementation ignores the
* load factor, but cannot do away with it entirely because it's
- * metioned in the API.
+ * mentioned in the API.
*
* <p>Note that this constant has no impact on the behavior of the program,
* but it is emitted as part of the serialized form. The load factor of
@@ -165,7 +182,7 @@
/*
* Note that this implementation ignores loadFactor; it always uses
- * a load facator of 3/4. This simplifies the code and generally
+ * a load factor of 3/4. This simplifies the code and generally
* improves performance.
*/
}
@@ -184,7 +201,7 @@
/**
* Inserts all of the elements of map into this HashMap in a manner
- * suitable for use by constructors and pseudocostructors (i.e., clone,
+ * suitable for use by constructors and pseudo-constructors (i.e., clone,
* readObject). Also used by LinkedHashMap.
*/
final void constructorPutAll(Map<? extends K, ? extends V> map) {
@@ -239,7 +256,7 @@
/**
* This method is called from the pseudo-constructors (clone and readObject)
* prior to invoking constructorPut/constructorPutAll, which invoke the
- * overriden constructorNewEntry method. Normally it is a VERY bad idea to
+ * overridden constructorNewEntry method. Normally it is a VERY bad idea to
* invoke an overridden method from a pseudo-constructor (Effective Java
* Item 17). In this cases it is unavoidable, and the init method provides a
* workaround.
@@ -411,7 +428,7 @@
}
/**
- * Give LinkedHashMap a chance to take action when we modify an exisitng
+ * Give LinkedHashMap a chance to take action when we modify an existing
* entry.
*
* @param e the entry we're about to modify.
@@ -879,7 +896,7 @@
return o1 == o2 || (o1 != null && o1.equals(o2));
}
- // Subclass (LinkedHashMap) overrrides these for correct iteration order
+ // Subclass (LinkedHashMap) overrides these for correct iteration order
Iterator<K> newKeyIterator() { return new KeyIterator(); }
Iterator<V> newValueIterator() { return new ValueIterator(); }
Iterator<Entry<K, V>> newEntryIterator() { return new EntryIterator(); }
diff --git a/luni/src/main/java/java/util/Hashtable.java b/luni/src/main/java/java/util/Hashtable.java
index ead0db3..af0f4b3 100644
--- a/luni/src/main/java/java/util/Hashtable.java
+++ b/luni/src/main/java/java/util/Hashtable.java
@@ -29,22 +29,15 @@
import java.io.Serializable;
/**
- * Hashtable associates keys with values. Both keys and values cannot be null.
- * The size of the Hashtable is the number of key/value pairs it contains. The
- * capacity is the number of key/value pairs the Hashtable can hold. The load
- * factor is a float value which determines how full the Hashtable gets before
- * expanding the capacity. If the load factor of the Hashtable is exceeded, the
- * capacity is doubled.
- *
+ * Hashtable is a synchronized implementation of {@link Map}. All optional operations are supported.
+ *
+ * <p>Neither keys nor values can be null. (Use {@code HashMap} or {@code LinkedHashMap} if you
+ * need null keys or values.)
+ *
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
- *
- * @see Enumeration
- * @see java.io.Serializable
- * @see java.lang.Object#equals
- * @see java.lang.Object#hashCode
+ * @see HashMap
*/
-
public class Hashtable<K, V> extends Dictionary<K, V>
implements Map<K, V>, Cloneable, Serializable {
/**
@@ -70,7 +63,7 @@
/**
* The default load factor. Note that this implementation ignores the
* load factor, but cannot do away with it entirely because it's
- * metioned in the API.
+ * mentioned in the API.
*
* <p>Note that this constant has no impact on the behavior of the program,
* but it is emitted as part of the serialized form. The load factor of
@@ -166,7 +159,7 @@
/*
* Note that this implementation ignores loadFactor; it always uses
- * a load facator of 3/4. This simplifies the code and generally
+ * a load factor of 3/4. This simplifies the code and generally
* improves performance.
*/
}
@@ -185,7 +178,7 @@
/**
* Inserts all of the elements of map into this Hashtable in a manner
- * suitable for use by constructors and pseudocostructors (i.e., clone,
+ * suitable for use by constructors and pseudo-constructors (i.e., clone,
* readObject).
*/
private void constructorPutAll(Map<? extends K, ? extends V> map) {
@@ -665,7 +658,7 @@
/**
* Note: technically the methods of this class should synchronize the
* backing map. However, this would require them to have a reference
- * to it, which would cause consiserable bloat. Moreover, the RI
+ * to it, which would cause considerable bloat. Moreover, the RI
* behaves the same way.
*/
private static class HashtableEntry<K, V> implements Entry<K, V> {
diff --git a/luni/src/main/java/java/util/Iterator.java b/luni/src/main/java/java/util/Iterator.java
index 6ebae04..bec797b 100644
--- a/luni/src/main/java/java/util/Iterator.java
+++ b/luni/src/main/java/java/util/Iterator.java
@@ -18,31 +18,29 @@
package java.util;
/**
- * An {@code Iterator} is used to sequence over a collection of objects.
- * Conceptually, an iterator is always positioned between two elements of a
- * collection. A fresh iterator is always positioned in front of the first
- * element.
+ * An iterator over a sequence of objects, such as a collection.
*
- * If a collection has been changed since its creation, methods {@code next} and
- * {@code hasNext()} may throw a {@code ConcurrentModificationException}.
- * Iterators with this behavior are called fail-fast iterators.
+ * <p>If a collection has been changed since the iterator was created,
+ * methods {@code next} and {@code hasNext()} may throw a {@code ConcurrentModificationException}.
+ * It is not possible to guarantee that this mechanism works in all cases of unsynchronized
+ * concurrent modification. It should only be used for debugging purposes. Iterators with this
+ * behavior are called fail-fast iterators.
+ *
+ * <p>Implementing {@link Iterable} and returning an {@code Iterator} allows your
+ * class to be used as a collection with the enhanced for loop.
*
* @param <E>
* the type of object returned by the iterator.
*/
public interface Iterator<E> {
/**
- * Returns whether there are more elements to iterate, i.e. whether the
- * iterator is positioned in front of an element.
- *
- * @return {@code true} if there are more elements, {@code false} otherwise.
+ * Returns true if there is at least one more element, false otherwise.
* @see #next
*/
public boolean hasNext();
/**
- * Returns the next object in the iteration, i.e. returns the element in
- * front of the iterator and advances the iterator by one position.
+ * Returns the next object and advances the iterator.
*
* @return the next object.
* @throws NoSuchElementException
@@ -53,7 +51,7 @@
/**
* Removes the last object returned by {@code next} from the collection.
- * This method can only be called once after {@code next} was called.
+ * This method can only be called once between each call to {@code next}.
*
* @throws UnsupportedOperationException
* if removing is not supported by the collection being
diff --git a/luni/src/main/java/java/util/LinkedHashMap.java b/luni/src/main/java/java/util/LinkedHashMap.java
index cd6825b..eeee1a9 100644
--- a/luni/src/main/java/java/util/LinkedHashMap.java
+++ b/luni/src/main/java/java/util/LinkedHashMap.java
@@ -22,28 +22,28 @@
package java.util;
/**
- * LinkedHashMap is a variant of HashMap. Its entries are kept in a
- * doubly-linked list. The iteration order is, by default, the order in which
- * keys were inserted. Reinserting an already existing key doesn't change the
- * order. A key is existing if a call to {@code containsKey} would return true.
- * <p>
- * If the three argument constructor is used, and {@code order} is specified as
+ * LinkedHashMap is an implementation of {@link Map} that guarantees iteration order.
+ * All optional operations are supported.
+ *
+ * <p>All elements are permitted as keys or values, including null.
+ *
+ * <p>Entries are kept in a doubly-linked list. The iteration order is, by default, the
+ * order in which keys were inserted. Reinserting an already-present key doesn't change the
+ * order. If the three argument constructor is used, and {@code accessOrder} is specified as
* {@code true}, the iteration will be in the order that entries were accessed.
- * The access order gets affected by put(), get(), putAll() operations, but not
- * by operations on the collection views.
- * <p>
- * Null elements are allowed, and all the optional map operations are supported.
- * <p>
- * <b>Note:</b> The implementation of {@code LinkedHashMap} is not synchronized.
+ * The access order is affected by {@code put}, {@code get}, and {@code putAll} operations,
+ * but not by operations on the collection views.
+ *
+ * <p>Note: the implementation of {@code LinkedHashMap} is not synchronized.
* If one thread of several threads accessing an instance modifies the map
* structurally, access to the map needs to be synchronized. For
* insertion-ordered instances a structural modification is an operation that
* removes or adds an entry. Access-ordered instances also are structurally
- * modified by put(), get() and putAll() since these methods change the order of
- * the entries. Changes in the value of an entry are not structural changes.
- * <p>
- * The Iterator that can be created by calling the {@code iterator} method
- * throws a {@code ConcurrentModificationException} if the map is structurally
+ * modified by {@code put}, {@code get}, and {@code putAll} since these methods
+ * change the order of the entries. Changes in the value of an entry are not structural changes.
+ *
+ * <p>The {@code Iterator} created by calling the {@code iterator} method
+ * may throw a {@code ConcurrentModificationException} if the map is structurally
* changed while an iterator is used to iterate over the elements. Only the
* {@code remove} method that is provided by the iterator allows for removal of
* elements during iteration. It is not possible to guarantee that this
diff --git a/luni/src/main/java/java/util/LinkedList.java b/luni/src/main/java/java/util/LinkedList.java
index 7a7e81f..9db9c9c 100644
--- a/luni/src/main/java/java/util/LinkedList.java
+++ b/luni/src/main/java/java/util/LinkedList.java
@@ -24,10 +24,16 @@
import java.lang.reflect.Array;
/**
- * LinkedList is an implementation of List, backed by a linked list. All
- * optional operations (adding, removing and replacing) are supported. The
- * elements can be any objects.
- *
+ * LinkedList is an implementation of {@link List}, backed by a doubly-linked list.
+ * All optional operations including adding, removing, and replacing elements are supported.
+ *
+ * <p>All elements are permitted, including null.
+ *
+ * <p>This class is primarily useful if you need queue-like behavior. It may also be useful
+ * as a list if you expect your lists to contain zero or one element, but still require the
+ * ability to scale to slightly larger numbers of elements. In general, though, you should
+ * probably use {@link ArrayList} if you don't need the queue-like behavior.
+ *
* @since 1.2
*/
public class LinkedList<E> extends AbstractSequentialList<E> implements
diff --git a/luni/src/main/java/java/util/Vector.java b/luni/src/main/java/java/util/Vector.java
index 9a1b81c..8129cfe 100644
--- a/luni/src/main/java/java/util/Vector.java
+++ b/luni/src/main/java/java/util/Vector.java
@@ -23,20 +23,18 @@
import java.lang.reflect.Array;
/**
- * Vector is a variable size contiguous indexable array of objects. The size of
- * the vector is the number of objects it contains. The capacity of the vector
- * is the number of objects it can hold.
- * <p>
- * Objects may be inserted at any position up to the size of the vector, thus
- * increasing the size of the vector. Objects at any position in the vector may
- * be removed, thus shrinking the size of the Vector. Objects at any position in
- * the Vector may be replaced, which does not affect the vector's size.
- * <p>
- * The capacity of a vector may be specified when the vector is created. If the
- * capacity of the vector is exceeded, the capacity is increased (doubled by
- * default).
- *
- * @see java.lang.StringBuffer
+ * Vector is an implementation of {@link List}, backed by an array and synchronized.
+ * All optional operations including adding, removing, and replacing elements are supported.
+ *
+ * <p>All elements are permitted, including null.
+ *
+ * <p>This class is equivalent to {@link ArrayList} with synchronized operations. This has a
+ * performance cost, and the synchronization is not necessarily meaningful to your application:
+ * synchronizing each call to {@code get}, for example, is not equivalent to synchronizing on the
+ * list and iterating over it (which is probably what you intended). If you do need very highly
+ * concurrent access, you should also consider {@link CopyOnWriteArrayList}.
+ *
+ * @param <E> The element type of this list.
*/
public class Vector<E> extends AbstractList<E> implements List<E>,
RandomAccess, Cloneable, Serializable {
diff --git a/nio/src/main/java/java/nio/DoubleBuffer.java b/nio/src/main/java/java/nio/DoubleBuffer.java
index 4e0ab01..3bea69e 100644
--- a/nio/src/main/java/java/nio/DoubleBuffer.java
+++ b/nio/src/main/java/java/nio/DoubleBuffer.java
@@ -233,12 +233,17 @@
public abstract DoubleBuffer duplicate();
/**
- * Checks whether this double buffer is equal to another object.
- * <p>
- * If {@code other} is not a double buffer then {@code false} is returned.
- * Two double buffers are equal if and only if their remaining doubles are
- * exactly the same. Position, limit, capacity and mark are not considered.
- *
+ * Checks whether this double buffer is equal to another object. If {@code
+ * other} is not a {@code DoubleBuffer} then {@code false} is returned.
+ *
+ * <p>Two double buffers are equal if their remaining doubles are equal.
+ * Position, limit, capacity and mark are not considered.
+ *
+ * <p>This method considers two doubles {@code a} and {@code b} to be equal
+ * if {@code a == b} or if {@code a} and {@code b} are both {@code NaN}.
+ * Unlike {@link Double#equals}, this method considers {@code -0.0} and
+ * {@code +0.0} to be equal.
+ *
* @param other
* the object to compare with this double buffer.
* @return {@code true} if this double buffer is equal to {@code other},
@@ -259,7 +264,9 @@
int otherPosition = otherBuffer.position;
boolean equalSoFar = true;
while (equalSoFar && (myPosition < limit)) {
- equalSoFar = get(myPosition++) == otherBuffer.get(otherPosition++);
+ double a = get(myPosition++);
+ double b = otherBuffer.get(otherPosition++);
+ equalSoFar = a == b || (a != a && b != b);
}
return equalSoFar;
diff --git a/nio/src/main/java/java/nio/FloatBuffer.java b/nio/src/main/java/java/nio/FloatBuffer.java
index cab94c3..15239b1 100644
--- a/nio/src/main/java/java/nio/FloatBuffer.java
+++ b/nio/src/main/java/java/nio/FloatBuffer.java
@@ -233,12 +233,17 @@
public abstract FloatBuffer duplicate();
/**
- * Checks whether this float buffer is equal to another object.
- * <p>
- * If {@code other} is not a float buffer then {@code false} is returned.
- * Two float buffers are equal if and only if their remaining floats are
- * exactly the same. Position, limit, capacity and mark are not considered.
- *
+ * Checks whether this float buffer is equal to another object. If {@code
+ * other} is not a {@code FloatBuffer} then {@code false} is returned.
+ *
+ * <p>Two float buffers are equal if their remaining floats are equal.
+ * Position, limit, capacity and mark are not considered.
+ *
+ * <p>This method considers two floats {@code a} and {@code b} to be equal
+ * if {@code a == b} or if {@code a} and {@code b} are both {@code NaN}.
+ * Unlike {@link Float#equals}, this method considers {@code -0.0} and
+ * {@code +0.0} to be equal.
+ *
* @param other
* the object to compare with this float buffer.
* @return {@code true} if this float buffer is equal to {@code other},
@@ -259,7 +264,9 @@
int otherPosition = otherBuffer.position;
boolean equalSoFar = true;
while (equalSoFar && (myPosition < limit)) {
- equalSoFar = get(myPosition++) == otherBuffer.get(otherPosition++);
+ float a = get(myPosition++);
+ float b = otherBuffer.get(otherPosition++);
+ equalSoFar = a == b || (a != a && b != b);
}
return equalSoFar;
diff --git a/security/src/main/files/certimport.sh b/security/src/main/files/certimport.sh
index c021a10..ca36a70 100755
--- a/security/src/main/files/certimport.sh
+++ b/security/src/main/files/certimport.sh
@@ -7,12 +7,40 @@
CERTSTORE=cacerts.bks
+# put required 1.6 VM at head of PATH
+JDK6PATH=/usr/lib/jvm/java-6-sun/bin
+if [ ! -e $JDK6PATH/java ] ; then
+ set +x
+ echo
+ echo "WARNING: could not find $JDK6PATH/java but continuing anyway."
+ echo " you might consider making sure the expected JDK is installed"
+ echo " or updating its location in this script."
+ echo
+ set -x
+fi
+export PATH=$JDK6PATH:$PATH
+
# Check java version.
JAVA_VERSION=`java -version 2>&1 | head -1`
JAVA_VERSION_MINOR=`expr match "$JAVA_VERSION" "java version \"[1-9]\.\([0-9]\).*\""`
if [ $JAVA_VERSION_MINOR -lt 6 ]; then
- echo "java version 1.6 or greater required for keytool usage"
- exit 255
+ set +x
+ echo
+ echo "ERROR: java version 1.6 or greater required for keytool usage"
+ echo
+ exit 1
+fi
+
+PROVIDER_CLASS=org.bouncycastle.jce.provider.BouncyCastleProvider
+PROVIDER_PATH=/usr/share/java/bcprov.jar
+
+if [ ! -e $PROVIDER_PATH ] ; then
+ set +x
+ echo
+ echo "ERROR: could not find provider path $PROVIDER_PATH. Try installing with:"
+ echo " sudo apt-get install libbcprov-java"
+ echo
+ exit 1
fi
if [ -a $CERTSTORE ]; then
@@ -34,8 +62,8 @@
-file <(openssl x509 -in cacerts/$cert) \
-keystore $CERTSTORE \
-storetype BKS \
- -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
- -providerpath /usr/share/java/bcprov.jar \
+ -provider $PROVIDER_CLASS \
+ -providerpath $PROVIDER_PATH \
-storepass $STOREPASS
let "COUNTER=$COUNTER + 1"
done
diff --git a/sql/src/main/java/java/sql/package.html b/sql/src/main/java/java/sql/package.html
index 9ae2488..0799bb7 100644
--- a/sql/src/main/java/java/sql/package.html
+++ b/sql/src/main/java/java/sql/package.html
@@ -1,8 +1,10 @@
<html>
- <body>
- <p>
- Provides a standard interface for accessing SQL-based databases.
- </p>
- @since Android 1.0
- </body>
-</html>
\ No newline at end of file
+<body>
+<p>Provides a compatibility interface for accessing SQL-based databases.
+The <code>android.database</code> and <code>android.database.sqlite</code>
+packages offer a higher-performance alternative where source compatibility
+is not an issue.</p>
+
+<p>Note that you must provide your own JDBC driver.</p>
+</body>
+</html>
diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java
index 4e689fb..e995174 100644
--- a/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java
+++ b/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java
@@ -32,7 +32,7 @@
* the DOM implementation can easily access them while maintaining the DOM tree
* structure.
*/
-public class AttrImpl extends NodeImpl implements Attr {
+public final class AttrImpl extends NodeImpl implements Attr {
// Maintained by ElementImpl.
ElementImpl ownerElement;
diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
index b1802a6..c677e58 100644
--- a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
+++ b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java
@@ -17,17 +17,14 @@
package org.apache.harmony.xml.dom;
import org.w3c.dom.Attr;
-import org.w3c.dom.CDATASection;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
-import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
-import org.w3c.dom.EntityReference;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -76,7 +73,7 @@
* attach user data to the document to save those fields. Xerces also takes
* this approach.
*/
- private WeakHashMap<Node, Map<String, UserData>> nodeToUserData;
+ private WeakHashMap<NodeImpl, Map<String, UserData>> nodeToUserData;
public DocumentImpl(DOMImplementationImpl impl, String namespaceURI,
String qualifiedName, DocumentType doctype, String inputEncoding) {
@@ -120,147 +117,232 @@
return true;
}
-
- /**
- * Clones a node and (if requested) its children. The source node(s) may
- * have been created by a different DocumentImpl or even DOM implementation.
- *
- * @param node The node to clone.
- * @param deep If true, a deep copy is created (including all child nodes).
- *
- * @return The new node.
- */
- Node cloneNode(Node node, boolean deep) throws DOMException {
- Node target;
-
- switch (node.getNodeType()) {
- case Node.ATTRIBUTE_NODE: {
- Attr source = (Attr)node;
- target = createAttributeNS(source.getNamespaceURI(), source.getLocalName());
- target.setPrefix(source.getPrefix());
- target.setNodeValue(source.getNodeValue());
- break;
- }
- case Node.CDATA_SECTION_NODE: {
- CharacterData source = (CharacterData)node;
- target = createCDATASection(source.getData());
- break;
- }
- case Node.COMMENT_NODE: {
- Comment source = (Comment)node;
- target = createComment(source.getData());
- break;
- }
- case Node.DOCUMENT_FRAGMENT_NODE: {
- // Source is irrelevant in this case.
- target = createDocumentFragment();
- break;
- }
- case Node.DOCUMENT_NODE: {
- throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a Document node");
- }
- case Node.DOCUMENT_TYPE_NODE: {
- throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a DocumentType node");
- }
- case Node.ELEMENT_NODE: {
- Element source = (Element)node;
- target = createElementNS(source.getNamespaceURI(), source.getLocalName());
- target.setPrefix(source.getPrefix());
- NamedNodeMap map = source.getAttributes();
- for (int i = 0; i < map.getLength(); i++) {
- Attr attr = (Attr)map.item(i);
- ((Element)target).setAttributeNodeNS((Attr)cloneNode(attr, deep));
+ /**
+ * Returns a shallow copy of the given node. If the node is an element node,
+ * its attributes are always copied.
+ *
+ * @param node a node belonging to any document or DOM implementation.
+ * @param operation the operation type to use when notifying user data
+ * handlers of copied element attributes. It is the caller's
+ * responsibility to notify user data handlers of the returned node.
+ * @return a new node whose document is this document and whose DOM
+ * implementation is this DOM implementation.
+ */
+ private NodeImpl shallowCopy(short operation, Node node) {
+ switch (node.getNodeType()) {
+ case Node.ATTRIBUTE_NODE:
+ Attr attr = (Attr) node;
+ AttrImpl attrCopy = createAttributeNS(attr.getNamespaceURI(), attr.getLocalName());
+ attrCopy.setPrefix(attr.getPrefix());
+ attrCopy.setNodeValue(attr.getNodeValue());
+ return attrCopy;
+
+ case Node.CDATA_SECTION_NODE:
+ return createCDATASection(((CharacterData) node).getData());
+
+ case Node.COMMENT_NODE:
+ return createComment(((Comment) node).getData());
+
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ return createDocumentFragment();
+
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_TYPE_NODE:
+ throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
+ "Cannot copy node of type " + node.getNodeType());
+
+ case Node.ELEMENT_NODE:
+ Element element = (Element) node;
+ ElementImpl elementCopy = createElementNS(
+ element.getNamespaceURI(), element.getLocalName());
+ elementCopy.setPrefix(element.getPrefix());
+ NamedNodeMap attributes = element.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ Node elementAttr = attributes.item(i);
+ AttrImpl elementAttrCopy = (AttrImpl) shallowCopy(operation, elementAttr);
+ notifyUserDataHandlers(operation, elementAttr, elementAttrCopy);
+ elementCopy.setAttributeNodeNS(elementAttrCopy);
}
- break;
- }
- case Node.ENTITY_NODE: {
- throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone an Entity node");
- }
- case Node.ENTITY_REFERENCE_NODE: {
- EntityReference source = (EntityReference)node;
- target = createEntityReference(source.getNodeName());
- break;
- }
- case Node.NOTATION_NODE: {
- throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a Notation node");
- }
- case Node.PROCESSING_INSTRUCTION_NODE: {
- ProcessingInstruction source = (ProcessingInstruction)node;
- target = createProcessingInstruction(source.getTarget(), source.getData());
- break;
- }
- case Node.TEXT_NODE: {
- Text source = (Text)node;
- target = createTextNode(source.getData());
- break;
- }
- default: {
- throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone unknown node type " + node.getNodeType() + " (" + node.getClass().getSimpleName() + ")");
- }
+ return elementCopy;
+
+ case Node.ENTITY_NODE:
+ case Node.NOTATION_NODE:
+ // TODO: implement this when we support these node types
+ throw new UnsupportedOperationException();
+
+ case Node.ENTITY_REFERENCE_NODE:
+ /*
+ * When we support entities in the doctype, this will need to
+ * behave differently for clones vs. imports. Clones copy
+ * entities by value, copying the referenced subtree from the
+ * original document. Imports copy entities by reference,
+ * possibly referring to a different subtree in the new
+ * document.
+ */
+ return createEntityReference(node.getNodeName());
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ ProcessingInstruction pi = (ProcessingInstruction) node;
+ return createProcessingInstruction(pi.getTarget(), pi.getData());
+
+ case Node.TEXT_NODE:
+ return createTextNode(((Text) node).getData());
+
+ default:
+ throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
+ "Unsupported node type " + node.getNodeType());
}
+ }
+
+ /**
+ * Returns a copy of the given node or subtree with this document as its
+ * owner.
+ *
+ * @param operation either {@link UserDataHandler#NODE_CLONED} or
+ * {@link UserDataHandler#NODE_IMPORTED}.
+ * @param node a node belonging to any document or DOM implementation.
+ * @param deep true to recursively copy any child nodes; false to do no such
+ * copying and return a node with no children.
+ */
+ Node cloneOrImportNode(short operation, Node node, boolean deep) {
+ NodeImpl copy = shallowCopy(operation, node);
if (deep) {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
- Node child = cloneNode(list.item(i), deep);
- target.appendChild(child);
+ copy.appendChild(cloneOrImportNode(operation, list.item(i), deep));
}
}
- notifyUserDataHandlers(UserDataHandler.NODE_CLONED, node, target);
-
- return target;
+ notifyUserDataHandlers(operation, node, copy);
+ return copy;
}
- public AttrImpl createAttribute(String name) throws DOMException {
+ public Node importNode(Node importedNode, boolean deep) {
+ return cloneOrImportNode(UserDataHandler.NODE_IMPORTED, importedNode, deep);
+ }
+
+ /**
+ * Detaches the node from its parent (if any) and changes its document to
+ * this document. The node's subtree and attributes will remain attached,
+ * but their document will be changed to this document.
+ */
+ public Node adoptNode(Node node) {
+ if (!(node instanceof NodeImpl)) {
+ return null; // the API specifies this quiet failure
+ }
+ NodeImpl nodeImpl = (NodeImpl) node;
+ switch (nodeImpl.getNodeType()) {
+ case Node.ATTRIBUTE_NODE:
+ AttrImpl attr = (AttrImpl) node;
+ if (attr.ownerElement != null) {
+ attr.ownerElement.removeAttributeNode(attr);
+ }
+ break;
+
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ case Node.ENTITY_REFERENCE_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ case Node.COMMENT_NODE:
+ case Node.ELEMENT_NODE:
+ break;
+
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_TYPE_NODE:
+ case Node.ENTITY_NODE:
+ case Node.NOTATION_NODE:
+ throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
+ "Cannot adopt nodes of type " + nodeImpl.getNodeType());
+
+ default:
+ throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
+ "Unsupported node type " + node.getNodeType());
+ }
+
+ Node parent = nodeImpl.getParentNode();
+ if (parent != null) {
+ parent.removeChild(nodeImpl);
+ }
+
+ changeDocumentToThis(nodeImpl);
+ notifyUserDataHandlers(UserDataHandler.NODE_ADOPTED, node, null);
+ return nodeImpl;
+ }
+
+ /**
+ * Recursively change the document of {@code node} without also changing its
+ * parent node. Only adoptNode() should invoke this method, otherwise nodes
+ * will be left in an inconsistent state.
+ */
+ private void changeDocumentToThis(NodeImpl node) {
+ Map<String, UserData> userData = node.document.getUserDataMapForRead(node);
+ if (!userData.isEmpty()) {
+ getUserDataMap(node).putAll(userData);
+ }
+ node.document = this;
+
+ // change the document on all child nodes
+ NodeList list = node.getChildNodes();
+ for (int i = 0; i < list.getLength(); i++) {
+ changeDocumentToThis((NodeImpl) list.item(i));
+ }
+
+ // change the document on all attribute nodes
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ NamedNodeMap attributes = node.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ changeDocumentToThis((AttrImpl) attributes.item(i));
+ }
+ }
+ }
+
+ public AttrImpl createAttribute(String name) {
return new AttrImpl(this, name);
}
- public Attr createAttributeNS(String namespaceURI, String qualifiedName)
- throws DOMException {
+ public AttrImpl createAttributeNS(String namespaceURI, String qualifiedName) {
return new AttrImpl(this, namespaceURI, qualifiedName);
}
- public CDATASection createCDATASection(String data) throws DOMException {
+ public CDATASectionImpl createCDATASection(String data) {
return new CDATASectionImpl(this, data);
}
- public Comment createComment(String data) {
+ public CommentImpl createComment(String data) {
return new CommentImpl(this, data);
}
- public DocumentFragment createDocumentFragment() {
+ public DocumentFragmentImpl createDocumentFragment() {
return new DocumentFragmentImpl(this);
}
- public Element createElement(String tagName) throws DOMException {
+ public ElementImpl createElement(String tagName) {
return new ElementImpl(this, tagName);
}
- public Element createElementNS(String namespaceURI, String qualifiedName)
- throws DOMException {
+ public ElementImpl createElementNS(String namespaceURI, String qualifiedName) {
return new ElementImpl(this, namespaceURI, qualifiedName);
}
- public EntityReference createEntityReference(String name)
- throws DOMException {
+ public EntityReferenceImpl createEntityReference(String name) {
return new EntityReferenceImpl(this, name);
}
- public ProcessingInstruction createProcessingInstruction(String target,
- String data) throws DOMException {
+ public ProcessingInstructionImpl createProcessingInstruction(String target, String data) {
return new ProcessingInstructionImpl(this, target, data);
}
- public Text createTextNode(String data) {
+ public TextImpl createTextNode(String data) {
return new TextImpl(this, data);
}
public DocumentType getDoctype() {
- for (int i = 0; i < children.size(); i++) {
- if (children.get(i) instanceof DocumentType) {
- return (DocumentType) children.get(i);
+ for (LeafNodeImpl child : children) {
+ if (child instanceof DocumentType) {
+ return (DocumentType) child;
}
}
@@ -268,9 +350,9 @@
}
public Element getDocumentElement() {
- for (int i = 0; i < children.size(); i++) {
- if (children.get(i) instanceof Element) {
- return (Element) children.get(i);
+ for (LeafNodeImpl child : children) {
+ if (child instanceof Element) {
+ return (Element) child;
}
}
@@ -311,13 +393,8 @@
return Node.DOCUMENT_NODE;
}
- public Node importNode(Node importedNode, boolean deep) throws DOMException {
- // TODO: callback the UserDataHandler with a NODE_IMPORTED event
- return cloneNode(importedNode, deep);
- }
-
@Override
- public Node insertChildAt(Node newChild, int index) throws DOMException {
+ public Node insertChildAt(Node newChild, int index) {
// Make sure we have at most one root element and one DTD element.
if (newChild instanceof Element && getDocumentElement() != null) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
@@ -330,7 +407,7 @@
return super.insertChildAt(newChild, index);
}
- @Override public String getTextContent() throws DOMException {
+ @Override public String getTextContent() {
return null;
}
@@ -346,7 +423,7 @@
return xmlStandalone;
}
- public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
+ public void setXmlStandalone(boolean xmlStandalone) {
this.xmlStandalone = xmlStandalone;
}
@@ -354,7 +431,7 @@
return xmlVersion;
}
- public void setXmlVersion(String xmlVersion) throws DOMException {
+ public void setXmlVersion(String xmlVersion) {
this.xmlVersion = xmlVersion;
}
@@ -374,11 +451,6 @@
this.documentUri = documentUri;
}
- public Node adoptNode(Node source) throws DOMException {
- // TODO: callback the UserDataHandler with a NODE_ADOPTED event
- throw new UnsupportedOperationException(); // TODO
- }
-
public DOMConfiguration getDomConfig() {
if (domConfiguration == null) {
domConfiguration = new DOMConfigurationImpl();
@@ -395,8 +467,7 @@
((DOMConfigurationImpl) getDomConfig()).normalize(root);
}
- public Node renameNode(Node n, String namespaceURI, String qualifiedName)
- throws DOMException {
+ public Node renameNode(Node n, String namespaceURI, String qualifiedName) {
// TODO: callback the UserDataHandler with a NODE_RENAMED event
throw new UnsupportedOperationException(); // TODO
}
@@ -405,9 +476,9 @@
* Returns a map with the user data objects attached to the specified node.
* This map is readable and writable.
*/
- Map<String, UserData> getUserDataMap(Node node) {
+ Map<String, UserData> getUserDataMap(NodeImpl node) {
if (nodeToUserData == null) {
- nodeToUserData = new WeakHashMap<Node, Map<String, UserData>>();
+ nodeToUserData = new WeakHashMap<NodeImpl, Map<String, UserData>>();
}
Map<String, UserData> userDataMap = nodeToUserData.get(node);
if (userDataMap == null) {
@@ -421,7 +492,7 @@
* Returns a map with the user data objects attached to the specified node.
* The returned map may be read-only.
*/
- Map<String, UserData> getUserDataMapForRead(Node node) {
+ Map<String, UserData> getUserDataMapForRead(NodeImpl node) {
if (nodeToUserData == null) {
return Collections.emptyMap();
}
@@ -434,13 +505,28 @@
/**
* Calls {@link UserDataHandler#handle} on each of the source node's
* value/handler pairs.
+ *
+ * <p>If the source node comes from another DOM implementation, user data
+ * handlers will <strong>not</strong> be notified. The DOM API provides no
+ * mechanism to inspect a foreign node's user data.
*/
- private void notifyUserDataHandlers(short operation, Node src, Node dst) {
- for (Map.Entry<String, UserData> entry : getUserDataMapForRead(src).entrySet()) {
+ private static void notifyUserDataHandlers(
+ short operation, Node source, NodeImpl destination) {
+ if (!(source instanceof NodeImpl)) {
+ return;
+ }
+
+ NodeImpl srcImpl = (NodeImpl) source;
+ if (srcImpl.document == null) {
+ return;
+ }
+
+ for (Map.Entry<String, UserData> entry
+ : srcImpl.document.getUserDataMapForRead(srcImpl).entrySet()) {
UserData userData = entry.getValue();
if (userData.handler != null) {
userData.handler.handle(
- operation, entry.getKey(), userData.value, src, dst);
+ operation, entry.getKey(), userData.value, source, destination);
}
}
}
diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
index 8376689..5940417 100644
--- a/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
+++ b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java
@@ -56,8 +56,8 @@
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
}
- public Node cloneNode(boolean deep) {
- return document.cloneNode(this, deep);
+ public final Node cloneNode(boolean deep) {
+ return document.cloneOrImportNode(UserDataHandler.NODE_CLONED, this, deep);
}
public NamedNodeMap getAttributes() {
diff --git a/xml/src/test/java/tests/xml/DomTest.java b/xml/src/test/java/tests/xml/DomTest.java
index 0bb27dc..a5fdbd8 100644
--- a/xml/src/test/java/tests/xml/DomTest.java
+++ b/xml/src/test/java/tests/xml/DomTest.java
@@ -16,6 +16,7 @@
package tests.xml;
+import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
@@ -27,7 +28,9 @@
import org.w3c.dom.Element;
import org.w3c.dom.Entity;
import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import org.w3c.dom.Notation;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
@@ -36,6 +39,7 @@
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
@@ -48,8 +52,12 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import static org.w3c.dom.UserDataHandler.NODE_ADOPTED;
import static org.w3c.dom.UserDataHandler.NODE_CLONED;
+import static org.w3c.dom.UserDataHandler.NODE_IMPORTED;
/**
* Construct a DOM and then interrogate it.
@@ -111,6 +119,7 @@
@Override protected void setUp() throws Exception {
transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
@@ -817,6 +826,186 @@
assertEquals(expected, handler.calls);
}
+ /**
+ * A shallow import requires importing the attributes but not the child
+ * nodes.
+ */
+ public void testUserDataHandlerNotifiedOfShallowImports() {
+ RecordingHandler handler = new RecordingHandler();
+ name.setUserData("a", "apple", handler);
+ name.setUserData("b", "banana", handler);
+ standard.setUserData("c", "cat", handler);
+ waffles.setUserData("d", "dog", handler);
+
+ Document newDocument = builder.newDocument();
+ Element importedName = (Element) newDocument.importNode(name, false);
+ Attr importedStandard = importedName.getAttributeNode("a:standard");
+
+ Set<String> expected = new HashSet<String>();
+ expected.add(notification(NODE_IMPORTED, "a", "apple", name, importedName));
+ expected.add(notification(NODE_IMPORTED, "b", "banana", name, importedName));
+ expected.add(notification(NODE_IMPORTED, "c", "cat", standard, importedStandard));
+ assertEquals(expected, handler.calls);
+ }
+
+ /**
+ * A deep import requires cloning both the attributes and the child nodes.
+ */
+ public void testUserDataHandlerNotifiedOfDeepImports() {
+ RecordingHandler handler = new RecordingHandler();
+ name.setUserData("a", "apple", handler);
+ name.setUserData("b", "banana", handler);
+ standard.setUserData("c", "cat", handler);
+ waffles.setUserData("d", "dog", handler);
+
+ Document newDocument = builder.newDocument();
+ Element importedName = (Element) newDocument.importNode(name, true);
+ Attr importedStandard = importedName.getAttributeNode("a:standard");
+ Text importedWaffles = (Text) importedName.getChildNodes().item(0);
+
+ Set<String> expected = new HashSet<String>();
+ expected.add(notification(NODE_IMPORTED, "a", "apple", name, importedName));
+ expected.add(notification(NODE_IMPORTED, "b", "banana", name, importedName));
+ expected.add(notification(NODE_IMPORTED, "c", "cat", standard, importedStandard));
+ expected.add(notification(NODE_IMPORTED, "d", "dog", waffles, importedWaffles));
+ assertEquals(expected, handler.calls);
+ }
+
+ public void testImportNodeDeep() throws TransformerException {
+ String original = domToStringStripElementWhitespace(document);
+
+ Document newDocument = builder.newDocument();
+ Element importedItem = (Element) newDocument.importNode(item, true);
+ assertDetached(item.getParentNode(), importedItem);
+
+ newDocument.appendChild(importedItem);
+ String expected = original.replaceAll("</?menu>", "");
+ assertEquals(expected, domToStringStripElementWhitespace(newDocument));
+ }
+
+ public void testImportNodeShallow() throws TransformerException {
+ Document newDocument = builder.newDocument();
+ Element importedItem = (Element) newDocument.importNode(item, false);
+ assertDetached(item.getParentNode(), importedItem);
+
+ newDocument.appendChild(importedItem);
+ assertEquals("<item xmlns=\"http://food\" xmlns:a=\"http://addons\"/>",
+ domToString(newDocument));
+ }
+
+ public void testNodeAdoption() throws Exception {
+ for (Node node : allNodes) {
+ if (node == document || node == doctype || node == sp || node == png) {
+ assertNotAdoptable(node);
+ } else {
+ adoptAndCheck(node);
+ }
+ }
+ }
+
+ private void assertNotAdoptable(Node node) {
+ try {
+ builder.newDocument().adoptNode(node);
+ fail();
+ } catch (DOMException e) {
+ }
+ }
+
+ /**
+ * Adopts the node into another document, then adopts the root element, and
+ * then attaches the adopted node in the proper place. The net result should
+ * be that the document's entire contents have moved to another document.
+ */
+ private void adoptAndCheck(Node node) throws Exception {
+ String original = domToString(document);
+ Document newDocument = builder.newDocument();
+
+ // remember where to insert the node in the new document
+ boolean isAttribute = node.getNodeType() == Node.ATTRIBUTE_NODE;
+ Node parent = isAttribute
+ ? ((Attr) node).getOwnerElement() : node.getParentNode();
+ Node nextSibling = node.getNextSibling();
+
+ // move the node and make sure it was detached
+ assertSame(node, newDocument.adoptNode(node));
+ assertDetached(parent, node);
+
+ // move the rest of the document and wire the adopted back into place
+ assertSame(menu, newDocument.adoptNode(menu));
+ newDocument.appendChild(menu);
+ if (isAttribute) {
+ ((Element) parent).setAttributeNodeNS((Attr) node);
+ } else if (nextSibling != null) {
+ parent.insertBefore(node, nextSibling);
+ } else if (parent != document) {
+ parent.appendChild(node);
+ }
+
+ assertEquals(original, domToString(newDocument));
+ document = newDocument;
+ }
+
+ private void assertDetached(Node formerParent, Node node) {
+ assertNull(node.getParentNode());
+ NodeList children = formerParent.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ assertTrue(children.item(i) != node);
+ }
+ if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
+ assertNull(((Attr) node).getOwnerElement());
+ NamedNodeMap attributes = formerParent.getAttributes();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ assertTrue(attributes.item(i) != node);
+ }
+ }
+ }
+
+ public void testAdoptionImmediatelyAfterParsing() throws Exception {
+ Document newDocument = builder.newDocument();
+ try {
+ assertSame(name, newDocument.adoptNode(name));
+ assertSame(newDocument, name.getOwnerDocument());
+ assertSame(newDocument, standard.getOwnerDocument());
+ assertSame(newDocument, waffles.getOwnerDocument());
+ } catch (Throwable e) {
+ AssertionFailedError failure = new AssertionFailedError(
+ "This implementation fails to adopt nodes before the "
+ + "document has been traversed");
+ failure.initCause(e);
+ throw failure;
+ }
+ }
+
+ /**
+ * There should be notifications for adopted node itself but none of its
+ * children. The DOM spec is vague on this, so we're consistent with the RI.
+ */
+ public void testUserDataHandlerNotifiedOfOnlyShallowAdoptions() throws Exception {
+ /*
+ * Force a traversal of the document, otherwise this test may fail for
+ * an unrelated reason on version 5 of the RI. That behavior is
+ * exercised by testAdoptionImmediatelyAfterParsing().
+ */
+ domToString(document);
+
+ RecordingHandler handler = new RecordingHandler();
+ name.setUserData("a", "apple", handler);
+ name.setUserData("b", "banana", handler);
+ standard.setUserData("c", "cat", handler);
+ waffles.setUserData("d", "dog", handler);
+
+ Document newDocument = builder.newDocument();
+ assertSame(name, newDocument.adoptNode(name));
+ assertSame(newDocument, name.getOwnerDocument());
+ assertSame(newDocument, standard.getOwnerDocument());
+ assertSame(newDocument, waffles.getOwnerDocument());
+
+ Set<String> expected = new HashSet<String>();
+ expected.add(notification(NODE_ADOPTED, "a", "apple", name, null));
+ expected.add(notification(NODE_ADOPTED, "b", "banana", name, null));
+ assertEquals(expected, handler.calls);
+ }
+
private class RecordingHandler implements UserDataHandler {
final Set<String> calls = new HashSet<String>();
public void handle(short operation, String key, Object data, Node src, Node dst) {
@@ -831,6 +1020,29 @@
private String domToString(Document document) throws TransformerException {
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
- return writer.toString();
+ String result = writer.toString();
+
+ /*
+ * Hack: swap <name>'s a:standard attribute and deluxe attribute if
+ * they're out of order. Some document transformations reorder the
+ * attributes, which causes pain when we try to use String comparison on
+ * them.
+ */
+ Matcher attributeMatcher = Pattern.compile(" a:standard=\"[^\"]+\"").matcher(result);
+ if (attributeMatcher.find()) {
+ result = result.substring(0, attributeMatcher.start())
+ + result.substring(attributeMatcher.end());
+ int insertionPoint = result.indexOf(" deluxe=\"");
+ result = result.substring(0, insertionPoint)
+ + attributeMatcher.group()
+ + result.substring(insertionPoint);
+ }
+
+ return result;
+ }
+
+ private String domToStringStripElementWhitespace(Document document)
+ throws TransformerException {
+ return domToString(document).replaceAll("(?m)>\\s+<", "><");
}
}
diff --git a/xml/src/test/java/tests/xml/NormalizeTest.java b/xml/src/test/java/tests/xml/NormalizeTest.java
index 6fa6c97..f35ca10 100644
--- a/xml/src/test/java/tests/xml/NormalizeTest.java
+++ b/xml/src/test/java/tests/xml/NormalizeTest.java
@@ -32,6 +32,8 @@
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
@@ -588,10 +590,10 @@
private String domToString(Document document) throws TransformerException {
StringWriter writer = new StringWriter();
- TransformerFactory.newInstance().newTransformer()
- .transform(new DOMSource(document), new StreamResult(writer));
- String xml = writer.toString();
- return xml.replaceFirst("<\\?xml[^?]*\\?>", "");
+ Transformer transformer = TransformerFactory.newInstance() .newTransformer();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.transform(new DOMSource(document), new StreamResult(writer));
+ return writer.toString();
}
private class ErrorRecorder implements DOMErrorHandler {