001    /*
002     * Copyright (C) 2007 Google Inc.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.collect;
018    
019    import static com.google.common.base.Preconditions.checkNotNull;
020    
021    import com.google.common.annotations.Beta;
022    import com.google.common.base.FinalizableReferenceQueue;
023    import com.google.common.base.FinalizableWeakReference;
024    import com.google.common.base.Function;
025    
026    import java.util.concurrent.ConcurrentMap;
027    
028    /**
029     * Contains static methods pertaining to instances of {@link Interner}.
030     *
031     * @author Kevin Bourrillion
032     * @since 3
033     */
034    @Beta
035    public final class Interners {
036      private Interners() {}
037    
038      /**
039       * Returns a new thread-safe interner which retains a strong reference to each
040       * instance it has interned, thus preventing these instances from being
041       * garbage-collected. If this retention is acceptable, this implementation may
042       * perform better than {@link #newWeakInterner}. Note that unlike {@link
043       * String#intern}, using this interner does not consume memory in the
044       * permanent generation.
045       */
046      public static <E> Interner<E> newStrongInterner() {
047        final ConcurrentMap<E, E> map = new MapMaker().makeMap();
048        return new Interner<E>() {
049          public E intern(E sample) {
050            E canonical = map.putIfAbsent(checkNotNull(sample), sample);
051            return (canonical == null) ? sample : canonical;
052          }
053        };
054      }
055    
056      /**
057       * Returns a new thread-safe interner which retains a weak reference to each
058       * instance it has interned, and so does not prevent these instances from
059       * being garbage-collected. This most likely does not perform as well as
060       * {@link #newStrongInterner}, but is the best alternative when the memory
061       * usage of that implementation is unacceptable. Note that unlike {@link
062       * String#intern}, using this interner does not consume memory in the
063       * permanent generation.
064       */
065      public static <E> Interner<E> newWeakInterner() {
066        return new WeakInterner<E>();
067      }
068    
069      private static class WeakInterner<E> implements Interner<E> {
070        private final ConcurrentMap<InternReference, InternReference> map
071            = new MapMaker().makeMap();
072    
073        public E intern(final E sample) {
074          final int hashCode = sample.hashCode();
075    
076          // TODO(kevinb): stop using the dummy instance; use custom Equivalence?
077          Object fakeReference = new Object() {
078            @Override public int hashCode() {
079              return hashCode;
080            }
081            @Override public boolean equals(Object object) {
082              if (object.hashCode() != hashCode) {
083                return false;
084              }
085              /*
086               * Implicitly an unchecked cast to WeakInterner<?>.InternReference,
087               * though until OpenJDK 7, the compiler doesn't recognize this. If we
088               * could explicitly cast to the wildcard type
089               * WeakInterner<?>.InternReference, that would be sufficient for our
090               * purposes. The compiler, however, rejects such casts (or rather, it
091               * does until OpenJDK 7).
092               *
093               * See Sun bug 6665356.
094               */
095              @SuppressWarnings("unchecked")
096              InternReference that = (InternReference) object;
097              return sample.equals(that.get());
098            }
099          };
100    
101          // Fast-path; avoid creating the reference if possible
102          InternReference existingRef = map.get(fakeReference);
103          if (existingRef != null) {
104            E canonical = existingRef.get();
105            if (canonical != null) {
106              return canonical;
107            }
108          }
109    
110          InternReference newRef = new InternReference(sample, hashCode);
111          while (true) {
112            InternReference sneakyRef = map.putIfAbsent(newRef, newRef);
113            if (sneakyRef == null) {
114              return sample;
115            } else {
116              E canonical = sneakyRef.get();
117              if (canonical != null) {
118                return canonical;
119              }
120            }
121          }
122        }
123    
124        private static final FinalizableReferenceQueue frq
125            = new FinalizableReferenceQueue();
126    
127        class InternReference extends FinalizableWeakReference<E> {
128          final int hashCode;
129    
130          InternReference(E key, int hash) {
131            super(key, frq);
132            hashCode = hash;
133          }
134          public void finalizeReferent() {
135            map.remove(this);
136          }
137          @Override public E get() {
138            E referent = super.get();
139            if (referent == null) {
140              finalizeReferent();
141            }
142            return referent;
143          }
144          @Override public int hashCode() {
145            return hashCode;
146          }
147          @Override public boolean equals(Object object) {
148            if (object == this) {
149              return true;
150            }
151            if (object instanceof WeakInterner.InternReference) {
152              /*
153               * On the following line, Eclipse wants a type parameter, producing
154               * WeakInterner<?>.InternReference. The problem is that javac rejects
155               * that form. Omitting WeakInterner satisfies both, though this seems
156               * odd, since we are inside a WeakInterner<E> and thus the
157               * WeakInterner<E> is implied, yet there is no reason to believe that
158               * the other object's WeakInterner has type E. That's right -- we've
159               * found a way to perform an unchecked cast without receiving a
160               * warning from either Eclipse or javac. Taking advantage of that
161               * seems questionable, even though we don't depend upon the type of
162               * that.get(), so we'll just suppress the warning.
163               */
164              @SuppressWarnings("unchecked")
165              WeakInterner.InternReference that =
166                  (WeakInterner.InternReference) object;
167              if (that.hashCode != hashCode) {
168                return false;
169              }
170              E referent = super.get();
171              return referent != null && referent.equals(that.get());
172            }
173            return object.equals(this);
174          }
175        }
176      }
177    
178      /**
179       * Returns a function that delegates to the {@link Interner#intern} method of
180       * the given interner.
181       *
182       * @since 8
183       */
184      public static <E> Function<E, E> asFunction(Interner<E> interner) {
185        return new InternerFunction<E>(checkNotNull(interner));
186      }
187    
188      private static class InternerFunction<E> implements Function<E, E> {
189    
190        private final Interner<E> interner;
191    
192        public InternerFunction(Interner<E> interner) {
193          this.interner = interner;
194        }
195    
196        @Override public E apply(E input) {
197          return interner.intern(input);
198        }
199    
200        @Override public int hashCode() {
201          return interner.hashCode();
202        }
203    
204        @Override public boolean equals(Object other) {
205          if (other instanceof InternerFunction<?>) {
206            InternerFunction<?> that = (InternerFunction<?>) other;
207            return interner.equals(that.interner);
208          }
209    
210          return false;
211        }
212      }
213    }