/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.util; import java.io.Serializable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; /** * Factory for generating immutable type wrappers. * * @author Jason T. Greene */ public class Immutables { /** * Whether or not this collection type is immutable * * @param o a Collection, Set, List, or Map * @return true if immutable, false if not */ public static boolean isImmutable(Object o) { return o instanceof Immutable; } /** * Converts a Collection to an immutable List by copying it. * * @param source the collection to convert * @return a copied/converted immutable list */ public static List immutableListConvert(Collection source) { return new ImmutableListCopy(source); } /** * Creates an immutable copy of the list. * * @param list the list to copy * @return the immutable copy */ public static List immutableListCopy(List list) { return new ImmutableListCopy(list); } /** * Wraps an array with an immutable list. There is no copying involved. * * @param * @param array the array to wrap * @return a list containing the array */ public static List immutableListWrap(T... array) { return new ImmutableListCopy(array); } /** * Creates a new immutable list containing the union (combined entries) of both lists. * * @param list1 contains the first elements of the new list * @param list2 contains the successor elements of the new list * @return a new immutable merged copy of list1 and list2 */ public static List immutableListMerge(List list1, List list2) { return new ImmutableListCopy(list1, list2); } /** * Converts a Collections into an immutable Set by copying it. * * @param collection the collection to convert/copy * @return a new immutable set containing the elements in collection */ public static Set immutableSetConvert(Collection collection) { return immutableSetWrap(new HashSet(collection)); } /** * Wraps a set with an immutable set. There is no copying involved. * * @param set the set to wrap * @return an immutable set wrapper that delegates to the original set */ public static Set immutableSetWrap(Set set) { return new ImmutableSetWrapper(set); } /** * Creates an immutable copy of the specified set. * * @param set the set to copy from * @return an immutable set copy */ public static Set immutableSetCopy(Set set) { Set copy = attemptKnownSetCopy(set); if (copy == null) attemptClone(set); if (copy == null) // Set uses Collection copy-ctor copy = attemptCopyConstructor(set, Collection.class); if (copy == null) copy = new HashSet(set); return new ImmutableSetWrapper(copy); } /** * Wraps a map with an immutable map. There is no copying involved. * * @param map the map to wrap * @return an immutable map wrapper that delegates to the original map */ public static Map immutableMapWrap(Map map) { return new ImmutableMapWrapper(map); } /** * Creates an immutable copy of the specified map. * * @param map the map to copy from * @return an immutable map copy */ public static Map immutableMapCopy(Map map) { Map copy = attemptKnownMapCopy(map); if (copy == null) attemptClone(map); if (copy == null) copy = attemptCopyConstructor(map, Map.class); if (copy == null) copy = new HashMap(map); return new ImmutableMapWrapper(copy); } /** * Creates a new immutable copy of the specified Collection. * * @param collection the collection to copy * @return an immutable copy */ public static Collection immutableCollectionCopy(Collection collection) { Collection copy = attemptKnownSetCopy(collection); if (copy == null) copy = attemptClone(collection); if (copy == null) copy = attemptCopyConstructor(collection, Collection.class); if (copy == null) copy = new ArrayList(collection); return new ImmutableCollectionWrapper(copy); } @SuppressWarnings("unchecked") private static T attemptKnownMapCopy(T map) { if (map instanceof FastCopyHashMap) return (T) ((FastCopyHashMap) map).clone(); if (map instanceof HashMap) return (T) ((HashMap) map).clone(); if (map instanceof LinkedHashMap) return (T) ((LinkedHashMap) map).clone(); if (map instanceof TreeMap) return (T) ((TreeMap) map).clone(); return null; } @SuppressWarnings("unchecked") private static T attemptKnownSetCopy(T set) { if (set instanceof HashSet) return (T) ((HashSet) set).clone(); if (set instanceof LinkedHashSet) return (T) ((LinkedHashSet) set).clone(); if (set instanceof TreeSet) return (T) ((TreeSet) set).clone(); return null; } @SuppressWarnings("unchecked") private static T attemptClone(T source) { if (source instanceof Cloneable) { try { return (T) source.getClass().getMethod("clone").invoke(source); } catch (Exception e) { } } return null; } @SuppressWarnings("unchecked") private static T attemptCopyConstructor(T source, Class clazz) { try { return (T) source.getClass().getConstructor(clazz).newInstance(source); } catch (Exception e) { } return null; } public interface Immutable { } /* * Immutable wrapper types. * * We have to re-implement Collections.unmodifiableXXX, since it is not * simple to detect them (the class names are JDK dependent). */ private static class ImmutableIteratorWrapper implements Iterator { private Iterator iterator; public ImmutableIteratorWrapper(Iterator iterator) { this.iterator = iterator; } public boolean hasNext() { return iterator.hasNext(); } public E next() { return iterator.next(); } public void remove() { throw new UnsupportedOperationException(); } } private static class ImmutableCollectionWrapper implements Collection, Serializable, Immutable { private static final long serialVersionUID = 6777564328198393535L; Collection collection; public ImmutableCollectionWrapper(Collection collection) { this.collection = collection; } public boolean add(E o) { throw new UnsupportedOperationException(); } public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } public void clear() { throw new UnsupportedOperationException(); } public boolean contains(Object o) { return collection.contains(o); } public boolean containsAll(Collection c) { return collection.containsAll(c); } public boolean equals(Object o) { return collection.equals(o); } public int hashCode() { return collection.hashCode(); } public boolean isEmpty() { return collection.isEmpty(); } public Iterator iterator() { return new ImmutableIteratorWrapper(collection.iterator()); } public boolean remove(Object o) { throw new UnsupportedOperationException(); } public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } public int size() { return collection.size(); } public Object[] toArray() { return collection.toArray(); } public T[] toArray(T[] a) { return collection.toArray(a); } public String toString() { return collection.toString(); } } private static class ImmutableSetWrapper extends ImmutableCollectionWrapper implements Set, Serializable, Immutable { private static final long serialVersionUID = 7991492805176142615L; public ImmutableSetWrapper(Set set) { super(set); } } static class ImmutableEntry implements Map.Entry { private K key; private V value; private int hash; ImmutableEntry(Map.Entry entry) { this.key = entry.getKey(); this.value = entry.getValue(); this.hash = entry.hashCode(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { throw new UnsupportedOperationException(); } private static boolean eq(Object o1, Object o2) { return o1 == o2 || (o1 != null && o1.equals(o2)); } @SuppressWarnings("unchecked") public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry entry = (Map.Entry) o; return eq(entry.getKey(), key) && eq(entry.getValue(), value); } public int hashCode() { return hash; } public String toString() { return getKey() + "=" + getValue(); } } private static class ImmutableEntrySetWrapper extends ImmutableSetWrapper> { private static final long serialVersionUID = 6378667653889667692L; @SuppressWarnings("unchecked") public ImmutableEntrySetWrapper(Set> set) { super((Set>) set); } public Object[] toArray() { Object[] array = new Object[collection.size()]; int i = 0; for (Map.Entry entry : this) array[i++] = entry; return array; } @SuppressWarnings("unchecked") public T[] toArray(T[] array) { int size = collection.size(); if (array.length < size) array = (T[]) Array.newInstance(array.getClass().getComponentType(), size); int i = 0; Object[] result = array; for (Map.Entry entry : this) result[i++] = entry; return array; } public Iterator> iterator() { return new ImmutableIteratorWrapper>(collection.iterator()) { public Entry next() { return new ImmutableEntry(super.next()); } }; } } private static class ImmutableMapWrapper implements Map, Serializable, Immutable { private static final long serialVersionUID = 708144227046742221L; private Map map; public ImmutableMapWrapper(Map map) { this.map = map; } public void clear() { throw new UnsupportedOperationException(); } public boolean containsKey(Object key) { return map.containsKey(key); } public boolean containsValue(Object value) { return map.containsValue(value); } public Set> entrySet() { return new ImmutableEntrySetWrapper(map.entrySet()); } public boolean equals(Object o) { return map.equals(o); } public V get(Object key) { return map.get(key); } public int hashCode() { return map.hashCode(); } public boolean isEmpty() { return map.isEmpty(); } public Set keySet() { return new ImmutableSetWrapper(map.keySet()); } public V put(K key, V value) { throw new UnsupportedOperationException(); } public void putAll(Map t) { throw new UnsupportedOperationException(); } public V remove(Object key) { throw new UnsupportedOperationException(); } public int size() { return map.size(); } public Collection values() { return new ImmutableCollectionWrapper(map.values()); } public String toString() { return map.toString(); } } }