You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by an...@apache.org on 2014/10/16 12:16:58 UTC
svn commit: r1632265 [2/5] - in /myfaces/trinidad/trunk:
trinidad-api/src/test/java/org/apache/myfaces/trinidad/bean/util/
trinidad-api/src/test/java/org/apache/myfaces/trinidad/util/
trinidad-api/src/test/java/org/apache/myfaces/trinidadbuild/test/ tr...
Modified: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java?rev=1632265&r1=1632264&r2=1632265&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/style/util/CSSUtils.java Thu Oct 16 10:16:54 2014
@@ -24,24 +24,22 @@ import java.net.URI;
import java.net.URISyntaxException;
-import java.util.Collections;
import java.util.HashSet;
-import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
+import java.util.concurrent.ConcurrentMap;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.myfaces.trinidad.logging.TrinidadLogger;
import org.apache.myfaces.trinidad.util.ArrayMap;
-import org.apache.myfaces.trinidad.style.Style;
import org.apache.myfaces.trinidadinternal.style.CSSStyle;
import org.apache.myfaces.trinidadinternal.style.CoreStyle;
import org.apache.myfaces.trinidadinternal.style.PropertyParseException;
-import org.apache.myfaces.trinidadinternal.util.LRUCache;
+import org.apache.myfaces.trinidadinternal.util.CopyOnWriteArrayMap;
/**
* CSS-related utilities. I think as we move away from xss, most of this code will
@@ -763,13 +761,13 @@ public class CSSUtils
{
Color sharedColor = _sColorCache.get(Integer.valueOf(rgb));
- if (sharedColor == null)
- {
- sharedColor = new Color(rgb);
- _sColorCache.put(Integer.valueOf(rgb), sharedColor);
- }
+ if (sharedColor != null)
+ return sharedColor;
+
+ sharedColor = new Color(rgb);
+ Color existing = _sColorCache.putIfAbsent(Integer.valueOf(rgb), sharedColor);
- return sharedColor;
+ return (existing != null) ? existing : sharedColor;
}
private static Color _getSharedColor(int r, int g, int b)
@@ -982,8 +980,7 @@ public class CSSUtils
}
return value.indexOf("url(") >= 0;
- }
-
+ }
private static final String _PARENTHESES_BEGIN = "(";
@@ -1007,8 +1004,7 @@ public class CSSUtils
// We keep a cache of shared Color instances, hashed by RGB value, so
// that we don't end up with one Color instance for each color in each
// cache key in the Tecate image cache.
- private static final Map<Integer, Color> _sColorCache =
- Collections.synchronizedMap(new LRUCache<Integer, Color>(50));
+ private static final ConcurrentMap<Integer, Color> _sColorCache = CopyOnWriteArrayMap.newLRUConcurrentMap(50);
// CSS named color values
private static final Object[] _NAMED_COLORS = new Object[]
@@ -1089,10 +1085,11 @@ public class CSSUtils
"smaller", 10,
"larger", 14
};
-
+
// Set of values that are legal for url() values
private static final Set<String> _URI_PROPERTIES = new HashSet<String>();
+
static
{
_URI_PROPERTIES.add("background-image");
@@ -1103,12 +1100,13 @@ public class CSSUtils
// Set of values that are legal for url() values
private static final Set<String> _SPECIAL_URI_VALUES = new HashSet<String>();
+
static
{
_SPECIAL_URI_VALUES.add("none");
_SPECIAL_URI_VALUES.add("inherit");
_SPECIAL_URI_VALUES.add("-moz-linear-gradient");
- _SPECIAL_URI_VALUES.add("-webkit-gradient");
+ _SPECIAL_URI_VALUES.add("-webkit-gradient");
_SPECIAL_URI_VALUES.add("-webkit-linear-gradient");
_SPECIAL_URI_VALUES.add("radial-gradient");
_SPECIAL_URI_VALUES.add("linear-gradient");
@@ -1117,5 +1115,4 @@ public class CSSUtils
}
private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(CSSUtils.class);
-
}
Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMap.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMap.java?rev=1632265&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMap.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMap.java Thu Oct 16 10:16:54 2014
@@ -0,0 +1,1580 @@
+/*
+ * 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.myfaces.trinidadinternal.util;
+
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+
+import java.lang.reflect.Array;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import java.util.Set;
+import java.io.Serializable;
+
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.myfaces.trinidad.util.Args;
+
+
+/**
+ * CopyOnWrite-implementation of ArrayMap
+ * Access is O(logN), using a binary search of the array of entries, sorted by key hash code.
+ * Null keys are not supported.
+ * Calling clone returns a new CopyOnWriteArrayMap with its own lock and its own set of
+ * Map.Entry objects. The keys and values themselves are not cloned.
+ *
+ * The iterators returned run off a snapshot of the array of entry objects until remove() is called
+ * on the Iterator, If the set of entry objects has been modified since the Iterator was created,
+ * a ConcurrentModificationException will be thrown. Otherwise, the remove() operation succeeds
+ * and the entry is removed from the CopyOnWriteArrayMap.
+ * @param <K>
+ * @param <V>
+ */
+public final class CopyOnWriteArrayMap<K,V> implements ConcurrentMap<K,V>, Cloneable, Serializable
+{
+ public static <K,V> CopyOnWriteArrayMap<K,V> newConcurrentMap()
+ {
+ ConcurrentEntryFactory<K,V> factory = ConcurrentEntryFactory.getInstance();
+
+ return new CopyOnWriteArrayMap<K,V>(factory);
+ }
+
+ /**
+ * Returns a CopyOnWriteArrayMap implementing a least-recently-used strategy to determine
+ * which entries to prune when the maxSize is exceeded.
+ *
+ * An entry is considered accessed if the entry's value is retreived or modified.
+ *
+ * The precision with which the least recently used entry is determined is dependent on the
+ * resolution of the platform's System.nanoTime() implementation. In addition, no effort is
+ * made to handle entries that are updated while the CopyOnWriteArrayMap is determining
+ * which entry to purge.
+ *
+ * @param <K> Type of the keys in the CopyOnWriteArrayMap
+ * @param <V> Type of the mapped values
+ * @param maxSize The maximum number of entries allowed in the CopyOnWriteArrayMap
+ * @return
+ * @see java.lang.System#nanoTime
+ */
+ public static <K,V> CopyOnWriteArrayMap<K,V> newLRUConcurrentMap(int maxSize)
+ {
+ if (maxSize < 0)
+ maxSize = 0;
+
+ LRUEntryFactory<K,V> entryFactory = new LRUEntryFactory<K,V>(maxSize, System.nanoTime());
+
+ return new CopyOnWriteArrayMap<K,V>(entryFactory);
+ }
+
+ private CopyOnWriteArrayMap(EntryFactory<?,K,V> entryFactory)
+ {
+ this(entryFactory, new ReentrantLock(), entryFactory.getEmptyEntries());
+ }
+
+ private CopyOnWriteArrayMap(
+ EntryFactory<?,K,V> entryFactory, ReentrantLock writeLock, ConcurrentEntry<K,V>[] entries)
+ {
+ _entryFactory = entryFactory;
+ _writeLock = writeLock;
+ _entries = entries;
+ }
+
+ @Override
+ public V putIfAbsent(K key, V value)
+ {
+ V oldValue;
+
+ final ReentrantLock writeLock = this._writeLock;
+
+ writeLock.lock();
+
+ try
+ {
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ int entryIndex = _getEntryIndex(entries, key);
+
+ if (entryIndex >= 0)
+ {
+ ConcurrentEntry<K,V> entry = entries[entryIndex];
+
+ oldValue = entry.getValue();
+ }
+ else
+ {
+ // the insert locations are returned as two's complement
+ int insertIndex = -(entryIndex + 1);
+
+ _entries = _insertEntryAt(entries, key, value, insertIndex, _entryFactory);
+
+ oldValue = null;
+ }
+ }
+ finally
+ {
+ writeLock.unlock();
+ }
+
+ return oldValue;
+ }
+
+ @Override
+ public boolean remove(Object key, Object value)
+ {
+ boolean removed = false;
+
+ final ReentrantLock writeLock = this._writeLock;
+
+ writeLock.lock();
+
+ try
+ {
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ int removeIndex = _getEntryIndex(entries, key);
+
+ if (removeIndex >= 0)
+ {
+ ConcurrentEntry<K,V> entry = entries[removeIndex];
+
+ V entryValue = entry.getValue();
+
+ boolean valuesEqual = (entryValue != null) ? entryValue.equals(value) : (value == null);
+
+ if (valuesEqual)
+ {
+ _entries = _removeEntryByIndex(entries, removeIndex);
+ removed = true;
+ }
+ }
+ }
+ finally
+ {
+ writeLock.unlock();
+ }
+
+ return removed;
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue)
+ {
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ ConcurrentEntry<K,V> entry = _getEntry(entries, key);
+
+ if (entry != null)
+ {
+ return entry.compareAndSetValue(oldValue, newValue);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public V replace(K key, V value)
+ {
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ ConcurrentEntry<K,V> entry = _getEntry(entries, key);
+
+ if (entry != null)
+ {
+ return entry.setValue(value);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public int size()
+ {
+ return _entries.length;
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return _entries.length == 0;
+ }
+
+ @Override
+ public boolean containsKey(Object key)
+ {
+ return _getEntry(_entries, key) != null;
+ }
+
+ private static <K,V> ConcurrentEntry<K,V> _getEntry(ConcurrentEntry<K,V>[] entries, Object key)
+ {
+ if (key == null)
+ return null;
+
+ int entryIndex = _getEntryIndex(entries, key);
+
+ if (entryIndex >= 0)
+ {
+ return entries[entryIndex];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Handles the more complicated case where we matched the keyHashCode, but the key didn't match.
+ * This implies we have a hash collision and need to search the adjacent entries for a match
+ * @param entries
+ * @param key
+ * @param keyHashCode
+ * @param startIndex
+ * @return
+ */
+ private static <K,V> int _getHashCollsionMatchingEntryIndex(
+ ConcurrentEntry<K,V>[] entries, Object key, int keyHashCode, int startIndex)
+ {
+ int beforeIndex = startIndex - 1;
+
+ // search before the match
+ while (beforeIndex >= 0)
+ {
+ ConcurrentEntry<K,V> entry = entries[beforeIndex];
+
+ if (keyHashCode != entry.keyHashCode)
+ break;
+
+ if (key.equals(entry.getKey()))
+ return beforeIndex;
+
+ beforeIndex--;
+ }
+
+ // search after the match
+ int entryCount = entries.length;
+ int afterIndex = startIndex + 1;
+
+ while (afterIndex < entryCount)
+ {
+ ConcurrentEntry<K,V> entry = entries[afterIndex];
+
+ if (keyHashCode != entry.keyHashCode)
+ break;
+
+ if (key.equals(entry.getKey()))
+ return afterIndex;
+
+ afterIndex++;
+ }
+
+ // no match, but try ot optimize the index to be at the beginning or end of the array
+ int insertIndex;
+
+ if (beforeIndex == -1)
+ insertIndex = 0;
+ else
+ insertIndex = afterIndex;
+
+ // convert to two's complement
+ return -(insertIndex + 1);
+ }
+
+ /**
+ * Returns the positive index of the the entry, if the array contains an entry with the desired
+ * key. If no entry is found, the desired insertion location is returned as the two's
+ * complement of the desired location. For example, inserting before location 1, will be returned
+ * as -2.
+ * @param <K>
+ * @param <V>
+ * @param entries
+ * @param key non-null key to search for
+ * @return
+ */
+ private static <K,V> int _getEntryIndex(ConcurrentEntry<K,V>[] entries, Object key)
+ {
+ int keyHashCode = key.hashCode();
+
+ // find key using a binary search of the key hash codes
+ int lowIndex = 0;
+ int highIndex = entries.length - 1;
+
+ while (lowIndex <= highIndex)
+ {
+ int midIndex = (lowIndex + highIndex) >>> 1;
+
+ ConcurrentEntry<K,V> entry = entries[midIndex];
+
+ int midVal = entry.keyHashCode;
+
+ if (midVal < keyHashCode)
+ {
+ lowIndex = midIndex + 1;
+ }
+ else if (midVal > keyHashCode)
+ {
+ highIndex = midIndex - 1;
+ }
+ else
+ {
+ if (key.equals(entry.getKey()))
+ {
+ // found it
+ return midIndex;
+ }
+ else
+ {
+ // handle matching with hash collisions
+ return _getHashCollsionMatchingEntryIndex(entries, key, keyHashCode, midIndex);
+ }
+ }
+ }
+
+ // key not found, so returns two's complement of the index where we would insert
+ return -(lowIndex + 1);
+ }
+
+ private static <K,V> ConcurrentEntry<K,V>[] _removeEntryByIndex(
+ ConcurrentEntry<K,V>[] entries, int removeIndex)
+ {
+ int originalSize = entries.length;
+ int newSize = originalSize - 1;
+
+ @SuppressWarnings("unchecked")
+ ConcurrentEntry<K,V>[] newEntries = (ConcurrentEntry<K,V>[])
+ Array.newInstance(entries.getClass().getComponentType(), newSize);
+
+ if ((removeIndex == 0) || (removeIndex == newSize))
+ {
+ int srcStart = (removeIndex == 0) ? 1 : 0;
+
+ System.arraycopy(entries, srcStart, newEntries, 0, newSize);
+ }
+ else
+ {
+ // copy everything before the removeIndex
+ System.arraycopy(entries, 0, newEntries, 0, removeIndex);
+
+ // copy everything after the removeIndex, shifting down 1
+ System.arraycopy(entries, removeIndex + 1, newEntries, removeIndex, newSize - removeIndex);
+ }
+
+ return newEntries;
+ }
+
+ private static <K,V> ConcurrentEntry<K,V>[] _addEntryAtIndex(
+ ConcurrentEntry<K,V>[] entries, ConcurrentEntry<K,V> entry, int insertIndex, int removeIndex)
+ {
+ int originalSize = entries.length;
+ int newSize = originalSize;
+
+ // if we haven't hit the LRU limit, increment the size
+ if (removeIndex < 0)
+ newSize++;
+
+ @SuppressWarnings("unchecked")
+ ConcurrentEntry<K,V>[] newEntries = (ConcurrentEntry<K,V>[])
+ Array.newInstance(entry.getClass(), newSize);
+
+ if (removeIndex >= 0)
+ {
+ if (removeIndex == insertIndex)
+ {
+ // inserting into same spot we removed, so just copy array
+ System.arraycopy(entries, 0, newEntries, 0, originalSize);
+ }
+ else
+ {
+ if (removeIndex < insertIndex)
+ {
+ // copy everything before the removeIndex
+ System.arraycopy(entries, 0, newEntries, 0, removeIndex);
+
+ // copy everything between the removeIndex and the insertIndex, shifting things down
+ System.arraycopy(entries, removeIndex + 1, newEntries, removeIndex, insertIndex - removeIndex - 1);
+
+ // copy everything from the entry index to the end
+ if (insertIndex < originalSize)
+ {
+ System.arraycopy(entries, insertIndex, newEntries, insertIndex, originalSize - insertIndex);
+ }
+
+ // we removed the entry before the insertion location, so decrement to account for this
+ insertIndex--;
+ }
+ else
+ {
+ // copy everything before the insertIndex
+ System.arraycopy(entries, 0, newEntries, 0, insertIndex);
+
+ // copy everything between the insertIndex and the removeIndex, shifting things up
+ System.arraycopy(entries, insertIndex, newEntries, insertIndex + 1, removeIndex - insertIndex);
+
+ int afterRemoveIndex = removeIndex + 1;
+
+ // copy everthing after the removeIndex
+ if (afterRemoveIndex < originalSize)
+ {
+ System.arraycopy(entries, afterRemoveIndex, newEntries, afterRemoveIndex, originalSize - afterRemoveIndex);
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((insertIndex == 0) || (insertIndex == originalSize))
+ {
+ int destStart = (insertIndex == 0) ? 1 : 0;
+
+ System.arraycopy(entries, 0, newEntries, destStart, originalSize);
+ }
+ else
+ {
+ // copy everything before the insertIndex
+ System.arraycopy(entries, 0, newEntries, 0, insertIndex);
+
+ // copy everything after the insertIndex
+ System.arraycopy(entries, insertIndex, newEntries, insertIndex + 1, originalSize - insertIndex);
+ }
+ }
+
+ newEntries[insertIndex] = entry;
+
+ return newEntries;
+ }
+
+ @Override
+ public boolean containsValue(Object value)
+ {
+ return _containsValue(_entries, value);
+ }
+
+ private static boolean _containsValue(ConcurrentEntry[] entries, Object value)
+ {
+ int entryCount = entries.length;
+
+ if (value == null)
+ {
+ for (int i = 0; i < entryCount; i++)
+ {
+ // don't touch the values to avoid messing up the LRU
+ if ( entries[i].getValueWithoutTouching() == null)
+ {
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < entryCount; i++)
+ {
+ // don't touch the values to avoid messing up the LRU
+ if (value.equals(entries[i].getValueWithoutTouching()))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public V get(Object key)
+ {
+ ConcurrentEntry<K,V> entry = _getEntry(_entries, key);
+
+ if (entry != null)
+ {
+ return entry.getValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private static <K,V> ConcurrentEntry<K,V>[] _insertEntryAt(
+ ConcurrentEntry<K,V>[] entries, K key, V value, int insertIndex, EntryFactory<?,K,V> entryFactory)
+ {
+ ConcurrentEntry<K,V> entry = entryFactory.newEntry(key, value);
+
+ int removeIndex = entryFactory.getIndexOfEntryToPurge(entries);
+
+ return _addEntryAtIndex(entries, entry, insertIndex, removeIndex);
+ }
+
+ @Override
+ public V put(K key, V value)
+ {
+ V oldValue;
+
+ final ReentrantLock writeLock = this._writeLock;
+
+ writeLock.lock();
+
+ try
+ {
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ int entryIndex = _getEntryIndex(entries, key);
+
+ if (entryIndex >= 0)
+ {
+ ConcurrentEntry<K,V> entry = entries[entryIndex];
+
+ oldValue = entry.setValue(value);
+ }
+ else
+ {
+ // the insert locations are returned as two's complement
+ int insertIndex = -(entryIndex + 1);
+
+ _entries = _insertEntryAt(entries, key, value, insertIndex, _entryFactory);
+
+ oldValue = null;
+ }
+ }
+ finally
+ {
+ writeLock.unlock();
+ }
+
+ return oldValue;
+ }
+
+ @Override
+ public V remove(Object key)
+ {
+ V oldValue;
+
+ final ReentrantLock writeLock = this._writeLock;
+
+ writeLock.lock();
+
+ try
+ {
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ int removeIndex = _getEntryIndex(entries, key);
+
+ if (removeIndex >= 0)
+ {
+ ConcurrentEntry<K,V> entry = entries[removeIndex];
+
+ oldValue = entry.getValue();
+
+ _entries = _removeEntryByIndex(entries, removeIndex);
+ }
+ else
+ {
+ oldValue = null;
+ }
+ }
+ finally
+ {
+ writeLock.unlock();
+ }
+
+ return oldValue;
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m)
+ {
+ final ReentrantLock writeLock = this._writeLock;
+
+ writeLock.lock();
+
+ try
+ {
+ for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
+ {
+ put(e.getKey(), e.getValue());
+ }
+ }
+ finally
+ {
+ writeLock.unlock();
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void clear()
+ {
+ final ReentrantLock writeLock = this._writeLock;
+
+ writeLock.lock();
+
+ try
+ {
+ _entries = _entryFactory.getEmptyEntries();
+ }
+ finally
+ {
+ writeLock.unlock();
+ }
+ }
+
+ private final class KeySet extends AbstractSet<K>
+ {
+ @Override
+ public Iterator<K> iterator()
+ {
+ return new Iterator<K>()
+ {
+ private final Iterator<Entry<K,V>> _i = entrySet().iterator();
+
+ @Override
+ public boolean hasNext()
+ {
+ return _i.hasNext();
+ }
+
+ @Override
+ public K next()
+ {
+ return _i.next().getKey();
+ }
+
+ @Override
+ public void remove()
+ {
+ _i.remove();
+ }
+ };
+ }
+
+ @Override
+ public int size()
+ {
+ return CopyOnWriteArrayMap.this.size();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return CopyOnWriteArrayMap.this.isEmpty();
+ }
+
+ @Override
+ public void clear()
+ {
+ CopyOnWriteArrayMap.this.clear();
+ }
+
+ @Override
+ public boolean contains(Object k)
+ {
+ return CopyOnWriteArrayMap.this.containsKey(k);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c)
+ {
+ return CopyOnWriteArrayMap.this.__removeOrRetainAll(this, c, false);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c)
+ {
+ return CopyOnWriteArrayMap.this.__removeOrRetainAll(this, c, true);
+ }
+ }
+
+ @Override
+ public Set<K> keySet()
+ {
+ return new KeySet();
+ }
+
+ private final class ValueCollection extends AbstractCollection<V>
+ {
+ public Iterator<V> iterator()
+ {
+ return new Iterator<V>()
+ {
+ private final Iterator<Entry<K,V>> _i = entrySet().iterator();
+
+ @Override
+ public boolean hasNext()
+ {
+ return _i.hasNext();
+ }
+
+ @Override
+ public V next()
+ {
+ ConcurrentEntry<K,V> entry = (ConcurrentEntry<K,V>)_i.next();
+
+ // don't touch the values when iterating
+ return entry.getValueWithoutTouching();
+ }
+
+ @Override
+ public void remove()
+ {
+ _i.remove();
+ }
+ };
+ }
+
+ @Override
+ public int size()
+ {
+ return CopyOnWriteArrayMap.this.size();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return CopyOnWriteArrayMap.this.isEmpty();
+ }
+
+ @Override
+ public void clear()
+ {
+ CopyOnWriteArrayMap.this.clear();
+ }
+
+ @Override
+ public boolean contains(Object v)
+ {
+ return CopyOnWriteArrayMap.this.containsValue(v);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c)
+ {
+ return CopyOnWriteArrayMap.this.__removeOrRetainAll(this, c, false);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c)
+ {
+ return CopyOnWriteArrayMap.this.__removeOrRetainAll(this, c, true);
+ }
+ }
+
+ @Override
+ public Collection<V> values()
+ {
+ return new ValueCollection();
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet()
+ {
+ return new EntrySet();
+ }
+
+ /**
+ * Either remove or retain all of the entries in the Collection c that are in ourIterable,
+ * grabbing a writeLock on the CopyOnWriteArrayMap before performing the operation
+ * @param <E>
+ * @param ourIterable
+ * @param c
+ * @param retain
+ * @return
+ */
+ boolean __removeOrRetainAll(Iterable<?> ourIterable, Collection<?> c, boolean retain)
+ {
+ boolean modified = false;
+
+ _writeLock.lock();
+
+ try
+ {
+ Iterator<?> it = ourIterable.iterator();
+
+ while (it.hasNext())
+ {
+ Object entry = it.next();
+
+ if (retain ^ c.contains(entry))
+ {
+ it.remove();
+ modified = true;
+ }
+ }
+ }
+ finally
+ {
+ _writeLock.unlock();
+ }
+
+ return modified;
+ }
+
+ private final class EntryIterator implements Iterator<Entry<K,V>>
+ {
+ EntryIterator()
+ {
+ _entries = CopyOnWriteArrayMap.this._entries;
+ _cursorIndex = 0;
+ _lastReturnedIndex = -1;
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return _cursorIndex < _entries.length;
+ }
+
+ @Override
+ public Entry<K, V> next()
+ {
+ try
+ {
+ Entry<K,V> entry = _entries[_cursorIndex];
+ _lastReturnedIndex = _cursorIndex;
+ _cursorIndex++;
+
+ return entry;
+ }
+ catch (IndexOutOfBoundsException ioobe)
+ {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove()
+ {
+ // can't remove an entry we haven't visited or one that we already removed
+ if (_lastReturnedIndex < 0)
+ throw new IllegalStateException();
+
+ final ReentrantLock writeLock = CopyOnWriteArrayMap.this._writeLock;
+
+ writeLock.lock();
+
+ try
+ {
+ ConcurrentEntry<K,V>[] ourEntries = _entries;
+ ConcurrentEntry<K,V>[] baseEntries = CopyOnWriteArrayMap.this._entries;
+
+ // somebody has alread messed with the map since we were created
+ if (ourEntries != baseEntries)
+ throw new ConcurrentModificationException();
+
+ _entries = _removeEntryByIndex(ourEntries, _lastReturnedIndex);
+ CopyOnWriteArrayMap.this._entries = _entries;
+ }
+ finally
+ {
+ writeLock.unlock();
+ }
+
+ _cursorIndex--;
+
+ // don't allow double removals
+ _lastReturnedIndex = -1;
+ }
+
+ private ConcurrentEntry<K,V>[] _entries;
+ private int _cursorIndex;
+ private int _lastReturnedIndex;
+ }
+
+
+ private final class EntrySet extends AbstractSet<Entry<K,V>>
+ {
+ @Override
+ public Iterator<Entry<K,V>> iterator()
+ {
+ return new EntryIterator();
+ }
+
+ @Override
+ public boolean contains(Object o)
+ {
+ if (!(o instanceof Entry))
+ return false;
+
+ @SuppressWarnings("unchecked")
+ Entry<K,V> theirEntry = (Entry<K,V>)o;
+
+ ConcurrentEntry<K, V> ourEntry = _getEntry(_entries, theirEntry.getKey());
+
+ if (ourEntry != null)
+ {
+ // entries are equal if their keys and values are equal
+ Object ourValue = ourEntry.getValue();
+ Object theirValue = theirEntry.getValue();
+
+ return (ourValue != null) ? ourValue.equals(theirValue) : (theirValue == null);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o)
+ {
+ if (!(o instanceof Entry))
+ return false;
+
+ @SuppressWarnings("unchecked")
+ Entry<K,V> removeEntry = (Entry<K,V>)o;
+
+ return CopyOnWriteArrayMap.this.remove(removeEntry.getKey(), removeEntry.getValue());
+ }
+
+ @Override
+ public int size()
+ {
+ return CopyOnWriteArrayMap.this.size();
+ }
+
+ @Override
+ public void clear()
+ {
+ CopyOnWriteArrayMap.this.clear();
+ }
+
+ @Override
+ public Object[] toArray()
+ {
+ // override for efficiency
+ ConcurrentEntry<K,V>[] entries = CopyOnWriteArrayMap.this._entries;
+ return Arrays.copyOf(entries, entries.length);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T[] toArray(T[] a)
+ {
+ // override for efficiency
+ ConcurrentEntry<K,V>[] entries = CopyOnWriteArrayMap.this._entries;
+ int entryCount = entries.length;
+
+ if (a.length < entryCount)
+ {
+ // destination array is too small, so return a new array of the correct type
+ return Arrays.copyOf(entries, entryCount, (Class<? extends T[]>) a.getClass());
+ }
+ else
+ {
+ System.arraycopy(entries, 0, a, 0, entryCount);
+
+ // add end marker, if the destination array is too big
+ if (a.length > entryCount)
+ a[entryCount] = null;
+
+ return a;
+ }
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c)
+ {
+ return CopyOnWriteArrayMap.this.__removeOrRetainAll(this, c, false);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c)
+ {
+ return CopyOnWriteArrayMap.this.__removeOrRetainAll(this, c, true);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ return true;
+
+ if (!(o instanceof Map))
+ return false;
+
+ Map<?,?> otherMap = (Map<?,?>)o;
+
+ ConcurrentEntry<K,V>[] entries = _entries;
+ int entryCount = entries.length;
+
+ if (entryCount != otherMap.size())
+ return false;
+
+ for (int i = 0; i < entryCount; i++)
+ {
+ ConcurrentEntry<K,V> entry = entries[i];
+
+ K entryKey = entry.getKey();
+ V entryValue = entry.getValue();
+
+ if (entryValue != null)
+ {
+ Object otherValue = otherMap.get(entryKey);
+
+ if (!entryValue.equals(otherValue))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ Object otherValue = otherMap.get(entryKey);
+
+ // use containsKey to handle case where the otherValue was null because the
+ // otherMap didn't contain an entry for this key
+ if ((otherValue != null) || !otherMap.containsKey(entryKey))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 0;
+
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ for (Entry<K,V> entry : entries)
+ {
+ hash += entry.hashCode();
+ }
+
+ return hash;
+ }
+
+ @Override
+ public String toString()
+ {
+ ConcurrentEntry<K,V>[] entries = _entries;
+
+ // optimize the empty case
+ if (entries.length == 0)
+ return "{}";
+
+ StringBuilder sb = new StringBuilder();
+
+ sb.append('{');
+
+ boolean isFirstEntry = true;
+
+ for (Entry<K,V> entry : entries)
+ {
+ if (isFirstEntry)
+ {
+ isFirstEntry = false;
+ }
+ else
+ {
+ sb.append(", ");
+ }
+
+ K key = entry.getKey();
+ V value = entry.getValue();
+
+ sb.append(key == this ? "(this Map)" : key);
+ sb.append('=');
+ sb.append(value == this ? "(this Map)" : value);
+ }
+
+ sb.append('}');
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns a shallow copy of this <tt>CopyOnWriteArrayMap</tt> instance with its own lock
+ * and entry objects: the keys and values themselves are not cloned.
+ *
+ * @return a shallow copy of this map
+ */
+ @Override
+ public CopyOnWriteArrayMap<K,V> clone()
+ {
+ ConcurrentEntry<K,V>[] entries = _entryFactory.cloneEntries(_entries);
+
+ // use a copy constructor since the writeLock is final, but the clone needs a new one
+ return new CopyOnWriteArrayMap<K,V>(_entryFactory, new ReentrantLock(), entries);
+ }
+
+ private void readObject(@SuppressWarnings("unused") ObjectInputStream inStream) throws InvalidObjectException
+ {
+ throw new InvalidObjectException("Proxy Required");
+ }
+
+ private Object writeReplace()
+ {
+ return _entryFactory.newSerializationProxy(_entries);
+ }
+
+ protected abstract static class SerializationProxy<E extends ConcurrentEntry<K,V>,K,V> implements Serializable
+ {
+ @SuppressWarnings("compatibility:-8476139976280416592")
+ private static final long serialVersionUID = 1L;
+
+ protected SerializationProxy(ConcurrentEntry<K,V>[] entries)
+ {
+ _keyValues = _createKeyValues(entries);
+ }
+
+ private Object readResolve()
+ {
+ ReentrantLock writeLock = new ReentrantLock();
+
+ EntryFactory<E,K,V> entryFactory = instantiateEntryFactory();
+ ConcurrentEntry<K,V>[] entries = instantiateEntries(_keyValues, entryFactory);
+
+ return new CopyOnWriteArrayMap<K,V>(entryFactory, writeLock, entries);
+ }
+
+ protected abstract E[] instantiateEntries(Object[] keyValues, EntryFactory<E,K,V> entryFactory);
+
+ protected abstract EntryFactory<E,K,V> instantiateEntryFactory();
+
+ private static <K,V> Serializable[] _createKeyValues(ConcurrentEntry<K,V>[] entries)
+ {
+ int entryCount = entries.length;
+
+ Serializable[] keyValues = new Serializable[entryCount *2];
+
+ for (int entryIndex = 0, keyValueIndex = 0; entryIndex < entryCount; entryIndex++)
+ {
+ ConcurrentEntry<K,V> entry = entries[entryIndex];
+ keyValues[keyValueIndex++] = (Serializable)entry.getKey();
+ keyValues[keyValueIndex++] = (Serializable)entry.getValue();
+ }
+
+ return keyValues;
+ }
+
+ private final Serializable[] _keyValues;
+ }
+
+ /**
+ * Manages the Entry-specific behavior between the ConcurrentEntries and the LRUEntries
+ */
+ private abstract static class EntryFactory<E extends ConcurrentEntry<K,V>, K, V>
+ {
+ /** Creates a new ConcurrentEntry with the specified key and value */
+ public abstract E newEntry(K key, V value);
+
+ /** Returns the index of an entry to remove as a result of adding another entry. If
+ * a value < 0 is returned, no entry will be removed
+ * */
+ public abstract int getIndexOfEntryToPurge(E[] entries);
+
+ /** Returns a empty array of the correct type for this EntryFactory */
+ public abstract E[] getEmptyEntries();
+
+ /** Returns the Serialization proxy to use to serialize the CopyOnWriteArrayMap */
+ public abstract SerializationProxy<E,K,V> newSerializationProxy(E[] entries);
+
+ /**
+ * @param entries
+ * @return a deep copy of the ConcurrentEntry[]. The keys and values themselves are not cloned
+ */
+ public final E[] cloneEntries(E[] entries)
+ {
+ E[] clonedEntries = entries.clone();
+
+ int entryCount = entries.length;
+
+ for (int i = 0; i < entryCount; i++)
+ {
+ E originalEntry = clonedEntries[i];
+ E clonedEntry = newEntry(originalEntry.getKey(), originalEntry.getValue());
+
+ clonedEntries[i] = clonedEntry;
+ }
+
+ return clonedEntries;
+ }
+ }
+
+ private final static class ConcurrentEntryFactory<K,V> extends EntryFactory<ConcurrentEntry<K,V>,K,V>
+ {
+ @SuppressWarnings("unchecked")
+ public static <K,V> ConcurrentEntryFactory<K,V> getInstance()
+ {
+ return (ConcurrentEntryFactory<K,V>)_INSTANCE;
+ }
+
+ @Override
+ public ConcurrentEntry<K,V> newEntry(K key, V value)
+ {
+ return new ConcurrentEntry<K,V>(key, value);
+ }
+
+ @Override
+ public int getIndexOfEntryToPurge(ConcurrentEntry<K,V>[] entries)
+ {
+ // we never purge entries
+ return -1;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ConcurrentEntry<K,V>[] getEmptyEntries()
+ {
+ return (ConcurrentEntry<K,V>[])_EMPTY_ENTRIES;
+ }
+
+ @Override
+ public SerializationProxy<ConcurrentEntry<K,V>,K,V> newSerializationProxy(ConcurrentEntry<K,V>[] entries)
+ {
+ return new ConcurrentSerializationProxy<K,V>(entries);
+ }
+
+ protected static final class ConcurrentSerializationProxy<K,V> extends SerializationProxy<ConcurrentEntry<K,V>, K,V>
+ {
+ @SuppressWarnings("compatibility:2346067066761682441")
+ private static final long serialVersionUID = 1L;
+
+ protected ConcurrentSerializationProxy(ConcurrentEntry<K,V>[] entries)
+ {
+ super(entries);
+ }
+
+ @Override
+ public EntryFactory<ConcurrentEntry<K,V>,K,V> instantiateEntryFactory()
+ {
+ return getInstance();
+ }
+
+ @Override
+ @SuppressWarnings({"cast", "unchecked"})
+ protected ConcurrentEntry<K,V>[] instantiateEntries(
+ Object[] keyValues, EntryFactory<ConcurrentEntry<K,V>,K,V> entryFactory)
+ {
+ int entryCount = keyValues.length / 2;
+
+ ConcurrentEntry<K,V>[] entries = (ConcurrentEntry<K,V>[])new ConcurrentEntry[entryCount];
+
+ for (int entryIndex = 0, keyValueIndex = 0; entryIndex < entryCount; entryIndex++)
+ {
+ K key = (K)keyValues[keyValueIndex++];
+ V value = (V)keyValues[keyValueIndex++];
+
+ ConcurrentEntry<K,V> entry = new ConcurrentEntry<K,V>(key, value);
+ entries[entryIndex] = entry;
+ }
+
+ return entries;
+ }
+ }
+
+ private static final EntryFactory<?,?,?> _INSTANCE = new ConcurrentEntryFactory();
+ private static final ConcurrentEntry[] _EMPTY_ENTRIES = new ConcurrentEntry[0];
+ }
+
+ private final static class LRUEntryFactory<K,V> extends EntryFactory<LRUEntry<K,V>, K, V>
+ {
+ public LRUEntryFactory(int maxEntries, long baseNanos)
+ {
+ _maxEntries = maxEntries;
+ _baseNanos = baseNanos;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public LRUEntry<K,V> newEntry(K key, V value)
+ {
+ return new LRUEntry<K,V>(key, value, this);
+ }
+
+ @Override
+ public int getIndexOfEntryToPurge(LRUEntry<K,V>[] entries)
+ {
+ if (_maxEntries <= entries.length)
+ {
+ // purge the oldest entry to make room for the new one
+ return _getOldestAccessesedEntryIndex(entries);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ /**
+ * @param entries
+ * @return The index of the least recently accessed entry
+ */
+ private static <K,V> int _getOldestAccessesedEntryIndex(LRUEntry<K,V>[] entries)
+ {
+ int entryCount = entries.length;
+ int oldestIndex = -1;
+ long oldestAccessedNanos = Long.MAX_VALUE;
+
+ for (int i = 0; i < entryCount; i++)
+ {
+ LRUEntry<K,V> entry = entries[i];
+ long currAccessedNanos = entry.getLastAccessed();
+
+ if (currAccessedNanos <= oldestAccessedNanos)
+ {
+ oldestIndex = i;
+ oldestAccessedNanos = currAccessedNanos;
+ }
+ }
+
+ return oldestIndex;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public LRUEntry<K,V>[] getEmptyEntries()
+ {
+ return (LRUEntry<K,V>[])_EMPTY_LRU_ENTRIES;
+ }
+
+ @Override
+ public SerializationProxy<LRUEntry<K,V>,K,V> newSerializationProxy(LRUEntry<K,V>[] entries)
+ {
+ return new LRUSerializationProxy<K,V>(_maxEntries, _baseNanos, entries);
+ }
+
+ /**
+ * @return delta in nano seconds from when the LRUConcurrentArrayMap was created. If we exceed
+ * the magnitude of a long, return Long.MAX_VALUE. In practice, this isn't a problem as
+ * it means that more than 292 years have elapsed.
+ */
+ public long nanosSinceCreated()
+ {
+ long nanos = System.nanoTime();
+ long delta = nanos - _baseNanos;
+
+ if (delta >= 0)
+ {
+ return delta;
+ }
+ else
+ {
+ // we have exceeded the magnitude of a long because the nano value wrapped around
+ return Long.MAX_VALUE;
+ }
+ }
+
+ protected static final class LRUSerializationProxy<K,V> extends SerializationProxy<LRUEntry<K,V>,K,V>
+ {
+ @SuppressWarnings("compatibility:-4809944737577473688")
+ private static final long serialVersionUID = 1L;
+
+ protected LRUSerializationProxy(int maxEntries, long baseNanos, ConcurrentEntry<K,V>[] entries)
+ {
+ super(entries);
+
+ _maxEntries = maxEntries;
+ _baseNanos = baseNanos;
+ }
+
+ @Override
+ public EntryFactory<LRUEntry<K,V>, K, V> instantiateEntryFactory()
+ {
+ return new LRUEntryFactory<K,V>(_maxEntries, _baseNanos);
+ }
+
+ @Override
+ @SuppressWarnings({"cast", "unchecked"})
+ protected LRUEntry<K,V>[] instantiateEntries(
+ Object[] keyValues, EntryFactory<LRUEntry<K,V>,K,V> entryFactory)
+
+ {
+ int entryCount = keyValues.length / 2;
+
+ LRUEntry<K,V>[] entries = (LRUEntry<K,V>[])new LRUEntry[entryCount];
+
+ for (int entryIndex = 0, keyValueIndex = 0; entryIndex < entryCount; entryIndex++)
+ {
+ K key = (K)keyValues[keyValueIndex++];
+ V value = (V)keyValues[keyValueIndex++];
+
+ LRUEntry<K,V> entry = new LRUEntry<K,V>(key, value, (LRUEntryFactory<K,V>)entryFactory);
+ entries[entryIndex] = entry;
+ }
+
+ return entries;
+ }
+
+ private final int _maxEntries;
+ private final long _baseNanos;
+ }
+
+ private static final ConcurrentEntry[] _EMPTY_LRU_ENTRIES = new LRUEntry[0];
+
+ private final int _maxEntries;
+ private final long _baseNanos;
+ }
+
+ protected static class ConcurrentEntry<K,V> implements Entry<K,V>
+ {
+ protected ConcurrentEntry(K key, V value)
+ {
+ Args.notNull(key, "key");
+
+ _key = key;
+ _value = value;
+ keyHashCode = key.hashCode();
+ }
+
+ @Override
+ public final K getKey()
+ {
+ return _key;
+ }
+
+ /** Returns the value without counting as accessing the entry */
+ public V getValueWithoutTouching()
+ {
+ return _value;
+ }
+
+ @Override
+ public V getValue()
+ {
+ return _value;
+ }
+
+ /**
+ * Version of setValue with compareAndSet semantics
+ * @param expected
+ * @param newValue
+ * @return
+ */
+ public boolean compareAndSetValue(V expected, V newValue)
+ {
+ return _VALUE_UPDATER.compareAndSet(this, expected, newValue);
+ }
+
+ @Override
+ @SuppressWarnings({"cast", "unchecked"})
+ public V setValue(V newValue)
+ {
+ return (V)_VALUE_UPDATER.getAndSet(this, newValue);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (o == this)
+ return true;
+
+ if (o instanceof Entry)
+ {
+ Entry otherEntry = (Entry)o;
+
+ if (getKey().equals(otherEntry.getKey()))
+ {
+ Object otherValue = otherEntry.getValue();
+ V value = _value;
+
+ return (value != null) ? value.equals(otherValue) : otherValue == null;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ V value = _value;
+ int valueHashCode = (value != null) ? value.hashCode() : 0;
+
+ return keyHashCode ^ valueHashCode;
+ }
+
+ @Override
+ public String toString()
+ {
+ return getKey() + "=" + _value;
+ }
+
+ private volatile V _value;
+ private final K _key;
+ public final int keyHashCode;
+
+ // Apply AtomicReference love to the _value field
+ private static final AtomicReferenceFieldUpdater<ConcurrentEntry, Object> _VALUE_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(ConcurrentEntry.class, Object.class, "_value");
+ }
+
+ private static final class LRUEntry<K,V> extends ConcurrentEntry<K,V>
+ {
+ public LRUEntry(K key, V value, LRUEntryFactory<K,V> nanoCalculator)
+ {
+ super(key, value);
+
+ _nanoCalculator = nanoCalculator;
+ _lastAccessed = _nanoCalculator.nanosSinceCreated();
+ }
+
+ public long getLastAccessed()
+ {
+ return _lastAccessed;
+ }
+
+ @Override
+ public V getValue()
+ {
+ // we don't especially care that we don't update the last accessed time atomically with
+ // updating the value
+ _lastAccessed = _nanoCalculator.nanosSinceCreated();
+
+ return super.getValue();
+ }
+
+ @Override
+ public V setValue(V newValue)
+ {
+ // we don't especially care that we don't update the last accessed time atomically with
+ // updating the value
+ _lastAccessed = _nanoCalculator.nanosSinceCreated();
+
+ return super.setValue(newValue);
+ }
+
+ private final LRUEntryFactory<K,V> _nanoCalculator;
+ private volatile long _lastAccessed;
+ }
+
+ @SuppressWarnings("compatibility:4274080938865508278")
+ private static final long serialVersionUID = 1;
+
+ private transient final EntryFactory<?,K,V> _entryFactory;
+
+ // lock protecting mutators
+ private transient final ReentrantLock _writeLock;
+
+ private volatile transient ConcurrentEntry<K,V>[] _entries;
+}
\ No newline at end of file
Modified: myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/RenderKitTestCase.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/RenderKitTestCase.java?rev=1632265&r1=1632264&r2=1632265&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/RenderKitTestCase.java (original)
+++ myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/renderkit/RenderKitTestCase.java Thu Oct 16 10:16:54 2014
@@ -228,14 +228,14 @@ abstract public class RenderKitTestCase
public BaseTest(String name,
String categoryName,
- String skin,
+ String skinFamily,
Agent agent,
RequestContext.Accessibility accMode,
boolean rightToLeft,
String outputMode)
{
super(name + "-" + categoryName);
- _skin = skin;
+ _skinFamily = skinFamily;
_agent = agent;
_accMode = accMode;
_rightToLeft = rightToLeft;
@@ -274,7 +274,7 @@ abstract public class RenderKitTestCase
{
_facesContext = createMockFacesContext(MApplication.sharedInstance(), true);
_requestContext = createRequestContext();
- _requestContext.setSkinFamily(_skin);
+ _requestContext.setSkinFamily(_skinFamily);
_requestContext.setAgent(_agent);
_requestContext.setRightToLeft(_rightToLeft);
_requestContext.setOutputMode(_outputMode);
@@ -397,14 +397,13 @@ abstract public class RenderKitTestCase
private TestResult _result;
private MFacesContext _facesContext;
private MRequestContext _requestContext;
- private String _skin;
+ private String _skinFamily;
private String _outputMode;
private Agent _agent;
private RequestContext.Accessibility _accMode;
private boolean _rightToLeft;
}
-
public class RendererTest extends BaseTest
{
public RendererTest(String name,
@@ -417,7 +416,6 @@ abstract public class RenderKitTestCase
_script = TestScriptParser.getTestScript(scriptFile, _facesConfigInfo);
_lenient = lenient;
-
// We run golden-file checks on each subtest - though all differences
// get counted only as a single diff. We also do a comparison
@@ -475,12 +473,12 @@ abstract public class RenderKitTestCase
renderRoot(root);
String golden = _loadGoldenFile();
-
+
String baseResults = base.toString();
-
+
_processResults(golden, baseResults);
}
-
+
private String _loadGoldenFile() throws IOException
{
File goldenFile = new File(_goldenDir, getName() + "-golden.xml");
@@ -501,7 +499,7 @@ abstract public class RenderKitTestCase
golden = buffer.toString();
in.close();
}
-
+
return golden;
}
@@ -510,19 +508,19 @@ abstract public class RenderKitTestCase
Iterator<TestScript.Test> tests = _script.getTests().iterator();
Accessibility accMode = getAccMode();
Agent agent = getAgent();
-
+
while (tests.hasNext())
{
TestScript.Test test = tests.next();
boolean supportsAccessibilityMode = test.supportsAccessibilityMode(accMode);
- boolean supportsAgent = test.supportsAgent(agent);
- boolean supportsLocale = test.supportsLocale(RequestContext.getCurrentInstance());
-
+ boolean supportsAgent = test.supportsAgent(agent);
+ boolean supportsLocale = test.supportsLocale(RequestContext.getCurrentInstance());
+
if (supportsAccessibilityMode && supportsAgent && supportsLocale)
{
UIComponent testComponent = _createComponent();
-
+
test.apply(getFacesContext(), testComponent);
docRoot.getChildren().add(new GatherContent(test.getOutput(),
testComponent,
@@ -530,7 +528,7 @@ abstract public class RenderKitTestCase
this,
_lenient));
}
- }
+ }
}
private void _processTest(TestScript.Test test, Writer out, String baseResults) throws IOException
@@ -539,8 +537,8 @@ abstract public class RenderKitTestCase
Agent agent = getAgent();
boolean supportsAccessibilityMode = test.supportsAccessibilityMode(accMode);
- boolean supportsAgent = test.supportsAgent(agent);
- boolean supportsLocale = test.supportsLocale(RequestContext.getCurrentInstance());
+ boolean supportsAgent = test.supportsAgent(agent);
+ boolean supportsLocale = test.supportsLocale(RequestContext.getCurrentInstance());
if (supportsAccessibilityMode && supportsAgent && supportsLocale)
{
@@ -548,12 +546,12 @@ abstract public class RenderKitTestCase
out.write("\n<!--");
out.write(test.toString());
out.write("-->\n");
-
+
// surround content in a CDATA block to make it more likely to be valid XML
//out.write("<![CDATA[\n");
-
+
String testResults;
-
+
try
{
testResults = test.getOutput().toString();
@@ -563,23 +561,22 @@ abstract public class RenderKitTestCase
{
//out.write("\n]]>");
}
-
+
if (!_lenient)
{
if (!test.shouldMatchBase() &&
baseResults.equals(testResults))
{
AssertionFailedError failure = new AssertionFailedError(
- "In " + getName() + ", result of " + test.toString() + " were identical to " +
- "base, but should not have been!");
+ "In " + getName() + ", result of " + test.toString() + " were identical to " +
+ "base, but should not have been!");
getResult().addError(this, failure);
- }
- else if (test.shouldMatchBase() &&
+ } else if (test.shouldMatchBase() &&
!baseResults.equals(testResults))
{
AssertionFailedError failure = new AssertionFailedError(
- "Result of " + test.toString() + " were not identical to " +
- "base, but should have been!");
+ "Result of " + test.toString() + " were not identical to " +
+ "base, but should have been!");
getResult().addError(this, failure);
}
}
@@ -613,17 +610,17 @@ abstract public class RenderKitTestCase
while (tests.hasNext())
{
TestScript.Test test = tests.next();
-
+
_processTest(test, out, baseResults);
}
-
- out.write("\n</results>\n");
+
+ out.write("\n</results>\n");
}
finally
{
- out.close();
+ out.close();
}
-
+
return out.toString();
}
@@ -636,7 +633,7 @@ abstract public class RenderKitTestCase
failureFile = new File(_goldenDir, getName() + "-golden.xml");
else
failureFile = new File(_failureDir, getName() + "-golden.xml");
-
+
failureFile.getParentFile().mkdirs();
FileWriter failureOut = new FileWriter(failureFile);
failureOut.write(results);
@@ -654,8 +651,7 @@ abstract public class RenderKitTestCase
throw new AssertionFailedError("No golden file for test " +
_scriptName);
}
- }
- else
+ } else
{
int index = StringUtils.indexOfDifference(golden, results);
String difference = StringUtils.difference(golden, results);
@@ -691,26 +687,26 @@ abstract public class RenderKitTestCase
}
*/
throw new AssertionFailedError(
- "Golden file for test "+ _scriptName + " did not match; " +
- "first difference at " + index + ", difference of length " +
- diffLength + ", \"" + difference + "\"");
- }
+ "Golden file for test " + _scriptName + " did not match; " +
+ "first difference at " + index + ", difference of length " +
+ diffLength + ", \"" + difference + "\"");
+ }
}
-
+
private void _processResults(String golden, String baseResults) throws IOException
{
String results = _processTests(golden, baseResults);
-
+
if ((golden == null) || !golden.equals(results))
{
boolean forceGolden = "true".equals(
- System.getProperty("org.apache.myfaces.trinidad.ForceGolden"));
-
+ System.getProperty("org.apache.myfaces.trinidad.ForceGolden"));
+
_writeFailureGoldenFile(results, forceGolden);
-
+
_throwAssertionFailure(golden, results, forceGolden);
}
- }
+ }
private UIComponent _createComponent()
{
@@ -723,7 +719,6 @@ abstract public class RenderKitTestCase
private boolean _lenient;
}
-
static private void _initGlobal() throws IOException, SAXException
{
RenderKitBootstrap bootstrap = new RenderKitBootstrap();
@@ -732,7 +727,7 @@ abstract public class RenderKitTestCase
_facesConfigInfo = bootstrap.getFacesConfigInfo();
String projectPath = System.getProperty("user.dir");
-
+
String scripts = System.getProperty("trinidad.renderkit.scripts", projectPath + "/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/testScripts/");
String golden = System.getProperty("trinidad.renderkit.golden", projectPath + "/src/test/resources/org/apache/myfaces/trinidadinternal/renderkit/golden/");
String failures = System.getProperty("trinidad.renderkit.failures", projectPath + "/target/test-failures/");
@@ -835,24 +830,24 @@ abstract public class RenderKitTestCase
{
public SuiteDefinition(
String category,
- String skin,
+ String skinFamily,
RequestContext.Accessibility accessibilityMode,
Agent agent,
boolean rightToLeft)
{
- this(category, skin, accessibilityMode, agent, rightToLeft, "default");
+ this(category, skinFamily, accessibilityMode, agent, rightToLeft, "default");
}
public SuiteDefinition(
String category,
- String skin,
+ String skinFamily,
RequestContext.Accessibility accessibilityMode,
Agent agent,
boolean rightToLeft,
String outputMode)
{
_category = Args.notNull(category, "category");
- _skin = Args.notNull(skin, "skin");
+ _skinFamily = Args.notNull(skinFamily, "skinFamily");
_accessibilityMode = Args.notNull(accessibilityMode, "accessibilityMode");
_agent = Args.notNull(agent, "agent");
_rightToLeft = Args.notNull(rightToLeft, "rightToLeft");
@@ -864,10 +859,7 @@ abstract public class RenderKitTestCase
return _category;
}
- public String getSkin()
- {
- return _skin;
- }
+ public String getSkin() { return _skinFamily; }
public String getOutputMode()
{
@@ -891,7 +883,7 @@ abstract public class RenderKitTestCase
}
private String _category;
- private String _skin;
+ private String _skinFamily;
private String _outputMode;
private RequestContext.Accessibility _accessibilityMode;
private Agent _agent;
@@ -899,18 +891,17 @@ abstract public class RenderKitTestCase
}
static private ExtendedRenderKitService _getExtendedRenderKitService(
- FacesContext context)
+ FacesContext context)
{
return Service.getService(context.getRenderKit(),
ExtendedRenderKitService.class);
}
-
static private FacesConfigInfo _facesConfigInfo;
static private File _scriptDir;
static private File _goldenDir;
static private File _failureDir;
-
+
static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(RenderKitTestCase.class);
}
Added: myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider1.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider1.java?rev=1632265&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider1.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider1.java Thu Oct 16 10:16:54 2014
@@ -0,0 +1,104 @@
+/*
+ * 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.myfaces.trinidadinternal.skin.custom;
+
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidad.skin.SkinFactory;
+import org.apache.myfaces.trinidad.skin.SkinMetadata;
+import org.apache.myfaces.trinidad.skin.SkinProvider;
+import org.apache.myfaces.trinidad.skin.SkinVersion;
+
+import javax.faces.context.ExternalContext;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class CustomSkinProvider1 extends SkinProvider
+{
+ @Override
+ public Skin getSkin(ExternalContext context, SkinMetadata skinMetadata)
+ {
+ String skinId = skinMetadata.getId();
+ String family = skinMetadata.getFamily();
+ if ("purple.desktop".equals(skinId) || "purple".equals(family))
+ {
+ if (_purpleSkin == null)
+ _purpleSkin = SkinFactory.getFactory().createSkin(context, PURPLE);
+ return _purpleSkin;
+ }
+ if ("blue.desktop".equals(skinId) || "blue".equals(family))
+ {
+ if (_blueSkin == null)
+ _blueSkin = SkinFactory.getFactory().createSkin(context, BLUE);
+ return _blueSkin;
+ }
+ if ("cyan.desktop".equals(skinId) || "cyan".equals(family))
+ {
+ if (_cyanSkin == null)
+ _cyanSkin = SkinFactory.getFactory().createSkin(context, NO_CACHE_CYAN);
+ return _cyanSkin;
+ }
+ return null;
+ }
+
+ @Override
+ public Collection<SkinMetadata> getSkinMetadata(ExternalContext context)
+ {
+ return Collections.unmodifiableCollection(_METADATA);
+ }
+
+ public static final String PURPLE_SKIN_ID = "purple.desktop";
+ public static final String NO_CACHE_CYAN_SKIN_ID = "cyan.desktop";
+ public static final String BLUE_SKIN_ID = "blue.desktop";
+
+ private Skin _purpleSkin;
+ private Skin _blueSkin;
+ private Skin _cyanSkin;
+ private final static SkinMetadata PURPLE = new SkinMetadata.Builder()
+ .id(PURPLE_SKIN_ID)
+ .family("purple")
+ .version(new SkinVersion("v1"))
+ .baseSkinId("minimal.desktop")
+ .styleSheetName("org/apache/myfaces/trinidadinternal/skin/purple/purpleSkin.css")
+ .build();
+ private final static SkinMetadata BLUE = new SkinMetadata.Builder()
+ .id(BLUE_SKIN_ID)
+ .family("blue")
+ .version(new SkinVersion("v1"))
+ .baseSkinId(PURPLE_SKIN_ID)
+ .styleSheetName("org/apache/myfaces/trinidadinternal/skin/purple/purpleBigFontSkin.css")
+ .build();
+ private final static SkinMetadata NO_CACHE_CYAN = new SkinMetadata.Builder()
+ .id(NO_CACHE_CYAN_SKIN_ID)
+ .family("cyan")
+ .version(new SkinVersion("v1"))
+ .baseSkinId("beach.desktop")
+ .styleSheetName("org/apache/myfaces/trinidadinternal/skin/purple/purpleBigFontSkinThree.css")
+ .build();
+ private final static Set<SkinMetadata> _METADATA = new HashSet<SkinMetadata>();
+
+ static
+ {
+ _METADATA.add(PURPLE);
+ _METADATA.add(BLUE);
+ _METADATA.add(NO_CACHE_CYAN);
+ }
+
+}
Added: myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider2.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider2.java?rev=1632265&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider2.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/custom/CustomSkinProvider2.java Thu Oct 16 10:16:54 2014
@@ -0,0 +1,89 @@
+/*
+ * 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.myfaces.trinidadinternal.skin.custom;
+
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidad.skin.SkinFactory;
+import org.apache.myfaces.trinidad.skin.SkinMetadata;
+import org.apache.myfaces.trinidad.skin.SkinProvider;
+import org.apache.myfaces.trinidad.skin.SkinVersion;
+
+import javax.faces.context.ExternalContext;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class CustomSkinProvider2 extends SkinProvider
+{
+ @Override
+ public Skin getSkin(ExternalContext context, SkinMetadata skinMetadata)
+ {
+ String skinId = skinMetadata.getId();
+ String family = skinMetadata.getFamily();
+
+ if ("violet.desktop".equals(skinId) || "violet".equals(family))
+ {
+ if (_violetSkin == null)
+ _violetSkin = SkinFactory.getFactory().createSkin(context, VIOLET);
+ return _violetSkin;
+ }
+ if ("green.desktop".equals(skinId) || "green".equals(family))
+ {
+ if (_greenSkin == null)
+ _greenSkin = SkinFactory.getFactory().createSkin(context, GREEN);
+ return _greenSkin;
+ }
+ return null;
+ }
+
+ @Override
+ public Collection<SkinMetadata> getSkinMetadata(ExternalContext context)
+ {
+ return Collections.unmodifiableCollection(_METADATA);
+ }
+
+ public static final String VIOLET_SKIN_ID = "violet.desktop";
+ public static final String GREEN_SKIN_ID = "green.desktop";
+
+ private Skin _violetSkin;
+ private Skin _greenSkin;
+ private final static SkinMetadata VIOLET = new SkinMetadata.Builder()
+ .id(VIOLET_SKIN_ID)
+ .family("violet")
+ .version(new SkinVersion("v1"))
+ .baseSkinId(CustomSkinProvider1.PURPLE_SKIN_ID)
+ .styleSheetName("org/apache/myfaces/trinidadinternal/skin/purple/purpleBigFontSkinTwo.css")
+ .build();
+ private final static SkinMetadata GREEN = new SkinMetadata.Builder()
+ .id(GREEN_SKIN_ID)
+ .family("green")
+ .version(new SkinVersion("v1"))
+ .baseSkinId("simple.desktop")
+ .styleSheetName("org/apache/myfaces/trinidadinternal/skin/purple/purpleBigFontSkinFour.css")
+ .build();
+ private final static Set<SkinMetadata> _METADATA = new HashSet<SkinMetadata>();
+
+ static
+ {
+ _METADATA.add(VIOLET);
+ _METADATA.add(GREEN);
+ }
+
+}
Added: myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/provider/SkinProviderTest.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/provider/SkinProviderTest.java?rev=1632265&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/provider/SkinProviderTest.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/skin/provider/SkinProviderTest.java Thu Oct 16 10:16:54 2014
@@ -0,0 +1,452 @@
+/*
+ * 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.myfaces.trinidadinternal.skin.provider;
+
+import junit.framework.TestCase;
+import org.apache.myfaces.trinidad.context.RequestContext;
+import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidad.skin.SkinFactory;
+import org.apache.myfaces.trinidad.skin.SkinMetadata;
+import org.apache.myfaces.trinidad.skin.SkinProvider;
+import org.apache.myfaces.trinidad.skin.SkinVersion;
+import org.apache.myfaces.trinidad.util.Service;
+import org.apache.myfaces.trinidadinternal.io.XhtmlResponseWriter;
+import org.apache.myfaces.trinidadinternal.renderkit.FacesConfigInfo;
+import org.apache.myfaces.trinidadinternal.renderkit.MApplication;
+import org.apache.myfaces.trinidadinternal.renderkit.MFacesContext;
+import org.apache.myfaces.trinidadinternal.renderkit.MRequestContext;
+import org.apache.myfaces.trinidadinternal.renderkit.NullWriter;
+import org.apache.myfaces.trinidadinternal.renderkit.RenderKitBootstrap;
+import org.apache.myfaces.trinidadinternal.renderkit.RenderUtils;
+import org.apache.myfaces.trinidadinternal.renderkit.TestResponseWriter;
+import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.TrinidadRenderingConstants;
+import org.apache.myfaces.trinidadinternal.skin.SkinFactoryImpl;
+import org.apache.myfaces.trinidadinternal.skin.custom.CustomSkinProvider1;
+import org.apache.myfaces.trinidadinternal.skin.custom.CustomSkinProvider2;
+
+import org.apache.myfaces.trinidad.component.core.CoreDocument;
+import org.apache.myfaces.trinidad.component.core.CoreForm;
+
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+public class SkinProviderTest extends TestCase
+{
+
+ public SkinProviderTest(String testName)
+ {
+ super(testName);
+ }
+
+ @Override
+ protected void setUp() throws IOException
+ {
+ MFacesContext ctx = new MFacesContext(MApplication.sharedInstance(), true);
+ ExternalContext externalContext = ctx.getExternalContext();
+
+ Map<String, Object> applicationMap = externalContext.getApplicationMap();
+ applicationMap.put(TrinidadSkinProvider.TRINDIAD_SKIN_PROVIDER_KEY,
+ new TrinidadSkinProvider());
+ applicationMap.put(ExternalSkinProvider.EXTERNAL_SKIN_PROVIDER_KEY,
+ new ExternalSkinProvider());
+ applicationMap.put(SkinProvider.SKIN_PROVIDER_INSTANCE_KEY,
+ new SkinProviderRegistry());
+ _facesContext = ctx;
+ _externalContext = externalContext;
+ _provider = SkinProvider.getCurrentInstance(_externalContext);
+
+ if (SkinFactory.getFactory() == null)
+ SkinFactory.setFactory(new SkinFactoryImpl());
+ RenderKitBootstrap.setFactories(_facesConfigInfo);
+ }
+
+ @Override
+ protected void tearDown() throws IOException
+ {
+ MFacesContext.clearContext();
+ _facesContext = null;
+ _externalContext = null;
+ _provider = null;
+ RenderKitBootstrap.clearFactories();
+ }
+
+ public void testTrinidadBaseSkinsUsingId()
+ {
+ _testUsingId(TrinidadRenderingConstants.SIMPLE_DESKTOP_ID);
+ _testUsingId(TrinidadRenderingConstants.SIMPLE_PORTLET_ID);
+ _testUsingId(TrinidadRenderingConstants.SIMPLE_PDA_ID);
+ _testUsingId(TrinidadRenderingConstants.MINIMAL_DESKTOP_ID);
+ _testUsingId(TrinidadRenderingConstants.MINIMAL_PORTLET_ID);
+ _testUsingId(TrinidadRenderingConstants.MINIMAL_PDA_ID);
+ _testUsingId(TrinidadRenderingConstants.CASABLANCA_DESKTOP_ID);
+ _testUsingId(TrinidadRenderingConstants.CASABLANCA_PORTLET_ID);
+ _testUsingId(TrinidadRenderingConstants.CASABLANCA_PDA_ID);
+ }
+
+ public void testTrinidadBaseSkinsUsingFamily()
+ {
+ _testUsingFamily(TrinidadRenderingConstants.SIMPLE_SKIN_FAMILY,
+ TrinidadRenderingConstants.SIMPLE_DESKTOP_ID);
+ _testUsingFamily(TrinidadRenderingConstants.MINIMAL_SKIN_FAMILY,
+ TrinidadRenderingConstants.MINIMAL_DESKTOP_ID);
+ _testUsingFamily(TrinidadRenderingConstants.CASABLANCA_SKIN_FAMILY,
+ TrinidadRenderingConstants.CASABLANCA_DESKTOP_ID);
+ }
+
+ public void testTrinidadBaseSkinsUsingFamilyAndRenderKit()
+ {
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.SIMPLE_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.DESKTOP.toString(),
+ TrinidadRenderingConstants.SIMPLE_DESKTOP_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.SIMPLE_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.PDA.toString(),
+ TrinidadRenderingConstants.SIMPLE_PDA_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.SIMPLE_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.PORTLET.toString(),
+ TrinidadRenderingConstants.SIMPLE_PORTLET_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.MINIMAL_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.DESKTOP.toString(),
+ TrinidadRenderingConstants.MINIMAL_DESKTOP_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.MINIMAL_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.PDA.toString(),
+ TrinidadRenderingConstants.MINIMAL_PDA_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.MINIMAL_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.PORTLET.toString(),
+ TrinidadRenderingConstants.MINIMAL_PORTLET_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.CASABLANCA_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.DESKTOP.toString(),
+ TrinidadRenderingConstants.CASABLANCA_DESKTOP_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.CASABLANCA_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.PDA.toString(),
+ TrinidadRenderingConstants.CASABLANCA_PDA_ID);
+ _testUsingFamilyAndRenderkit(TrinidadRenderingConstants.CASABLANCA_SKIN_FAMILY,
+ SkinMetadata.RenderKitId.PORTLET.toString(),
+ TrinidadRenderingConstants.CASABLANCA_PORTLET_ID);
+ }
+
+ public void testNonExistingSkin()
+ {
+ SkinMetadata skinMetadata = new SkinMetadata.Builder().id("non-existing-skin").build();
+ Skin skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertTrue(TrinidadRenderingConstants.SIMPLE_DESKTOP_ID.equals(skin.getId()));
+
+ skinMetadata = new SkinMetadata.Builder().family("non-existing-family").build();
+ skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertTrue(TrinidadRenderingConstants.SIMPLE_DESKTOP_ID.equals(skin.getId()));
+
+ skinMetadata = new SkinMetadata.Builder().id("non-existing-skin")
+ .renderKitId(SkinMetadata.RenderKitId.PDA)
+ .build();
+ skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertTrue(TrinidadRenderingConstants.SIMPLE_PDA_ID.equals(skin.getId()));
+
+ skinMetadata = new SkinMetadata.Builder().family("non-existing-family")
+ .renderKitId(SkinMetadata.RenderKitId.PORTLET)
+ .build();
+ skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertTrue(TrinidadRenderingConstants.SIMPLE_PORTLET_ID.equals(skin.getId()));
+ }
+
+ public void testCircularSkin()
+ {
+ try
+ {
+ _testUsingId("circular1.desktop");
+ fail("Failed to detect circular dependency in trinidad-skins.xml");
+ }
+ catch (Exception e)
+ {
+ assertEquals("Unexpected type of exception caught.", "IllegalStateException", e.getClass().getSimpleName());
+ }
+ }
+
+ public void testXmlConfiguredSkins()
+ {
+ _testUsingId("empty.desktop");
+ _testUsingId("suede.desktop");
+ _testUsingId("beach.desktop");
+
+ _testUsingFamily("test", "empty.desktop");
+ _testUsingFamilyAndVersion("test", new SkinVersion(null, true), "empty.desktop");
+ _testUsingFamilyAndVersion("test", new SkinVersion("v1"), "empty.desktop");
+ _testUsingFamilyAndVersion("test", new SkinVersion("v2"), "suede.desktop");
+ _testUsingFamilyAndVersion("test", new SkinVersion("v3"), "beach.desktop");
+
+ SkinMetadata skinMetadata = new SkinMetadata.Builder().id("beach.desktop").build();
+ Skin beach = _provider.getSkin(_externalContext, skinMetadata);
+ assertEquals("Unexpected number of skin additions in beach.desktop.", 1, beach.getSkinAdditions().size());
+ }
+
+ public void testSkinMetadataFromProvider()
+ {
+ Collection<SkinProvider> providers = ((SkinProviderRegistry) _provider).getProviders();
+
+ assertEquals("Unexpected number of providers.", 5, providers.size());
+
+
+ for (SkinProvider provider : providers)
+ {
+ if (provider instanceof TrinidadBaseSkinProvider)
+ assertEquals("Unexpected number of skins in " + provider, 9, provider.getSkinMetadata(_externalContext).size());
+ else if (provider instanceof TrinidadSkinProvider)
+ assertEquals("Unexpected number of skins in " + provider, 5, provider.getSkinMetadata(_externalContext).size());
+ else if (provider instanceof ExternalSkinProvider)
+ assertEquals("Unexpected number of skins in " + provider, 0, provider.getSkinMetadata(_externalContext).size());
+ else if (provider instanceof CustomSkinProvider1)
+ assertEquals("Unexpected number of skins in " + provider, 3, provider.getSkinMetadata(_externalContext).size());
+ else if (provider instanceof CustomSkinProvider2)
+ assertEquals("Unexpected number of skins in " + provider, 2, provider.getSkinMetadata(_externalContext).size());
+ }
+
+ Collection<SkinMetadata> allSkins = _provider.getSkinMetadata(_externalContext);
+ assertEquals("Unexpected number of total skins.", 19, allSkins.size());
+ }
+
+ public void testCustomSkinProvider()
+ {
+ Skin greenSkin = _testUsingId("green.desktop");
+ Skin blueSkin = _testUsingId("blue.desktop");
+ Skin cyanSkin = _testUsingId("cyan.desktop");
+ Skin purpleSkin = _testUsingId("purple.desktop");
+ Skin violetSkin = _testUsingId("violet.desktop");
+ assertEquals("Unexpected base skin for green skin.", "simple.desktop", greenSkin.getBaseSkin().getId());
+ assertEquals("Unexpected base skin for blue skin.", "purple.desktop", blueSkin.getBaseSkin().getId());
+ assertEquals("Unexpected base skin for cyan skin.", "beach.desktop", cyanSkin.getBaseSkin().getId());
+ assertEquals("Unexpected base skin for cyan skin.", "minimal.desktop", purpleSkin.getBaseSkin().getId());
+ assertEquals("Unexpected base skin for cyan skin.", "purple.desktop", violetSkin.getBaseSkin().getId());
+ assertEquals("Unexpected purple base skin in violet skin.", purpleSkin, violetSkin.getBaseSkin());
+ }
+
+ public void testSkinCache()
+ {
+
+ _cleanTempDir();
+
+ // access skin from external provider
+ _initRequestContextAndGenerateCss("green");
+ _verifyNumberOfSkinInCache(0);
+ _verifyNumberOfCssFiles(1);
+ _releaseRequestContext();
+
+ // access skin from external provider
+ _initRequestContextAndGenerateCss("blue");
+ _verifyNumberOfSkinInCache(0);
+ _verifyNumberOfCssFiles(2);
+ _releaseRequestContext();
+
+ // access internal skin defined in trinidad-skins.xml
+ _initRequestContextAndGenerateCss("suede");
+ _verifyNumberOfSkinInCache(1);
+ _verifyNumberOfCssFiles(3);
+ _releaseRequestContext();
+
+ // access skin from external provider
+ _initRequestContextAndGenerateCss("cyan");
+ _verifyNumberOfSkinInCache(1);
+ _verifyNumberOfCssFiles(4);
+ _releaseRequestContext();
+
+ // access internal static skin
+ _initRequestContextAndGenerateCss("casablanca");
+ _verifyNumberOfSkinInCache(2);
+ _verifyNumberOfCssFiles(5);
+ _releaseRequestContext();
+
+ // access skin from external provider
+ _initRequestContextAndGenerateCss("purple");
+ _verifyNumberOfSkinInCache(2);
+ _verifyNumberOfCssFiles(6);
+ _releaseRequestContext();
+
+ // access skin from external provider
+ _initRequestContextAndGenerateCss("violet");
+ _verifyNumberOfSkinInCache(2);
+ _verifyNumberOfCssFiles(7);
+ _releaseRequestContext();
+ }
+
+ private void _verifyNumberOfCssFiles(int skinNumber)
+ {
+ assertEquals("Skin file not generated as expected.", skinNumber, _getTempDir().list().length);
+ }
+
+ private void _verifyNumberOfSkinInCache(int skinNumber)
+ {
+ assertEquals("StyleProvider cache size does not match.", skinNumber, _getStyleProviderCacheSize());
+ }
+
+ private int _getStyleProviderCacheSize()
+ {
+ ConcurrentMap<String, Object> appMap = RequestContext.getCurrentInstance().getApplicationScopedConcurrentMap();
+ Object cache = appMap.get(_SKIN_PROVIDERS_CACHE_KEY);
+ if (cache == null)
+ fail("Skin provider cache cannot be null.");
+ if (!(cache instanceof Map))
+ fail("Skin provider cache should be a map.");
+ return ((Map) appMap.get(_SKIN_PROVIDERS_CACHE_KEY)).keySet().size();
+ }
+
+ private File _getTempDir()
+ {
+ Map<String, Object> applicationMap = _externalContext.getApplicationMap();
+ String path = ((File) applicationMap.get("javax.servlet.context.tempdir")).getAbsolutePath() + "/adf/styles/cache/";
+ File tempDir = new File(path);
+
+ if (!tempDir.exists())
+ tempDir.mkdirs();
+
+ return tempDir;
+ }
+
+ private void _cleanTempDir()
+ {
+ File tempDir = _getTempDir();
+ System.out.println("Cleaning temp directory: " + tempDir.getAbsolutePath());
+
+ if (tempDir.exists())
+ for (File file : tempDir.listFiles())
+ file.delete();
+ }
+
+ private Skin _testUsingId(String skinId)
+ {
+ SkinMetadata skinMetadata = new SkinMetadata.Builder().id(skinId).build();
+ Skin skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertEquals("Unexpected skin id.", skinId, skin.getId());
+ return skin;
+ }
+
+ private Skin _testUsingFamily(String family,
+ String expectedSkinId)
+ {
+ SkinMetadata skinMetadata = new SkinMetadata.Builder().family(family).build();
+ Skin skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertEquals("Unexpected skin id.", expectedSkinId, skin.getId());
+ assertEquals("Unexpected skin renderKitId.", SkinMetadata.RenderKitId.DESKTOP.toString(), skin.getRenderKitId());
+ return skin;
+ }
+
+ private Skin _testUsingFamilyAndRenderkit(String family,
+ String renderkitId,
+ String expectedSkinId)
+ {
+ SkinMetadata skinMetadata = new SkinMetadata.Builder()
+ .family(family)
+ .renderKitId(SkinMetadata.RenderKitId.fromId(renderkitId))
+ .build();
+ Skin skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertEquals("Unexpected skin id.", expectedSkinId, skin.getId());
+ assertEquals("Unexpected skin renderKitId.", renderkitId, skin.getRenderKitId());
+ return skin;
+ }
+
+ private Skin _testUsingFamilyAndVersion(String family,
+ SkinVersion version,
+ String expectedSkinId)
+ {
+ SkinMetadata skinMetadata = new SkinMetadata.Builder()
+ .family(family)
+ .version(version)
+ .build();
+ Skin skin = _provider.getSkin(_externalContext, skinMetadata);
+ assertEquals("Unexpected skin id.", expectedSkinId, skin.getId());
+ assertEquals("Unexpected skin renderKitId.", SkinMetadata.RenderKitId.DESKTOP.toString(), skin.getRenderKitId());
+ return skin;
+ }
+
+ /**
+ * This method sets skinFamily into request context and executes
+ * encode of a simple page. This results in generation of the css
+ * for the skin family specified.
+ * This code is similar to what we do in RenderKitTestCase.BaseTest
+ * @see org.apache.myfaces.trinidadinternal.renderkit.RenderKitTestCase.BaseTest
+ * @param skinFamily
+ */
+ private void _initRequestContextAndGenerateCss(String skinFamily)
+ {
+ try
+ {
+ _requestContext = new MRequestContext();
+ _requestContext.setSkinFamily(skinFamily);
+ _requestContext.setRightToLeft(false);
+ _requestContext.setAgent(RenderKitBootstrap.getGeckoAgent());
+
+ UIViewRoot root = RenderKitBootstrap.createUIViewRoot(_facesContext);
+ root.setRenderKitId("org.apache.myfaces.trinidad.core");
+ _facesContext.setViewRoot(root);
+ _facesContext.setResponseWriter(new TestResponseWriter(new NullWriter(),
+ XhtmlResponseWriter.XHTML_CONTENT_TYPE,
+ "UTF-8",
+ this,
+ null));
+ CoreDocument doc = new CoreDocument();
+ doc.setId("docId");
+ root.getChildren().add(doc);
+ CoreForm form = new CoreForm();
+ form.setId("formId");
+ doc.getChildren().add(form);
+
+ ExtendedRenderKitService service = Service.getService(_facesContext.getRenderKit(),
+ ExtendedRenderKitService.class);
+ if (service != null)
+ service.encodeBegin(_facesContext);
+
+ RenderUtils.encodeRecursive(_facesContext, root);
+ }
+ catch (Exception e)
+ {
+ System.out.println("Exception in _initRequestContextAndGenerateCss");
+ e.printStackTrace();
+ }
+ }
+
+ private void _releaseRequestContext()
+ {
+ _requestContext.release();
+ _requestContext = null;
+ }
+
+ private MRequestContext _requestContext;
+ private MFacesContext _facesContext;
+ private SkinProvider _provider;
+ private ExternalContext _externalContext;
+
+ private static FacesConfigInfo _facesConfigInfo;
+
+ private static final String _SKIN_PROVIDERS_CACHE_KEY = "org.apache.myfaces.trinidadinternal.skin.SKIN_PROVIDERS_KEY";
+
+ static
+ {
+ try
+ {
+ RenderKitBootstrap bootstrap = new RenderKitBootstrap();
+ bootstrap.init();
+ _facesConfigInfo = bootstrap.getFacesConfigInfo();
+ }
+ catch (Exception e)
+ {
+ }
+ }
+}
Added: myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMapTest.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMapTest.java?rev=1632265&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMapTest.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/test/java/org/apache/myfaces/trinidadinternal/util/CopyOnWriteArrayMapTest.java Thu Oct 16 10:16:54 2014
@@ -0,0 +1,28 @@
+package org.apache.myfaces.trinidadinternal.util;
+
+import org.apache.myfaces.trinidadbuild.test.ConcurrentMapTestCase;
+
+import java.util.concurrent.ConcurrentMap;
+
+public class CopyOnWriteArrayMapTest extends ConcurrentMapTestCase
+{
+ public CopyOnWriteArrayMapTest(String testName)
+ {
+ super(testName);
+ }
+
+ protected boolean supportsNullKeys()
+ {
+ return false;
+ }
+
+ protected ConcurrentMap<String, Object> createMap()
+ {
+ return CopyOnWriteArrayMap.newConcurrentMap();
+ }
+
+ protected ConcurrentMap<LameKey, Object> createMapWithLameKey()
+ {
+ return CopyOnWriteArrayMap.newConcurrentMap();
+ }
+}