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    
025    import java.util.concurrent.ConcurrentMap;
026    
027    /**
028     * Contains static methods pertaining to instances of {@link Interner}.
029     *
030     * @author Kevin Bourrillion
031     * @since 3
032     */
033    @Beta
034    public final class Interners {
035      private Interners() {}
036    
037      /**
038       * Returns a new thread-safe interner which retains a strong reference to
039       * each instance it has interned, thus preventing these instances from being
040       * garbage-collected. If this retention is acceptable, this implementation may
041       * perform better than {@link #newWeakInterner}.
042       */
043      public static <E> Interner<E> newStrongInterner() {
044        final ConcurrentMap<E, E> map = new MapMaker().makeMap();
045        return new Interner<E>() {
046          public E intern(E sample) {
047            E canonical = map.putIfAbsent(checkNotNull(sample), sample);
048            return (canonical == null) ? sample : canonical;
049          }
050        };
051      }
052    
053      /**
054       * Returns a new thread-safe interner which retains a weak reference to each
055       * instance it has interned, and so does not prevent these instances from
056       * being garbage-collected. This most likely does not perform as well as
057       * {@link #newStrongInterner}, but is the best alternative when the memory
058       * usage of that implementation is unacceptable.
059       */
060      public static <E> Interner<E> newWeakInterner() {
061        return new WeakInterner<E>();
062      }
063    
064      private static class WeakInterner<E> implements Interner<E> {
065        private final ConcurrentMap<InternReference, InternReference> map
066            = new MapMaker().makeMap();
067    
068        public E intern(final E sample) {
069          final int hashCode = sample.hashCode();
070    
071          // TODO: once MapMaker supports arbitrary Equivalence, we won't need the
072          // dummy instance anymore
073          Object fakeReference = new Object() {
074            @Override public int hashCode() {
075              return hashCode;
076            }
077            @Override public boolean equals(Object object) {
078              if (object.hashCode() != hashCode) {
079                return false;
080              }
081              /*
082               * Implicitly an unchecked cast to WeakInterner<?>.InternReference,
083               * though until OpenJDK 7, the compiler doesn't recognize this. If we
084               * could explicitly cast to the wildcard type
085               * WeakInterner<?>.InternReference, that would be sufficient for our
086               * purposes. The compiler, however, rejects such casts (or rather, it
087               * does until OpenJDK 7).
088               *
089               * See Sun bug 6665356.
090               */
091              @SuppressWarnings("unchecked")
092              InternReference that = (InternReference) object;
093              return sample.equals(that.get());
094            }
095          };
096    
097          // Fast-path; avoid creating the reference if possible
098          InternReference existingRef = map.get(fakeReference);
099          if (existingRef != null) {
100            E canonical = existingRef.get();
101            if (canonical != null) {
102              return canonical;
103            }
104          }
105    
106          InternReference newRef = new InternReference(sample, hashCode);
107          while (true) {
108            InternReference sneakyRef = map.putIfAbsent(newRef, newRef);
109            if (sneakyRef == null) {
110              return sample;
111            } else {
112              E canonical = sneakyRef.get();
113              if (canonical != null) {
114                return canonical;
115              }
116            }
117          }
118        }
119    
120        private static final FinalizableReferenceQueue frq
121            = new FinalizableReferenceQueue();
122    
123        class InternReference extends FinalizableWeakReference<E> {
124          final int hashCode;
125    
126          InternReference(E key, int hash) {
127            super(key, frq);
128            hashCode = hash;
129          }
130          public void finalizeReferent() {
131            map.remove(this);
132          }
133          @Override public E get() {
134            E referent = super.get();
135            if (referent == null) {
136              finalizeReferent();
137            }
138            return referent;
139          }
140          @Override public int hashCode() {
141            return hashCode;
142          }
143          @Override public boolean equals(Object object) {
144            // TODO: should we try to do something to make equals() somewhat more
145            // normal?
146            if (object == this) {
147              return true;
148            }
149            if (object instanceof WeakInterner.InternReference) {
150              /*
151               * On the following line, Eclipse wants a type parameter, producing
152               * WeakInterner<?>.InternReference. The problem is that javac rejects
153               * that form. Omitting WeakInterner satisfies both, though this seems
154               * odd, since we are inside a WeakInterner<E> and thus the
155               * WeakInterner<E> is implied, yet there is no reason to believe that
156               * the other object's WeakInterner has type E. That's right -- we've
157               * found a way to perform an unchecked cast without receiving a
158               * warning from either Eclipse or javac. Taking advantage of that
159               * seems questionable, even though we don't depend upon the type of
160               * that.get(), so we'll just suppress the warning.
161               */
162              @SuppressWarnings("unchecked")
163              WeakInterner.InternReference that =
164                  (WeakInterner.InternReference) object;
165              if (that.hashCode != hashCode) {
166                return false;
167              }
168              E referent = super.get();
169              return referent != null && referent.equals(that.get());
170            }
171            return object.equals(this);
172          }
173        }
174      }
175    }