You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rp...@apache.org on 2016/09/22 12:38:49 UTC
[2/2] logging-log4j2 git commit: LOG4J2-1447 LOG4J2-1349 renamed
ArrayContextData to SortedStringArrayMap
LOG4J2-1447 LOG4J2-1349 renamed ArrayContextData to SortedStringArrayMap
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/d676967b
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/d676967b
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/d676967b
Branch: refs/heads/master
Commit: d676967bd624da42d9042f1ff75015a4883c97e2
Parents: 35eda8a
Author: rpopma <rp...@apache.org>
Authored: Thu Sep 22 21:38:33 2016 +0900
Committer: rpopma <rp...@apache.org>
Committed: Thu Sep 22 21:38:33 2016 +0900
----------------------------------------------------------------------
.../CopyOnWriteSortedArrayThreadContextMap.java | 10 +-
.../GarbageFreeSortedArrayThreadContextMap.java | 8 +-
.../logging/log4j/util/ArrayContextData.java | 480 ------------
.../log4j/util/SortedStringArrayMap.java | 480 ++++++++++++
.../log4j/util/ArrayContextDataTest.java | 772 -------------------
.../log4j/util/SortedStringArrayMapTest.java | 772 +++++++++++++++++++
.../log4j/core/impl/ContextDataFactory.java | 10 +-
.../ContextDataAttributeConverterTest.java | 6 +-
.../ContextDataJsonAttributeConverterTest.java | 6 +-
.../log4j/core/impl/Log4jLogEventTest.java | 6 +-
.../log4j/core/impl/MutableLogEventTest.java | 4 +-
.../jmh/ArrayContextDataVsHashMapBenchmark.java | 239 ------
.../perf/jmh/SortedArrayVsHashMapBenchmark.java | 239 ++++++
.../log4j/perf/jmh/ThreadContextBenchmark.java | 4 +-
.../org/apache/logging/slf4j/MDCContextMap.java | 6 +-
15 files changed, 1521 insertions(+), 1521 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
index 71afbef..6bb13c1 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
@@ -20,11 +20,11 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
import org.apache.logging.log4j.util.PropertiesUtil;
/**
- * {@code ArrayContextData}-based implementation of the {@code ThreadContextMap} interface that creates a copy of
+ * {@code SortedStringArrayMap}-based implementation of the {@code ThreadContextMap} interface that creates a copy of
* the data structure on every modification. Any particular instance of the data structure is a snapshot of the
* ThreadContext at some point in time and can safely be passed off to other threads. Since it is
* expected that the Map will be passed to many more log events than the number of keys it contains the performance
@@ -51,7 +51,7 @@ class CopyOnWriteSortedArrayThreadContextMap implements ThreadContextMap, Thread
*/
protected static final String PROPERTY_NAME_INITIAL_CAPACITY = "log4j2.ThreadContext.initial.capacity";
- private static final MutableContextData EMPTY_CONTEXT_DATA = new ArrayContextData();
+ private static final MutableContextData EMPTY_CONTEXT_DATA = new SortedStringArrayMap();
static {
EMPTY_CONTEXT_DATA.freeze();
}
@@ -87,7 +87,7 @@ class CopyOnWriteSortedArrayThreadContextMap implements ThreadContextMap, Thread
* @return an implementation of the {@code MutableContextData} used to back this thread context map
*/
protected MutableContextData createMutableContextData() {
- return new ArrayContextData(PropertiesUtil.getProperties().getIntegerProperty(
+ return new SortedStringArrayMap(PropertiesUtil.getProperties().getIntegerProperty(
PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
}
@@ -101,7 +101,7 @@ class CopyOnWriteSortedArrayThreadContextMap implements ThreadContextMap, Thread
* @return an implementation of the {@code MutableContextData} used to back this thread context map
*/
protected MutableContextData createMutableContextData(final ContextData original) {
- return new ArrayContextData(original);
+ return new SortedStringArrayMap(original);
}
@Override
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
index 4709004..7aaeae8 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
@@ -20,11 +20,11 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
import org.apache.logging.log4j.util.PropertiesUtil;
/**
- * {@code ArrayContextData}-based implementation of the {@code ThreadContextMap} interface that attempts not to
+ * {@code SortedStringArrayMap}-based implementation of the {@code ThreadContextMap} interface that attempts not to
* create temporary objects. Adding and removing key-value pairs will not create temporary objects.
* <p>
* This implementation does <em>not</em> make a copy of its contents on every operation, so this data structure cannot
@@ -82,7 +82,7 @@ class GarbageFreeSortedArrayThreadContextMap implements ThreadContextMap, Thread
* @return an implementation of the {@code MutableContextData} used to back this thread context map
*/
protected MutableContextData createMutableContextData() {
- return new ArrayContextData(PropertiesUtil.getProperties().getIntegerProperty(
+ return new SortedStringArrayMap(PropertiesUtil.getProperties().getIntegerProperty(
PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
}
@@ -96,7 +96,7 @@ class GarbageFreeSortedArrayThreadContextMap implements ThreadContextMap, Thread
* @return an implementation of the {@code MutableContextData} used to back this thread context map
*/
protected MutableContextData createMutableContextData(final ContextData original) {
- return new ArrayContextData(original);
+ return new SortedStringArrayMap(original);
}
private MutableContextData getThreadLocalMap() {
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
deleted file mode 100644
index 41edc96..0000000
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ArrayContextData.java
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-package org.apache.logging.log4j.util;
-
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.util.Arrays;
-import java.util.ConcurrentModificationException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.logging.log4j.spi.ContextData;
-import org.apache.logging.log4j.spi.MutableContextData;
-
-/**
- * <em>Consider this class private.</em>
- * Array-based implementation of the {@code ContextData} interface. Keys are held in a sorted array.
- * <p>
- * This is not a generic collection, but makes some trade-offs to optimize for the Log4j ContextData use case:
- * </p>
- * <ul>
- * <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} and {@code TriConsumer}.</li>
- * <li>Fast copy. If the ThreadContextMap is also an instance of {@code ArrayContextData}, the full thread context
- * data can be transferred with two array copies and two field updates.</li>
- * <li>Acceptable performance for small data sets. The current implementation stores keys in a sorted array, values
- * are stored in a separate array at the same index.
- * Worst-case performance of {@code get} and {@code containsKey} is O(log N),
- * worst-case performance of {@code put} and {@code remove} is O(N log N).
- * The expectation is that for the small values of {@code N} (less than 100) that are the vast majority of
- * ThreadContext use cases, the constants dominate performance more than the asymptotic performance of the
- * algorithms used.
- * </li>
- * <li>Compact representation.</li>
- * </ul>
- *
- * @since 2.7
- */
-public class ArrayContextData implements MutableContextData {
-
- /**
- * The default initial capacity.
- */
- private static final int DEFAULT_INITIAL_CAPACITY = 4;
- private static final long serialVersionUID = -5748905872274478116L;
- private static final int HASHVAL = 31;
-
- private static final TriConsumer<String, Object, MutableContextData> PUT_ALL = new TriConsumer<String, Object, MutableContextData>() {
- @Override
- public void accept(final String key, final Object value, final MutableContextData contextData) {
- contextData.putValue(key, value);
- }
- };
-
- /**
- * An empty array instance to share when the table is not inflated.
- */
- private static final String[] EMPTY = {};
- private static final String FROZEN = "Frozen collection cannot be modified";
-
- private transient String[] keys = EMPTY;
- private transient Object[] values = EMPTY;
-
- /**
- * The number of key-value mappings contained in this map.
- */
- private 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.
- private int threshold;
- private boolean immutable;
- private transient boolean iterating;
-
- public ArrayContextData() {
- this(DEFAULT_INITIAL_CAPACITY);
- }
-
- public ArrayContextData(final int initialCapacity) {
- if (initialCapacity < 1) {
- throw new IllegalArgumentException("Initial capacity must be at least one but was " + initialCapacity);
- }
- threshold = ceilingNextPowerOfTwo(initialCapacity);
- }
-
- public ArrayContextData(final ContextData other) {
- if (other instanceof ArrayContextData) {
- initFrom0((ArrayContextData) other);
- } else if (other != null) {
- resize(ceilingNextPowerOfTwo(other.size()));
- other.forEach(PUT_ALL, this);
- }
- }
-
- private void assertNotFrozen() {
- if (immutable) {
- throw new UnsupportedOperationException(FROZEN);
- }
- }
-
- private void assertNoConcurrentModification() {
- if (iterating) {
- throw new ConcurrentModificationException();
- }
- }
-
- @Override
- public void clear() {
- if (keys == EMPTY) {
- return;
- }
- assertNotFrozen();
- assertNoConcurrentModification();
-
- Arrays.fill(keys, 0, size, null);
- Arrays.fill(values, 0, size, null);
- size = 0;
- }
-
- @Override
- public boolean containsKey(final String key) {
- return indexOfKey(key) >= 0;
- }
-
- @Override
- public Map<String, String> toMap() {
- final Map<String, String> result = new HashMap<>(size());
- for (int i = 0; i < size(); i++) {
- final Object value = getValueAt(i);
- result.put(getKeyAt(i), value == null ? null : String.valueOf(value));
- }
- return result;
- }
-
- @Override
- public void freeze() {
- immutable = true;
- }
-
- @Override
- public boolean isFrozen() {
- return immutable;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <V> V getValue(final String key) {
- final int index = indexOfKey(key);
- if (index < 0) {
- return null;
- }
- return (V) values[index];
- }
-
- @Override
- public boolean isEmpty() {
- return size == 0;
- }
-
- int indexOfKey(final String key) {
- if (keys == EMPTY) {
- return -1;
- }
- if (key == null) { // null key is located at the start of the array
- return nullKeyIndex(); // insert at index zero
- }
- final int start = size > 0 && keys[0] == null ? 1 : 0;
- return Arrays.binarySearch(keys, start, size, key);
- }
-
- private int nullKeyIndex() {
- return size > 0 && keys[0] == null ? 0 : ~0;
- }
-
- @Override
- public void putValue(final String key, final Object value) {
- assertNotFrozen();
- assertNoConcurrentModification();
-
- if (keys == EMPTY) {
- inflateTable(threshold);
- }
- final int index = indexOfKey(key);
- if (index >= 0) {
- keys[index] = key;
- values[index] = value;
- } else { // not found, so insert.
- insertAt(~index, key, value);
- }
- }
-
- private void insertAt(final int index, final String key, final Object value) {
- ensureCapacity();
- System.arraycopy(keys, index, keys, index + 1, size - index);
- System.arraycopy(values, index, values, index + 1, size - index);
- keys[index] = key;
- values[index] = value;
- size++;
- }
-
- @Override
- public void putAll(final ContextData source) {
- if (source == this) {
- return; // this.putAll(this) does not modify this collection
- }
- assertNotFrozen();
- assertNoConcurrentModification();
-
- if (source instanceof ArrayContextData && this.size == 0) {
- initFrom0((ArrayContextData) source);
- } else if (source != null) {
- source.forEach(PUT_ALL, this);
- }
- }
-
- private void initFrom0(final ArrayContextData other) {
- if (keys.length < other.size) {
- keys = new String[other.threshold];
- values = new Object[other.threshold];
- }
- System.arraycopy(other.keys, 0, keys, 0, other.size);
- System.arraycopy(other.values, 0, values, 0, other.size);
-
- size = other.size;
- threshold = other.threshold;
- }
-
- private void ensureCapacity() {
- if (size >= threshold) {
- resize(threshold * 2);
- }
- }
-
- private void resize(final int newCapacity) {
- final String[] oldKeys = keys;
- final Object[] oldValues = values;
-
- keys = new String[newCapacity];
- values = new Object[newCapacity];
-
- System.arraycopy(oldKeys, 0, keys, 0, size);
- System.arraycopy(oldValues, 0, values, 0, size);
-
- threshold = newCapacity;
- }
-
- /**
- * Inflates the table.
- */
- private void inflateTable(final int toSize) {
- threshold = toSize;
- keys = new String[toSize];
- values = new Object[toSize];
- }
-
- @Override
- public void remove(final String key) {
- if (keys == EMPTY) {
- return;
- }
-
- final int index = indexOfKey(key);
- if (index >= 0) {
- assertNotFrozen();
- assertNoConcurrentModification();
-
- System.arraycopy(keys, index + 1, keys, index, size - 1 - index);
- System.arraycopy(values, index + 1, values, index, size - 1 - index);
- keys[size - 1] = null;
- values[size - 1] = null;
- size--;
- }
- }
-
- String getKeyAt(final int index) {
- if (index < 0 || index >= size) {
- return null;
- }
- return keys[index];
- }
-
- @SuppressWarnings("unchecked")
- <V> V getValueAt(final int index) {
- if (index < 0 || index >= size) {
- return null;
- }
- return (V) values[index];
- }
-
- @Override
- public int size() {
- return size;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <V> void forEach(final BiConsumer<String, ? super V> action) {
- iterating = true;
- try {
- for (int i = 0; i < size; i++) {
- action.accept(keys[i], (V) values[i]);
- }
- } finally {
- iterating = false;
- }
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <V, T> void forEach(final TriConsumer<String, ? super V, T> action, final T state) {
- iterating = true;
- try {
- for (int i = 0; i < size; i++) {
- action.accept(keys[i], (V) values[i], state);
- }
- } finally {
- iterating = false;
- }
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (obj == this) {
- return true;
- }
- if (!(obj instanceof ArrayContextData)) {
- return false;
- }
- final ArrayContextData other = (ArrayContextData) obj;
- if (this.size() != other.size()) {
- return false;
- }
- for (int i = 0; i < size(); i++) {
- if (!Objects.equals(keys[i], other.keys[i])) {
- return false;
- }
- if (!Objects.equals(values[i], other.values[i])) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = 37;
- result = HASHVAL * result + size;
- result = HASHVAL * result + hashCode(keys, size);
- result = HASHVAL * result + hashCode(values, size);
- return result;
- }
-
- private static int hashCode(final Object[] values, final int length) {
- int result = 1;
- for (int i = 0; i < length; i++) {
- result = HASHVAL * result + (values[i] == null ? 0 : values[i].hashCode());
- }
- return result;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(256);
- sb.append('{');
- for (int i = 0; i < size; i++) {
- if (i > 0) {
- sb.append(", ");
- }
- sb.append(keys[i]).append('=');
- sb.append(values[i] == this ? "(this map)" : values[i]);
- }
- sb.append('}');
- return sb.toString();
- }
-
- /**
- * Save the state of the {@code ArrayContextData} instance to a stream (i.e.,
- * serialize it).
- *
- * @serialData The <i>capacity</i> of the ArrayContextData (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(final java.io.ObjectOutputStream s) throws IOException {
- // Write out the threshold, and any hidden stuff
- s.defaultWriteObject();
-
- // Write out number of buckets
- if (keys == EMPTY) {
- s.writeInt(ceilingNextPowerOfTwo(threshold));
- } else {
- s.writeInt(keys.length);
- }
-
- // Write out size (number of Mappings)
- s.writeInt(size);
-
- // Write out keys and values (alternating)
- if (size > 0) {
- for (int i = 0; i < size; i++) {
- s.writeObject(keys[i]);
- s.writeObject(values[i]);
- }
- }
- }
-
-
- /**
- * Calculate the next power of 2, greater than or equal to x.
- * <p>
- * From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
- *
- * @param x Value to round up
- * @return The next power of 2 from x inclusive
- */
- private static int ceilingNextPowerOfTwo(final int x) {
- final int BITS_PER_INT = 32;
- return 1 << (BITS_PER_INT - Integer.numberOfLeadingZeros(x - 1));
- }
-
- /**
- * Reconstitute the {@code ArrayContextData} instance from a stream (i.e.,
- * deserialize it).
- */
- private void readObject(final java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
- // Read in the threshold (ignored), and any hidden stuff
- s.defaultReadObject();
-
- // set other fields that need values
- keys = EMPTY;
- values = EMPTY;
-
- // Read in number of buckets
- final int capacity = s.readInt();
- if (capacity < 0) {
- throw new InvalidObjectException("Illegal capacity: " + capacity);
- }
-
- // Read number of mappings
- final int mappings = s.readInt();
- if (mappings < 0) {
- throw new InvalidObjectException("Illegal mappings count: " + mappings);
- }
-
- // allocate the bucket array;
- if (mappings > 0) {
- inflateTable(capacity);
- } else {
- threshold = capacity;
- }
-
- // Read the keys and values, and put the mappings in the arrays
- for (int i = 0; i < mappings; i++) {
- keys[i] = (String) s.readObject();
- values[i] = s.readObject();
- }
- size = mappings;
- }
-}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java
new file mode 100644
index 0000000..7cdf3a4
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java
@@ -0,0 +1,480 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.util;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.logging.log4j.spi.ContextData;
+import org.apache.logging.log4j.spi.MutableContextData;
+
+/**
+ * <em>Consider this class private.</em>
+ * Array-based implementation of the {@code ContextData} interface. Keys are held in a sorted array.
+ * <p>
+ * This is not a generic collection, but makes some trade-offs to optimize for the Log4j ContextData use case:
+ * </p>
+ * <ul>
+ * <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} and {@code TriConsumer}.</li>
+ * <li>Fast copy. If the ThreadContextMap is also an instance of {@code SortedStringArrayMap}, the full thread context
+ * data can be transferred with two array copies and two field updates.</li>
+ * <li>Acceptable performance for small data sets. The current implementation stores keys in a sorted array, values
+ * are stored in a separate array at the same index.
+ * Worst-case performance of {@code get} and {@code containsKey} is O(log N),
+ * worst-case performance of {@code put} and {@code remove} is O(N log N).
+ * The expectation is that for the small values of {@code N} (less than 100) that are the vast majority of
+ * ThreadContext use cases, the constants dominate performance more than the asymptotic performance of the
+ * algorithms used.
+ * </li>
+ * <li>Compact representation.</li>
+ * </ul>
+ *
+ * @since 2.7
+ */
+public class SortedStringArrayMap implements MutableContextData {
+
+ /**
+ * The default initial capacity.
+ */
+ private static final int DEFAULT_INITIAL_CAPACITY = 4;
+ private static final long serialVersionUID = -5748905872274478116L;
+ private static final int HASHVAL = 31;
+
+ private static final TriConsumer<String, Object, MutableContextData> PUT_ALL = new TriConsumer<String, Object, MutableContextData>() {
+ @Override
+ public void accept(final String key, final Object value, final MutableContextData contextData) {
+ contextData.putValue(key, value);
+ }
+ };
+
+ /**
+ * An empty array instance to share when the table is not inflated.
+ */
+ private static final String[] EMPTY = {};
+ private static final String FROZEN = "Frozen collection cannot be modified";
+
+ private transient String[] keys = EMPTY;
+ private transient Object[] values = EMPTY;
+
+ /**
+ * The number of key-value mappings contained in this map.
+ */
+ private 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.
+ private int threshold;
+ private boolean immutable;
+ private transient boolean iterating;
+
+ public SortedStringArrayMap() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ public SortedStringArrayMap(final int initialCapacity) {
+ if (initialCapacity < 1) {
+ throw new IllegalArgumentException("Initial capacity must be at least one but was " + initialCapacity);
+ }
+ threshold = ceilingNextPowerOfTwo(initialCapacity);
+ }
+
+ public SortedStringArrayMap(final ContextData other) {
+ if (other instanceof SortedStringArrayMap) {
+ initFrom0((SortedStringArrayMap) other);
+ } else if (other != null) {
+ resize(ceilingNextPowerOfTwo(other.size()));
+ other.forEach(PUT_ALL, this);
+ }
+ }
+
+ private void assertNotFrozen() {
+ if (immutable) {
+ throw new UnsupportedOperationException(FROZEN);
+ }
+ }
+
+ private void assertNoConcurrentModification() {
+ if (iterating) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public void clear() {
+ if (keys == EMPTY) {
+ return;
+ }
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
+ Arrays.fill(keys, 0, size, null);
+ Arrays.fill(values, 0, size, null);
+ size = 0;
+ }
+
+ @Override
+ public boolean containsKey(final String key) {
+ return indexOfKey(key) >= 0;
+ }
+
+ @Override
+ public Map<String, String> toMap() {
+ final Map<String, String> result = new HashMap<>(size());
+ for (int i = 0; i < size(); i++) {
+ final Object value = getValueAt(i);
+ result.put(getKeyAt(i), value == null ? null : String.valueOf(value));
+ }
+ return result;
+ }
+
+ @Override
+ public void freeze() {
+ immutable = true;
+ }
+
+ @Override
+ public boolean isFrozen() {
+ return immutable;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V> V getValue(final String key) {
+ final int index = indexOfKey(key);
+ if (index < 0) {
+ return null;
+ }
+ return (V) values[index];
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ int indexOfKey(final String key) {
+ if (keys == EMPTY) {
+ return -1;
+ }
+ if (key == null) { // null key is located at the start of the array
+ return nullKeyIndex(); // insert at index zero
+ }
+ final int start = size > 0 && keys[0] == null ? 1 : 0;
+ return Arrays.binarySearch(keys, start, size, key);
+ }
+
+ private int nullKeyIndex() {
+ return size > 0 && keys[0] == null ? 0 : ~0;
+ }
+
+ @Override
+ public void putValue(final String key, final Object value) {
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
+ if (keys == EMPTY) {
+ inflateTable(threshold);
+ }
+ final int index = indexOfKey(key);
+ if (index >= 0) {
+ keys[index] = key;
+ values[index] = value;
+ } else { // not found, so insert.
+ insertAt(~index, key, value);
+ }
+ }
+
+ private void insertAt(final int index, final String key, final Object value) {
+ ensureCapacity();
+ System.arraycopy(keys, index, keys, index + 1, size - index);
+ System.arraycopy(values, index, values, index + 1, size - index);
+ keys[index] = key;
+ values[index] = value;
+ size++;
+ }
+
+ @Override
+ public void putAll(final ContextData source) {
+ if (source == this) {
+ return; // this.putAll(this) does not modify this collection
+ }
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
+ if (source instanceof SortedStringArrayMap && this.size == 0) {
+ initFrom0((SortedStringArrayMap) source);
+ } else if (source != null) {
+ source.forEach(PUT_ALL, this);
+ }
+ }
+
+ private void initFrom0(final SortedStringArrayMap other) {
+ if (keys.length < other.size) {
+ keys = new String[other.threshold];
+ values = new Object[other.threshold];
+ }
+ System.arraycopy(other.keys, 0, keys, 0, other.size);
+ System.arraycopy(other.values, 0, values, 0, other.size);
+
+ size = other.size;
+ threshold = other.threshold;
+ }
+
+ private void ensureCapacity() {
+ if (size >= threshold) {
+ resize(threshold * 2);
+ }
+ }
+
+ private void resize(final int newCapacity) {
+ final String[] oldKeys = keys;
+ final Object[] oldValues = values;
+
+ keys = new String[newCapacity];
+ values = new Object[newCapacity];
+
+ System.arraycopy(oldKeys, 0, keys, 0, size);
+ System.arraycopy(oldValues, 0, values, 0, size);
+
+ threshold = newCapacity;
+ }
+
+ /**
+ * Inflates the table.
+ */
+ private void inflateTable(final int toSize) {
+ threshold = toSize;
+ keys = new String[toSize];
+ values = new Object[toSize];
+ }
+
+ @Override
+ public void remove(final String key) {
+ if (keys == EMPTY) {
+ return;
+ }
+
+ final int index = indexOfKey(key);
+ if (index >= 0) {
+ assertNotFrozen();
+ assertNoConcurrentModification();
+
+ System.arraycopy(keys, index + 1, keys, index, size - 1 - index);
+ System.arraycopy(values, index + 1, values, index, size - 1 - index);
+ keys[size - 1] = null;
+ values[size - 1] = null;
+ size--;
+ }
+ }
+
+ String getKeyAt(final int index) {
+ if (index < 0 || index >= size) {
+ return null;
+ }
+ return keys[index];
+ }
+
+ @SuppressWarnings("unchecked")
+ <V> V getValueAt(final int index) {
+ if (index < 0 || index >= size) {
+ return null;
+ }
+ return (V) values[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V> void forEach(final BiConsumer<String, ? super V> action) {
+ iterating = true;
+ try {
+ for (int i = 0; i < size; i++) {
+ action.accept(keys[i], (V) values[i]);
+ }
+ } finally {
+ iterating = false;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <V, T> void forEach(final TriConsumer<String, ? super V, T> action, final T state) {
+ iterating = true;
+ try {
+ for (int i = 0; i < size; i++) {
+ action.accept(keys[i], (V) values[i], state);
+ }
+ } finally {
+ iterating = false;
+ }
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof SortedStringArrayMap)) {
+ return false;
+ }
+ final SortedStringArrayMap other = (SortedStringArrayMap) obj;
+ if (this.size() != other.size()) {
+ return false;
+ }
+ for (int i = 0; i < size(); i++) {
+ if (!Objects.equals(keys[i], other.keys[i])) {
+ return false;
+ }
+ if (!Objects.equals(values[i], other.values[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 37;
+ result = HASHVAL * result + size;
+ result = HASHVAL * result + hashCode(keys, size);
+ result = HASHVAL * result + hashCode(values, size);
+ return result;
+ }
+
+ private static int hashCode(final Object[] values, final int length) {
+ int result = 1;
+ for (int i = 0; i < length; i++) {
+ result = HASHVAL * result + (values[i] == null ? 0 : values[i].hashCode());
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(256);
+ sb.append('{');
+ for (int i = 0; i < size; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(keys[i]).append('=');
+ sb.append(values[i] == this ? "(this map)" : values[i]);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+
+ /**
+ * Save the state of the {@code SortedStringArrayMap} instance to a stream (i.e.,
+ * serialize it).
+ *
+ * @serialData The <i>capacity</i> of the SortedStringArrayMap (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(final java.io.ObjectOutputStream s) throws IOException {
+ // Write out the threshold, and any hidden stuff
+ s.defaultWriteObject();
+
+ // Write out number of buckets
+ if (keys == EMPTY) {
+ s.writeInt(ceilingNextPowerOfTwo(threshold));
+ } else {
+ s.writeInt(keys.length);
+ }
+
+ // Write out size (number of Mappings)
+ s.writeInt(size);
+
+ // Write out keys and values (alternating)
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ s.writeObject(keys[i]);
+ s.writeObject(values[i]);
+ }
+ }
+ }
+
+
+ /**
+ * Calculate the next power of 2, greater than or equal to x.
+ * <p>
+ * From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
+ *
+ * @param x Value to round up
+ * @return The next power of 2 from x inclusive
+ */
+ private static int ceilingNextPowerOfTwo(final int x) {
+ final int BITS_PER_INT = 32;
+ return 1 << (BITS_PER_INT - Integer.numberOfLeadingZeros(x - 1));
+ }
+
+ /**
+ * Reconstitute the {@code SortedStringArrayMap} instance from a stream (i.e.,
+ * deserialize it).
+ */
+ private void readObject(final java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
+ // Read in the threshold (ignored), and any hidden stuff
+ s.defaultReadObject();
+
+ // set other fields that need values
+ keys = EMPTY;
+ values = EMPTY;
+
+ // Read in number of buckets
+ final int capacity = s.readInt();
+ if (capacity < 0) {
+ throw new InvalidObjectException("Illegal capacity: " + capacity);
+ }
+
+ // Read number of mappings
+ final int mappings = s.readInt();
+ if (mappings < 0) {
+ throw new InvalidObjectException("Illegal mappings count: " + mappings);
+ }
+
+ // allocate the bucket array;
+ if (mappings > 0) {
+ inflateTable(capacity);
+ } else {
+ threshold = capacity;
+ }
+
+ // Read the keys and values, and put the mappings in the arrays
+ for (int i = 0; i < mappings; i++) {
+ keys[i] = (String) s.readObject();
+ values[i] = s.readObject();
+ }
+ size = mappings;
+ }
+}
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
----------------------------------------------------------------------
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
deleted file mode 100644
index bff68b5..0000000
--- a/log4j-api/src/test/java/org/apache/logging/log4j/util/ArrayContextDataTest.java
+++ /dev/null
@@ -1,772 +0,0 @@
-package org.apache.logging.log4j.util;/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.lang.reflect.Field;
-import java.util.ConcurrentModificationException;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Tests the ArrayContextData class.
- */
-public class ArrayContextDataTest {
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructorDisallowsNegativeCapacity() throws Exception {
- new ArrayContextData(-1);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testConstructorDisallowsZeroCapacity() throws Exception {
- new ArrayContextData(0);
- }
-
- @Test
- public void testConstructorIgnoresNull() throws Exception {
- assertEquals(0, new ArrayContextData(null).size());
- }
-
- @Test
- public void testToString() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals("{3=3value, B=Bvalue, a=avalue}", original.toString());
- }
-
- @Test
- public void testSerialization() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final byte[] binary = serialize(original);
- final ArrayContextData copy = deserialize(binary);
- assertEquals(original, copy);
- }
-
- private byte[] serialize(final ArrayContextData data) throws IOException {
- final ByteArrayOutputStream arr = new ByteArrayOutputStream();
- final ObjectOutputStream out = new ObjectOutputStream(arr);
- out.writeObject(data);
- return arr.toByteArray();
- }
-
- private ArrayContextData deserialize(final byte[] binary) throws IOException, ClassNotFoundException {
- final ByteArrayInputStream inArr = new ByteArrayInputStream(binary);
- final ObjectInputStream in = new ObjectInputStream(inArr);
- final ArrayContextData result = (ArrayContextData) in.readObject();
- return result;
- }
-
- @Test
- public void testPutAll() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final ArrayContextData other = new ArrayContextData();
- other.putAll(original);
- assertEquals(original, other);
-
- other.putValue("3", "otherValue");
- assertNotEquals(original, other);
-
- other.putValue("3", null);
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
- }
-
- @Test
- public void testEquals() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals(original, original); // equal to itself
-
- final ArrayContextData other = new ArrayContextData();
- other.putValue("a", "avalue");
- assertNotEquals(original, other);
-
- other.putValue("B", "Bvalue");
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
-
- other.putValue("3", "otherValue");
- assertNotEquals(original, other);
-
- other.putValue("3", null);
- assertNotEquals(original, other);
-
- other.putValue("3", "3value");
- assertEquals(original, other);
- }
-
- @Test
- public void testToMap() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final Map<String, Object> expected = new HashMap<>();
- expected.put("a", "avalue");
- expected.put("B", "Bvalue");
- expected.put("3", "3value");
-
- assertEquals(expected, original.toMap());
-
- try {
- original.toMap().put("abc", "xyz");
- } catch (final UnsupportedOperationException ex) {
- fail("Expected map to be mutable, but " + ex);
- }
- }
-
- @Test
- public void testPutAll_KeepsExistingValues() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.putValue("b", "bbb");
- original.putValue("c", "ccc");
- assertEquals("size", 3, original.size());
-
- // add empty context data
- original.putAll(new ArrayContextData());
- assertEquals("size after put empty", 3, original.size());
- assertEquals("aaa", original.getValue("a"));
- assertEquals("bbb", original.getValue("b"));
- assertEquals("ccc", original.getValue("c"));
-
- final ArrayContextData other = new ArrayContextData();
- other.putValue("1", "111");
- other.putValue("2", "222");
- other.putValue("3", "333");
- original.putAll(other);
-
- assertEquals("size after put other", 6, original.size());
- assertEquals("aaa", original.getValue("a"));
- assertEquals("bbb", original.getValue("b"));
- assertEquals("ccc", original.getValue("c"));
- assertEquals("111", original.getValue("1"));
- assertEquals("222", original.getValue("2"));
- assertEquals("333", original.getValue("3"));
- }
-
- @Test
- public void testPutAllSelfDoesNotModify() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.putValue("b", "bbb");
- original.putValue("c", "ccc");
- assertEquals("size", 3, original.size());
-
- // putAll with self
- original.putAll(original);
- assertEquals("size after put empty", 3, original.size());
- assertEquals("aaa", original.getValue("a"));
- assertEquals("bbb", original.getValue("b"));
- assertEquals("ccc", original.getValue("c"));
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationBiConsumerPut() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new BiConsumer<String, Object>() {
- @Override
- public void accept(final String s, final Object o) {
- original.putValue("c", "other");
- }
- });
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationBiConsumerPutValue() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new BiConsumer<String, Object>() {
- @Override
- public void accept(final String s, final Object o) {
- original.putValue("c", "other");
- }
- });
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationBiConsumerRemove() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new BiConsumer<String, Object>() {
- @Override
- public void accept(final String s, final Object o) {
- original.remove("a");
- }
- });
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationBiConsumerClear() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new BiConsumer<String, Object>() {
- @Override
- public void accept(final String s, final Object o) {
- original.clear();
- }
- });
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationTriConsumerPut() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new TriConsumer<String, Object, Object>() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.putValue("c", "other");
- }
- }, null);
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationTriConsumerPutValue() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new TriConsumer<String, Object, Object>() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.putValue("c", "other");
- }
- }, null);
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationTriConsumerRemove() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new TriConsumer<String, Object, Object>() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.remove("a");
- }
- }, null);
- }
-
- @Test(expected = ConcurrentModificationException.class)
- public void testConcurrentModificationTriConsumerClear() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.forEach(new TriConsumer<String, Object, Object>() {
- @Override
- public void accept(final String s, final Object o, final Object o2) {
- original.clear();
- }
- }, null);
- }
-
- @Test
- public void testInitiallyNotFrozen() {
- assertFalse(new ArrayContextData().isFrozen());
- }
-
- @Test
- public void testIsFrozenAfterCallingFreeze() {
- final ArrayContextData original = new ArrayContextData();
- assertFalse("before freeze", original.isFrozen());
- original.freeze();
- assertTrue("after freeze", original.isFrozen());
- }
-
- @Test(expected = UnsupportedOperationException.class)
- public void testFreezeProhibitsPutValue() {
- final ArrayContextData original = new ArrayContextData();
- original.freeze();
- original.putValue("a", "aaa");
- }
-
- @Test(expected = UnsupportedOperationException.class)
- public void testFreezeProhibitsRemove() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("b", "bbb");
- original.freeze();
- original.remove("b"); // existing key: modifies the collection
- }
-
- @Test
- public void testFreezeAllowsRemoveOfNonExistingKey() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("b", "bbb");
- original.freeze();
- original.remove("a"); // no actual modification
- }
-
- @Test
- public void testFreezeAllowsRemoveIfEmpty() {
- final ArrayContextData original = new ArrayContextData();
- original.freeze();
- original.remove("a"); // no exception
- }
-
- @Test(expected = UnsupportedOperationException.class)
- public void testFreezeProhibitsClear() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "aaa");
- original.freeze();
- original.clear();
- }
-
- @Test
- public void testFreezeAllowsClearIfEmpty() {
- final ArrayContextData original = new ArrayContextData();
- original.freeze();
- original.clear();
- }
-
- @Test
- public void testPutInsertsInAlphabeticOrder() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(2));
-
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(1));
-
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(0));
-
- assertEquals("cvalue", original.getValue("c"));
- assertEquals("cvalue", original.getValueAt(3));
-
- assertEquals("dvalue", original.getValue("d"));
- assertEquals("dvalue", original.getValueAt(4));
- }
-
- @Test
- public void testPutValueInsertsInAlphabeticOrder() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(2));
-
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(1));
-
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(0));
-
- assertEquals("cvalue", original.getValue("c"));
- assertEquals("cvalue", original.getValueAt(3));
-
- assertEquals("dvalue", original.getValue("d"));
- assertEquals("dvalue", original.getValueAt(4));
- }
-
- @Test
- public void testNullKeysAllowed() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
- assertEquals(5, original.size());
- assertEquals("{3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
-
- original.putValue(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
-
- original.putValue(null, "otherNullvalue");
- assertEquals("{null=otherNullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
- assertEquals(6, original.size());
-
- original.putValue(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
-
- original.putValue(null, "abc");
- assertEquals(6, original.size());
- assertEquals("{null=abc, 3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", original.toString());
- }
-
- @Test
- public void testNullKeysCopiedToAsMap() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
- assertEquals(5, original.size());
-
- final HashMap<String, String> expected = new HashMap<>();
- expected.put("a", "avalue");
- expected.put("B", "Bvalue");
- expected.put("3", "3value");
- expected.put("c", "cvalue");
- expected.put("d", "dvalue");
- assertEquals("initial", expected, original.toMap());
-
- original.putValue(null, "nullvalue");
- expected.put(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key", expected, original.toMap());
-
- original.putValue(null, "otherNullvalue");
- expected.put(null, "otherNullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key value2", expected, original.toMap());
-
- original.putValue(null, "nullvalue");
- expected.put(null, "nullvalue");
- assertEquals(6, original.size());
- assertEquals("with null key value1 again", expected, original.toMap());
-
- original.putValue(null, "abc");
- expected.put(null, "abc");
- assertEquals(6, original.size());
- assertEquals("with null key value3", expected, original.toMap());
- }
-
- @Test
- public void testRemove() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertEquals("avalue", original.getValue("a"));
-
- original.remove("a");
- assertEquals(0, original.size());
- assertNull("no a val", original.getValue("a"));
-
- original.remove("B");
- assertEquals(0, original.size());
- assertNull("no B val", original.getValue("B"));
- }
-
- @Test
- public void testRemoveNullsOutRemovedSlot() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("b", "bvalue");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue");
- original.remove("a");
- original.remove("b");
- original.remove("c");
- original.remove("d");
- assertNull(original.getValueAt(0));
-
- // ensure slots in the values array are nulled out
- final Field f = ArrayContextData.class.getDeclaredField("values");
- f.setAccessible(true);
- final Object[] values = (Object[]) f.get(original);
- for (int i = 0; i < values.length; i++) {
- assertNull(values[i]);
- }
- }
-
- @Test
- public void testRemoveWhenFull() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("b", "bvalue");
- original.putValue("c", "cvalue");
- original.putValue("d", "dvalue"); // default capacity = 4
- original.remove("d");
- }
-
- @Test
- public void testNullValuesArePreserved() {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertEquals("avalue", original.getValue("a"));
-
- original.putValue("a", null);
- assertEquals(1, original.size());
- assertNull("no a val", original.getValue("a"));
-
- original.putValue("B", null);
- assertEquals(2, original.size());
- assertNull("no B val", original.getValue("B"));
- }
-
- @Test
- public void testGet() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("3value", original.getValue("3"));
-
- original.putValue("0", "0value");
- assertEquals("0value", original.getValue("0"));
- assertEquals("3value", original.getValue("3"));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("avalue", original.getValue("a"));
- }
-
- @Test
- public void testGetValue_GetValueAt() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(2));
-
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(1));
-
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(0));
-
- original.putValue("0", "0value");
- assertEquals("0value", original.getValue("0"));
- assertEquals("0value", original.getValueAt(0));
- assertEquals("3value", original.getValue("3"));
- assertEquals("3value", original.getValueAt(1));
- assertEquals("Bvalue", original.getValue("B"));
- assertEquals("Bvalue", original.getValueAt(2));
- assertEquals("avalue", original.getValue("a"));
- assertEquals("avalue", original.getValueAt(3));
- }
-
- @Test
- public void testClear() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
- assertEquals(3, original.size());
-
- original.clear();
- assertEquals(0, original.size());
-
- // ensure slots in the values array are nulled out
- final Field f = ArrayContextData.class.getDeclaredField("values");
- f.setAccessible(true);
- final Object[] values = (Object[]) f.get(original);
- for (int i = 0; i < values.length; i++) {
- assertNull(values[i]);
- }
- }
-
- @Test
- public void testIndexOfKey() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals(0, original.indexOfKey("a"));
-
- original.putValue("B", "Bvalue");
- assertEquals(1, original.indexOfKey("a"));
- assertEquals(0, original.indexOfKey("B"));
-
- original.putValue("3", "3value");
- assertEquals(2, original.indexOfKey("a"));
- assertEquals(1, original.indexOfKey("B"));
- assertEquals(0, original.indexOfKey("3"));
-
- original.putValue("A", "AAA");
- assertEquals(3, original.indexOfKey("a"));
- assertEquals(2, original.indexOfKey("B"));
- assertEquals(1, original.indexOfKey("A"));
- assertEquals(0, original.indexOfKey("3"));
-
- original.putValue("C", "CCC");
- assertEquals(4, original.indexOfKey("a"));
- assertEquals(3, original.indexOfKey("C"));
- assertEquals(2, original.indexOfKey("B"));
- assertEquals(1, original.indexOfKey("A"));
- assertEquals(0, original.indexOfKey("3"));
-
- original.putValue("2", "222");
- assertEquals(5, original.indexOfKey("a"));
- assertEquals(4, original.indexOfKey("C"));
- assertEquals(3, original.indexOfKey("B"));
- assertEquals(2, original.indexOfKey("A"));
- assertEquals(1, original.indexOfKey("3"));
- assertEquals(0, original.indexOfKey("2"));
- }
-
- @Test
- public void testContainsKey() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- assertFalse("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("a", "avalue");
- assertTrue("a", original.containsKey("a"));
- assertFalse("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("B", "Bvalue");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertFalse("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("3", "3value");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertFalse("A", original.containsKey("A"));
-
- original.putValue("A", "AAA");
- assertTrue("a", original.containsKey("a"));
- assertTrue("B", original.containsKey("B"));
- assertTrue("3", original.containsKey("3"));
- assertTrue("A", original.containsKey("A"));
- }
-
- @Test
- public void testGetValueAt() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- assertEquals("a", original.getKeyAt(0));
- assertEquals("avalue", original.getValueAt(0));
-
- original.putValue("B", "Bvalue");
- assertEquals("B", original.getKeyAt(0));
- assertEquals("Bvalue", original.getValueAt(0));
- assertEquals("a", original.getKeyAt(1));
- assertEquals("avalue", original.getValueAt(1));
-
- original.putValue("3", "3value");
- assertEquals("3", original.getKeyAt(0));
- assertEquals("3value", original.getValueAt(0));
- assertEquals("B", original.getKeyAt(1));
- assertEquals("Bvalue", original.getValueAt(1));
- assertEquals("a", original.getKeyAt(2));
- assertEquals("avalue", original.getValueAt(2));
- }
-
- @Test
- public void testSizeAndIsEmpty() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- assertEquals(0, original.size());
- assertTrue("initial", original.isEmpty());
-
- original.putValue("a", "avalue");
- assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.putValue("B", "Bvalue");
- assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.putValue("3", "3value");
- assertEquals(3, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("B");
- assertEquals(2, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("3");
- assertEquals(1, original.size());
- assertFalse("size=" + original.size(), original.isEmpty());
-
- original.remove("a");
- assertEquals(0, original.size());
- assertTrue("size=" + original.size(), original.isEmpty());
- }
-
- @Test
- public void testForEachBiConsumer() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- original.forEach(new BiConsumer<String, String>() {
- int count = 0;
- @Override
- public void accept(final String key, final String value) {
- assertEquals("key", key, original.getKeyAt(count));
- assertEquals("val", value, original.getValueAt(count));
- count++;
- assertTrue("count should not exceed size but was " + count, count <= original.size());
- }
- });
- }
-
- static class State {
- ArrayContextData data;
- int count;
- }
- static TriConsumer<String, String, State> COUNTER = new TriConsumer<String, String, State>() {
- @Override
- public void accept(final String key, final String value, final State state) {
- assertEquals("key", key, state.data.getKeyAt(state.count));
- assertEquals("val", value, state.data.getValueAt(state.count));
- state.count++;
- assertTrue("count should not exceed size but was " + state.count,
- state.count <= state.data.size());
- }
- };
-
- @Test
- public void testForEachTriConsumer() throws Exception {
- final ArrayContextData original = new ArrayContextData();
- original.putValue("a", "avalue");
- original.putValue("B", "Bvalue");
- original.putValue("3", "3value");
-
- final State state = new State();
- state.data = original;
- original.forEach(COUNTER, state);
- assertEquals(state.count, original.size());
- }
-}
\ No newline at end of file