You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2012/09/11 09:10:14 UTC
svn commit: r1383262 - in /karaf/trunk: features/core/
features/core/src/main/java/org/apache/karaf/features/internal/
jaas/config/ jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/
util/src/main/java/org/apache/karaf/util/collections/
Author: gnodet
Date: Tue Sep 11 07:10:13 2012
New Revision: 1383262
URL: http://svn.apache.org/viewvc?rev=1383262&view=rev
Log:
[KARAF-1797] Do not use equals method on proxies list
Added:
karaf/trunk/util/src/main/java/org/apache/karaf/util/collections/
karaf/trunk/util/src/main/java/org/apache/karaf/util/collections/CopyOnWriteArrayIdentityList.java
Modified:
karaf/trunk/features/core/pom.xml
karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
karaf/trunk/jaas/config/pom.xml
karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiConfiguration.java
karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiKeystoreManager.java
Modified: karaf/trunk/features/core/pom.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/pom.xml?rev=1383262&r1=1383261&r2=1383262&view=diff
==============================================================================
--- karaf/trunk/features/core/pom.xml (original)
+++ karaf/trunk/features/core/pom.xml Tue Sep 11 07:10:13 2012
@@ -67,6 +67,11 @@
</dependency>
<dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.util</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.junit</artifactId>
<scope>test</scope>
@@ -118,7 +123,8 @@
org.apache.karaf.features.internal.model,
org.apache.karaf.features.management.internal,
org.apache.felix.utils.version,
- org.apache.felix.utils.manifest
+ org.apache.felix.utils.manifest,
+ org.apache.karaf.util.collections
</Private-Package>
</instructions>
</configuration>
Modified: karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java?rev=1383262&r1=1383261&r2=1383262&view=diff
==============================================================================
--- karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java (original)
+++ karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java Tue Sep 11 07:10:13 2012
@@ -46,7 +46,6 @@ import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.TreeSet;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -70,6 +69,7 @@ import org.apache.karaf.features.Reposit
import org.apache.karaf.features.RepositoryEvent;
import org.apache.karaf.features.Resolver;
import org.apache.karaf.region.persist.RegionsPersistence;
+import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
@@ -111,7 +111,7 @@ public class FeaturesServiceImpl impleme
private Map<Feature, Set<Long>> installed = new HashMap<Feature, Set<Long>>();
private String boot;
AtomicBoolean bootFeaturesInstalled = new AtomicBoolean();
- private List<FeaturesListener> listeners = new CopyOnWriteArrayList<FeaturesListener>();
+ private List<FeaturesListener> listeners = new CopyOnWriteArrayIdentityList<FeaturesListener>();
private Queue<RegionsPersistence> regionsPersistenceQueue = new LinkedList<RegionsPersistence>();
private CountDownLatch regionsPersistenceLatch = new CountDownLatch(1);
private ThreadLocal<Repository> repo = new ThreadLocal<Repository>();
Modified: karaf/trunk/jaas/config/pom.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/jaas/config/pom.xml?rev=1383262&r1=1383261&r2=1383262&view=diff
==============================================================================
--- karaf/trunk/jaas/config/pom.xml (original)
+++ karaf/trunk/jaas/config/pom.xml Tue Sep 11 07:10:13 2012
@@ -44,6 +44,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.util</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
@@ -100,7 +105,10 @@
org.osgi.service.blueprint.reflect,
*
</Import-Package>
- <Private-Package>${project.artifactId}.impl</Private-Package>
+ <Private-Package>
+ ${project.artifactId}.impl,
+ org.apache.karaf.util.collections
+ </Private-Package>
</instructions>
</configuration>
</plugin>
Modified: karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiConfiguration.java
URL: http://svn.apache.org/viewvc/karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiConfiguration.java?rev=1383262&r1=1383261&r2=1383262&view=diff
==============================================================================
--- karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiConfiguration.java (original)
+++ karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiConfiguration.java Tue Sep 11 07:10:13 2012
@@ -18,15 +18,15 @@ package org.apache.karaf.jaas.config.imp
import java.util.List;
import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import org.apache.karaf.jaas.config.JaasRealm;
+import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
public class OsgiConfiguration extends Configuration {
- private final List<JaasRealm> realms = new CopyOnWriteArrayList<JaasRealm>();
+ private final List<JaasRealm> realms = new CopyOnWriteArrayIdentityList<JaasRealm>();
public void init() {
Configuration.setConfiguration(this);
Modified: karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiKeystoreManager.java
URL: http://svn.apache.org/viewvc/karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiKeystoreManager.java?rev=1383262&r1=1383261&r2=1383262&view=diff
==============================================================================
--- karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiKeystoreManager.java (original)
+++ karaf/trunk/jaas/config/src/main/java/org/apache/karaf/jaas/config/impl/OsgiKeystoreManager.java Tue Sep 11 07:10:13 2012
@@ -20,8 +20,6 @@ import java.security.GeneralSecurityExce
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
@@ -29,6 +27,7 @@ import javax.net.ssl.SSLSocketFactory;
import org.apache.karaf.jaas.config.KeystoreInstance;
import org.apache.karaf.jaas.config.KeystoreIsLocked;
import org.apache.karaf.jaas.config.KeystoreManager;
+import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,7 +38,7 @@ public class OsgiKeystoreManager impleme
private final static transient Logger logger = LoggerFactory.getLogger(OsgiKeystoreManager.class);
- private List<KeystoreInstance> keystores = new CopyOnWriteArrayList<KeystoreInstance>();
+ private List<KeystoreInstance> keystores = new CopyOnWriteArrayIdentityList<KeystoreInstance>();
public void register(KeystoreInstance keystore, Map<String, ?> properties) {
keystores.add(keystore);
@@ -156,4 +155,4 @@ public class OsgiKeystoreManager impleme
return found;
}
-}
\ No newline at end of file
+}
Added: karaf/trunk/util/src/main/java/org/apache/karaf/util/collections/CopyOnWriteArrayIdentityList.java
URL: http://svn.apache.org/viewvc/karaf/trunk/util/src/main/java/org/apache/karaf/util/collections/CopyOnWriteArrayIdentityList.java?rev=1383262&view=auto
==============================================================================
--- karaf/trunk/util/src/main/java/org/apache/karaf/util/collections/CopyOnWriteArrayIdentityList.java (added)
+++ karaf/trunk/util/src/main/java/org/apache/karaf/util/collections/CopyOnWriteArrayIdentityList.java Tue Sep 11 07:10:13 2012
@@ -0,0 +1,1170 @@
+/*
+ * 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.karaf.util.collections;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.RandomAccess;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Implements a {@link java.util.ArrayList} variant that is thread-safe. All
+ * write operation result in a new copy of the underlying data being created.
+ * Iterators reflect the state of the CopyOnWriteArrayIdentityList at the time they were
+ * created. They are not updated to reflect subsequent changes to the list. In
+ * addition, these iterators cannot be used for modifying the underlying
+ * CopyOnWriteArrayIdentityList.
+ *
+ * In addition, elements are compared based on reference equality instead of
+ * object equality when comparing values.
+ *
+ * @param <E> the element type
+ */
+public class CopyOnWriteArrayIdentityList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
+
+ private static final long serialVersionUID = 8673264195747942595L;
+
+ private transient volatile E[] arr;
+
+ /**
+ * Lock for the queue write methods
+ */
+ private final transient ReentrantLock lock = new ReentrantLock();
+
+ /**
+ * Creates a new, empty instance of CopyOnWriteArrayList.
+ */
+ public CopyOnWriteArrayIdentityList() {
+ }
+
+ /**
+ * Creates a new instance of CopyOnWriteArrayList and fills it with the
+ * contents of a given Collection.
+ *
+ * @param c the collection the elements of which are to be copied into
+ * the new instance.
+ */
+ public CopyOnWriteArrayIdentityList(Collection<? extends E> c) {
+ this((E[]) c.toArray());
+ }
+
+ /**
+ * Creates a new instance of CopyOnWriteArrayList and fills it with the
+ * contents of a given array.
+ *
+ * @param array the array the elements of which are to be copied into the
+ * new instance.
+ */
+ public CopyOnWriteArrayIdentityList(E[] array) {
+ int size = array.length;
+ E[] data = newElementArray(size);
+ for (int i = 0; i < size; i++) {
+ data[i] = array[i];
+ }
+ arr = data;
+ }
+
+ public boolean add(E e) {
+ lock.lock();
+ try {
+ E[] data;
+ E[] old = getData();
+ int size = old.length;
+ data = newElementArray(size + 1);
+ System.arraycopy(old, 0, data, 0, size);
+ data[size] = e;
+ setData(data);
+ return true;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void add(int index, E e) {
+ lock.lock();
+ try {
+ E[] data;
+ E[] old = getData();
+ int size = old.length;
+ checkIndexInclusive(index, size);
+ data = newElementArray(size+1);
+ System.arraycopy(old, 0, data, 0, index);
+ data[index] = e;
+ if (size > index) {
+ System.arraycopy(old, index, data, index + 1, size - index);
+ }
+ setData(data);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean addAll(Collection<? extends E> c) {
+ Iterator it = c.iterator();
+ int ssize = c.size();
+ lock.lock();
+ try {
+ int size = size();
+ E[] data;
+ E[] old = getData();
+ int nSize = size + ssize;
+ data = newElementArray(nSize);
+ System.arraycopy(old, 0, data, 0, size);
+ while (it.hasNext()) {
+ data[size++] = (E) it.next();
+ }
+ setData(data);
+ } finally {
+ lock.unlock();
+ }
+ return true;
+ }
+
+ public boolean addAll(int index, Collection<? extends E> c) {
+ Iterator it = c.iterator();
+ int ssize = c.size();
+ lock.lock();
+ try {
+ int size = size();
+ checkIndexInclusive(index, size);
+ E[] data;
+ E[] old = getData();
+ int nSize = size + ssize;
+ data = newElementArray(nSize);
+ System.arraycopy(old, 0, data, 0, index);
+ int i = index;
+ while (it.hasNext()) {
+ data[i++] = (E) it.next();
+ }
+ if (size > index) {
+ System.arraycopy(old, index, data, index + ssize, size - index);
+ }
+ setData(data);
+ } finally {
+ lock.unlock();
+ }
+ return true;
+ }
+
+ /**
+ * Adds to this CopyOnWriteArrayList all those elements from a given
+ * collection that are not yet part of the list.
+ *
+ * @param c the collection from which the potential new elements are
+ * taken.
+ *
+ * @return the number of elements actually added to this list.
+ */
+ public int addAllAbsent(Collection<? extends E> c) {
+ if (c.size() == 0) {
+ return 0;
+ }
+ lock.lock();
+ try {
+ E[] old = getData();
+ int size = old.length;
+ E[] toAdd = newElementArray(c.size());
+ int i = 0;
+ for (Iterator it = c.iterator(); it.hasNext();) {
+ E o = (E) it.next();
+ if (indexOf(o) < 0) {
+ toAdd[i++] = o;
+ }
+ }
+ E[] data = newElementArray(size + i);
+ System.arraycopy(old, 0, data, 0, size);
+ System.arraycopy(toAdd, 0, data, size, i);
+ setData(data);
+ return i;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Adds to this CopyOnWriteArrayList another element, given that this
+ * element is not yet part of the list.
+ *
+ * @param e the potential new element.
+ *
+ * @return true if the element was added, or false otherwise.
+ */
+ public boolean addIfAbsent(E e) {
+ lock.lock();
+ try {
+ E[] data;
+ E[] old = getData();
+ int size = old.length;
+ if (size != 0) {
+ if (indexOf(e) >= 0) {
+ return false;
+ }
+ }
+ data = newElementArray(size + 1);
+ System.arraycopy(old, 0, data, 0, size);
+ data[size] = e;
+ setData(data);
+ return true;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public void clear() {
+ lock.lock();
+ try {
+ setData(newElementArray(0));
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ CopyOnWriteArrayIdentityList thisClone = (CopyOnWriteArrayIdentityList) super.clone();
+ thisClone.setData(this.getData());
+ return thisClone;
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("CloneNotSupportedException is not expected here");
+ }
+ }
+
+ public boolean contains(Object o) {
+ return indexOf(o) >= 0;
+ }
+
+ public boolean containsAll(Collection<?> c) {
+ E[] data = getData();
+ return containsAll(c, data, 0, data.length);
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof List)) {
+ return false;
+ }
+ List l = (List) o;
+ Iterator it = l.listIterator();
+ Iterator ourIt = listIterator();
+ while (it.hasNext()) {
+ if (!ourIt.hasNext()) {
+ return false;
+ }
+ Object thisListElem = it.next();
+ Object anotherListElem = ourIt.next();
+ if (thisListElem != anotherListElem) {
+ return false;
+ }
+ }
+ if (ourIt.hasNext()) {
+ return false;
+ }
+ return true;
+ }
+
+ public E get(int index) {
+ E[] data = getData();
+ return data[index];
+ }
+
+ public int hashCode() {
+ int hashCode = 1;
+ Iterator it = listIterator();
+ while (it.hasNext()) {
+ Object obj = it.next();
+ hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
+ }
+ return hashCode;
+ }
+
+ /**
+ * Returns the index of a given element, starting the search from a given
+ * position in the list.
+ *
+ * @param e the element to search.
+ * @param index the index at which to start the search.
+ *
+ * @return the index of the element or null, if the element has not been
+ * found at or beyond the given start index.
+ */
+ public int indexOf(E e, int index) {
+ E[] data = getData();
+ return indexOf(e, data, index, data.length - index);
+ }
+
+ public int indexOf(Object o) {
+ E[] data = getData();
+ return indexOf(o, data, 0, data.length);
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public Iterator<E> iterator() {
+ return new ListIteratorImpl(getData(), 0);
+ }
+
+ /**
+ * Returns the last index of a given element, starting the search from
+ * a given position in the list and going backwards.
+ *
+ * @param e the element to search.
+ * @param index the index at which to start the search.
+ *
+ * @return the index of the element or null, if the element has not been
+ * found at or before the given start index.
+ */
+ public int lastIndexOf(E e, int index) {
+ E[] data = getData();
+ return lastIndexOf(e, data, 0, index);
+ }
+
+ public int lastIndexOf(Object o) {
+ E[] data = getData();
+ return lastIndexOf(o, data, 0, data.length);
+ }
+
+ public ListIterator<E> listIterator() {
+ return new ListIteratorImpl(getData(), 0);
+ }
+
+ public ListIterator<E> listIterator(int index) {
+ E[] data = getData();
+ checkIndexInclusive(index, data.length);
+ return new ListIteratorImpl(data, index);
+ }
+
+ public E remove(int index) {
+ return removeRange(index, 1);
+ }
+
+ public boolean remove(Object o) {
+ lock.lock();
+ try {
+ int index = indexOf(o);
+ if (index == -1) {
+ return false;
+ }
+ remove(index);
+ return true;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean removeAll(Collection<?> c) {
+ lock.lock();
+ try {
+ return removeAll(c, 0, getData().length) != 0;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public boolean retainAll(Collection<?> c) {
+ if (c == null) {
+ throw new NullPointerException();
+ }
+ lock.lock();
+ try {
+ return retainAll(c, 0, getData().length) != 0;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public E set(int index, E e) {
+ lock.lock();
+ try {
+ int size = size();
+ checkIndexExlusive(index, size);
+ E[] data;
+ data = newElementArray(size);
+ E[] oldArr = getData();
+ System.arraycopy(oldArr, 0, data, 0, size);
+ E old = data[index];
+ data[index] = e;
+ setData(data);
+ return old;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public int size() {
+ return getData().length;
+ }
+
+ public List<E> subList(int fromIndex, int toIndex) {
+ return new SubList(this, fromIndex, toIndex);
+ }
+
+ public Object[] toArray() {
+ E[] data = getData();
+ return toArray(data, 0, data.length);
+ }
+
+ public <T> T[] toArray(T[] a) {
+ E[] data = getData();
+ return (T[]) toArray(a, data, 0, data.length);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("[");
+
+ Iterator it = listIterator();
+ while (it.hasNext()) {
+ sb.append(String.valueOf(it.next()));
+ sb.append(", ");
+ }
+ if (sb.length() > 1) {
+ sb.setLength(sb.length() - 2);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ // private and package private methods
+
+ @SuppressWarnings("unchecked")
+ private final E[] newElementArray(int size) {
+ return (E[])new Object[size];
+ }
+
+ /**
+ * sets the internal data array
+ *
+ * @param data array to set
+ */
+ private final void setData(E[] data) {
+ arr = data;
+ }
+
+ /**
+ * gets the internal data array (or a new array if it is null)
+ *
+ * @return the data array
+ */
+ final E[] getData() {
+ if (arr == null) {
+ return newElementArray(0);
+ }
+ return arr;
+ }
+
+ /**
+ * Removes from the specified range of this list
+ * all the elements that are contained in the specified collection
+ * <p/>
+ * !should be called under lock
+ *
+ * @return Returns the number of removed elements
+ */
+ final int removeAll(Collection c, int start, int size) {
+ int ssize = c.size();
+ if (ssize == 0) {
+ return 0;
+ }
+ Object[] old = getData();
+ int arrsize = old.length;
+ if (arrsize == 0) {
+ return 0;
+ }
+ Object[] data = new Object[size];
+ int j = 0;
+ for (int i = start; i < (start + size); i++) {
+ if (!c.contains(old[i])) {
+ data[j++] = old[i];
+ }
+ }
+ if (j != size) {
+ E[] result = newElementArray(arrsize - (size - j));
+ System.arraycopy(old, 0, result, 0, start);
+ System.arraycopy(data, 0, result, start, j);
+ System.arraycopy(old, start + size, result, start + j, arrsize
+ - (start + size));
+ setData(result);
+ return (size - j);
+ }
+ return 0;
+ }
+
+ /**
+ * Retains only the elements in the specified range of this list
+ * that are contained in the specified collection
+ *
+ * @return Returns the number of removed elements
+ */
+ // should be called under lock
+ int retainAll(Collection c, int start, int size) {
+ Object[] old = getData();
+ if (size == 0) {
+ return 0;
+ }
+ if (c.size() == 0) {
+ E[] data;
+ if (size == old.length) {
+ data = newElementArray(0);
+ } else {
+ data = newElementArray(old.length - size);
+ System.arraycopy(old, 0, data, 0, start);
+ System.arraycopy(old, start + size, data, start, old.length
+ - start - size);
+ }
+ setData(data);
+ return size;
+ }
+ Object[] temp = new Object[size];
+ int pos = 0;
+ for (int i = start; i < (start + size); i++) {
+ if (c.contains(old[i])) {
+ temp[pos++] = old[i];
+ }
+ }
+ if (pos == size) {
+ return 0;
+ }
+ E[] data = newElementArray(pos + old.length - size);
+ System.arraycopy(old, 0, data, 0, start);
+ System.arraycopy(temp, 0, data, start, pos);
+ System.arraycopy(old, start + size, data, start + pos, old.length
+ - start - size);
+ setData(data);
+ return (size - pos);
+ }
+
+ /**
+ * Removes specified range from this list
+ */
+ E removeRange(int start, int size) {
+ lock.lock();
+ try {
+ int sizeArr = size();
+ checkIndexExlusive(start, sizeArr);
+ checkIndexInclusive(start + size, sizeArr);
+ E[] data;
+ data = newElementArray(sizeArr - size);
+ E[] oldArr = getData();
+ System.arraycopy(oldArr, 0, data, 0, start);
+ E old = oldArr[start];
+ if (sizeArr > (start + size)) {
+ System.arraycopy(oldArr, start + size, data, start, sizeArr
+ - (start + size));
+ }
+ setData(data);
+ return old;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ // some util static functions to use by iterators and methods
+ /**
+ * Returns an array containing all of the elements
+ * in the specified range of the array in proper sequence
+ */
+ static Object[] toArray(Object[] data, int start, int size) {
+ Object[] result = new Object[size];
+ System.arraycopy(data, start, result, 0, size);
+ return result;
+ }
+
+ /**
+ * Returns an array containing all of the elements
+ * in the specified range of the array in proper sequence,
+ * stores the result in the array, specified by first parameter
+ * (as for public instance method toArray(Object[] to)
+ */
+ static Object[] toArray(Object[] to, Object[] data, int start, int size) {
+ int l = data.length;
+ if (to.length < l) {
+ to = (Object[]) Array.newInstance(to.getClass().getComponentType(),
+ l);
+ } else {
+ if (to.length > l) {
+ to[l] = null;
+ }
+ }
+ System.arraycopy(data, start, to, 0, size);
+ return to;
+ }
+
+ /**
+ * Checks if the specified range of the
+ * array contains all of the elements in the collection
+ *
+ * @param c collection with elements
+ * @param data array where to search the elements
+ * @param start start index
+ * @param size size of the range
+ */
+ static final boolean containsAll(Collection c, Object[] data, int start,
+ int size) {
+ if (size == 0) {
+ return false;
+ }
+ Iterator it = c.iterator();
+ while (it.hasNext()) {
+ Object next = it.next();
+ if (indexOf(next, data, start, size) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the index in the specified range of the data array
+ * of the last occurrence of the specified element
+ *
+ * @param o element to search
+ * @param data array where to search
+ * @param start start index
+ * @param size size of the range
+ * @return
+ */
+ static final int lastIndexOf(Object o, Object[] data, int start, int size) {
+ if (size == 0) {
+ return -1;
+ }
+ for (int i = start + size - 1; i > start - 1; i--) {
+ if (data[i] == o) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index in the specified range of the data array
+ * of the first occurrence of the specified element
+ *
+ * @param o element to search
+ * @param data array where to search
+ * @param start start index
+ * @param size end index
+ * @return
+ */
+ static final int indexOf(Object o, Object[] data, int start, int size) {
+ if (size == 0) {
+ return -1;
+ }
+ for (int i = start; i < start + size; i++) {
+ if (data[i] == o) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Throws <code>IndexOutOfBoundsException</code> if <code>index</code>
+ * is out of the list bounds.
+ *
+ * @param index element index to check.
+ */
+ static final void checkIndexInclusive(int index, int size) {
+ if (index < 0 || index > size) {
+ throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size);
+ }
+ }
+
+ /**
+ * Throws <code>IndexOutOfBoundsException</code> if <code>index</code>
+ * is out of the list bounds. Excluding the last element.
+ *
+ * @param index element index to check.
+ */
+ static final void checkIndexExlusive(int index, int size) {
+ if (index < 0 || index >= size) {
+ throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size);
+ }
+ }
+
+ /**
+ * gets the internal data array
+ *
+ * @return the data array
+ */
+ final E[] getArray() {
+ return arr;
+ }
+
+ /**
+ * list iterator implementation,
+ * when created gets snapshot of the list,
+ * so never throws ConcurrentModificationException
+ */
+ private static class ListIteratorImpl implements ListIterator {
+ private final Object[] arr;
+
+ private int current;
+
+ private final int size;
+
+ final int size() {
+ return size;
+ }
+
+ public ListIteratorImpl(Object[] data, int current) {
+ this.current = current;
+ arr = data;
+ size = data.length;
+ }
+
+ public void add(Object o) {
+ throw new UnsupportedOperationException("Unsupported operation add");
+ }
+
+ public boolean hasNext() {
+ if (current < size) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean hasPrevious() {
+ return current > 0;
+ }
+
+ public Object next() {
+ if (hasNext()) {
+ return arr[current++];
+ }
+ throw new NoSuchElementException("pos is " + current + ", size is " + size);
+ }
+
+ public int nextIndex() {
+ return current;
+ }
+
+ public Object previous() {
+ if (hasPrevious()) {
+ return arr[--current];
+ }
+ throw new NoSuchElementException("pos is " + (current-1) + ", size is " + size);
+ }
+
+ public int previousIndex() {
+ return current - 1;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Unsupported operation remove");
+ }
+
+ public void set(Object o) {
+ throw new UnsupportedOperationException("Unsupported operation set");
+ }
+
+ }
+
+ /**
+ * Keeps a state of sublist implementation,
+ * size and array declared as final,
+ * so we'll never get the unconsistent state
+ */
+ static final class SubListReadData {
+ final int size;
+
+ final Object[] data;
+
+ SubListReadData(int size, Object[] data) {
+ this.size = size;
+ this.data = data;
+ }
+ }
+
+ /**
+ * Represents a list returned by <code>sublist()</code>.
+ */
+ static class SubList implements List {
+ private final CopyOnWriteArrayIdentityList list;
+
+ private volatile SubListReadData read;
+
+ private final int start;
+
+ /**
+ * Sublist constructor.
+ *
+ * @param list backing list.
+ * @param fromIdx startingIndex, inclusive
+ * @param toIdx endIndex, exclusive
+ */
+ public SubList(CopyOnWriteArrayIdentityList list, int fromIdx, int toIdx) {
+ this.list = list;
+ Object[] data = list.getData();
+ int size = toIdx - fromIdx;
+ checkIndexExlusive(fromIdx, data.length);
+ checkIndexInclusive(toIdx, data.length);
+ read = new SubListReadData(size, list.getData());
+ start = fromIdx;
+ }
+
+ /**
+ * throws ConcurrentModificationException when the list
+ * is structurally modified in the other way other than via this subList
+ * <p/>
+ * Should be called under lock!
+ */
+ private void checkModifications() {
+ if (read.data != list.getData()) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ /**
+ * @see java.util.List#listIterator(int)
+ */
+ public ListIterator listIterator(int startIdx) {
+ return new SubListIterator(startIdx, read);
+ }
+
+ /**
+ * @see java.util.List#set(int, java.lang.Object)
+ */
+ public Object set(int index, Object obj) {
+ list.lock.lock();
+ try {
+ checkIndexExlusive(index, read.size);
+ checkModifications();
+ Object result = list.set(index + start, obj);
+ read = new SubListReadData(read.size, list.getData());
+ return result;
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ /**
+ * @see java.util.List#get(int)
+ */
+ public Object get(int index) {
+ SubListReadData data = read;
+ if (data.data != list.getData()) {
+ list.lock.lock();
+ try {
+ data = read;
+ if (data.data != list.getData()) {
+ throw new ConcurrentModificationException();
+ }
+ } finally {
+ list.lock.unlock();
+ }
+ }
+ checkIndexExlusive(index, data.size);
+ return data.data[index + start];
+ }
+
+ /**
+ * @see java.util.Collection#size()
+ */
+ public int size() {
+ return read.size;
+ }
+
+ /**
+ * @see java.util.List#remove(int)
+ */
+ public Object remove(int index) {
+ list.lock.lock();
+ try {
+ checkIndexExlusive(index, read.size);
+ checkModifications();
+ Object obj = list.remove(index + start);
+ read = new SubListReadData(read.size - 1, list.getData());
+ return obj;
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ /**
+ * @see java.util.List#add(int, java.lang.Object)
+ */
+ public void add(int index, Object object) {
+ list.lock.lock();
+ try {
+ checkIndexInclusive(index, read.size);
+ checkModifications();
+ list.add(index + start, object);
+ read = new SubListReadData(read.size + 1, list.getData());
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ public boolean add(Object o) {
+ list.lock.lock();
+ try {
+ checkModifications();
+ list.add(start + read.size, o);
+ read = new SubListReadData(read.size + 1, list.getData());
+ return true;
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ public boolean addAll(Collection c) {
+ list.lock.lock();
+ try {
+ checkModifications();
+ int d = list.size();
+ list.addAll(start + read.size, c);
+ read = new SubListReadData(read.size + (list.size() - d), list
+ .getData());
+ return true;
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ public void clear() {
+ list.lock.lock();
+ try {
+ checkModifications();
+ list.removeRange(start, read.size);
+ read = new SubListReadData(0, list.getData());
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ public boolean contains(Object o) {
+ return indexOf(o) != -1;
+ }
+
+ public boolean containsAll(Collection c) {
+ SubListReadData b = read;
+ return CopyOnWriteArrayIdentityList.containsAll(c, b.data, start, b.size);
+ }
+
+ public int indexOf(Object o) {
+ SubListReadData b = read;
+ int ind = CopyOnWriteArrayIdentityList.indexOf(o, b.data, start, b.size)
+ - start;
+ return ind < 0 ? -1 : ind;
+ }
+
+ public boolean isEmpty() {
+ return read.size == 0;
+ }
+
+ public Iterator iterator() {
+ return new SubListIterator(0, read);
+ }
+
+ public int lastIndexOf(Object o) {
+ SubListReadData b = read;
+ int ind = CopyOnWriteArrayIdentityList
+ .lastIndexOf(o, b.data, start, b.size)
+ - start;
+ return ind < 0 ? -1 : ind;
+ }
+
+ public ListIterator listIterator() {
+ return new SubListIterator(0, read);
+ }
+
+ public boolean remove(Object o) {
+ list.lock.lock();
+ try {
+ checkModifications();
+ int i = indexOf(o);
+ if (i == -1) {
+ return false;
+ }
+ boolean result = list.remove(i + start) != null;
+ if (result) {
+ read = new SubListReadData(read.size - 1, list.getData());
+ }
+ return result;
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ public boolean removeAll(Collection c) {
+ list.lock.lock();
+ try {
+ checkModifications();
+ int removed = list.removeAll(c, start, read.size);
+ if (removed > 0) {
+ read = new SubListReadData(read.size - removed, list
+ .getData());
+ return true;
+ }
+ } finally {
+ list.lock.unlock();
+ }
+ return false;
+ }
+
+ public boolean retainAll(Collection c) {
+ list.lock.lock();
+ try {
+ checkModifications();
+ int removed = list.retainAll(c, start, read.size);
+ if (removed > 0) {
+ read = new SubListReadData(read.size - removed, list
+ .getData());
+ return true;
+ }
+ return false;
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ public List subList(int fromIndex, int toIndex) {
+ return new SubList(list, start + fromIndex, start + toIndex);
+ }
+
+ public Object[] toArray() {
+ SubListReadData r = read;
+ return CopyOnWriteArrayIdentityList.toArray(r.data, start, r.size);
+ }
+
+ public Object[] toArray(Object[] a) {
+ SubListReadData r = read;
+ return CopyOnWriteArrayIdentityList.toArray(a, r.data, start, r.size);
+ }
+
+ /**
+ * @see java.util.List#addAll(int, java.util.Collection)
+ */
+ public boolean addAll(int index, Collection collection) {
+ list.lock.lock();
+ try {
+ checkIndexInclusive(index, read.size);
+ checkModifications();
+ int d = list.size();
+ boolean rt = list.addAll(index + start, collection);
+ read = new SubListReadData(read.size + list.size() - d, list
+ .getData());
+ return rt;
+ } finally {
+ list.lock.unlock();
+ }
+ }
+
+ /**
+ * Implementation of <code>ListIterator</code> for the
+ * <code>SubList</code>
+ * gets a snapshot of the sublist,
+ * never throws ConcurrentModificationException
+ */
+ private class SubListIterator extends ListIteratorImpl {
+ private final SubListReadData dataR;
+
+ /**
+ * Constructs an iterator starting with the given index
+ *
+ * @param index index of the first element to iterate.
+ */
+ private SubListIterator(int index, SubListReadData d) {
+ super(d.data, index + start);
+ this.dataR = d;
+ }
+
+ /**
+ * @see java.util.ListIterator#nextIndex()
+ */
+ public int nextIndex() {
+ return super.nextIndex() - start;
+ }
+
+ /**
+ * @see java.util.ListIterator#previousIndex()
+ */
+ public int previousIndex() {
+ return super.previousIndex() - start;
+ }
+
+ /**
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ return nextIndex() < dataR.size;
+ }
+
+ /**
+ * @see java.util.ListIterator#hasPrevious()
+ */
+ public boolean hasPrevious() {
+ return previousIndex() > -1;
+ }
+ }
+
+ }
+
+ //serialization support
+ /**
+ * Writes the object state to the ObjectOutputStream.
+ *
+ * @param oos ObjectOutputStream to write object to.
+ * @throws IOException if an I/O error occur.
+ */
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ E[] back = getData();
+ int size = back.length;
+ oos.defaultWriteObject();
+ oos.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ oos.writeObject(back[i]);
+ }
+ }
+
+ /**
+ * Reads the object state from the ObjectOutputStream.
+ *
+ * @param ois ObjectInputStream to read object from.
+ * @throws IOException if an I/O error occur.
+ */
+ private void readObject(ObjectInputStream ois) throws IOException,
+ ClassNotFoundException {
+ ois.defaultReadObject();
+ int length = ois.readInt();
+ if (length == 0) {
+ setData(newElementArray(0));
+ } else {
+ E[] back = newElementArray(length);
+ for (int i = 0; i < back.length; i++) {
+ back[i] = (E) ois.readObject();
+ }
+ setData(back);
+ }
+ }
+
+}
\ No newline at end of file