All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.commons.util.Immutables Maven / Gradle / Ivy

There is a newer version: 15.1.0.Dev04
Show newest version
package org.infinispan.commons.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Reader;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

import org.infinispan.commons.marshall.AbstractExternalizer;
import org.infinispan.commons.marshall.Ids;
import org.infinispan.commons.marshall.MarshallUtil;


/**
 * Factory for generating immutable type wrappers.
 *
 * @author Jason T. Greene
 * @author Galder Zamarreño
 * @author Tristan Tarrant
 * @since 4.0
 */
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) {
      if (list == null) return null;
      if (list.isEmpty()) return Collections.emptyList();
      if (list.size() == 1) return Collections.singletonList(list.get(0));
      return new ImmutableListCopy<>(list);
   }

   /**
    * Creates an immutable copy of the properties.
    *
    * @param properties the TypedProperties to copy
    * @return the immutable copy
    */
   public static TypedProperties immutableTypedProperties(TypedProperties properties) {
      if (properties == null) return null;
      return new ImmutableTypedProperties(properties);
   }

   /**
    * Wraps an array with an immutable list. There is no copying involved.
    *
    * @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);
   }

   public static  ImmutableListCopy immutableListAdd(List list, int position, T element) {
      T[] copy = (T[]) new Object[list.size() + 1];
      for (int i = 0; i < position; i++) {
         copy[i] = list.get(i);
      }
      copy[position] = element;
      for (int i = position; i < list.size(); i++) {
         copy[i + 1] = list.get(i);
      }
      return new ImmutableListCopy<>(copy);
   }

   public static  ImmutableListCopy immutableListReplace(List list, int position, T element) {
      T[] copy = (T[]) new Object[list.size()];
      for (int i = 0; i < position; i++) {
         copy[i] = list.get(i);
      }
      copy[position] = element;
      for (int i = position + 1; i < list.size(); i++) {
         copy[i] = list.get(i);
      }
      return new ImmutableListCopy<>(copy);
   }

   public static  List immutableListRemove(List list, int position) {
      T[] copy = (T[]) new Object[list.size() - 1];
      for (int i = 0; i < position; i++) {
         copy[i] = list.get(i);
      }
      for (int i = position + 1; i < list.size(); i++) {
         copy[i - 1] = list.get(i);
      }
      return new ImmutableListCopy<>(copy);
   }

   /**
    * 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) {
      if (set == null) return null;
      if (set.isEmpty()) return Collections.emptySet();
      if (set.size() == 1) return Collections.singleton(set.iterator().next());

      return new ImmutableSetWrapper<>(new HashSet<>(set));
   }


   /**
    * 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) {
      if (map == null) return null;
      if (map.isEmpty()) return Collections.emptyMap();
      if (map.size() == 1) {
         Map.Entry me = map.entrySet().iterator().next();
         return Collections.singletonMap(me.getKey(), me.getValue());
      }

      return new ImmutableMapWrapper<>(new HashMap<>(map));
   }

   /**
    * 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) {
      if (collection == null) return null;
      if (collection.isEmpty()) return Collections.emptySet();
      if (collection.size() == 1) return Collections.singleton(collection.iterator().next());

      return new ImmutableCollectionWrapper<>(new ArrayList<>(collection));
   }

   /**
    * Wraps a collection with an immutable collection. There is no copying involved.
    *
    * @param collection the collection to wrap
    * @return an immutable collection wrapper that delegates to the original collection
    */
   public static  Collection immutableCollectionWrap(Collection collection) {
      return new ImmutableCollectionWrapper<>(collection);
   }

   /**
    * Wraps a {@link Map.Entry}} with an immutable {@link Map.Entry}}. There is no copying involved.
    *
    * @param entry the mapping to wrap.
    * @return an immutable {@link Map.Entry}} wrapper that delegates to the original mapping.
    */
   public static  Map.Entry immutableEntry(Map.Entry entry) {
      return new ImmutableEntry<>(entry);
   }

   /**
    * Wraps a key and value with an immutable {@link Map.Entry}}. There is no copying involved.
    *
    * @param key the key to wrap.
    * @param value the value to wrap.
    * @return an immutable {@link Map.Entry}} wrapper that delegates to the original mapping.
    */
   public static  Map.Entry immutableEntry(K key, V value) {
      return new ImmutableEntry<>(key, value);
   }

   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).
    */
   public static class ImmutableIteratorWrapper implements Iterator {
      private final Iterator iterator;

      public ImmutableIteratorWrapper(Iterator iterator) {
         this.iterator = iterator;
      }

      @Override
      public boolean hasNext() {
         return iterator.hasNext();
      }

      @Override
      public E next() {
         return iterator.next();
      }

      // Use the default remove() implementation

      @Override
      public void forEachRemaining(Consumer action) {
         iterator.forEachRemaining(action);
      }
   }

   private static class ImmutableCollectionWrapper implements Collection, Serializable, Immutable {
      private static final long serialVersionUID = 6777564328198393535L;

      Collection collection;

      public ImmutableCollectionWrapper(Collection collection) {
         this.collection = collection;
      }

      @Override
      public boolean add(E o) {
         throw new UnsupportedOperationException();
      }

      @Override
      public boolean addAll(Collection c) {
         throw new UnsupportedOperationException();
      }

      @Override
      public void clear() {
         throw new UnsupportedOperationException();
      }

      @Override
      public boolean contains(Object o) {
         return collection.contains(o);
      }

      @Override
      public boolean containsAll(Collection c) {
         return collection.containsAll(c);
      }

      @Override
      public boolean equals(Object o) {
         return collection.equals(o);
      }

      @Override
      public int hashCode() {
         return collection.hashCode();
      }

      @Override
      public boolean isEmpty() {
         return collection.isEmpty();
      }

      @Override
      public Iterator iterator() {
         return new ImmutableIteratorWrapper<>(collection.iterator());
      }

      @Override
      public boolean remove(Object o) {
         throw new UnsupportedOperationException();
      }

      @Override
      public boolean removeAll(Collection c) {
         throw new UnsupportedOperationException();
      }

      @Override
      public boolean retainAll(Collection c) {
         throw new UnsupportedOperationException();
      }

      @Override
      public int size() {
         return collection.size();
      }

      @Override
      public Object[] toArray() {
         return collection.toArray();
      }

      @Override
      public  T[] toArray(T[] a) {
         return collection.toArray(a);
      }

      @Override
      public String toString() {
         return collection.toString();
      }
   }

   /**
    * Immutable version of Map.Entry for traversing immutable collections.
    */
   private static class ImmutableEntry implements Entry, Immutable {
      private final K key;
      private final V value;
      private final int hash;

      ImmutableEntry(Entry entry) {
         this.key = entry.getKey();
         this.value = entry.getValue();
         this.hash = entry.hashCode();
      }

      ImmutableEntry(K key, V value) {
         this.key = key;
         this.value = value;
         this.hash = Objects.hashCode(key) ^ Objects.hashCode(value);
      }

      @Override
      public K getKey() {
         return key;
      }

      @Override
      public V getValue() {
         return value;
      }

      @Override
      public V setValue(V value) {
         throw new UnsupportedOperationException();
      }

      private static boolean eq(Object o1, Object o2) {
         return o1 == o2 || (o1 != null && o1.equals(o2));
      }

      @Override
      @SuppressWarnings("unchecked")
      public boolean equals(Object o) {
         if (!(o instanceof Entry))
            return false;

         Entry entry = (Entry) o;
         return eq(entry.getKey(), key) && eq(entry.getValue(), value);
      }

      @Override
      public int hashCode() {
         return hash;
      }

      @Override
      public String toString() {
         return getKey() + "=" + getValue();
      }
   }

   private static class ImmutableSetWrapper extends ImmutableCollectionWrapper implements Set, Serializable, Immutable {
      private static final long serialVersionUID = 7991492805176142615L;

      public ImmutableSetWrapper(Set set) {
         super(set);
      }
   }

   private static class ImmutableEntrySetWrapper extends ImmutableSetWrapper> {
      private static final long serialVersionUID = 6378667653889667692L;

      @SuppressWarnings("unchecked")
      public ImmutableEntrySetWrapper(Set> set) {
         super((Set>) set);
      }

      @Override
      public Object[] toArray() {
         Object[] array = new Object[collection.size()];
         int i = 0;
         for (Entry entry : this)
            array[i++] = entry;
         return array;
      }

      @Override
      @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 (Entry entry : this)
            result[i++] = entry;

         return array;
      }

      @Override
      public Iterator> iterator() {
         return new ImmutableIteratorWrapper>(collection.iterator()) {
            @Override
            public Entry next() {
               return new ImmutableEntry<>(super.next());
            }
         };
      }
   }

   public static class ImmutableSetWrapperExternalizer extends AbstractExternalizer {
      @Override
      public void writeObject(ObjectOutput output, Set set) throws IOException {
         MarshallUtil.marshallCollection(set, output);
      }

      @Override
      public Set readObject(ObjectInput input) throws IOException, ClassNotFoundException {
         Set set = MarshallUtil.unmarshallCollection(input, HashSet::new);
         return Immutables.immutableSetWrap(set);
      }

      @Override
      public Integer getId() {
         return Ids.IMMUTABLE_SET;
      }

      @Override
      public Set> getTypeClasses() {
         return Util.asSet(ImmutableSetWrapper.class);
      }
   }

   private static class ImmutableMapWrapper implements Map, Serializable, Immutable {
      private static final long serialVersionUID = 708144227046742221L;

      private final Map map;

      public ImmutableMapWrapper(Map map) {
         this.map = map;
      }

      @Override
      public void clear() {
         throw new UnsupportedOperationException();
      }

      @Override
      public boolean containsKey(Object key) {
         return map.containsKey(key);
      }

      @Override
      public boolean containsValue(Object value) {
         return map.containsValue(value);
      }

      @Override
      public Set> entrySet() {
         return new ImmutableEntrySetWrapper<>(map.entrySet());
      }

      @Override
      public boolean equals(Object o) {
         return map.equals(o);
      }

      @Override
      public V get(Object key) {
         return map.get(key);
      }

      @Override
      public int hashCode() {
         return map.hashCode();
      }

      @Override
      public boolean isEmpty() {
         return map.isEmpty();
      }

      @Override
      public Set keySet() {
         return new ImmutableSetWrapper<>(map.keySet());
      }

      @Override
      public V put(K key, V value) {
         throw new UnsupportedOperationException();
      }

      @Override
      public void putAll(Map t) {
         throw new UnsupportedOperationException();
      }

      @Override
      public V remove(Object key) {
         throw new UnsupportedOperationException();
      }

      @Override
      public int size() {
         return map.size();
      }

      @Override
      public Collection values() {
         return new ImmutableCollectionWrapper<>(map.values());
      }

      @Override
      public String toString() {
         return map.toString();
      }
   }

   public static class ImmutableMapWrapperExternalizer extends AbstractExternalizer {
      @Override
      public void writeObject(ObjectOutput output, Map map) throws IOException {
         MarshallUtil.marshallMap(map, output);
      }

      @Override
      public Map readObject(ObjectInput input) throws IOException, ClassNotFoundException {
         return Immutables.immutableMapWrap(MarshallUtil.unmarshallMap(input, HashMap::new));
      }

      @Override
      public Integer getId() {
         return Ids.IMMUTABLE_MAP;
      }

      @Override
      public Set> getTypeClasses() {
         return Util.asSet(ImmutableMapWrapper.class);
      }
   }

   private static class ImmutableTypedProperties extends TypedProperties {

      ImmutableTypedProperties(TypedProperties properties) {
         super();
         if (properties != null && !properties.isEmpty()) {
            for (Map.Entry e: properties.entrySet()) super.put(e.getKey(), e.getValue());
         }
      }

      @Override
      public synchronized void clear() {
         throw new UnsupportedOperationException();
      }

      @Override
      public Set> entrySet() {
         return new ImmutableEntrySetWrapper<>(super.entrySet());
      }

      @Override
      public Set keySet() {
         return new ImmutableSetWrapper<>(super.keySet());
      }

      @Override
      public synchronized void load(InputStream inStream) {
         throw new UnsupportedOperationException();
      }

      @Override
      public synchronized void load(Reader reader) {
         throw new UnsupportedOperationException();
      }

      @Override
      public synchronized void loadFromXML(InputStream in) {
         throw new UnsupportedOperationException();
      }

      @Override
      public synchronized Object put(Object key, Object value) {
         throw new UnsupportedOperationException();
      }

      @Override
      public synchronized void putAll(Map t) {
         throw new UnsupportedOperationException();
      }

      @Override
      public synchronized Object remove(Object key) {
         throw new UnsupportedOperationException();
      }

      @Override
      public synchronized TypedProperties setProperty(String key, String value) {
         throw new UnsupportedOperationException();
      }

      @Override
      public Set stringPropertyNames() {
         return new ImmutableSetWrapper<>(super.stringPropertyNames());
      }

      @Override
      public Collection values() {
         return new ImmutableCollectionWrapper<>(super.values());
      }

   }


}