aboutsummaryrefslogtreecommitdiff
path: root/guava-gwt
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2015-01-06 16:17:43 +0000
committerPaul Duffin <paulduffin@google.com>2015-01-08 14:36:14 +0000
commit3c77433663281544363151bf284b0240dfd22a42 (patch)
tree80bc061726b4720c9bf713a2a494920b393c2a7e /guava-gwt
parent5e6db342fc75b1945298142530f2d1d1861bce73 (diff)
downloadguava-3c77433663281544363151bf284b0240dfd22a42.tar.gz
Upgraded Guava to unmodified v14.0.1
This simply copies the Guava source for v14.0.1 straight from its github repository into this one. Additional commits will be made which will allow this to compile on Android. Change-Id: If0a8231e1d9530b7bdd94474403f7055e013979f
Diffstat (limited to 'guava-gwt')
-rw-r--r--guava-gwt/pom.xml109
-rw-r--r--guava-gwt/src-super/com/google/common/base/super/com/google/common/base/CharMatcher.java1244
-rw-r--r--guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Charsets.java52
-rw-r--r--guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Enums.java108
-rw-r--r--guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Predicates.java22
-rw-r--r--guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Splitter.java136
-rw-r--r--guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Stopwatch.java40
-rw-r--r--guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheBuilder.java741
-rw-r--r--guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheLoader.java50
-rw-r--r--guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LocalCache.java829
-rw-r--r--guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAddables.java (renamed from guava-gwt/test/com/google/common/net/TestPlatform.java)16
-rw-r--r--guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java40
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractBiMap.java76
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultimap.java1259
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultiset.java117
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractSortedMultiset.java142
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayListMultimap.java6
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayTable.java817
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ContiguousSet.java142
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/DescendingMultiset.java140
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyContiguousSet.java5
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableBiMap.java (renamed from guava-gwt/src-super/com/google/common/escape/super/com/google/common/escape/Platform.java)25
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableList.java15
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSet.java7
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedMap.java42
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedSet.java2
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumBiMap.java15
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumHashBiMap.java13
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumMultiset.java17
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/FluentIterable.java508
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableList.java94
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableMap.java125
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableSet.java76
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/GenericMapMaker.java17
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashBiMap.java9
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashMultimap.java2
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList.java38
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableBiMap.java23
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableCollection.java15
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumMap.java41
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumSet.java13
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableList.java102
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableListMultimap.java26
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMap.java200
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapEntrySet.java57
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapKeySet.java77
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapValues.java73
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMultimap.java280
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSet.java20
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSetMultimap.java85
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedAsList.java53
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedMap.java102
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedSet.java22
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterables.java146
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterators.java182
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultimap.java539
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultiset.java4
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedListMultimap.java452
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Lists.java1122
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/MapMaker.java28
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Maps.java919
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Multimaps.java834
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ObjectArrays.java15
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Platform.java42
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularContiguousSet.java52
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableList.java13
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableMap.java4
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSet.java2
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedMap.java31
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedSet.java4
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Sets.java420
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableBiMap.java (renamed from guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableMap.java)31
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableList.java12
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableSet.java23
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultiset.java57
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultisets.java93
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Synchronized.java67
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultimap.java42
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultiset.java1066
-rw-r--r--guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/UnmodifiableSortedMultiset.java112
-rw-r--r--guava-gwt/src-super/com/google/common/io/super/com/google/common/io/BaseEncoding.java794
-rw-r--r--guava-gwt/src-super/com/google/common/io/super/com/google/common/io/GwtWorkarounds.java118
-rw-r--r--guava-gwt/src-super/com/google/common/math/super/com/google/common/math/BigIntegerMath.java268
-rw-r--r--guava-gwt/src-super/com/google/common/math/super/com/google/common/math/IntMath.java292
-rw-r--r--guava-gwt/src-super/com/google/common/math/super/com/google/common/math/LongMath.java300
-rw-r--r--guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Chars.java9
-rw-r--r--guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Doubles.java (renamed from guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Longs.java)256
-rw-r--r--guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Floats.java530
-rw-r--r--guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Ints.java20
-rw-r--r--guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Shorts.java20
-rw-r--r--guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/UnsignedInteger.java312
-rw-r--r--guava-gwt/src-super/java/nio/charset/Charset.gwt.xml4
-rw-r--r--guava-gwt/src-super/java/nio/charset/Charset.java101
-rw-r--r--guava-gwt/src-super/java/nio/charset/IllegalCharsetNameException.java35
-rw-r--r--guava-gwt/src-super/java/nio/charset/UnsupportedCharsetException.java35
-rw-r--r--guava-gwt/src-super/java/util/super/java/util/concurrent/Callable.java2
-rw-r--r--guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentHashMap.java2
-rw-r--r--guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentMap.java2
-rw-r--r--guava-gwt/src-super/java/util/super/java/util/concurrent/ExecutionException.java4
-rw-r--r--guava-gwt/src-super/java/util/super/java/util/concurrent/TimeUnit.java4
-rw-r--r--guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicInteger.java2
-rw-r--r--guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicLong.java2
-rw-r--r--guava-gwt/src/com/google/common/ForceGuavaCompilation.gwt.xml15
-rw-r--r--guava-gwt/src/com/google/common/ForceGuavaCompilationEntryPoint.java (renamed from guava-gwt/src-super/com/google/common/testing/super/com/google/common/testing/Platform.java)19
-rw-r--r--guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml3
-rw-r--r--guava-gwt/src/com/google/common/base/Absent_CustomFieldSerializer.java41
-rw-r--r--guava-gwt/src/com/google/common/base/Base.gwt.xml10
-rw-r--r--guava-gwt/src/com/google/common/base/GwtSerializationDependencies.java57
-rw-r--r--guava-gwt/src/com/google/common/base/PairwiseEquivalence_CustomFieldSerializer.java2
-rw-r--r--guava-gwt/src/com/google/common/base/Present_CustomFieldSerializer.java42
-rw-r--r--guava-gwt/src/com/google/common/cache/Cache.gwt.xml5
-rw-r--r--guava-gwt/src/com/google/common/collect/AllEqualOrdering_CustomFieldSerializer.java37
-rw-r--r--guava-gwt/src/com/google/common/collect/Collect.gwt.xml7
-rw-r--r--guava-gwt/src/com/google/common/collect/EmptyImmutableBiMap_CustomFieldSerializer.java (renamed from guava-gwt/src/com/google/common/collect/EmptyImmutableMap_CustomFieldSerializer.java)18
-rw-r--r--guava-gwt/src/com/google/common/collect/EmptyImmutableSortedMap_CustomFieldSerializer.java44
-rw-r--r--guava-gwt/src/com/google/common/collect/ForwardingImmutableList_CustomFieldSerializer.java (renamed from guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableMap.java)9
-rw-r--r--guava-gwt/src/com/google/common/collect/ForwardingImmutableSet_CustomFieldSerializer.java25
-rw-r--r--guava-gwt/src/com/google/common/collect/GwtPlatform.java57
-rw-r--r--guava-gwt/src/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java38
-rw-r--r--guava-gwt/src/com/google/common/collect/ImmutableBiMap_CustomFieldSerializer.java26
-rw-r--r--guava-gwt/src/com/google/common/collect/ImmutableEntry_CustomFieldSerializer.java2
-rw-r--r--guava-gwt/src/com/google/common/collect/ImmutableEnumMap_CustomFieldSerializer.java54
-rw-r--r--guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializer.java41
-rw-r--r--guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializerBase.java58
-rw-r--r--guava-gwt/src/com/google/common/collect/LexicographicalOrdering_CustomFieldSerializer.java3
-rw-r--r--guava-gwt/src/com/google/common/collect/LinkedHashMultimap_CustomFieldSerializer.java45
-rw-r--r--guava-gwt/src/com/google/common/collect/Multiset_CustomFieldSerializerBase.java15
-rw-r--r--guava-gwt/src/com/google/common/collect/RegularImmutableAsList_CustomFieldSerializer.java (renamed from guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java)28
-rw-r--r--guava-gwt/src/com/google/common/collect/RegularImmutableSortedMap_CustomFieldSerializer.java44
-rw-r--r--guava-gwt/src/com/google/common/collect/SingletonImmutableBiMap_CustomFieldSerializer.java (renamed from guava-gwt/src/com/google/common/collect/SingletonImmutableMap_CustomFieldSerializer.java)12
-rw-r--r--guava-gwt/src/com/google/common/io/Io.gwt.xml (renamed from guava-gwt/test/com/google/common/testing/Testing.gwt.xml)7
-rw-r--r--guava-gwt/src/com/google/common/math/Math.gwt.xml7
-rw-r--r--guava-gwt/src/com/google/common/net/Net.gwt.xml13
-rw-r--r--guava-gwt/src/com/google/common/primitives/Primitives.gwt.xml15
-rw-r--r--guava-gwt/src/com/google/common/primitives/UnsignedLong_CustomFieldSerializer.java39
-rw-r--r--guava-gwt/src/com/google/common/util/concurrent/Concurrent.gwt.xml9
-rw-r--r--guava-gwt/test/com/google/common/cache/TestModuleEntryPoint.java (renamed from guava-gwt/test/com/google/common/testing/TestModuleEntryPoint.java)2
-rw-r--r--guava-gwt/test/com/google/common/cache/testModule.gwt.xml20
-rw-r--r--guava-gwt/test/com/google/common/collect/testModule.gwt.xml3
-rw-r--r--guava-gwt/test/com/google/common/io/TestModuleEntryPoint.java30
-rw-r--r--guava-gwt/test/com/google/common/io/testModule.gwt.xml18
-rw-r--r--guava-gwt/test/com/google/common/math/testModule.gwt.xml2
-rw-r--r--guava-gwt/test/com/google/common/net/testModule.gwt.xml1
-rw-r--r--guava-gwt/test/com/google/common/primitives/testModule.gwt.xml2
-rw-r--r--guava-gwt/test/com/google/common/util/concurrent/TestModuleEntryPoint.java30
-rw-r--r--guava-gwt/test/com/google/common/util/concurrent/testModule.gwt.xml (renamed from guava-gwt/test/com/google/common/testing/testModule.gwt.xml)6
146 files changed, 15211 insertions, 3459 deletions
diff --git a/guava-gwt/pom.xml b/guava-gwt/pom.xml
index f087b3d50..6b44fac43 100644
--- a/guava-gwt/pom.xml
+++ b/guava-gwt/pom.xml
@@ -5,7 +5,7 @@
<parent>
<groupId>com.google.guava</groupId>
<artifactId>guava-parent</artifactId>
- <version>11.0.2</version>
+ <version>14.0.1</version>
</parent>
<artifactId>guava-gwt</artifactId>
<name>Guava GWT compatible libs</name>
@@ -16,53 +16,89 @@
This project includes GWT-friendly sources.
</description>
+ <properties>
+ <gwt.version>2.5.0-rc1</gwt.version>
+ </properties>
<dependencies>
+ <!-- GWT requires a library's transitive dependencies to be present when compiling a project that uses that library, thanks to its full-program compilation, so we don't use scope=provided. -->
<dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>${project.version}</version>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ <version>1.3.9</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${project.version}</version>
- <classifier>sources</classifier>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.5</version>
- <scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
- <version>2.2.0</version>
- <type>jar</type>
+ <version>${gwt.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
- <version>2.2.0</version>
- <type>jar</type>
+ <version>${gwt.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
- <source>1.5</source>
- <target>1.5</target>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.3.1</version>
+ <configuration>
+ <excludes>
+ <exclude>**/ForceGuavaCompilation*</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.1.2</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>post-integration-test</phase>
+ <goals><goal>jar</goal></goals>
+ </execution>
+ </executions>
+ <configuration>
+ <excludes>
+ <exclude>**/ForceGuavaCompilation*</exclude>
+ </excludes>
</configuration>
</plugin>
<plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.8</version>
+ <executions>
+ <execution>
+ <id>attach-docs</id>
+ <phase>post-integration-test</phase>
+ <goals><goal>jar</goal></goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Disable "normal" testing, which doesn't work for GWT tests. -->
+ <plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
@@ -75,7 +111,6 @@
<classifier>sources</classifier>
<overWrite>true</overWrite>
<excludeTransitive>true</excludeTransitive>
- <includeScope>provided</includeScope>
<excludes>META-INF/MANIFEST.MF</excludes>
<outputDirectory>${project.build.directory}/guava-sources</outputDirectory>
<type>java-source</type>
@@ -83,9 +118,16 @@
</configuration>
</execution>
</executions>
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${project.version}</version>
+ <classifier>sources</classifier>
+ </dependency>
+ </dependencies>
</plugin>
<plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.6</version>
<executions>
@@ -109,9 +151,27 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>gwt-maven-plugin</artifactId>
+ <version>${gwt.version}</version>
+ <executions>
+ <execution>
+ <id>gwt-compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <configuration>
+ <module>com.google.common.ForceGuavaCompilation</module>
+ <strict>true</strict>
+ <validateOnly>true</validateOnly>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
<sourceDirectory>src</sourceDirectory>
- <testSourceDirectory>disabled</testSourceDirectory>
+ <testSourceDirectory>test</testSourceDirectory>
<resources>
<resource>
<directory>src</directory>
@@ -123,5 +183,10 @@
<directory>${project.build.directory}/guava-gwt-sources</directory>
</resource>
</resources>
+ <testResources>
+ <testResource>
+ <directory>test</directory>
+ </testResource>
+ </testResources>
</build>
</project>
diff --git a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/CharMatcher.java b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/CharMatcher.java
new file mode 100644
index 000000000..ed30766bd
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/CharMatcher.java
@@ -0,0 +1,1244 @@
+/*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed 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 com.google.common.base;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.Arrays;
+
+import javax.annotation.CheckReturnValue;
+
+/**
+ * Determines a true or false value for any Java {@code char} value, just as {@link Predicate} does
+ * for any {@link Object}. Also offers basic text processing methods based on this function.
+ * Implementations are strongly encouraged to be side-effect-free and immutable.
+ *
+ * <p>Throughout the documentation of this class, the phrase "matching character" is used to mean
+ * "any character {@code c} for which {@code this.matches(c)} returns {@code true}".
+ *
+ * <p><b>Note:</b> This class deals only with {@code char} values; it does not understand
+ * supplementary Unicode code points in the range {@code 0x10000} to {@code 0x10FFFF}. Such logical
+ * characters are encoded into a {@code String} using surrogate pairs, and a {@code CharMatcher}
+ * treats these just as two separate characters.
+ *
+ * <p>Example usages: <pre>
+ * String trimmed = {@link #WHITESPACE WHITESPACE}.{@link #trimFrom trimFrom}(userInput);
+ * if ({@link #ASCII ASCII}.{@link #matchesAllOf matchesAllOf}(s)) { ... }</pre>
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/StringsExplained#CharMatcher">
+ * {@code CharMatcher}</a>.
+ *
+ * @author Kevin Bourrillion
+ * @since 1.0
+ */
+@Beta // Possibly change from chars to code points; decide constants vs. methods
+@GwtCompatible(emulated = true)
+public abstract class CharMatcher implements Predicate<Character> {
+ // Constants
+ /**
+ * Determines whether a character is a breaking whitespace (that is, a whitespace which can be
+ * interpreted as a break between words for formatting purposes). See {@link #WHITESPACE} for a
+ * discussion of that term.
+ *
+ * @since 2.0
+ */
+ public static final CharMatcher BREAKING_WHITESPACE = new CharMatcher() {
+ @Override
+ public boolean matches(char c) {
+ switch (c) {
+ case '\t':
+ case '\n':
+ case '\013':
+ case '\f':
+ case '\r':
+ case ' ':
+ case '\u0085':
+ case '\u1680':
+ case '\u2028':
+ case '\u2029':
+ case '\u205f':
+ case '\u3000':
+ return true;
+ case '\u2007':
+ return false;
+ default:
+ return c >= '\u2000' && c <= '\u200a';
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "CharMatcher.BREAKING_WHITESPACE";
+ }
+ };
+
+ /**
+ * Determines whether a character is ASCII, meaning that its code point is less than 128.
+ */
+ public static final CharMatcher ASCII = inRange('\0', '\u007f', "CharMatcher.ASCII");
+
+ private static class RangesMatcher extends CharMatcher {
+ private final char[] rangeStarts;
+ private final char[] rangeEnds;
+
+ RangesMatcher(String description, char[] rangeStarts, char[] rangeEnds) {
+ super(description);
+ this.rangeStarts = rangeStarts;
+ this.rangeEnds = rangeEnds;
+ checkArgument(rangeStarts.length == rangeEnds.length);
+ for (int i = 0; i < rangeStarts.length; i++) {
+ checkArgument(rangeStarts[i] <= rangeEnds[i]);
+ if (i + 1 < rangeStarts.length) {
+ checkArgument(rangeEnds[i] < rangeStarts[i + 1]);
+ }
+ }
+ }
+
+ @Override
+ public boolean matches(char c) {
+ int index = Arrays.binarySearch(rangeStarts, c);
+ if (index >= 0) {
+ return true;
+ } else {
+ index = ~index - 1;
+ return index >= 0 && c <= rangeEnds[index];
+ }
+ }
+ }
+
+ // Must be in ascending order.
+ private static final String ZEROES = "0\u0660\u06f0\u07c0\u0966\u09e6\u0a66\u0ae6\u0b66\u0be6"
+ + "\u0c66\u0ce6\u0d66\u0e50\u0ed0\u0f20\u1040\u1090\u17e0\u1810\u1946\u19d0\u1b50\u1bb0"
+ + "\u1c40\u1c50\ua620\ua8d0\ua900\uaa50\uff10";
+
+ private static final String NINES;
+ static {
+ StringBuilder builder = new StringBuilder(ZEROES.length());
+ for (int i = 0; i < ZEROES.length(); i++) {
+ builder.append((char) (ZEROES.charAt(i) + 9));
+ }
+ NINES = builder.toString();
+ }
+
+ /**
+ * Determines whether a character is a digit according to
+ * <a href="http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5Cp%7Bdigit%7D">Unicode</a>.
+ */
+ public static final CharMatcher DIGIT = new RangesMatcher(
+ "CharMatcher.DIGIT", ZEROES.toCharArray(), NINES.toCharArray());
+
+ /**
+ * Determines whether a character is a digit according to {@link Character#isDigit(char) Java's
+ * definition}. If you only care to match ASCII digits, you can use {@code inRange('0', '9')}.
+ */
+ public static final CharMatcher JAVA_DIGIT = new CharMatcher("CharMatcher.JAVA_DIGIT") {
+ @Override public boolean matches(char c) {
+ return Character.isDigit(c);
+ }
+ };
+
+ /**
+ * Determines whether a character is a letter according to {@link Character#isLetter(char) Java's
+ * definition}. If you only care to match letters of the Latin alphabet, you can use {@code
+ * inRange('a', 'z').or(inRange('A', 'Z'))}.
+ */
+ public static final CharMatcher JAVA_LETTER = new CharMatcher("CharMatcher.JAVA_LETTER") {
+ @Override public boolean matches(char c) {
+ return Character.isLetter(c);
+ }
+ };
+
+ /**
+ * Determines whether a character is a letter or digit according to {@link
+ * Character#isLetterOrDigit(char) Java's definition}.
+ */
+ public static final CharMatcher JAVA_LETTER_OR_DIGIT =
+ new CharMatcher("CharMatcher.JAVA_LETTER_OR_DIGIT") {
+ @Override public boolean matches(char c) {
+ return Character.isLetterOrDigit(c);
+ }
+ };
+
+ /**
+ * Determines whether a character is upper case according to {@link Character#isUpperCase(char)
+ * Java's definition}.
+ */
+ public static final CharMatcher JAVA_UPPER_CASE =
+ new CharMatcher("CharMatcher.JAVA_UPPER_CASE") {
+ @Override public boolean matches(char c) {
+ return Character.isUpperCase(c);
+ }
+ };
+
+ /**
+ * Determines whether a character is lower case according to {@link Character#isLowerCase(char)
+ * Java's definition}.
+ */
+ public static final CharMatcher JAVA_LOWER_CASE =
+ new CharMatcher("CharMatcher.JAVA_LOWER_CASE") {
+ @Override public boolean matches(char c) {
+ return Character.isLowerCase(c);
+ }
+ };
+
+ /**
+ * Determines whether a character is an ISO control character as specified by {@link
+ * Character#isISOControl(char)}.
+ */
+ public static final CharMatcher JAVA_ISO_CONTROL =
+ inRange('\u0000', '\u001f')
+ .or(inRange('\u007f', '\u009f'))
+ .withToString("CharMatcher.JAVA_ISO_CONTROL");
+
+ /**
+ * Determines whether a character is invisible; that is, if its Unicode category is any of
+ * SPACE_SEPARATOR, LINE_SEPARATOR, PARAGRAPH_SEPARATOR, CONTROL, FORMAT, SURROGATE, and
+ * PRIVATE_USE according to ICU4J.
+ */
+ public static final CharMatcher INVISIBLE = new RangesMatcher("CharMatcher.INVISIBLE", (
+ "\u0000\u007f\u00ad\u0600\u06dd\u070f\u1680\u180e\u2000\u2028\u205f\u206a\u3000\ud800\ufeff"
+ + "\ufff9\ufffa").toCharArray(), (
+ "\u0020\u00a0\u00ad\u0604\u06dd\u070f\u1680\u180e\u200f\u202f\u2064\u206f\u3000\uf8ff\ufeff"
+ + "\ufff9\ufffb").toCharArray());
+
+ private static String showCharacter(char c) {
+ String hex = "0123456789ABCDEF";
+ char[] tmp = {'\\', 'u', '\0', '\0', '\0', '\0'};
+ for (int i = 0; i < 4; i++) {
+ tmp[5 - i] = hex.charAt(c & 0xF);
+ c >>= 4;
+ }
+ return String.copyValueOf(tmp);
+
+ }
+
+ /**
+ * Determines whether a character is single-width (not double-width). When in doubt, this matcher
+ * errs on the side of returning {@code false} (that is, it tends to assume a character is
+ * double-width).
+ *
+ * <p><b>Note:</b> as the reference file evolves, we will modify this constant to keep it up to
+ * date.
+ */
+ public static final CharMatcher SINGLE_WIDTH = new RangesMatcher("CharMatcher.SINGLE_WIDTH",
+ "\u0000\u05be\u05d0\u05f3\u0600\u0750\u0e00\u1e00\u2100\ufb50\ufe70\uff61".toCharArray(),
+ "\u04f9\u05be\u05ea\u05f4\u06ff\u077f\u0e7f\u20af\u213a\ufdff\ufeff\uffdc".toCharArray());
+
+ /** Matches any character. */
+ public static final CharMatcher ANY =
+ new FastMatcher("CharMatcher.ANY") {
+ @Override public boolean matches(char c) {
+ return true;
+ }
+
+ @Override public int indexIn(CharSequence sequence) {
+ return (sequence.length() == 0) ? -1 : 0;
+ }
+
+ @Override public int indexIn(CharSequence sequence, int start) {
+ int length = sequence.length();
+ Preconditions.checkPositionIndex(start, length);
+ return (start == length) ? -1 : start;
+ }
+
+ @Override public int lastIndexIn(CharSequence sequence) {
+ return sequence.length() - 1;
+ }
+
+ @Override public boolean matchesAllOf(CharSequence sequence) {
+ checkNotNull(sequence);
+ return true;
+ }
+
+ @Override public boolean matchesNoneOf(CharSequence sequence) {
+ return sequence.length() == 0;
+ }
+
+ @Override public String removeFrom(CharSequence sequence) {
+ checkNotNull(sequence);
+ return "";
+ }
+
+ @Override public String replaceFrom(CharSequence sequence, char replacement) {
+ char[] array = new char[sequence.length()];
+ Arrays.fill(array, replacement);
+ return new String(array);
+ }
+
+ @Override public String replaceFrom(CharSequence sequence, CharSequence replacement) {
+ StringBuilder retval = new StringBuilder(sequence.length() * replacement.length());
+ for (int i = 0; i < sequence.length(); i++) {
+ retval.append(replacement);
+ }
+ return retval.toString();
+ }
+
+ @Override public String collapseFrom(CharSequence sequence, char replacement) {
+ return (sequence.length() == 0) ? "" : String.valueOf(replacement);
+ }
+
+ @Override public String trimFrom(CharSequence sequence) {
+ checkNotNull(sequence);
+ return "";
+ }
+
+ @Override public int countIn(CharSequence sequence) {
+ return sequence.length();
+ }
+
+ @Override public CharMatcher and(CharMatcher other) {
+ return checkNotNull(other);
+ }
+
+ @Override public CharMatcher or(CharMatcher other) {
+ checkNotNull(other);
+ return this;
+ }
+
+ @Override public CharMatcher negate() {
+ return NONE;
+ }
+ };
+
+ /** Matches no characters. */
+ public static final CharMatcher NONE =
+ new FastMatcher("CharMatcher.NONE") {
+ @Override public boolean matches(char c) {
+ return false;
+ }
+
+ @Override public int indexIn(CharSequence sequence) {
+ checkNotNull(sequence);
+ return -1;
+ }
+
+ @Override public int indexIn(CharSequence sequence, int start) {
+ int length = sequence.length();
+ Preconditions.checkPositionIndex(start, length);
+ return -1;
+ }
+
+ @Override public int lastIndexIn(CharSequence sequence) {
+ checkNotNull(sequence);
+ return -1;
+ }
+
+ @Override public boolean matchesAllOf(CharSequence sequence) {
+ return sequence.length() == 0;
+ }
+
+ @Override public boolean matchesNoneOf(CharSequence sequence) {
+ checkNotNull(sequence);
+ return true;
+ }
+
+ @Override public String removeFrom(CharSequence sequence) {
+ return sequence.toString();
+ }
+
+ @Override public String replaceFrom(CharSequence sequence, char replacement) {
+ return sequence.toString();
+ }
+
+ @Override public String replaceFrom(CharSequence sequence, CharSequence replacement) {
+ checkNotNull(replacement);
+ return sequence.toString();
+ }
+
+ @Override public String collapseFrom(CharSequence sequence, char replacement) {
+ return sequence.toString();
+ }
+
+ @Override public String trimFrom(CharSequence sequence) {
+ return sequence.toString();
+ }
+
+ @Override
+ public String trimLeadingFrom(CharSequence sequence) {
+ return sequence.toString();
+ }
+
+ @Override
+ public String trimTrailingFrom(CharSequence sequence) {
+ return sequence.toString();
+ }
+
+ @Override public int countIn(CharSequence sequence) {
+ checkNotNull(sequence);
+ return 0;
+ }
+
+ @Override public CharMatcher and(CharMatcher other) {
+ checkNotNull(other);
+ return this;
+ }
+
+ @Override public CharMatcher or(CharMatcher other) {
+ return checkNotNull(other);
+ }
+
+ @Override public CharMatcher negate() {
+ return ANY;
+ }
+ };
+
+ // Static factories
+
+ /**
+ * Returns a {@code char} matcher that matches only one specified character.
+ */
+ public static CharMatcher is(final char match) {
+ String description = "CharMatcher.is('" + showCharacter(match) + "')";
+ return new FastMatcher(description) {
+ @Override public boolean matches(char c) {
+ return c == match;
+ }
+
+ @Override public String replaceFrom(CharSequence sequence, char replacement) {
+ return sequence.toString().replace(match, replacement);
+ }
+
+ @Override public CharMatcher and(CharMatcher other) {
+ return other.matches(match) ? this : NONE;
+ }
+
+ @Override public CharMatcher or(CharMatcher other) {
+ return other.matches(match) ? other : super.or(other);
+ }
+
+ @Override public CharMatcher negate() {
+ return isNot(match);
+ }
+ };
+ }
+
+ /**
+ * Returns a {@code char} matcher that matches any character except the one specified.
+ *
+ * <p>To negate another {@code CharMatcher}, use {@link #negate()}.
+ */
+ public static CharMatcher isNot(final char match) {
+ String description = "CharMatcher.isNot(" + Integer.toHexString(match) + ")";
+ return new FastMatcher(description) {
+ @Override public boolean matches(char c) {
+ return c != match;
+ }
+
+ @Override public CharMatcher and(CharMatcher other) {
+ return other.matches(match) ? super.and(other) : other;
+ }
+
+ @Override public CharMatcher or(CharMatcher other) {
+ return other.matches(match) ? ANY : this;
+ }
+
+ @Override public CharMatcher negate() {
+ return is(match);
+ }
+ };
+ }
+
+ /**
+ * Returns a {@code char} matcher that matches any character present in the given character
+ * sequence.
+ */
+ public static CharMatcher anyOf(final CharSequence sequence) {
+ switch (sequence.length()) {
+ case 0:
+ return NONE;
+ case 1:
+ return is(sequence.charAt(0));
+ case 2:
+ return isEither(sequence.charAt(0), sequence.charAt(1));
+ default:
+ // continue below to handle the general case
+ }
+ // TODO(user): is it potentially worth just going ahead and building a precomputed matcher?
+ final char[] chars = sequence.toString().toCharArray();
+ Arrays.sort(chars);
+ StringBuilder description = new StringBuilder("CharMatcher.anyOf(\"");
+ for (char c : chars) {
+ description.append(showCharacter(c));
+ }
+ description.append("\")");
+ return new CharMatcher(description.toString()) {
+ @Override public boolean matches(char c) {
+ return Arrays.binarySearch(chars, c) >= 0;
+ }
+ };
+ }
+
+ private static CharMatcher isEither(
+ final char match1,
+ final char match2) {
+ String description = "CharMatcher.anyOf(\"" +
+ showCharacter(match1) + showCharacter(match2) + "\")";
+ return new FastMatcher(description) {
+ @Override public boolean matches(char c) {
+ return c == match1 || c == match2;
+ }
+ };
+ }
+
+ /**
+ * Returns a {@code char} matcher that matches any character not present in the given character
+ * sequence.
+ */
+ public static CharMatcher noneOf(CharSequence sequence) {
+ return anyOf(sequence).negate();
+ }
+
+ /**
+ * Returns a {@code char} matcher that matches any character in a given range (both endpoints are
+ * inclusive). For example, to match any lowercase letter of the English alphabet, use {@code
+ * CharMatcher.inRange('a', 'z')}.
+ *
+ * @throws IllegalArgumentException if {@code endInclusive < startInclusive}
+ */
+ public static CharMatcher inRange(final char startInclusive, final char endInclusive) {
+ checkArgument(endInclusive >= startInclusive);
+ String description = "CharMatcher.inRange('" +
+ showCharacter(startInclusive) + "', '" +
+ showCharacter(endInclusive) + "')";
+ return inRange(startInclusive, endInclusive, description);
+ }
+
+ static CharMatcher inRange(final char startInclusive, final char endInclusive,
+ String description) {
+ return new FastMatcher(description) {
+ @Override public boolean matches(char c) {
+ return startInclusive <= c && c <= endInclusive;
+ }
+ };
+ }
+
+ /**
+ * Returns a matcher with identical behavior to the given {@link Character}-based predicate, but
+ * which operates on primitive {@code char} instances instead.
+ */
+ public static CharMatcher forPredicate(final Predicate<? super Character> predicate) {
+ checkNotNull(predicate);
+ if (predicate instanceof CharMatcher) {
+ return (CharMatcher) predicate;
+ }
+ String description = "CharMatcher.forPredicate(" + predicate + ")";
+ return new CharMatcher(description) {
+ @Override public boolean matches(char c) {
+ return predicate.apply(c);
+ }
+
+ @Override public boolean apply(Character character) {
+ return predicate.apply(checkNotNull(character));
+ }
+ };
+ }
+
+ // State
+ final String description;
+
+ // Constructors
+
+ /**
+ * Sets the {@code toString()} from the given description.
+ */
+ CharMatcher(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Constructor for use by subclasses. When subclassing, you may want to override
+ * {@code toString()} to provide a useful description.
+ */
+ protected CharMatcher() {
+ description = super.toString();
+ }
+
+ // Abstract methods
+
+ /** Determines a true or false value for the given character. */
+ public abstract boolean matches(char c);
+
+ // Non-static factories
+
+ /**
+ * Returns a matcher that matches any character not matched by this matcher.
+ */
+ public CharMatcher negate() {
+ return new NegatedMatcher(this);
+ }
+
+ private static class NegatedMatcher extends CharMatcher {
+ final CharMatcher original;
+
+ NegatedMatcher(String toString, CharMatcher original) {
+ super(toString);
+ this.original = original;
+ }
+
+ NegatedMatcher(CharMatcher original) {
+ this(original + ".negate()", original);
+ }
+
+ @Override public boolean matches(char c) {
+ return !original.matches(c);
+ }
+
+ @Override public boolean matchesAllOf(CharSequence sequence) {
+ return original.matchesNoneOf(sequence);
+ }
+
+ @Override public boolean matchesNoneOf(CharSequence sequence) {
+ return original.matchesAllOf(sequence);
+ }
+
+ @Override public int countIn(CharSequence sequence) {
+ return sequence.length() - original.countIn(sequence);
+ }
+
+ @Override public CharMatcher negate() {
+ return original;
+ }
+
+ @Override
+ CharMatcher withToString(String description) {
+ return new NegatedMatcher(description, original);
+ }
+ }
+
+ /**
+ * Returns a matcher that matches any character matched by both this matcher and {@code other}.
+ */
+ public CharMatcher and(CharMatcher other) {
+ return new And(this, checkNotNull(other));
+ }
+
+ private static class And extends CharMatcher {
+ final CharMatcher first;
+ final CharMatcher second;
+
+ And(CharMatcher a, CharMatcher b) {
+ this(a, b, "CharMatcher.and(" + a + ", " + b + ")");
+ }
+
+ And(CharMatcher a, CharMatcher b, String description) {
+ super(description);
+ first = checkNotNull(a);
+ second = checkNotNull(b);
+ }
+
+ @Override
+ public boolean matches(char c) {
+ return first.matches(c) && second.matches(c);
+ }
+
+ @Override
+ CharMatcher withToString(String description) {
+ return new And(first, second, description);
+ }
+ }
+
+ /**
+ * Returns a matcher that matches any character matched by either this matcher or {@code other}.
+ */
+ public CharMatcher or(CharMatcher other) {
+ return new Or(this, checkNotNull(other));
+ }
+
+ private static class Or extends CharMatcher {
+ final CharMatcher first;
+ final CharMatcher second;
+
+ Or(CharMatcher a, CharMatcher b, String description) {
+ super(description);
+ first = checkNotNull(a);
+ second = checkNotNull(b);
+ }
+
+ Or(CharMatcher a, CharMatcher b) {
+ this(a, b, "CharMatcher.or(" + a + ", " + b + ")");
+ }
+
+ @Override
+ public boolean matches(char c) {
+ return first.matches(c) || second.matches(c);
+ }
+
+ @Override
+ CharMatcher withToString(String description) {
+ return new Or(first, second, description);
+ }
+ }
+
+ /**
+ * Returns a {@code char} matcher functionally equivalent to this one, but which may be faster to
+ * query than the original; your mileage may vary. Precomputation takes time and is likely to be
+ * worthwhile only if the precomputed matcher is queried many thousands of times.
+ *
+ * <p>This method has no effect (returns {@code this}) when called in GWT: it's unclear whether a
+ * precomputed matcher is faster, but it certainly consumes more memory, which doesn't seem like a
+ * worthwhile tradeoff in a browser.
+ */
+ public CharMatcher precomputed() {
+ return Platform.precomputeCharMatcher(this);
+ }
+
+ /**
+ * Subclasses should provide a new CharMatcher with the same characteristics as {@code this},
+ * but with their {@code toString} method overridden with the new description.
+ *
+ * <p>This is unsupported by default.
+ */
+ CharMatcher withToString(String description) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static final int DISTINCT_CHARS = Character.MAX_VALUE - Character.MIN_VALUE + 1;
+
+ /**
+ * A matcher for which precomputation will not yield any significant benefit.
+ */
+ abstract static class FastMatcher extends CharMatcher {
+ FastMatcher() {
+ super();
+ }
+
+ FastMatcher(String description) {
+ super(description);
+ }
+
+ @Override
+ public final CharMatcher precomputed() {
+ return this;
+ }
+
+ @Override
+ public CharMatcher negate() {
+ return new NegatedFastMatcher(this);
+ }
+ }
+
+ static final class NegatedFastMatcher extends NegatedMatcher {
+ NegatedFastMatcher(CharMatcher original) {
+ super(original);
+ }
+
+ NegatedFastMatcher(String toString, CharMatcher original) {
+ super(toString, original);
+ }
+
+ @Override
+ public final CharMatcher precomputed() {
+ return this;
+ }
+
+ @Override
+ CharMatcher withToString(String description) {
+ return new NegatedFastMatcher(description, original);
+ }
+ }
+
+ private static boolean isSmall(int totalCharacters, int tableLength) {
+ return totalCharacters <= SmallCharMatcher.MAX_SIZE
+ && tableLength > (totalCharacters * Character.SIZE);
+ }
+
+ // Text processing routines
+
+ /**
+ * Returns {@code true} if a character sequence contains at least one matching character.
+ * Equivalent to {@code !matchesNoneOf(sequence)}.
+ *
+ * <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
+ * character, until this returns {@code true} or the end is reached.
+ *
+ * @param sequence the character sequence to examine, possibly empty
+ * @return {@code true} if this matcher matches at least one character in the sequence
+ * @since 8.0
+ */
+ public boolean matchesAnyOf(CharSequence sequence) {
+ return !matchesNoneOf(sequence);
+ }
+
+ /**
+ * Returns {@code true} if a character sequence contains only matching characters.
+ *
+ * <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
+ * character, until this returns {@code false} or the end is reached.
+ *
+ * @param sequence the character sequence to examine, possibly empty
+ * @return {@code true} if this matcher matches every character in the sequence, including when
+ * the sequence is empty
+ */
+ public boolean matchesAllOf(CharSequence sequence) {
+ for (int i = sequence.length() - 1; i >= 0; i--) {
+ if (!matches(sequence.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@code true} if a character sequence contains no matching characters. Equivalent to
+ * {@code !matchesAnyOf(sequence)}.
+ *
+ * <p>The default implementation iterates over the sequence, invoking {@link #matches} for each
+ * character, until this returns {@code false} or the end is reached.
+ *
+ * @param sequence the character sequence to examine, possibly empty
+ * @return {@code true} if this matcher matches every character in the sequence, including when
+ * the sequence is empty
+ */
+ public boolean matchesNoneOf(CharSequence sequence) {
+ return indexIn(sequence) == -1;
+ }
+
+ /**
+ * Returns the index of the first matching character in a character sequence, or {@code -1} if no
+ * matching character is present.
+ *
+ * <p>The default implementation iterates over the sequence in forward order calling {@link
+ * #matches} for each character.
+ *
+ * @param sequence the character sequence to examine from the beginning
+ * @return an index, or {@code -1} if no character matches
+ */
+ public int indexIn(CharSequence sequence) {
+ int length = sequence.length();
+ for (int i = 0; i < length; i++) {
+ if (matches(sequence.charAt(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index of the first matching character in a character sequence, starting from a
+ * given position, or {@code -1} if no character matches after that position.
+ *
+ * <p>The default implementation iterates over the sequence in forward order, beginning at {@code
+ * start}, calling {@link #matches} for each character.
+ *
+ * @param sequence the character sequence to examine
+ * @param start the first index to examine; must be nonnegative and no greater than {@code
+ * sequence.length()}
+ * @return the index of the first matching character, guaranteed to be no less than {@code start},
+ * or {@code -1} if no character matches
+ * @throws IndexOutOfBoundsException if start is negative or greater than {@code
+ * sequence.length()}
+ */
+ public int indexIn(CharSequence sequence, int start) {
+ int length = sequence.length();
+ Preconditions.checkPositionIndex(start, length);
+ for (int i = start; i < length; i++) {
+ if (matches(sequence.charAt(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index of the last matching character in a character sequence, or {@code -1} if no
+ * matching character is present.
+ *
+ * <p>The default implementation iterates over the sequence in reverse order calling {@link
+ * #matches} for each character.
+ *
+ * @param sequence the character sequence to examine from the end
+ * @return an index, or {@code -1} if no character matches
+ */
+ public int lastIndexIn(CharSequence sequence) {
+ for (int i = sequence.length() - 1; i >= 0; i--) {
+ if (matches(sequence.charAt(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the number of matching characters found in a character sequence.
+ */
+ public int countIn(CharSequence sequence) {
+ int count = 0;
+ for (int i = 0; i < sequence.length(); i++) {
+ if (matches(sequence.charAt(i))) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Returns a string containing all non-matching characters of a character sequence, in order. For
+ * example: <pre> {@code
+ *
+ * CharMatcher.is('a').removeFrom("bazaar")}</pre>
+ *
+ * ... returns {@code "bzr"}.
+ */
+ @CheckReturnValue
+ public String removeFrom(CharSequence sequence) {
+ String string = sequence.toString();
+ int pos = indexIn(string);
+ if (pos == -1) {
+ return string;
+ }
+
+ char[] chars = string.toCharArray();
+ int spread = 1;
+
+ // This unusual loop comes from extensive benchmarking
+ OUT: while (true) {
+ pos++;
+ while (true) {
+ if (pos == chars.length) {
+ break OUT;
+ }
+ if (matches(chars[pos])) {
+ break;
+ }
+ chars[pos - spread] = chars[pos];
+ pos++;
+ }
+ spread++;
+ }
+ return new String(chars, 0, pos - spread);
+ }
+
+ /**
+ * Returns a string containing all matching characters of a character sequence, in order. For
+ * example: <pre> {@code
+ *
+ * CharMatcher.is('a').retainFrom("bazaar")}</pre>
+ *
+ * ... returns {@code "aaa"}.
+ */
+ @CheckReturnValue
+ public String retainFrom(CharSequence sequence) {
+ return negate().removeFrom(sequence);
+ }
+
+ /**
+ * Returns a string copy of the input character sequence, with each character that matches this
+ * matcher replaced by a given replacement character. For example: <pre> {@code
+ *
+ * CharMatcher.is('a').replaceFrom("radar", 'o')}</pre>
+ *
+ * ... returns {@code "rodor"}.
+ *
+ * <p>The default implementation uses {@link #indexIn(CharSequence)} to find the first matching
+ * character, then iterates the remainder of the sequence calling {@link #matches(char)} for each
+ * character.
+ *
+ * @param sequence the character sequence to replace matching characters in
+ * @param replacement the character to append to the result string in place of each matching
+ * character in {@code sequence}
+ * @return the new string
+ */
+ @CheckReturnValue
+ public String replaceFrom(CharSequence sequence, char replacement) {
+ String string = sequence.toString();
+ int pos = indexIn(string);
+ if (pos == -1) {
+ return string;
+ }
+ char[] chars = string.toCharArray();
+ chars[pos] = replacement;
+ for (int i = pos + 1; i < chars.length; i++) {
+ if (matches(chars[i])) {
+ chars[i] = replacement;
+ }
+ }
+ return new String(chars);
+ }
+
+ /**
+ * Returns a string copy of the input character sequence, with each character that matches this
+ * matcher replaced by a given replacement sequence. For example: <pre> {@code
+ *
+ * CharMatcher.is('a').replaceFrom("yaha", "oo")}</pre>
+ *
+ * ... returns {@code "yoohoo"}.
+ *
+ * <p><b>Note:</b> If the replacement is a fixed string with only one character, you are better
+ * off calling {@link #replaceFrom(CharSequence, char)} directly.
+ *
+ * @param sequence the character sequence to replace matching characters in
+ * @param replacement the characters to append to the result string in place of each matching
+ * character in {@code sequence}
+ * @return the new string
+ */
+ @CheckReturnValue
+ public String replaceFrom(CharSequence sequence, CharSequence replacement) {
+ int replacementLen = replacement.length();
+ if (replacementLen == 0) {
+ return removeFrom(sequence);
+ }
+ if (replacementLen == 1) {
+ return replaceFrom(sequence, replacement.charAt(0));
+ }
+
+ String string = sequence.toString();
+ int pos = indexIn(string);
+ if (pos == -1) {
+ return string;
+ }
+
+ int len = string.length();
+ StringBuilder buf = new StringBuilder((len * 3 / 2) + 16);
+
+ int oldpos = 0;
+ do {
+ buf.append(string, oldpos, pos);
+ buf.append(replacement);
+ oldpos = pos + 1;
+ pos = indexIn(string, oldpos);
+ } while (pos != -1);
+
+ buf.append(string, oldpos, len);
+ return buf.toString();
+ }
+
+ /**
+ * Returns a substring of the input character sequence that omits all characters this matcher
+ * matches from the beginning and from the end of the string. For example: <pre> {@code
+ *
+ * CharMatcher.anyOf("ab").trimFrom("abacatbab")}</pre>
+ *
+ * ... returns {@code "cat"}.
+ *
+ * <p>Note that: <pre> {@code
+ *
+ * CharMatcher.inRange('\0', ' ').trimFrom(str)}</pre>
+ *
+ * ... is equivalent to {@link String#trim()}.
+ */
+ @CheckReturnValue
+ public String trimFrom(CharSequence sequence) {
+ int len = sequence.length();
+ int first;
+ int last;
+
+ for (first = 0; first < len; first++) {
+ if (!matches(sequence.charAt(first))) {
+ break;
+ }
+ }
+ for (last = len - 1; last > first; last--) {
+ if (!matches(sequence.charAt(last))) {
+ break;
+ }
+ }
+
+ return sequence.subSequence(first, last + 1).toString();
+ }
+
+ /**
+ * Returns a substring of the input character sequence that omits all characters this matcher
+ * matches from the beginning of the string. For example: <pre> {@code
+ *
+ * CharMatcher.anyOf("ab").trimLeadingFrom("abacatbab")}</pre>
+ *
+ * ... returns {@code "catbab"}.
+ */
+ @CheckReturnValue
+ public String trimLeadingFrom(CharSequence sequence) {
+ int len = sequence.length();
+ for (int first = 0; first < len; first++) {
+ if (!matches(sequence.charAt(first))) {
+ return sequence.subSequence(first, len).toString();
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Returns a substring of the input character sequence that omits all characters this matcher
+ * matches from the end of the string. For example: <pre> {@code
+ *
+ * CharMatcher.anyOf("ab").trimTrailingFrom("abacatbab")}</pre>
+ *
+ * ... returns {@code "abacat"}.
+ */
+ @CheckReturnValue
+ public String trimTrailingFrom(CharSequence sequence) {
+ int len = sequence.length();
+ for (int last = len - 1; last >= 0; last--) {
+ if (!matches(sequence.charAt(last))) {
+ return sequence.subSequence(0, last + 1).toString();
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Returns a string copy of the input character sequence, with each group of consecutive
+ * characters that match this matcher replaced by a single replacement character. For example:
+ * <pre> {@code
+ *
+ * CharMatcher.anyOf("eko").collapseFrom("bookkeeper", '-')}</pre>
+ *
+ * ... returns {@code "b-p-r"}.
+ *
+ * <p>The default implementation uses {@link #indexIn(CharSequence)} to find the first matching
+ * character, then iterates the remainder of the sequence calling {@link #matches(char)} for each
+ * character.
+ *
+ * @param sequence the character sequence to replace matching groups of characters in
+ * @param replacement the character to append to the result string in place of each group of
+ * matching characters in {@code sequence}
+ * @return the new string
+ */
+ @CheckReturnValue
+ public String collapseFrom(CharSequence sequence, char replacement) {
+ // This implementation avoids unnecessary allocation.
+ int len = sequence.length();
+ for (int i = 0; i < len; i++) {
+ char c = sequence.charAt(i);
+ if (matches(c)) {
+ if (c == replacement
+ && (i == len - 1 || !matches(sequence.charAt(i + 1)))) {
+ // a no-op replacement
+ i++;
+ } else {
+ StringBuilder builder = new StringBuilder(len)
+ .append(sequence.subSequence(0, i))
+ .append(replacement);
+ return finishCollapseFrom(sequence, i + 1, len, replacement, builder, true);
+ }
+ }
+ }
+ // no replacement needed
+ return sequence.toString();
+ }
+
+ /**
+ * Collapses groups of matching characters exactly as {@link #collapseFrom} does, except that
+ * groups of matching characters at the start or end of the sequence are removed without
+ * replacement.
+ */
+ @CheckReturnValue
+ public String trimAndCollapseFrom(CharSequence sequence, char replacement) {
+ // This implementation avoids unnecessary allocation.
+ int len = sequence.length();
+ int first;
+ int last;
+
+ for (first = 0; first < len && matches(sequence.charAt(first)); first++) {}
+ for (last = len - 1; last > first && matches(sequence.charAt(last)); last--) {}
+
+ return (first == 0 && last == len - 1)
+ ? collapseFrom(sequence, replacement)
+ : finishCollapseFrom(
+ sequence, first, last + 1, replacement,
+ new StringBuilder(last + 1 - first),
+ false);
+ }
+
+ private String finishCollapseFrom(
+ CharSequence sequence, int start, int end, char replacement,
+ StringBuilder builder, boolean inMatchingGroup) {
+ for (int i = start; i < end; i++) {
+ char c = sequence.charAt(i);
+ if (matches(c)) {
+ if (!inMatchingGroup) {
+ builder.append(replacement);
+ inMatchingGroup = true;
+ }
+ } else {
+ builder.append(c);
+ inMatchingGroup = false;
+ }
+ }
+ return builder.toString();
+ }
+
+ // Predicate interface
+
+ /**
+ * Equivalent to {@link #matches}; provided only to satisfy the {@link Predicate} interface. When
+ * using a reference of type {@code CharMatcher}, invoke {@link #matches} directly instead.
+ */
+ @Override public boolean apply(Character character) {
+ return matches(character);
+ }
+
+ /**
+ * Returns a string representation of this {@code CharMatcher}, such as
+ * {@code CharMatcher.or(WHITESPACE, JAVA_DIGIT)}.
+ */
+ @Override
+ public String toString() {
+ return description;
+ }
+
+ /**
+ * A special-case CharMatcher for Unicode whitespace characters that is extremely
+ * efficient both in space required and in time to check for matches.
+ *
+ * Implementation details.
+ * It turns out that all current (early 2012) Unicode characters are unique modulo 79:
+ * so we can construct a lookup table of exactly 79 entries, and just check the character code
+ * mod 79, and see if that character is in the table.
+ *
+ * There is a 1 at the beginning of the table so that the null character is not listed
+ * as whitespace.
+ *
+ * Other things we tried that did not prove to be beneficial, mostly due to speed concerns:
+ *
+ * * Binary search into the sorted list of characters, i.e., what
+ * CharMatcher.anyOf() does</li>
+ * * Perfect hash function into a table of size 26 (using an offset table and a special
+ * Jenkins hash function)</li>
+ * * Perfect-ish hash function that required two lookups into a single table of size 26.</li>
+ * * Using a power-of-2 sized hash table (size 64) with linear probing.</li>
+ *
+ * --Christopher Swenson, February 2012.
+ */
+ private static final String WHITESPACE_TABLE = "\u0001\u0000\u00a0\u0000\u0000\u0000\u0000\u0000"
+ + "\u0000\u0009\n\u000b\u000c\r\u0000\u0000\u2028\u2029\u0000\u0000\u0000\u0000\u0000\u202f"
+ + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0020\u0000\u0000\u0000\u0000\u0000"
+ + "\u0000\u0000\u0000\u0000\u0000\u3000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ + "\u0000\u0000\u0085\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a"
+ + "\u0000\u0000\u0000\u0000\u0000\u205f\u1680\u0000\u0000\u180e\u0000\u0000\u0000";
+
+ /**
+ * Determines whether a character is whitespace according to the latest Unicode standard, as
+ * illustrated
+ * <a href="http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5Cp%7Bwhitespace%7D">here</a>.
+ * This is not the same definition used by other Java APIs. (See a
+ * <a href="http://spreadsheets.google.com/pub?key=pd8dAQyHbdewRsnE5x5GzKQ">comparison of several
+ * definitions of "whitespace"</a>.)
+ *
+ * <p><b>Note:</b> as the Unicode definition evolves, we will modify this constant to keep it up
+ * to date.
+ */
+ public static final CharMatcher WHITESPACE = new FastMatcher("CharMatcher.WHITESPACE") {
+
+ @Override public boolean matches(char c) {
+ return WHITESPACE_TABLE.charAt(c % 79) == c;
+ }
+ };
+}
diff --git a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Charsets.java b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Charsets.java
new file mode 100644
index 000000000..4c080be83
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Charsets.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed 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 com.google.common.base;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.nio.charset.Charset;
+
+/**
+ * Contains constant definitions for the six standard {@link Charset} instances, which are
+ * guaranteed to be supported by all Java platform implementations.
+ *
+ * <p>Assuming you're free to choose, note that <b>{@link #UTF_8} is widely preferred</b>.
+ *
+ * <p>See the Guava User Guide article on <a
+ * href="http://code.google.com/p/guava-libraries/wiki/StringsExplained#Charsets">
+ * {@code Charsets}</a>.
+ *
+ * @author Mike Bostock
+ * @since 1.0
+ */
+@GwtCompatible(emulated = true)
+public final class Charsets {
+ private Charsets() {}
+
+ /**
+ * UTF-8: eight-bit UCS Transformation Format.
+ */
+ public static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ /*
+ * Please do not add new Charset references to this class, unless those character encodings are
+ * part of the set required to be supported by all Java platform implementations! Any Charsets
+ * initialized here may cause unexpected delays when this class is loaded. See the Charset
+ * Javadocs for the list of built-in character encodings.
+ */
+}
+
diff --git a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Enums.java b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Enums.java
new file mode 100644
index 000000000..61dd6e143
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Enums.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.base;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+
+import java.io.Serializable;
+
+import javax.annotation.Nullable;
+
+/**
+ * Utility methods for working with {@link Enum} instances.
+ *
+ * @author Steve McKay
+ *
+ * @since 9.0
+ */
+@GwtCompatible(emulated = true)
+@Beta
+public final class Enums {
+
+ private Enums() {}
+
+ /**
+ * Returns a {@link Function} that maps an {@link Enum} name to the associated
+ * {@code Enum} constant. The {@code Function} will return {@code null} if the
+ * {@code Enum} constant does not exist.
+ *
+ * @param enumClass the {@link Class} of the {@code Enum} declaring the
+ * constant values.
+ */
+ public static <T extends Enum<T>> Function<String, T> valueOfFunction(Class<T> enumClass) {
+ return new ValueOfFunction<T>(enumClass);
+ }
+
+ /**
+ * A {@link Function} that maps an {@link Enum} name to the associated
+ * constant, or {@code null} if the constant does not exist.
+ */
+ private static final class ValueOfFunction<T extends Enum<T>>
+ implements Function<String, T>, Serializable {
+
+ private final Class<T> enumClass;
+
+ private ValueOfFunction(Class<T> enumClass) {
+ this.enumClass = checkNotNull(enumClass);
+ }
+
+ @Override
+ public T apply(String value) {
+ try {
+ return Enum.valueOf(enumClass, value);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ @Override public boolean equals(@Nullable Object obj) {
+ return obj instanceof ValueOfFunction &&
+ enumClass.equals(((ValueOfFunction) obj).enumClass);
+ }
+
+ @Override public int hashCode() {
+ return enumClass.hashCode();
+ }
+
+ @Override public String toString() {
+ return "Enums.valueOf(" + enumClass + ")";
+ }
+
+ private static final long serialVersionUID = 0;
+ }
+
+ /**
+ * Returns an optional enum constant for the given type, using {@link Enum#valueOf}. If the
+ * constant does not exist, {@link Optional#absent} is returned. A common use case is for parsing
+ * user input or falling back to a default enum constant. For example,
+ * {@code Enums.getIfPresent(Country.class, countryInput).or(Country.DEFAULT);}
+ *
+ * @since 12.0
+ */
+ public static <T extends Enum<T>> Optional<T> getIfPresent(Class<T> enumClass, String value) {
+ checkNotNull(enumClass);
+ checkNotNull(value);
+ try {
+ return Optional.of(Enum.valueOf(enumClass, value));
+ } catch (IllegalArgumentException iae) {
+ return Optional.absent();
+ }
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Predicates.java b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Predicates.java
index 366ca0d01..04f088e91 100644
--- a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Predicates.java
+++ b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Predicates.java
@@ -34,6 +34,10 @@ import javax.annotation.Nullable;
* <p>All methods returns serializable predicates as long as they're given
* serializable parameters.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/FunctionalExplained">the
+ * use of {@code Predicate}</a>.
+ *
* @author Kevin Bourrillion
* @since 2.0 (imported from Google Collections Library)
*/
@@ -226,7 +230,7 @@ public final class Predicates {
return o != null;
}
};
-
+
@SuppressWarnings("unchecked") // these Object predicates work for any T
<T> Predicate<T> withNarrowedType() {
return (Predicate<T>) this;
@@ -241,7 +245,7 @@ public final class Predicates {
this.predicate = checkNotNull(predicate);
}
@Override
- public boolean apply(T t) {
+ public boolean apply(@Nullable T t) {
return !predicate.apply(t);
}
@Override public int hashCode() {
@@ -270,7 +274,8 @@ public final class Predicates {
this.components = components;
}
@Override
- public boolean apply(T t) {
+ public boolean apply(@Nullable T t) {
+ // Avoid using the Iterator to avoid generating garbage (issue 820).
for (int i = 0; i < components.size(); i++) {
if (!components.get(i).apply(t)) {
return false;
@@ -279,7 +284,7 @@ public final class Predicates {
return true;
}
@Override public int hashCode() {
- // 0x12472c2c is a random number to help avoid collisions with OrPredicate
+ // add a random number to avoid collisions with OrPredicate
return components.hashCode() + 0x12472c2c;
}
@Override public boolean equals(@Nullable Object obj) {
@@ -303,7 +308,8 @@ public final class Predicates {
this.components = components;
}
@Override
- public boolean apply(T t) {
+ public boolean apply(@Nullable T t) {
+ // Avoid using the Iterator to avoid generating garbage (issue 820).
for (int i = 0; i < components.size(); i++) {
if (components.get(i).apply(t)) {
return true;
@@ -312,7 +318,7 @@ public final class Predicates {
return false;
}
@Override public int hashCode() {
- // 0x053c91cf is a random number to help avoid collisions with AndPredicate
+ // add a random number to avoid collisions with AndPredicate
return components.hashCode() + 0x053c91cf;
}
@Override public boolean equals(@Nullable Object obj) {
@@ -365,7 +371,7 @@ public final class Predicates {
}
@Override
- public boolean apply(T t) {
+ public boolean apply(@Nullable T t) {
try {
return target.contains(t);
} catch (NullPointerException e) {
@@ -405,7 +411,7 @@ public final class Predicates {
}
@Override
- public boolean apply(A a) {
+ public boolean apply(@Nullable A a) {
return p.apply(f.apply(a));
}
diff --git a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Splitter.java b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Splitter.java
index 142fbec68..458b5ade0 100644
--- a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Splitter.java
+++ b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Splitter.java
@@ -30,60 +30,63 @@ import java.util.Map;
import javax.annotation.CheckReturnValue;
/**
- * An object that divides strings (or other instances of {@code CharSequence})
- * into substrings, by recognizing a <i>separator</i> (a.k.a. "delimiter")
- * which can be expressed as a single character, literal string, regular
- * expression, {@code CharMatcher}, or by using a fixed substring length. This
- * class provides the complementary functionality to {@link Joiner}.
+ * Extracts non-overlapping substrings from an input string, typically by
+ * recognizing appearances of a <i>separator</i> sequence. This separator can be
+ * specified as a single {@linkplain #on(char) character}, fixed {@linkplain
+ * #on(String) string}, {@linkplain #onPattern regular expression} or {@link
+ * #on(CharMatcher) CharMatcher} instance. Or, instead of using a separator at
+ * all, a splitter can extract adjacent substrings of a given {@linkplain
+ * #fixedLength fixed length}.
*
- * <p>Here is the most basic example of {@code Splitter} usage: <pre> {@code
+ * <p>For example, this expression: <pre> {@code
*
- * Splitter.on(',').split("foo,bar")}</pre>
+ * Splitter.on(',').split("foo,bar,qux")}</pre>
*
- * This invocation returns an {@code Iterable<String>} containing {@code "foo"}
- * and {@code "bar"}, in that order.
+ * ... produces an {@code Iterable} containing {@code "foo"}, {@code "bar"} and
+ * {@code "qux"}, in that order.
*
- * <p>By default {@code Splitter}'s behavior is very simplistic: <pre> {@code
+ * <p>By default, {@code Splitter}'s behavior is simplistic and unassuming. The
+ * following expression: <pre> {@code
*
- * Splitter.on(',').split("foo,,bar, quux")}</pre>
+ * Splitter.on(',').split(" foo,,, bar ,")}</pre>
*
- * This returns an iterable containing {@code ["foo", "", "bar", " quux"]}.
- * Notice that the splitter does not assume that you want empty strings removed,
- * or that you wish to trim whitespace. If you want features like these, simply
- * ask for them: <pre> {@code
+ * ... yields the substrings {@code [" foo", "", "", " bar ", ""]}. If this
+ * is not the desired behavior, use configuration methods to obtain a <i>new</i>
+ * splitter instance with modified behavior: <pre> {@code
*
* private static final Splitter MY_SPLITTER = Splitter.on(',')
* .trimResults()
* .omitEmptyStrings();}</pre>
*
- * Now {@code MY_SPLITTER.split("foo, ,bar, quux,")} returns an iterable
- * containing just {@code ["foo", "bar", "quux"]}. Note that the order in which
- * the configuration methods are called is never significant; for instance,
- * trimming is always applied first before checking for an empty result,
- * regardless of the order in which the {@link #trimResults()} and
- * {@link #omitEmptyStrings()} methods were invoked.
+ * Now {@code MY_SPLITTER.split("foo,,, bar ,")} returns just {@code ["foo",
+ * "bar"]}. Note that the order in which these configuration methods are called
+ * is never significant.
*
- * <p><b>Warning: splitter instances are always immutable</b>; a configuration
- * method such as {@code omitEmptyStrings} has no effect on the instance it
- * is invoked on! You must store and use the new splitter instance returned by
- * the method. This makes splitters thread-safe, and safe to store as {@code
- * static final} constants (as illustrated above). <pre> {@code
+ * <p><b>Warning:</b> Splitter instances are immutable. Invoking a configuration
+ * method has no effect on the receiving instance; you must store and use the
+ * new splitter instance it returns instead. <pre> {@code
*
- * // Bad! Do not do this!
+ * // Do NOT do this
* Splitter splitter = Splitter.on('/');
* splitter.trimResults(); // does nothing!
* return splitter.split("wrong / wrong / wrong");}</pre>
*
- * The separator recognized by the splitter does not have to be a single
- * literal character as in the examples above. See the methods {@link
- * #on(String)}, {@link #on(Pattern)} and {@link #on(CharMatcher)} for examples
- * of other ways to specify separators.
+ * <p>For separator-based splitters that do not use {@code omitEmptyStrings}, an
+ * input string containing {@code n} occurrences of the separator naturally
+ * yields an iterable of size {@code n + 1}. So if the separator does not occur
+ * anywhere in the input, a single substring is returned containing the entire
+ * input. Consequently, all splitters split the empty string to {@code [""]}
+ * (note: even fixed-length splitters).
*
- * <p><b>Note:</b> this class does not mimic any of the quirky behaviors of
- * similar JDK methods; for instance, it does not silently discard trailing
- * separators, as does {@link String#split(String)}, nor does it have a default
- * behavior of using five particular whitespace characters as separators, like
- * {@link java.util.StringTokenizer}.
+ * <p>Splitter instances are thread-safe immutable, and are therefore safe to
+ * store as {@code static final} constants.
+ *
+ * <p>The {@link Joiner} class provides the inverse operation to splitting, but
+ * note that a round-trip between the two should be assumed to be lossy.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/StringsExplained#Splitter">
+ * {@code Splitter}</a>.
*
* @author Julien Silland
* @author Jesse Wilson
@@ -153,8 +156,8 @@ public final class Splitter {
/**
* Returns a splitter that uses the given fixed string as a separator. For
- * example, {@code Splitter.on(", ").split("foo, bar, baz,qux")} returns an
- * iterable containing {@code ["foo", "bar", "baz,qux"]}.
+ * example, {@code Splitter.on(", ").split("foo, bar,baz")} returns an
+ * iterable containing {@code ["foo", "bar,baz"]}.
*
* @param separator the literal, nonempty string to recognize as a separator
* @return a splitter, with default settings, that recognizes that separator
@@ -197,9 +200,18 @@ public final class Splitter {
* iterable containing {@code ["ab", "cd", "e"]}. The last piece can be
* smaller than {@code length} but will never be empty.
*
- * @param length the desired length of pieces after splitting
+ * <p><b>Exception:</b> for consistency with separator-based splitters, {@code
+ * split("")} does not yield an empty iterable, but an iterable containing
+ * {@code ""}. This is the only case in which {@code
+ * Iterables.size(split(input))} does not equal {@code
+ * IntMath.divide(input.length(), length, CEILING)}. To avoid this behavior,
+ * use {@code omitEmptyStrings}.
+ *
+ * @param length the desired length of pieces after splitting, a positive
+ * integer
* @return a splitter, with default settings, that can split into fixed sized
* pieces
+ * @throws IllegalArgumentException if {@code length} is zero or negative
*/
public static Splitter fixedLength(final int length) {
checkArgument(length > 0, "The length may not be less than 1");
@@ -316,6 +328,12 @@ public final class Splitter {
@Override public Iterator<String> iterator() {
return spliterator(sequence);
}
+ @Override public String toString() {
+ return Joiner.on(", ")
+ .appendTo(new StringBuilder().append('['), this)
+ .append(']')
+ .toString();
+ }
};
}
@@ -337,6 +355,18 @@ public final class Splitter {
/**
* Returns a {@code MapSplitter} which splits entries based on this splitter,
+ * and splits entries into keys and values using the specified separator.
+ *
+ * @since 14.0
+ */
+ @CheckReturnValue
+ @Beta
+ public MapSplitter withKeyValueSeparator(char separator) {
+ return withKeyValueSeparator(on(separator));
+ }
+
+ /**
+ * Returns a {@code MapSplitter} which splits entries based on this splitter,
* and splits entries into keys and values using the specified key-value
* splitter.
*
@@ -405,8 +435,7 @@ public final class Splitter {
Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
}
- private abstract static class SplittingIterator
- extends AbstractIterator<String> {
+ private abstract static class SplittingIterator extends AbstractIterator<String> {
final CharSequence toSplit;
final CharMatcher trimmer;
final boolean omitEmptyStrings;
@@ -435,8 +464,15 @@ public final class Splitter {
}
@Override protected String computeNext() {
+ /*
+ * The returned string will be from the end of the last match to the
+ * beginning of the next one. nextStart is the start position of the
+ * returned substring, while offset is the place to start looking for a
+ * separator.
+ */
+ int nextStart = offset;
while (offset != -1) {
- int start = offset;
+ int start = nextStart;
int end;
int separatorPosition = separatorStart(offset);
@@ -447,6 +483,20 @@ public final class Splitter {
end = separatorPosition;
offset = separatorEnd(separatorPosition);
}
+ if (offset == nextStart) {
+ /*
+ * This occurs when some pattern has an empty match, even if it
+ * doesn't match the empty string -- for example, if it requires
+ * lookahead or the like. The offset must be increased to look for
+ * separators beyond this point, without changing the start position
+ * of the next returned substring -- so nextStart stays the same.
+ */
+ offset++;
+ if (offset >= toSplit.length()) {
+ offset = -1;
+ }
+ continue;
+ }
while (start < end && trimmer.matches(toSplit.charAt(start))) {
start++;
@@ -456,6 +506,8 @@ public final class Splitter {
}
if (omitEmptyStrings && start == end) {
+ // Don't include the (unused) separator in next split string.
+ nextStart = offset;
continue;
}
diff --git a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Stopwatch.java b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Stopwatch.java
index 841362a60..d591f621f 100644
--- a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Stopwatch.java
+++ b/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Stopwatch.java
@@ -49,7 +49,7 @@ import java.util.concurrent.TimeUnit;
* doSomething();
* stopwatch.{@link #stop stop}(); // optional
*
- * long millis = stopwatch.{@link #elapsedMillis elapsedMillis}();
+ * long millis = stopwatch.elapsed(MILLISECONDS);
*
* log.info("that took: " + stopwatch); // formatted string like "12.3 ms"
* </pre>
@@ -68,7 +68,7 @@ import java.util.concurrent.TimeUnit;
* @since 10.0
*/
@Beta
-@GwtCompatible(emulated=true)
+@GwtCompatible(emulated = true)
public final class Stopwatch {
private final Ticker ticker;
private boolean isRunning;
@@ -88,7 +88,7 @@ public final class Stopwatch {
* source.
*/
public Stopwatch(Ticker ticker) {
- this.ticker = checkNotNull(ticker);
+ this.ticker = checkNotNull(ticker, "ticker");
}
/**
@@ -107,7 +107,8 @@ public final class Stopwatch {
* @throws IllegalStateException if the stopwatch is already running.
*/
public Stopwatch start() {
- checkState(!isRunning);
+ checkState(!isRunning,
+ "This stopwatch is already running; it cannot be started more than once.");
isRunning = true;
startTick = ticker.read();
return this;
@@ -122,7 +123,8 @@ public final class Stopwatch {
*/
public Stopwatch stop() {
long tick = ticker.read();
- checkState(isRunning);
+ checkState(isRunning,
+ "This stopwatch is already stopped; it cannot be stopped more than once.");
isRunning = false;
elapsedNanos += tick - startTick;
return this;
@@ -151,18 +153,40 @@ public final class Stopwatch {
* <p>Note that the overhead of measurement can be more than a microsecond, so
* it is generally not useful to specify {@link TimeUnit#NANOSECONDS}
* precision here.
+ *
+ * @since 14.0 (since 10.0 as {@code elapsedTime()})
*/
- public long elapsedTime(TimeUnit desiredUnit) {
+ public long elapsed(TimeUnit desiredUnit) {
return desiredUnit.convert(elapsedNanos(), NANOSECONDS);
}
/**
* Returns the current elapsed time shown on this stopwatch, expressed
+ * in the desired time unit, with any fraction rounded down.
+ *
+ * <p>Note that the overhead of measurement can be more than a microsecond, so
+ * it is generally not useful to specify {@link TimeUnit#NANOSECONDS}
+ * precision here.
+ *
+ * @deprecated Use {@link Stopwatch#elapsed(TimeUnit)} instead. This method is
+ * scheduled to be removed in Guava release 16.0.
+ */
+ @Deprecated
+ public long elapsedTime(TimeUnit desiredUnit) {
+ return elapsed(desiredUnit);
+ }
+
+ /**
+ * Returns the current elapsed time shown on this stopwatch, expressed
* in milliseconds, with any fraction rounded down. This is identical to
- * {@code elapsedTime(TimeUnit.MILLISECONDS}.
+ * {@code elapsed(TimeUnit.MILLISECONDS)}.
+ *
+ * @deprecated Use {@code stopwatch.elapsed(MILLISECONDS)} instead. This
+ * method is scheduled to be removed in Guava release 16.0.
*/
+ @Deprecated
public long elapsedMillis() {
- return elapsedTime(MILLISECONDS);
+ return elapsed(MILLISECONDS);
}
private static TimeUnit chooseUnit(long nanos) {
diff --git a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheBuilder.java b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheBuilder.java
index 4c3081577..8b01a0e9f 100644
--- a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheBuilder.java
+++ b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Guava Authors
+ * Copyright (C) 2009 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,48 +16,251 @@
package com.google.common.cache;
+import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import com.google.common.base.Function;
-import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.ExecutionError;
-import com.google.common.util.concurrent.UncheckedExecutionException;
-import com.google.gwt.user.client.Timer;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutionException;
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Ascii;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Objects;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.base.Ticker;
+import com.google.common.cache.AbstractCache.SimpleStatsCounter;
+import com.google.common.cache.AbstractCache.StatsCounter;
+import com.google.common.cache.LocalCache.Strength;
+
import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
-import javax.annotation.Nullable;
+import javax.annotation.CheckReturnValue;
/**
- * CacheBuilder emulation.
+ * <p>A builder of {@link LoadingCache} and {@link Cache} instances having any combination of the
+ * following features:
+ *
+ * <ul>
+ * <li>automatic loading of entries into the cache
+ * <li>least-recently-used eviction when a maximum size is exceeded
+ * <li>time-based expiration of entries, measured since last access or last write
+ * <li>keys automatically wrapped in {@linkplain WeakReference weak} references
+ * <li>values automatically wrapped in {@linkplain WeakReference weak} or
+ * {@linkplain SoftReference soft} references
+ * <li>notification of evicted (or otherwise removed) entries
+ * <li>accumulation of cache access statistics
+ * </ul>
+ *
+ * These features are all optional; caches can be created using all or none of them. By default
+ * cache instances created by {@code CacheBuilder} will not perform any type of eviction.
+ *
+ * <p>Usage example: <pre> {@code
+ *
+ * LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
+ * .maximumSize(10000)
+ * .expireAfterWrite(10, TimeUnit.MINUTES)
+ * .removalListener(MY_LISTENER)
+ * .build(
+ * new CacheLoader<Key, Graph>() {
+ * public Graph load(Key key) throws AnyException {
+ * return createExpensiveGraph(key);
+ * }
+ * });}</pre>
+ *
+ * Or equivalently, <pre> {@code
+ *
+ * // In real life this would come from a command-line flag or config file
+ * String spec = "maximumSize=10000,expireAfterWrite=10m";
+ *
+ * LoadingCache<Key, Graph> graphs = CacheBuilder.from(spec)
+ * .removalListener(MY_LISTENER)
+ * .build(
+ * new CacheLoader<Key, Graph>() {
+ * public Graph load(Key key) throws AnyException {
+ * return createExpensiveGraph(key);
+ * }
+ * });}</pre>
+ *
+ * <p>The returned cache is implemented as a hash table with similar performance characteristics to
+ * {@link ConcurrentHashMap}. It implements all optional operations of the {@link LoadingCache} and
+ * {@link Cache} interfaces. The {@code asMap} view (and its collection views) have <i>weakly
+ * consistent iterators</i>. This means that they are safe for concurrent use, but if other threads
+ * modify the cache after the iterator is created, it is undefined which of these changes, if any,
+ * are reflected in that iterator. These iterators never throw {@link
+ * ConcurrentModificationException}.
+ *
+ * <p><b>Note:</b> by default, the returned cache uses equality comparisons (the
+ * {@link Object#equals equals} method) to determine equality for keys or values. However, if
+ * {@link #weakKeys} was specified, the cache uses identity ({@code ==})
+ * comparisons instead for keys. Likewise, if {@link #weakValues} or {@link #softValues} was
+ * specified, the cache uses identity comparisons for values.
*
+ * <p>Entries are automatically evicted from the cache when any of
+ * {@linkplain #maximumSize(long) maximumSize}, {@linkplain #maximumWeight(long) maximumWeight},
+ * {@linkplain #expireAfterWrite expireAfterWrite},
+ * {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys},
+ * {@linkplain #weakValues weakValues}, or {@linkplain #softValues softValues} are requested.
+ *
+ * <p>If {@linkplain #maximumSize(long) maximumSize} or
+ * {@linkplain #maximumWeight(long) maximumWeight} is requested entries may be evicted on each cache
+ * modification.
+ *
+ * <p>If {@linkplain #expireAfterWrite expireAfterWrite} or
+ * {@linkplain #expireAfterAccess expireAfterAccess} is requested entries may be evicted on each
+ * cache modification, on occasional cache accesses, or on calls to {@link Cache#cleanUp}. Expired
+ * entries may be counted in {@link Cache#size}, but will never be visible to read or write
+ * operations.
+ *
+ * <p>If {@linkplain #weakKeys weakKeys}, {@linkplain #weakValues weakValues}, or
+ * {@linkplain #softValues softValues} are requested, it is possible for a key or value present in
+ * the cache to be reclaimed by the garbage collector. Entries with reclaimed keys or values may be
+ * removed from the cache on each cache modification, on occasional cache accesses, or on calls to
+ * {@link Cache#cleanUp}; such entries may be counted in {@link Cache#size}, but will never be
+ * visible to read or write operations.
+ *
+ * <p>Certain cache configurations will result in the accrual of periodic maintenance tasks which
+ * will be performed during write operations, or during occasional read operations in the absense of
+ * writes. The {@link Cache#cleanUp} method of the returned cache will also perform maintenance, but
+ * calling it should not be necessary with a high throughput cache. Only caches built with
+ * {@linkplain #removalListener removalListener}, {@linkplain #expireAfterWrite expireAfterWrite},
+ * {@linkplain #expireAfterAccess expireAfterAccess}, {@linkplain #weakKeys weakKeys},
+ * {@linkplain #weakValues weakValues}, or {@linkplain #softValues softValues} perform periodic
+ * maintenance.
+ *
+ * <p>The caches produced by {@code CacheBuilder} are serializable, and the deserialized caches
+ * retain all the configuration properties of the original cache. Note that the serialized form does
+ * <i>not</i> include cache contents, but only configuration.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/CachesExplained">caching</a> for a higher-level
+ * explanation.
+ *
+ * @param <K> the base key type for all caches created by this builder
+ * @param <V> the base value type for all caches created by this builder
* @author Charles Fry
+ * @author Kevin Bourrillion
+ * @since 10.0
*/
-// TODO(fry): eventually we should emmulate LocalCache instead of CacheBuilder
-public class CacheBuilder<K, V> {
- private static final int UNSET_INT = -1;
+@GwtCompatible(emulated = true)
+public final class CacheBuilder<K, V> {
private static final int DEFAULT_INITIAL_CAPACITY = 16;
+ private static final int DEFAULT_CONCURRENCY_LEVEL = 4;
private static final int DEFAULT_EXPIRATION_NANOS = 0;
+ private static final int DEFAULT_REFRESH_NANOS = 0;
+
+ static final Supplier<? extends StatsCounter> NULL_STATS_COUNTER = Suppliers.ofInstance(
+ new StatsCounter() {
+ @Override
+ public void recordHits(int count) {}
- private int initialCapacity = -1;
- private int concurrencyLevel = -1;
- private long expirationMillis = -1;
- private int maximumSize = -1;
+ @Override
+ public void recordMisses(int count) {}
+
+ @Override
+ public void recordLoadSuccess(long loadTime) {}
+
+ @Override
+ public void recordLoadException(long loadTime) {}
+
+ @Override
+ public void recordEviction() {}
+
+ @Override
+ public CacheStats snapshot() {
+ return EMPTY_STATS;
+ }
+ });
+ static final CacheStats EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0);
+ static final Supplier<StatsCounter> CACHE_STATS_COUNTER =
+ new Supplier<StatsCounter>() {
+ @Override
+ public StatsCounter get() {
+ return new SimpleStatsCounter();
+ }
+ };
+
+ enum NullListener implements RemovalListener<Object, Object> {
+ INSTANCE;
+
+ @Override
+ public void onRemoval(RemovalNotification<Object, Object> notification) {}
+ }
+
+ enum OneWeigher implements Weigher<Object, Object> {
+ INSTANCE;
+
+ @Override
+ public int weigh(Object key, Object value) {
+ return 1;
+ }
+ }
+
+ static final Ticker NULL_TICKER = new Ticker() {
+ @Override
+ public long read() {
+ return 0;
+ }
+ };
+
+ private static final Logger logger = Logger.getLogger(CacheBuilder.class.getName());
+
+ static final int UNSET_INT = -1;
+
+ boolean strictParsing = true;
+
+ int initialCapacity = UNSET_INT;
+ int concurrencyLevel = UNSET_INT;
+ long maximumSize = UNSET_INT;
+ long maximumWeight = UNSET_INT;
+ Weigher<? super K, ? super V> weigher;
+
+ Strength keyStrength;
+ Strength valueStrength;
+
+ long expireAfterWriteNanos = UNSET_INT;
+ long expireAfterAccessNanos = UNSET_INT;
+ long refreshNanos = UNSET_INT;
+
+ Equivalence<Object> keyEquivalence;
+ Equivalence<Object> valueEquivalence;
+
+ RemovalListener<? super K, ? super V> removalListener;
+ Ticker ticker;
+
+ Supplier<? extends StatsCounter> statsCounterSupplier = NULL_STATS_COUNTER;
+
+ // TODO(fry): make constructor private and update tests to use newBuilder
CacheBuilder() {}
+ /**
+ * Constructs a new {@code CacheBuilder} instance with default settings, including strong keys,
+ * strong values, and no automatic eviction of any kind.
+ */
public static CacheBuilder<Object, Object> newBuilder() {
return new CacheBuilder<Object, Object>();
}
+ Equivalence<Object> getKeyEquivalence() {
+ return firstNonNull(keyEquivalence, getKeyStrength().defaultEquivalence());
+ }
+
+ Equivalence<Object> getValueEquivalence() {
+ return firstNonNull(valueEquivalence, getValueStrength().defaultEquivalence());
+ }
+
+ /**
+ * Sets the minimum total size for the internal hash tables. For example, if the initial capacity
+ * is {@code 60}, and the concurrency level is {@code 8}, then eight segments are created, each
+ * having a hash table of size eight. Providing a large enough estimate at construction time
+ * avoids the need for expensive resizing operations later, but setting this value unnecessarily
+ * high wastes memory.
+ *
+ * @throws IllegalArgumentException if {@code initialCapacity} is negative
+ * @throws IllegalStateException if an initial capacity was already set
+ */
public CacheBuilder<K, V> initialCapacity(int initialCapacity) {
checkState(this.initialCapacity == UNSET_INT, "initial capacity was already set to %s",
this.initialCapacity);
@@ -66,258 +269,354 @@ public class CacheBuilder<K, V> {
return this;
}
- private int getInitialCapacity() {
+ int getInitialCapacity() {
return (initialCapacity == UNSET_INT) ? DEFAULT_INITIAL_CAPACITY : initialCapacity;
}
+ /**
+ * Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The
+ * table is internally partitioned to try to permit the indicated number of concurrent updates
+ * without contention. Because assignment of entries to these partitions is not necessarily
+ * uniform, the actual concurrency observed may vary. Ideally, you should choose a value to
+ * accommodate as many threads as will ever concurrently modify the table. Using a significantly
+ * higher value than you need can waste space and time, and a significantly lower value can lead
+ * to thread contention. But overestimates and underestimates within an order of magnitude do not
+ * usually have much noticeable impact. A value of one permits only one thread to modify the cache
+ * at a time, but since read operations and cache loading computations can proceed concurrently,
+ * this still yields higher concurrency than full synchronization.
+ *
+ * <p> Defaults to 4. <b>Note:</b>The default may change in the future. If you care about this
+ * value, you should always choose it explicitly.
+ *
+ * <p>The current implementation uses the concurrency level to create a fixed number of hashtable
+ * segments, each governed by its own write lock. The segment lock is taken once for each explicit
+ * write, and twice for each cache loading computation (once prior to loading the new value,
+ * and once after loading completes). Much internal cache management is performed at the segment
+ * granularity. For example, access queues and write queues are kept per segment when they are
+ * required by the selected eviction algorithm. As such, when writing unit tests it is not
+ * uncommon to specify {@code concurrencyLevel(1)} in order to achieve more deterministic eviction
+ * behavior.
+ *
+ * <p>Note that future implementations may abandon segment locking in favor of more advanced
+ * concurrency controls.
+ *
+ * @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive
+ * @throws IllegalStateException if a concurrency level was already set
+ */
public CacheBuilder<K, V> concurrencyLevel(int concurrencyLevel) {
checkState(this.concurrencyLevel == UNSET_INT, "concurrency level was already set to %s",
this.concurrencyLevel);
checkArgument(concurrencyLevel > 0);
- // GWT technically only supports concurrencyLevel == 1, but we silently
- // ignore other positive values.
this.concurrencyLevel = concurrencyLevel;
return this;
}
- public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
- checkState(expirationMillis == UNSET_INT, "expireAfterWrite was already set to %s ms",
- expirationMillis);
- checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
- this.expirationMillis = unit.toMillis(duration);
- return this;
+ int getConcurrencyLevel() {
+ return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel;
}
- private long getExpirationMillis() {
- return (expirationMillis == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expirationMillis;
+ /**
+ * Specifies the maximum number of entries the cache may contain. Note that the cache <b>may evict
+ * an entry before this limit is exceeded</b>. As the cache size grows close to the maximum, the
+ * cache evicts entries that are less likely to be used again. For example, the cache may evict an
+ * entry because it hasn't been used recently or very often.
+ *
+ * <p>When {@code size} is zero, elements will be evicted immediately after being loaded into the
+ * cache. This can be useful in testing, or to disable caching temporarily without a code change.
+ *
+ * <p>This feature cannot be used in conjunction with {@link #maximumWeight}.
+ *
+ * @param size the maximum size of the cache
+ * @throws IllegalArgumentException if {@code size} is negative
+ * @throws IllegalStateException if a maximum size or weight was already set
+ */
+ public CacheBuilder<K, V> maximumSize(long size) {
+ checkState(this.maximumSize == UNSET_INT, "maximum size was already set to %s",
+ this.maximumSize);
+ checkState(this.maximumWeight == UNSET_INT, "maximum weight was already set to %s",
+ this.maximumWeight);
+ checkState(this.weigher == null, "maximum size can not be combined with weigher");
+ checkArgument(size >= 0, "maximum size must not be negative");
+ this.maximumSize = size;
+ return this;
}
- public CacheBuilder<K, V> maximumSize(int maximumSize) {
- if (this.maximumSize != -1) {
- throw new IllegalStateException("maximum size of " + maximumSize + " was already set");
- }
- if (maximumSize < 0) {
- throw new IllegalArgumentException("invalid maximum size: " + maximumSize);
+ long getMaximumWeight() {
+ if (expireAfterWriteNanos == 0 || expireAfterAccessNanos == 0) {
+ return 0;
}
- this.maximumSize = maximumSize;
- return this;
+ return (weigher == null) ? maximumSize : maximumWeight;
}
- public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
- return new LocalManualCache<K1, V1>(this);
+ // Make a safe contravariant cast now so we don't have to do it over and over.
+ @SuppressWarnings("unchecked")
+ <K1 extends K, V1 extends V> Weigher<K1, V1> getWeigher() {
+ return (Weigher<K1, V1>) Objects.firstNonNull(weigher, OneWeigher.INSTANCE);
}
- public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build(
- CacheLoader<? super K1, V1> loader) {
- return new LocalLoadingCache<K1, V1>(this, loader);
+ CacheBuilder<K, V> setKeyStrength(Strength strength) {
+ checkState(keyStrength == null, "Key strength was already set to %s", keyStrength);
+ keyStrength = checkNotNull(strength);
+ return this;
}
- private static class LocalManualCache<K, V>
- extends AbstractCache<K, V> implements Function<K, V> {
- final LocalCache<K, V> localCache;
+ Strength getKeyStrength() {
+ return firstNonNull(keyStrength, Strength.STRONG);
+ }
- LocalManualCache(CacheBuilder<? super K, ? super V> builder) {
- this(builder, null);
- }
+ CacheBuilder<K, V> setValueStrength(Strength strength) {
+ checkState(valueStrength == null, "Value strength was already set to %s", valueStrength);
+ valueStrength = checkNotNull(strength);
+ return this;
+ }
- protected LocalManualCache(CacheBuilder<? super K, ? super V> builder,
- CacheLoader<? super K, V> loader) {
- this.localCache = new LocalCache<K, V>(builder, loader);
- }
+ Strength getValueStrength() {
+ return firstNonNull(valueStrength, Strength.STRONG);
+ }
- // Cache methods
+ /**
+ * Specifies that each entry should be automatically removed from the cache once a fixed duration
+ * has elapsed after the entry's creation, or the most recent replacement of its value.
+ *
+ * <p>When {@code duration} is zero, this method hands off to
+ * {@link #maximumSize(long) maximumSize}{@code (0)}, ignoring any otherwise-specificed maximum
+ * size or weight. This can be useful in testing, or to disable caching temporarily without a code
+ * change.
+ *
+ * <p>Expired entries may be counted in {@link Cache#size}, but will never be visible to read or
+ * write operations. Expired entries are cleaned up as part of the routine maintenance described
+ * in the class javadoc.
+ *
+ * @param duration the length of time after an entry is created that it should be automatically
+ * removed
+ * @param unit the unit that {@code duration} is expressed in
+ * @throws IllegalArgumentException if {@code duration} is negative
+ * @throws IllegalStateException if the time to live or time to idle was already set
+ */
+ public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
+ checkState(expireAfterWriteNanos == UNSET_INT, "expireAfterWrite was already set to %s ns",
+ expireAfterWriteNanos);
+ checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
+ this.expireAfterWriteNanos = unit.toNanos(duration);
+ return this;
+ }
- @Override
- public V get(K key) throws ExecutionException {
- return localCache.getOrLoad(key);
- }
+ long getExpireAfterWriteNanos() {
+ return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos;
+ }
- @Override
- public V getUnchecked(K key) {
- try {
- return get(key);
- } catch (ExecutionException e) {
- throw new UncheckedExecutionException(e.getCause());
- }
- }
+ /**
+ * Specifies that each entry should be automatically removed from the cache once a fixed duration
+ * has elapsed after the entry's creation, the most recent replacement of its value, or its last
+ * access. Access time is reset by all cache read and write operations (including
+ * {@code Cache.asMap().get(Object)} and {@code Cache.asMap().put(K, V)}), but not by operations
+ * on the collection-views of {@link Cache#asMap}.
+ *
+ * <p>When {@code duration} is zero, this method hands off to
+ * {@link #maximumSize(long) maximumSize}{@code (0)}, ignoring any otherwise-specificed maximum
+ * size or weight. This can be useful in testing, or to disable caching temporarily without a code
+ * change.
+ *
+ * <p>Expired entries may be counted in {@link Cache#size}, but will never be visible to read or
+ * write operations. Expired entries are cleaned up as part of the routine maintenance described
+ * in the class javadoc.
+ *
+ * @param duration the length of time after an entry is last accessed that it should be
+ * automatically removed
+ * @param unit the unit that {@code duration} is expressed in
+ * @throws IllegalArgumentException if {@code duration} is negative
+ * @throws IllegalStateException if the time to idle or time to live was already set
+ */
+ public CacheBuilder<K, V> expireAfterAccess(long duration, TimeUnit unit) {
+ checkState(expireAfterAccessNanos == UNSET_INT, "expireAfterAccess was already set to %s ns",
+ expireAfterAccessNanos);
+ checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
+ this.expireAfterAccessNanos = unit.toNanos(duration);
+ return this;
+ }
- @Override
- public final V apply(K key) {
- return getUnchecked(key);
- }
+ long getExpireAfterAccessNanos() {
+ return (expireAfterAccessNanos == UNSET_INT)
+ ? DEFAULT_EXPIRATION_NANOS : expireAfterAccessNanos;
+ }
- @Override
- @Nullable
- public V getIfPresent(K key) {
- return localCache.get(key);
- }
+ long getRefreshNanos() {
+ return (refreshNanos == UNSET_INT) ? DEFAULT_REFRESH_NANOS : refreshNanos;
+ }
- @Override
- public void put(K key, V value) {
- localCache.put(key, value);
- }
+ /**
+ * Specifies a nanosecond-precision time source for use in determining when entries should be
+ * expired. By default, {@link System#nanoTime} is used.
+ *
+ * <p>The primary intent of this method is to facilitate testing of caches which have been
+ * configured with {@link #expireAfterWrite} or {@link #expireAfterAccess}.
+ *
+ * @throws IllegalStateException if a ticker was already set
+ */
+ public CacheBuilder<K, V> ticker(Ticker ticker) {
+ checkState(this.ticker == null);
+ this.ticker = checkNotNull(ticker);
+ return this;
+ }
- @Override
- public void invalidate(Object key) {
- checkNotNull(key);
- localCache.remove(key);
+ Ticker getTicker(boolean recordsTime) {
+ if (ticker != null) {
+ return ticker;
}
+ return recordsTime ? Ticker.systemTicker() : NULL_TICKER;
+ }
- @Override
- public void invalidateAll() {
- localCache.clear();
- }
+ /**
+ * Specifies a listener instance that caches should notify each time an entry is removed for any
+ * {@linkplain RemovalCause reason}. Each cache created by this builder will invoke this listener
+ * as part of the routine maintenance described in the class documentation above.
+ *
+ * <p><b>Warning:</b> after invoking this method, do not continue to use <i>this</i> cache
+ * builder reference; instead use the reference this method <i>returns</i>. At runtime, these
+ * point to the same instance, but only the returned reference has the correct generic type
+ * information so as to ensure type safety. For best results, use the standard method-chaining
+ * idiom illustrated in the class documentation above, configuring a builder and building your
+ * cache in a single statement. Failure to heed this advice can result in a {@link
+ * ClassCastException} being thrown by a cache operation at some <i>undefined</i> point in the
+ * future.
+ *
+ * <p><b>Warning:</b> any exception thrown by {@code listener} will <i>not</i> be propagated to
+ * the {@code Cache} user, only logged via a {@link Logger}.
+ *
+ * @return the cache builder reference that should be used instead of {@code this} for any
+ * remaining configuration and cache building
+ * @throws IllegalStateException if a removal listener was already set
+ */
+ @CheckReturnValue
+ public <K1 extends K, V1 extends V> CacheBuilder<K1, V1> removalListener(
+ RemovalListener<? super K1, ? super V1> listener) {
+ checkState(this.removalListener == null);
+
+ // safely limiting the kinds of caches this can produce
+ @SuppressWarnings("unchecked")
+ CacheBuilder<K1, V1> me = (CacheBuilder<K1, V1>) this;
+ me.removalListener = checkNotNull(listener);
+ return me;
+ }
- @Override
- public long size() {
- return localCache.size();
- }
+ // Make a safe contravariant cast now so we don't have to do it over and over.
+ @SuppressWarnings("unchecked")
+ <K1 extends K, V1 extends V> RemovalListener<K1, V1> getRemovalListener() {
+ return (RemovalListener<K1, V1>) Objects.firstNonNull(removalListener, NullListener.INSTANCE);
+ }
- @Override
- public ConcurrentMap<K, V> asMap() {
- return localCache;
- }
+ /**
+ * Enable the accumulation of {@link CacheStats} during the operation of the cache. Without this
+ * {@link Cache#stats} will return zero for all statistics. Note that recording stats requires
+ * bookkeeping to be performed with each operation, and thus imposes a performance penalty on
+ * cache operation.
+ *
+ * @since 12.0 (previously, stats collection was automatic)
+ */
+ public CacheBuilder<K, V> recordStats() {
+ statsCounterSupplier = CACHE_STATS_COUNTER;
+ return this;
}
- private static class LocalLoadingCache<K, V>
- extends LocalManualCache<K, V> implements LoadingCache<K, V> {
+ Supplier<? extends StatsCounter> getStatsCounterSupplier() {
+ return statsCounterSupplier;
+ }
- LocalLoadingCache(CacheBuilder<? super K, ? super V> builder,
- CacheLoader<? super K, V> loader) {
- super(builder, checkNotNull(loader));
- }
+ /**
+ * Builds a cache, which either returns an already-loaded value for a given key or atomically
+ * computes or retrieves it using the supplied {@code CacheLoader}. If another thread is currently
+ * loading the value for this key, simply waits for that thread to finish and returns its
+ * loaded value. Note that multiple threads can concurrently load values for distinct keys.
+ *
+ * <p>This method does not alter the state of this {@code CacheBuilder} instance, so it can be
+ * invoked again to create multiple independent caches.
+ *
+ * @param loader the cache loader used to obtain new values
+ * @return a cache having the requested features
+ */
+ public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build(
+ CacheLoader<? super K1, V1> loader) {
+ checkWeightWithWeigher();
+ return new LocalCache.LocalLoadingCache<K1, V1>(this, loader);
+ }
- // Cache methods
+ /**
+ * Builds a cache which does not automatically load values when keys are requested.
+ *
+ * <p>Consider {@link #build(CacheLoader)} instead, if it is feasible to implement a
+ * {@code CacheLoader}.
+ *
+ * <p>This method does not alter the state of this {@code CacheBuilder} instance, so it can be
+ * invoked again to create multiple independent caches.
+ *
+ * @return a cache having the requested features
+ * @since 11.0
+ */
+ public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
+ checkWeightWithWeigher();
+ checkNonLoadingCache();
+ return new LocalCache.LocalManualCache<K1, V1>(this);
+ }
- @Override
- public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
- throw new UnsupportedOperationException();
- }
+ private void checkNonLoadingCache() {
+ checkState(refreshNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache");
+ }
- @Override
- public void refresh(K key) {
- throw new UnsupportedOperationException();
+ private void checkWeightWithWeigher() {
+ if (weigher == null) {
+ checkState(maximumWeight == UNSET_INT, "maximumWeight requires weigher");
+ } else {
+ if (strictParsing) {
+ checkState(maximumWeight != UNSET_INT, "weigher requires maximumWeight");
+ } else {
+ if (maximumWeight == UNSET_INT) {
+ logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight");
+ }
+ }
}
}
- // TODO(fry,user): ConcurrentHashMap never throws a CME when mutating the map during iteration, but
- // this implementation (based on a LHM) does. This will all be replaced soon anyways, so leaving
- // it as is for now.
- private static class LocalCache<K, V> extends LinkedHashMap<K, V>
- implements ConcurrentMap<K, V> {
- private final CacheLoader<? super K, V> loader;
- private final long expirationMillis;
- private final int maximumSize;
-
- LocalCache(CacheBuilder<? super K, ? super V> builder, CacheLoader<? super K, V> loader) {
- super(builder.getInitialCapacity(), 0.75f, (builder.maximumSize != UNSET_INT));
- this.loader = loader;
- this.expirationMillis = builder.getExpirationMillis();
- this.maximumSize = builder.maximumSize;
+ /**
+ * Returns a string representation for this CacheBuilder instance. The exact form of the returned
+ * string is not specified.
+ */
+ @Override
+ public String toString() {
+ Objects.ToStringHelper s = Objects.toStringHelper(this);
+ if (initialCapacity != UNSET_INT) {
+ s.add("initialCapacity", initialCapacity);
}
-
- @Override
- public V put(K key, V value) {
- V result = super.put(key, value);
- if (expirationMillis > 0) {
- scheduleRemoval(key, value);
- }
- return result;
+ if (concurrencyLevel != UNSET_INT) {
+ s.add("concurrencyLevel", concurrencyLevel);
}
-
- @Override
- protected boolean removeEldestEntry(Map.Entry<K, V> ignored) {
- return (maximumSize == -1) ? false : size() > maximumSize;
+ if (maximumSize != UNSET_INT) {
+ s.add("maximumSize", maximumSize);
}
-
- @Override
- public V putIfAbsent(K key, V value) {
- if (!containsKey(key)) {
- return put(key, value);
- } else {
- return get(key);
- }
+ if (maximumWeight != UNSET_INT) {
+ s.add("maximumWeight", maximumWeight);
}
-
- @Override
- public boolean remove(Object key, Object value) {
- if (containsKey(key) && get(key).equals(value)) {
- remove(key);
- return true;
- }
- return false;
+ if (expireAfterWriteNanos != UNSET_INT) {
+ s.add("expireAfterWrite", expireAfterWriteNanos + "ns");
}
-
- @Override
- public boolean replace(K key, V oldValue, V newValue) {
- if (containsKey(key) && get(key).equals(oldValue)) {
- put(key, newValue);
- return true;
- }
- return false;
+ if (expireAfterAccessNanos != UNSET_INT) {
+ s.add("expireAfterAccess", expireAfterAccessNanos + "ns");
}
-
- @Override
- public V replace(K key, V value) {
- return containsKey(key) ? put(key, value) : null;
+ if (keyStrength != null) {
+ s.add("keyStrength", Ascii.toLowerCase(keyStrength.toString()));
}
-
- private void scheduleRemoval(final K key, final V value) {
- /*
- * TODO: Keep weak reference to map, too. Build a priority queue out of the entries themselves
- * instead of creating a task per entry. Then, we could have one recurring task per map (which
- * would clean the entire map and then reschedule itself depending upon when the next
- * expiration comes). We also want to avoid removing an entry prematurely if the entry was set
- * to the same value again.
- */
- Timer timer = new Timer() {
- @Override
- public void run() {
- remove(key, value);
- }
- };
- timer.schedule((int) expirationMillis);
+ if (valueStrength != null) {
+ s.add("valueStrength", Ascii.toLowerCase(valueStrength.toString()));
}
-
- public V getOrLoad(Object k) throws ExecutionException {
- // from CustomConcurrentHashMap
- V result = super.get(k);
- if (result == null) {
- /*
- * This cast isn't safe, but we can rely on the fact that K is almost always passed to
- * Map.get(), and tools like IDEs and Findbugs can catch situations where this isn't the
- * case.
- *
- * The alternative is to add an overloaded method, but the chances of a user calling get()
- * instead of the new API and the risks inherent in adding a new API outweigh this little
- * hole.
- */
- @SuppressWarnings("unchecked")
- K key = (K) k;
- result = compute(key);
- }
- return result;
+ if (keyEquivalence != null) {
+ s.addValue("keyEquivalence");
}
-
- private V compute(K key) throws ExecutionException {
- V value;
- try {
- value = loader.load(key);
- } catch (RuntimeException e) {
- throw new UncheckedExecutionException(e);
- } catch (Exception e) {
- throw new ExecutionException(e);
- } catch (Error e) {
- throw new ExecutionError(e);
- }
-
- if (value == null) {
- String message = loader + " returned null for key " + key + ".";
- throw new InvalidCacheLoadException(message);
- }
- put(key, value);
- return value;
+ if (valueEquivalence != null) {
+ s.addValue("valueEquivalence");
+ }
+ if (removalListener != null) {
+ s.addValue("removalListener");
}
+ return s.toString();
}
-
}
+
diff --git a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheLoader.java b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheLoader.java
index c0815ddda..c345553f0 100644
--- a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheLoader.java
+++ b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/CacheLoader.java
@@ -18,6 +18,7 @@ package com.google.common.cache;
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
@@ -26,11 +27,20 @@ import java.io.Serializable;
import java.util.Map;
/**
- * Computes or retrieves values, based on a key, for use in populating a {@code Cache}.
+ * Computes or retrieves values, based on a key, for use in populating a {@link LoadingCache}.
*
* <p>Most implementations will only need to implement {@link #load}. Other methods may be
* overridden as desired.
*
+ * <p>Usage example: <pre> {@code
+ *
+ * CacheLoader<Key, Graph> loader = new CacheLoader<Key, Graph>() {
+ * public Graph load(Key key) throws AnyException {
+ * return createExpensiveGraph(key);
+ * }
+ * };
+ * LoadingCache<Key, Graph> cache = CacheBuilder.newBuilder().build(loader);}</pre>
+ *
* @author Charles Fry
* @since 10.0
*/
@@ -46,12 +56,16 @@ public abstract class CacheLoader<K, V> {
*
* @param key the non-null key whose value should be loaded
* @return the value associated with {@code key}; <b>must not be null</b>
+ * @throws Exception if unable to load the result
+ * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is
+ * treated like any other {@code Exception} in all respects except that, when it is caught,
+ * the thread's interrupt status is set
*/
public abstract V load(K key) throws Exception;
/**
* Computes or retrieves the values corresponding to {@code keys}. This method is called by
- * {@link Cache#getAll}.
+ * {@link LoadingCache#getAll}.
*
* <p>If the returned map doesn't contain all requested {@code keys} then the entries it does
* contain will be cached, but {@code getAll} will throw an exception. If the returned map
@@ -59,22 +73,33 @@ public abstract class CacheLoader<K, V> {
* but only the entries for {@code keys} will be returned from {@code getAll}.
*
* <p>This method should be overriden when bulk retrieval is significantly more efficient than
- * many individual lookups. Note that {@link Cache#getAll} will defer to individual calls to
- * {@link Cache#get} if this method is not overriden.
+ * many individual lookups. Note that {@link LoadingCache#getAll} will defer to individual calls
+ * to {@link LoadingCache#get} if this method is not overriden.
*
* @param keys the unique, non-null keys whose values should be loaded
* @return a map from each key in {@code keys} to the value associated with that key;
* <b>may not contain null values</b>
+ * @throws Exception if unable to load the result
+ * @throws InterruptedException if this method is interrupted. {@code InterruptedException} is
+ * treated like any other {@code Exception} in all respects except that, when it is caught,
+ * the thread's interrupt status is set
* @since 11.0
*/
public Map<K, V> loadAll(Iterable<? extends K> keys) throws Exception {
- // This will be caught by getAll(), causing it to fall back to multiple calls to Cache.get
+ // This will be caught by getAll(), causing it to fall back to multiple calls to
+ // LoadingCache.get
throw new UnsupportedLoadingOperationException();
}
/**
- * Returns a {@code CacheLoader} which creates values by applying a {@code Function} to the key.
+ * Returns a cache loader based on an <i>existing</i> function instance. Note that there's no need
+ * to create a <i>new</i> function just to pass it in here; just subclass {@code CacheLoader} and
+ * implement {@link #load load} instead.
+ *
+ * @param function the function to be used for loading values; must never return {@code null}
+ * @return a cache loader that loads values by passing each key to {@code function}
*/
+ @Beta
public static <K, V> CacheLoader<K, V> from(Function<K, V> function) {
return new FunctionToCacheLoader<K, V>(function);
}
@@ -89,16 +114,22 @@ public abstract class CacheLoader<K, V> {
@Override
public V load(K key) {
- return computingFunction.apply(key);
+ return computingFunction.apply(checkNotNull(key));
}
private static final long serialVersionUID = 0;
}
/**
- * Returns a {@code CacheLoader} which obtains values from a {@code Supplier} (independent of the
- * key).
+ * Returns a cache loader based on an <i>existing</i> supplier instance. Note that there's no need
+ * to create a <i>new</i> supplier just to pass it in here; just subclass {@code CacheLoader} and
+ * implement {@link #load load} instead.
+ *
+ * @param supplier the supplier to be used for loading values; must never return {@code null}
+ * @return a cache loader that loads values by calling {@link Supplier#get}, irrespective of the
+ * key
*/
+ @Beta
public static <V> CacheLoader<Object, V> from(Supplier<V> supplier) {
return new SupplierToCacheLoader<V>(supplier);
}
@@ -113,6 +144,7 @@ public abstract class CacheLoader<K, V> {
@Override
public V load(Object key) {
+ checkNotNull(key);
return computingSupplier.get();
}
diff --git a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LocalCache.java b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LocalCache.java
new file mode 100644
index 000000000..44528bf65
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LocalCache.java
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.cache;
+
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.Equivalence;
+import com.google.common.base.Ticker;
+import com.google.common.cache.AbstractCache.StatsCounter;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.ExecutionError;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+
+import javax.annotation.Nullable;
+
+/**
+ * LocalCache emulation for GWT.
+ *
+ * @param <K> the base key type
+ * @param <V> the base value type
+ * @author Charles Fry
+ * @author Jon Donovan
+ */
+public class LocalCache<K, V> implements ConcurrentMap<K, V> {
+ private static final int UNSET_INT = CacheBuilder.UNSET_INT;
+
+ private final LinkedHashMap<K, Timestamped<V>> cachingHashMap;
+ private final CacheLoader<? super K, V> loader;
+ private final RemovalListener removalListener;
+ private final StatsCounter statsCounter;
+ private final Ticker ticker;
+ private final long expireAfterWrite;
+ private final long expireAfterAccess;
+
+ LocalCache(CacheBuilder<? super K, ? super V> builder, CacheLoader<? super K, V> loader) {
+ this.loader = loader;
+ this.removalListener = builder.removalListener;
+ this.expireAfterAccess = builder.expireAfterAccessNanos;
+ this.expireAfterWrite = builder.expireAfterWriteNanos;
+ this.statsCounter = builder.getStatsCounterSupplier().get();
+
+ /* Implements size-capped LinkedHashMap */
+ final long maximumSize = builder.maximumSize;
+ this.cachingHashMap = new CapacityEnforcingLinkedHashMap<K, V>(
+ builder.getInitialCapacity(),
+ 0.75f,
+ (builder.maximumSize != UNSET_INT),
+ builder.maximumSize,
+ statsCounter,
+ removalListener);
+
+ this.ticker = firstNonNull(builder.ticker, Ticker.systemTicker());
+ }
+
+ @Override
+ public int size() {
+ return cachingHashMap.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return cachingHashMap.isEmpty();
+ }
+
+ @Override
+ public V get(Object key) {
+ key = checkNotNull(key);
+ Timestamped<V> value = cachingHashMap.get(key);
+
+ if (value == null) {
+ statsCounter.recordMisses(1);
+ return null;
+ } else if (!isExpired(value)) {
+ statsCounter.recordHits(1);
+ value.updateTimestamp();
+ return value.getValue();
+ } else {
+ statsCounter.recordEviction();
+ statsCounter.recordMisses(1);
+ alertListenerIfPresent(key, value.getValue(), RemovalCause.EXPIRED);
+ cachingHashMap.remove(key);
+ return null;
+ }
+ }
+
+ @Override
+ public V put(K key, V value) {
+ key = checkNotNull(key);
+ value = checkNotNull(value);
+ Timestamped<V> oldValue = cachingHashMap.put(key, new Timestamped<V>(value, ticker));
+ if (oldValue == null) {
+ return null;
+ }
+ alertListenerIfPresent(key, oldValue.getValue(), RemovalCause.REPLACED);
+ return oldValue.getValue();
+ }
+
+ @Override
+ public V remove(Object key) {
+ Timestamped<V> stamped = cachingHashMap.remove(key);
+ if (stamped != null) {
+ V value = stamped.getValue();
+
+ if (!isExpired(stamped)) {
+ alertListenerIfPresent(key, value, RemovalCause.EXPLICIT);
+ return value;
+ }
+
+ alertListenerIfPresent(key, value, RemovalCause.EXPIRED);
+ }
+ return null;
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public void clear() {
+ if (removalListener != null) {
+ for (Map.Entry<K, Timestamped<V>> entry : cachingHashMap.entrySet()) {
+ alertListenerIfPresent(entry.getKey(), entry.getValue().getValue(), RemovalCause.EXPLICIT);
+ }
+ }
+ cachingHashMap.clear();
+ }
+
+ @Override
+ public V putIfAbsent(K key, V value) {
+ V currentValue = get(key);
+ if (currentValue != null) {
+ return currentValue;
+ }
+ return put(key, value);
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ if (value.equals(get(key))) {
+ alertListenerIfPresent(key, value, RemovalCause.EXPLICIT);
+ remove(key);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ if (oldValue.equals(get(key))) {
+ alertListenerIfPresent(key, oldValue, RemovalCause.REPLACED);
+ put(key, newValue);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public V replace(K key, V value) {
+ V currentValue = get(key);
+ if (currentValue != null) {
+ alertListenerIfPresent(key, currentValue, RemovalCause.REPLACED);
+ return put(key, value);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return cachingHashMap.containsKey(key) && !isExpired(cachingHashMap.get(key));
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ for (Timestamped<V> val : cachingHashMap.values()) {
+ if (val.getValue().equals(value)) {
+ if (!isExpired(val)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isExpired(Timestamped<V> stamped) {
+ if ((expireAfterAccess == UNSET_INT) && (expireAfterWrite == UNSET_INT)) {
+ return false;
+ }
+
+ boolean expireWrite = (stamped.getWriteTimestamp() + expireAfterWrite <= currentTimeNanos());
+ boolean expireAccess = (stamped.getAccessTimestamp() + expireAfterAccess <= currentTimeNanos());
+
+ if (expireAfterAccess == UNSET_INT) {
+ return expireWrite;
+ }
+ if (expireAfterWrite == UNSET_INT) {
+ return expireAccess;
+ }
+
+ return expireWrite || expireAccess;
+ }
+
+ private long currentTimeNanos() {
+ return ticker.read();
+ }
+
+ private void alertListenerIfPresent(Object key, Object value, RemovalCause cause) {
+ if (removalListener != null) {
+ removalListener.onRemoval(new RemovalNotification(key, value, cause));
+ }
+ }
+
+ private V load(Object key) throws ExecutionException {
+ long startTime = ticker.read();
+ V calculatedValue;
+ try {
+ /*
+ * This cast isn't safe, but we can rely on the fact that K is almost always passed to
+ * Map.get(), and tools like IDEs and Findbugs can catch situations where this isn't the
+ * case.
+ *
+ * The alternative is to add an overloaded method, but the chances of a user calling get()
+ * instead of the new API and the risks inherent in adding a new API outweigh this little
+ * hole.
+ */
+ K castKey = (K) key;
+ calculatedValue = loader.load(castKey);
+ put(castKey, calculatedValue);
+ } catch (RuntimeException e) {
+ statsCounter.recordLoadException(ticker.read() - startTime);
+ throw new UncheckedExecutionException(e);
+ } catch (Exception e) {
+ statsCounter.recordLoadException(ticker.read() - startTime);
+ throw new ExecutionException(e);
+ } catch (Error e) {
+ statsCounter.recordLoadException(ticker.read() - startTime);
+ throw new ExecutionError(e);
+ }
+
+ if (calculatedValue == null) {
+ String message = loader + " returned null for key " + key + ".";
+ throw new CacheLoader.InvalidCacheLoadException(message);
+ }
+ statsCounter.recordLoadSuccess(ticker.read() - startTime);
+ return calculatedValue;
+ }
+
+ private V getIfPresent(Object key) {
+ key = checkNotNull(key);
+ Timestamped<V> value = cachingHashMap.get(key);
+
+ if (value == null) {
+ return null;
+ } else if (!isExpired(value)) {
+ value.updateTimestamp();
+ return value.getValue();
+ } else {
+ alertListenerIfPresent(key, value.getValue(), RemovalCause.EXPIRED);
+ cachingHashMap.remove(key);
+ return null;
+ }
+ }
+
+ private V getOrLoad(K key) throws ExecutionException{
+ V value = get(key);
+ if (value != null) {
+ return value;
+ }
+ return load(key);
+ }
+
+ private static class Timestamped<V> {
+ private final V value;
+ private final Ticker ticker;
+ private long writeTimestamp;
+ private long accessTimestamp;
+
+ public Timestamped(V value, Ticker ticker) {
+ this.value = checkNotNull(value);
+ this.ticker = checkNotNull(ticker);
+ this.writeTimestamp = ticker.read();
+ this.accessTimestamp = this.writeTimestamp;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public void updateTimestamp() {
+ accessTimestamp = ticker.read();
+ }
+
+ public long getAccessTimestamp() {
+ return accessTimestamp;
+ }
+
+ public long getWriteTimestamp() {
+ return writeTimestamp;
+ }
+
+ public boolean equals(Object o) {
+ return value.equals(o);
+ }
+
+ public int hashCode() {
+ return value.hashCode();
+ }
+ }
+
+ /**
+ * LocalManualCache is a wrapper around LocalCache for a cache without loading.
+ *
+ * @param <K> the base key type
+ * @param <V> the base value type
+ */
+ public static class LocalManualCache<K, V> extends AbstractCache<K, V> {
+ final LocalCache<K, V> localCache;
+
+ LocalManualCache(CacheBuilder<? super K, ? super V> builder) {
+ this(builder, null);
+ }
+
+ protected LocalManualCache(CacheBuilder<? super K, ? super V> builder,
+ CacheLoader<? super K, V> loader) {
+ this.localCache = new LocalCache<K, V>(builder, loader);
+ }
+
+ // Cache methods
+
+ @Override
+ public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException {
+ V value = localCache.get(key);
+ if (value != null) {
+ return value;
+ }
+
+ try {
+ V newValue = valueLoader.call();
+ localCache.put(key, newValue);
+ return newValue;
+ } catch (Exception e) {
+ throw new ExecutionException(e);
+ }
+ }
+
+ @Override
+ @Nullable
+ public V getIfPresent(Object key) {
+ return localCache.getIfPresent(key);
+ }
+
+ @Override
+ public void put(K key, V value) {
+ localCache.put(key, value);
+ }
+
+ @Override
+ public void invalidate(Object key) {
+ key = checkNotNull(key);
+ localCache.remove(key);
+ }
+
+ @Override
+ public void invalidateAll() {
+ localCache.clear();
+ }
+
+ @Override
+ public long size() {
+ return localCache.size();
+ }
+
+ @Override
+ public ConcurrentMap<K, V> asMap() {
+ return localCache;
+ }
+ }
+
+ /**
+ * LocalLoadingCache is a wrapper around LocalCache for a cache with loading.
+ *
+ * @param <K> the base key type
+ * @param <V> the base value type
+ */
+ public static class LocalLoadingCache<K, V>
+ extends LocalManualCache<K, V> implements LoadingCache<K, V> {
+
+ LocalLoadingCache(CacheBuilder<? super K, ? super V> builder,
+ CacheLoader<? super K, V> loader) {
+ super(builder, checkNotNull(loader));
+ }
+
+ // Cache methods
+
+ @Override
+ public V get(K key) throws ExecutionException {
+ return localCache.getOrLoad(key);
+ }
+
+ @Override
+ public V getUnchecked(K key) {
+ try {
+ return get(key);
+ } catch (ExecutionException e) {
+ throw new UncheckedExecutionException(e.getCause());
+ }
+ }
+
+ @Override
+ public final V apply(K key) {
+ return getUnchecked(key);
+ }
+
+ @Override
+ public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
+ Map<K, V> map = new HashMap<K, V>();
+ for (K key : keys) {
+ map.put(key, localCache.getOrLoad(key));
+ }
+ return ImmutableMap.copyOf(map);
+ }
+
+ @Override
+ public void refresh(K key) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * LinkedHashMap that enforces it's maximum size and logs events in a StatsCounter object
+ * and an optional RemovalListener.
+ *
+ * @param <K> the base key type
+ * @param <V> the base value type
+ */
+ private class CapacityEnforcingLinkedHashMap<K, V> extends LinkedHashMap<K, Timestamped<V>> {
+
+ private final StatsCounter statsCounter;
+ private final RemovalListener removalListener;
+ private final long maximumSize;
+
+ public CapacityEnforcingLinkedHashMap(
+ int initialCapacity,
+ float loadFactor,
+ boolean accessOrder,
+ long maximumSize,
+ StatsCounter statsCounter,
+ @Nullable RemovalListener removalListener) {
+ super(initialCapacity, loadFactor, accessOrder);
+ this.maximumSize = maximumSize;
+ this.statsCounter = statsCounter;
+ this.removalListener = removalListener;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry<K, Timestamped<V>> ignored) {
+ boolean removal = (maximumSize == UNSET_INT) ? false : (size() > maximumSize);
+ if ((removalListener != null) && removal) {
+ removalListener.onRemoval(new RemovalNotification(
+ ignored.getKey(),
+ ignored.getValue().getValue(),
+ RemovalCause.SIZE));
+ }
+ statsCounter.recordEviction();
+ return removal;
+ }
+ }
+
+ /**
+ * Any updates to LocalCache.Strength used in CacheBuilder need to be matched in this class for
+ * compilation purposes.
+ */
+ enum Strength {
+ /*
+ * TODO(kevinb): If we strongly reference the value and aren't loading, we needn't wrap the
+ * value. This could save ~8 bytes per entry.
+ */
+
+ STRONG {
+ @Override
+ Equivalence<Object> defaultEquivalence() {
+ return Equivalence.equals();
+ }
+ },
+
+ SOFT {
+ @Override
+ Equivalence<Object> defaultEquivalence() {
+ return Equivalence.identity();
+ }
+ },
+
+ WEAK {
+ @Override
+ Equivalence<Object> defaultEquivalence() {
+ return Equivalence.identity();
+ }
+ };
+
+ abstract Equivalence<Object> defaultEquivalence();
+ }
+
+ /**
+ * <p>Implementation for the EntryIterator, which is used to build Key and Value iterators.
+ *
+ * <p>Expiration is only checked on hasNext(), so as to ensure that a next() call never returns
+ * null when hasNext() has already been called.
+ */
+ class EntryIterator implements Iterator<Entry<K, V>> {
+ Iterator<Entry<K, Timestamped<V>>> iterator;
+ Entry<K, Timestamped<V>> lastEntry;
+ Entry<K, Timestamped<V>> nextEntry;
+
+ EntryIterator() {
+ this.iterator = LocalCache.this.cachingHashMap.entrySet().iterator();
+ }
+
+ @Override
+ public Entry<K, V> next(){
+ if (nextEntry == null) {
+ hasNext();
+
+ if (nextEntry == null) {
+ throw new NoSuchElementException();
+ }
+ }
+
+ lastEntry = nextEntry;
+ nextEntry = null;
+ return new WriteThroughEntry(lastEntry.getKey(), lastEntry.getValue().getValue());
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (nextEntry == null) {
+ while (iterator.hasNext()) {
+ Entry<K, Timestamped<V>> next = iterator.next();
+ if (!isExpired(next.getValue())) {
+ nextEntry = next;
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void remove() {
+ checkState(lastEntry != null);
+ LocalCache.this.remove(lastEntry.getKey(), lastEntry.getValue());
+ lastEntry = null;
+ }
+ }
+
+ /**
+ * KeyIterator build on top of EntryIterator.
+ */
+ final class KeyIterator implements Iterator<K> {
+ private EntryIterator iterator;
+
+ KeyIterator() {
+ iterator = new EntryIterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public K next() {
+ return iterator.next().getKey();
+ }
+
+ @Override
+ public void remove() {
+ iterator.remove();
+ }
+ }
+
+ /**
+ * ValueIterator build on top of EntryIterator.
+ */
+ final class ValueIterator implements Iterator<V> {
+ private EntryIterator iterator;
+
+ ValueIterator() {
+ iterator = new EntryIterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public V next() {
+ return iterator.next().getValue();
+ }
+
+ @Override
+ public void remove() {
+ iterator.remove();
+ }
+ }
+
+ Set<K> keySet;
+
+ @Override
+ public Set<K> keySet() {
+ // does not impact recency ordering
+ Set<K> ks = keySet;
+ return (ks != null) ? ks : (keySet = new KeySet(this));
+ }
+
+ Collection<V> values;
+
+ @Override
+ public Collection<V> values() {
+ // does not impact recency ordering
+ Collection<V> vs = values;
+ return (vs != null) ? vs : (values = new Values(this));
+ }
+
+ Set<Entry<K, V>> entrySet;
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ // does not impact recency ordering
+ Set<Entry<K, V>> es = entrySet;
+ return (es != null) ? es : (entrySet = new EntrySet(this));
+ }
+
+ /**
+ * Custom Entry class used by EntryIterator.next(), that relays setValue changes to the
+ * underlying map.
+ */
+ private final class WriteThroughEntry implements Entry<K, V> {
+ final K key;
+ V value;
+
+ WriteThroughEntry(K key, V value) {
+ this.key = checkNotNull(key);
+ this.value = checkNotNull(value);
+ }
+
+ @Override
+ public K getKey() {
+ return key;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ // Cannot use key and value equivalence
+ if (object instanceof Entry) {
+ Entry<?, ?> that = (Entry<?, ?>) object;
+ return key.equals(that.getKey()) && value.equals(that.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ // Cannot use key and value equivalence
+ return key.hashCode() ^ value.hashCode();
+ }
+
+ @Override
+ public V setValue(V newValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Returns a string representation of the form <code>{key}={value}</code>.
+ */
+ @Override
+ public String toString() {
+ return getKey() + "=" + getValue();
+ }
+ }
+
+ // TODO(fry): Separate logic for consistency between emul and nonemul implementation.
+ // TODO(fry): Look into Maps.KeySet and Maps.Values, which can ideally be reused here but are
+ // currently only package visible.
+ abstract class AbstractCacheSet<T> extends AbstractSet<T> {
+ final ConcurrentMap<?, ?> map;
+
+ AbstractCacheSet(ConcurrentMap<?, ?> map) {
+ this.map = map;
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+ }
+
+ /**
+ * Abstraction layer for the KeySet, which redirects to cache methods.
+ */
+ private final class KeySet extends AbstractCacheSet<K> {
+
+ KeySet(ConcurrentMap<?, ?> map) {
+ super(map);
+ }
+
+ @Override
+ public Iterator<K> iterator() {
+ return new KeyIterator();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return map.containsKey(o);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return map.remove(o) != null;
+ }
+ }
+
+ /**
+ * Abstraction layer for the Values set, which redirects to cache methods.
+ */
+ private final class Values extends AbstractCacheSet<V> {
+
+ Values(ConcurrentMap<?, ?> map) {
+ super(map);
+ }
+
+ @Override
+ public Iterator<V> iterator() {
+ return new ValueIterator();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return map.containsValue(o);
+ }
+ }
+
+ /**
+ * Abstraction layer for the EntrySet, which redirects to cache methods.
+ */
+ private final class EntrySet extends AbstractCacheSet<Entry<K, V>> {
+
+ EntrySet(ConcurrentMap<?, ?> map) {
+ super(map);
+ }
+
+ @Override
+ public Iterator<Entry<K, V>> iterator() {
+ return new EntryIterator();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ if (!(o instanceof Entry)) {
+ return false;
+ }
+ Entry<?, ?> e = (Entry<?, ?>) o;
+ Object key = e.getKey();
+ if (key == null) {
+ return false;
+ }
+ V v = LocalCache.this.get(key);
+
+ return (v != null) && e.getValue().equals(v);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (!(o instanceof Entry)) {
+ return false;
+ }
+ Entry<?, ?> e = (Entry<?, ?>) o;
+ Object key = e.getKey();
+ return (key != null) && LocalCache.this.remove(key, e.getValue());
+ }
+ }
+}
diff --git a/guava-gwt/test/com/google/common/net/TestPlatform.java b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAddables.java
index 39301d593..a911ef32c 100644
--- a/guava-gwt/test/com/google/common/net/TestPlatform.java
+++ b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAddables.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Guava Authors
+ * Copyright (C) 2012 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,15 @@
* limitations under the License.
*/
-package com.google.common.net;
-
-import com.google.common.annotations.GwtCompatible;
+package com.google.common.cache;
/**
- * @author Hayward Chan
+ * GWT emulation for LongAddables.
+ *
+ * @author Louis Wasserman
*/
-@GwtCompatible(emulated = true)
-class TestPlatform {
+final class LongAddables {
+ public static LongAddable create() {
+ return new LongAdder();
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java
new file mode 100644
index 000000000..34b6f37e3
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.cache;
+
+/**
+ * GWT emulated version of LongAdder.
+ *
+ * @author Charles Fry
+ */
+class LongAdder implements LongAddable {
+
+ private long value;
+
+ public void increment() {
+ value++;
+ }
+
+ public void add(long x) {
+ value += x;
+ }
+
+ public long sum() {
+ return value;
+ }
+
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractBiMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractBiMap.java
index a6cec16ee..7c882feee 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractBiMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractBiMap.java
@@ -45,7 +45,7 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
implements BiMap<K, V>, Serializable {
private transient Map<K, V> delegate;
- private transient AbstractBiMap<V, K> inverse;
+ transient AbstractBiMap<V, K> inverse;
/** Package-private constructor for creating a map-backed bimap. */
AbstractBiMap(Map<K, V> forward, Map<V, K> backward) {
@@ -63,6 +63,20 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
}
/**
+ * Returns its input, or throws an exception if this is not a valid key.
+ */
+ K checkKey(@Nullable K key) {
+ return key;
+ }
+
+ /**
+ * Returns its input, or throws an exception if this is not a valid value.
+ */
+ V checkValue(@Nullable V value) {
+ return value;
+ }
+
+ /**
* Specifies the delegate maps going in each direction. Called by the
* constructor and by subclasses during deserialization.
*/
@@ -82,22 +96,24 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
// Query Operations (optimizations)
- @Override public boolean containsValue(Object value) {
+ @Override public boolean containsValue(@Nullable Object value) {
return inverse.containsKey(value);
}
// Modification Operations
- @Override public V put(K key, V value) {
+ @Override public V put(@Nullable K key, @Nullable V value) {
return putInBothMaps(key, value, false);
}
@Override
- public V forcePut(K key, V value) {
+ public V forcePut(@Nullable K key, @Nullable V value) {
return putInBothMaps(key, value, true);
}
private V putInBothMaps(@Nullable K key, @Nullable V value, boolean force) {
+ checkKey(key);
+ checkValue(value);
boolean containedKey = containsKey(key);
if (containedKey && Objects.equal(value, get(key))) {
return value;
@@ -120,7 +136,7 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
inverse.delegate.put(newValue, key);
}
- @Override public V remove(Object key) {
+ @Override public V remove(@Nullable Object key) {
return containsKey(key) ? removeFromBothMaps(key) : null;
}
@@ -187,27 +203,7 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
}
@Override public Iterator<K> iterator() {
- final Iterator<Entry<K, V>> iterator = delegate.entrySet().iterator();
- return new Iterator<K>() {
- Entry<K, V> entry;
-
- @Override
- public boolean hasNext() {
- return iterator.hasNext();
- }
- @Override
- public K next() {
- entry = iterator.next();
- return entry.getKey();
- }
- @Override
- public void remove() {
- checkState(entry != null);
- V value = entry.getValue();
- iterator.remove();
- removeFromInverseMap(value);
- }
- };
+ return Maps.keyIterator(entrySet().iterator());
}
}
@@ -230,23 +226,7 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
}
@Override public Iterator<V> iterator() {
- final Iterator<V> iterator = delegate.values().iterator();
- return new Iterator<V>() {
- V valueToRemove;
-
- @Override public boolean hasNext() {
- return iterator.hasNext();
- }
-
- @Override public V next() {
- return valueToRemove = iterator.next();
- }
-
- @Override public void remove() {
- iterator.remove();
- removeFromInverseMap(valueToRemove);
- }
- };
+ return Maps.valueIterator(entrySet().iterator());
}
@Override public Object[] toArray() {
@@ -378,6 +358,16 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
* If a bimap and its inverse are serialized together, the deserialized
* instances have inverse() methods that return the other.
*/
+
+ @Override
+ K checkKey(K key) {
+ return inverse.checkValue(key);
+ }
+
+ @Override
+ V checkValue(V value) {
+ return inverse.checkKey(value);
+ }
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultimap.java
new file mode 100644
index 000000000..2cfb3efde
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultimap.java
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.RandomAccess;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
+import javax.annotation.Nullable;
+
+/**
+ * Basic implementation of the {@link Multimap} interface. This class represents
+ * a multimap as a map that associates each key with a collection of values. All
+ * methods of {@link Multimap} are supported, including those specified as
+ * optional in the interface.
+ *
+ * <p>To implement a multimap, a subclass must define the method {@link
+ * #createCollection()}, which creates an empty collection of values for a key.
+ *
+ * <p>The multimap constructor takes a map that has a single entry for each
+ * distinct key. When you insert a key-value pair with a key that isn't already
+ * in the multimap, {@code AbstractMapBasedMultimap} calls {@link #createCollection()}
+ * to create the collection of values for that key. The subclass should not call
+ * {@link #createCollection()} directly, and a new instance should be created
+ * every time the method is called.
+ *
+ * <p>For example, the subclass could pass a {@link java.util.TreeMap} during
+ * construction, and {@link #createCollection()} could return a {@link
+ * java.util.TreeSet}, in which case the multimap's iterators would propagate
+ * through the keys and values in sorted order.
+ *
+ * <p>Keys and values may be null, as long as the underlying collection classes
+ * support null elements.
+ *
+ * <p>The collections created by {@link #createCollection()} may or may not
+ * allow duplicates. If the collection, such as a {@link Set}, does not support
+ * duplicates, an added key-value pair will replace an existing pair with the
+ * same key and value, if such a pair is present. With collections like {@link
+ * List} that allow duplicates, the collection will keep the existing key-value
+ * pairs while adding a new pair.
+ *
+ * <p>This class is not threadsafe when any concurrent operations update the
+ * multimap, even if the underlying map and {@link #createCollection()} method
+ * return threadsafe classes. Concurrent read operations will work correctly. To
+ * allow concurrent update operations, wrap your multimap with a call to {@link
+ * Multimaps#synchronizedMultimap}.
+ *
+ * <p>For serialization to work, the subclass must specify explicit
+ * {@code readObject} and {@code writeObject} methods.
+ *
+ * @author Jared Levy
+ * @author Louis Wasserman
+ */
+@GwtCompatible(emulated = true)
+abstract class AbstractMapBasedMultimap<K, V> extends AbstractMultimap<K, V>
+ implements Serializable {
+ /*
+ * Here's an outline of the overall design.
+ *
+ * The map variable contains the collection of values associated with each
+ * key. When a key-value pair is added to a multimap that didn't previously
+ * contain any values for that key, a new collection generated by
+ * createCollection is added to the map. That same collection instance
+ * remains in the map as long as the multimap has any values for the key. If
+ * all values for the key are removed, the key and collection are removed
+ * from the map.
+ *
+ * The get method returns a WrappedCollection, which decorates the collection
+ * in the map (if the key is present) or an empty collection (if the key is
+ * not present). When the collection delegate in the WrappedCollection is
+ * empty, the multimap may contain subsequently added values for that key. To
+ * handle that situation, the WrappedCollection checks whether map contains
+ * an entry for the provided key, and if so replaces the delegate.
+ */
+
+ private transient Map<K, Collection<V>> map;
+ private transient int totalSize;
+
+ /**
+ * Creates a new multimap that uses the provided map.
+ *
+ * @param map place to store the mapping from each key to its corresponding
+ * values
+ * @throws IllegalArgumentException if {@code map} is not empty
+ */
+ protected AbstractMapBasedMultimap(Map<K, Collection<V>> map) {
+ checkArgument(map.isEmpty());
+ this.map = map;
+ }
+
+ /** Used during deserialization only. */
+ final void setMap(Map<K, Collection<V>> map) {
+ this.map = map;
+ totalSize = 0;
+ for (Collection<V> values : map.values()) {
+ checkArgument(!values.isEmpty());
+ totalSize += values.size();
+ }
+ }
+
+ /**
+ * Creates an unmodifiable, empty collection of values.
+ *
+ * <p>This is used in {@link #removeAll} on an empty key.
+ */
+ Collection<V> createUnmodifiableEmptyCollection() {
+ return unmodifiableCollectionSubclass(createCollection());
+ }
+
+ /**
+ * Creates the collection of values for a single key.
+ *
+ * <p>Collections with weak, soft, or phantom references are not supported.
+ * Each call to {@code createCollection} should create a new instance.
+ *
+ * <p>The returned collection class determines whether duplicate key-value
+ * pairs are allowed.
+ *
+ * @return an empty collection of values
+ */
+ abstract Collection<V> createCollection();
+
+ /**
+ * Creates the collection of values for an explicitly provided key. By
+ * default, it simply calls {@link #createCollection()}, which is the correct
+ * behavior for most implementations. The {@link LinkedHashMultimap} class
+ * overrides it.
+ *
+ * @param key key to associate with values in the collection
+ * @return an empty collection of values
+ */
+ Collection<V> createCollection(@Nullable K key) {
+ return createCollection();
+ }
+
+ Map<K, Collection<V>> backingMap() {
+ return map;
+ }
+
+ // Query Operations
+
+ @Override
+ public int size() {
+ return totalSize;
+ }
+
+ @Override
+ public boolean containsKey(@Nullable Object key) {
+ return map.containsKey(key);
+ }
+
+ // Modification Operations
+
+ @Override
+ public boolean put(@Nullable K key, @Nullable V value) {
+ Collection<V> collection = map.get(key);
+ if (collection == null) {
+ collection = createCollection(key);
+ if (collection.add(value)) {
+ totalSize++;
+ map.put(key, collection);
+ return true;
+ } else {
+ throw new AssertionError("New Collection violated the Collection spec");
+ }
+ } else if (collection.add(value)) {
+ totalSize++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private Collection<V> getOrCreateCollection(@Nullable K key) {
+ Collection<V> collection = map.get(key);
+ if (collection == null) {
+ collection = createCollection(key);
+ map.put(key, collection);
+ }
+ return collection;
+ }
+
+ // Bulk Operations
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The returned collection is immutable.
+ */
+ @Override
+ public Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
+ Iterator<? extends V> iterator = values.iterator();
+ if (!iterator.hasNext()) {
+ return removeAll(key);
+ }
+
+ // TODO(user): investigate atomic failure?
+ Collection<V> collection = getOrCreateCollection(key);
+ Collection<V> oldValues = createCollection();
+ oldValues.addAll(collection);
+
+ totalSize -= collection.size();
+ collection.clear();
+
+ while (iterator.hasNext()) {
+ if (collection.add(iterator.next())) {
+ totalSize++;
+ }
+ }
+
+ return unmodifiableCollectionSubclass(oldValues);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The returned collection is immutable.
+ */
+ @Override
+ public Collection<V> removeAll(@Nullable Object key) {
+ Collection<V> collection = map.remove(key);
+
+ if (collection == null) {
+ return createUnmodifiableEmptyCollection();
+ }
+
+ Collection<V> output = createCollection();
+ output.addAll(collection);
+ totalSize -= collection.size();
+ collection.clear();
+
+ return unmodifiableCollectionSubclass(output);
+ }
+
+ Collection<V> unmodifiableCollectionSubclass(Collection<V> collection) {
+ // We don't deal with NavigableSet here yet for GWT reasons -- instead,
+ // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet.
+ if (collection instanceof SortedSet) {
+ return Collections.unmodifiableSortedSet((SortedSet<V>) collection);
+ } else if (collection instanceof Set) {
+ return Collections.unmodifiableSet((Set<V>) collection);
+ } else if (collection instanceof List) {
+ return Collections.unmodifiableList((List<V>) collection);
+ } else {
+ return Collections.unmodifiableCollection(collection);
+ }
+ }
+
+ @Override
+ public void clear() {
+ // Clear each collection, to make previously returned collections empty.
+ for (Collection<V> collection : map.values()) {
+ collection.clear();
+ }
+ map.clear();
+ totalSize = 0;
+ }
+
+ // Views
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The returned collection is not serializable.
+ */
+ @Override
+ public Collection<V> get(@Nullable K key) {
+ Collection<V> collection = map.get(key);
+ if (collection == null) {
+ collection = createCollection(key);
+ }
+ return wrapCollection(key, collection);
+ }
+
+ /**
+ * Generates a decorated collection that remains consistent with the values in
+ * the multimap for the provided key. Changes to the multimap may alter the
+ * returned collection, and vice versa.
+ */
+ Collection<V> wrapCollection(@Nullable K key, Collection<V> collection) {
+ // We don't deal with NavigableSet here yet for GWT reasons -- instead,
+ // non-GWT TreeMultimap explicitly overrides this and uses NavigableSet.
+ if (collection instanceof SortedSet) {
+ return new WrappedSortedSet(key, (SortedSet<V>) collection, null);
+ } else if (collection instanceof Set) {
+ return new WrappedSet(key, (Set<V>) collection);
+ } else if (collection instanceof List) {
+ return wrapList(key, (List<V>) collection, null);
+ } else {
+ return new WrappedCollection(key, collection, null);
+ }
+ }
+
+ private List<V> wrapList(
+ @Nullable K key, List<V> list, @Nullable WrappedCollection ancestor) {
+ return (list instanceof RandomAccess)
+ ? new RandomAccessWrappedList(key, list, ancestor)
+ : new WrappedList(key, list, ancestor);
+ }
+
+ /**
+ * Collection decorator that stays in sync with the multimap values for a key.
+ * There are two kinds of wrapped collections: full and subcollections. Both
+ * have a delegate pointing to the underlying collection class.
+ *
+ * <p>Full collections, identified by a null ancestor field, contain all
+ * multimap values for a given key. Its delegate is a value in {@link
+ * AbstractMapBasedMultimap#map} whenever the delegate is non-empty. The {@code
+ * refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods ensure
+ * that the {@code WrappedCollection} and map remain consistent.
+ *
+ * <p>A subcollection, such as a sublist, contains some of the values for a
+ * given key. Its ancestor field points to the full wrapped collection with
+ * all values for the key. The subcollection {@code refreshIfEmpty}, {@code
+ * removeIfEmpty}, and {@code addToMap} methods call the corresponding methods
+ * of the full wrapped collection.
+ */
+ private class WrappedCollection extends AbstractCollection<V> {
+ final K key;
+ Collection<V> delegate;
+ final WrappedCollection ancestor;
+ final Collection<V> ancestorDelegate;
+
+ WrappedCollection(@Nullable K key, Collection<V> delegate,
+ @Nullable WrappedCollection ancestor) {
+ this.key = key;
+ this.delegate = delegate;
+ this.ancestor = ancestor;
+ this.ancestorDelegate
+ = (ancestor == null) ? null : ancestor.getDelegate();
+ }
+
+ /**
+ * If the delegate collection is empty, but the multimap has values for the
+ * key, replace the delegate with the new collection for the key.
+ *
+ * <p>For a subcollection, refresh its ancestor and validate that the
+ * ancestor delegate hasn't changed.
+ */
+ void refreshIfEmpty() {
+ if (ancestor != null) {
+ ancestor.refreshIfEmpty();
+ if (ancestor.getDelegate() != ancestorDelegate) {
+ throw new ConcurrentModificationException();
+ }
+ } else if (delegate.isEmpty()) {
+ Collection<V> newDelegate = map.get(key);
+ if (newDelegate != null) {
+ delegate = newDelegate;
+ }
+ }
+ }
+
+ /**
+ * If collection is empty, remove it from {@code AbstractMapBasedMultimap.this.map}.
+ * For subcollections, check whether the ancestor collection is empty.
+ */
+ void removeIfEmpty() {
+ if (ancestor != null) {
+ ancestor.removeIfEmpty();
+ } else if (delegate.isEmpty()) {
+ map.remove(key);
+ }
+ }
+
+ K getKey() {
+ return key;
+ }
+
+ /**
+ * Add the delegate to the map. Other {@code WrappedCollection} methods
+ * should call this method after adding elements to a previously empty
+ * collection.
+ *
+ * <p>Subcollection add the ancestor's delegate instead.
+ */
+ void addToMap() {
+ if (ancestor != null) {
+ ancestor.addToMap();
+ } else {
+ map.put(key, delegate);
+ }
+ }
+
+ @Override public int size() {
+ refreshIfEmpty();
+ return delegate.size();
+ }
+
+ @Override public boolean equals(@Nullable Object object) {
+ if (object == this) {
+ return true;
+ }
+ refreshIfEmpty();
+ return delegate.equals(object);
+ }
+
+ @Override public int hashCode() {
+ refreshIfEmpty();
+ return delegate.hashCode();
+ }
+
+ @Override public String toString() {
+ refreshIfEmpty();
+ return delegate.toString();
+ }
+
+ Collection<V> getDelegate() {
+ return delegate;
+ }
+
+ @Override public Iterator<V> iterator() {
+ refreshIfEmpty();
+ return new WrappedIterator();
+ }
+
+ /** Collection iterator for {@code WrappedCollection}. */
+ class WrappedIterator implements Iterator<V> {
+ final Iterator<V> delegateIterator;
+ final Collection<V> originalDelegate = delegate;
+
+ WrappedIterator() {
+ delegateIterator = iteratorOrListIterator(delegate);
+ }
+
+ WrappedIterator(Iterator<V> delegateIterator) {
+ this.delegateIterator = delegateIterator;
+ }
+
+ /**
+ * If the delegate changed since the iterator was created, the iterator is
+ * no longer valid.
+ */
+ void validateIterator() {
+ refreshIfEmpty();
+ if (delegate != originalDelegate) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ validateIterator();
+ return delegateIterator.hasNext();
+ }
+
+ @Override
+ public V next() {
+ validateIterator();
+ return delegateIterator.next();
+ }
+
+ @Override
+ public void remove() {
+ delegateIterator.remove();
+ totalSize--;
+ removeIfEmpty();
+ }
+
+ Iterator<V> getDelegateIterator() {
+ validateIterator();
+ return delegateIterator;
+ }
+ }
+
+ @Override public boolean add(V value) {
+ refreshIfEmpty();
+ boolean wasEmpty = delegate.isEmpty();
+ boolean changed = delegate.add(value);
+ if (changed) {
+ totalSize++;
+ if (wasEmpty) {
+ addToMap();
+ }
+ }
+ return changed;
+ }
+
+ WrappedCollection getAncestor() {
+ return ancestor;
+ }
+
+ // The following methods are provided for better performance.
+
+ @Override public boolean addAll(Collection<? extends V> collection) {
+ if (collection.isEmpty()) {
+ return false;
+ }
+ int oldSize = size(); // calls refreshIfEmpty
+ boolean changed = delegate.addAll(collection);
+ if (changed) {
+ int newSize = delegate.size();
+ totalSize += (newSize - oldSize);
+ if (oldSize == 0) {
+ addToMap();
+ }
+ }
+ return changed;
+ }
+
+ @Override public boolean contains(Object o) {
+ refreshIfEmpty();
+ return delegate.contains(o);
+ }
+
+ @Override public boolean containsAll(Collection<?> c) {
+ refreshIfEmpty();
+ return delegate.containsAll(c);
+ }
+
+ @Override public void clear() {
+ int oldSize = size(); // calls refreshIfEmpty
+ if (oldSize == 0) {
+ return;
+ }
+ delegate.clear();
+ totalSize -= oldSize;
+ removeIfEmpty(); // maybe shouldn't be removed if this is a sublist
+ }
+
+ @Override public boolean remove(Object o) {
+ refreshIfEmpty();
+ boolean changed = delegate.remove(o);
+ if (changed) {
+ totalSize--;
+ removeIfEmpty();
+ }
+ return changed;
+ }
+
+ @Override public boolean removeAll(Collection<?> c) {
+ if (c.isEmpty()) {
+ return false;
+ }
+ int oldSize = size(); // calls refreshIfEmpty
+ boolean changed = delegate.removeAll(c);
+ if (changed) {
+ int newSize = delegate.size();
+ totalSize += (newSize - oldSize);
+ removeIfEmpty();
+ }
+ return changed;
+ }
+
+ @Override public boolean retainAll(Collection<?> c) {
+ checkNotNull(c);
+ int oldSize = size(); // calls refreshIfEmpty
+ boolean changed = delegate.retainAll(c);
+ if (changed) {
+ int newSize = delegate.size();
+ totalSize += (newSize - oldSize);
+ removeIfEmpty();
+ }
+ return changed;
+ }
+ }
+
+ private Iterator<V> iteratorOrListIterator(Collection<V> collection) {
+ return (collection instanceof List)
+ ? ((List<V>) collection).listIterator()
+ : collection.iterator();
+ }
+
+ /** Set decorator that stays in sync with the multimap values for a key. */
+ private class WrappedSet extends WrappedCollection implements Set<V> {
+ WrappedSet(@Nullable K key, Set<V> delegate) {
+ super(key, delegate, null);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ if (c.isEmpty()) {
+ return false;
+ }
+ int oldSize = size(); // calls refreshIfEmpty
+
+ // Guava issue 1013: AbstractSet and most JDK set implementations are
+ // susceptible to quadratic removeAll performance on lists;
+ // use a slightly smarter implementation here
+ boolean changed = Sets.removeAllImpl((Set<V>) delegate, c);
+ if (changed) {
+ int newSize = delegate.size();
+ totalSize += (newSize - oldSize);
+ removeIfEmpty();
+ }
+ return changed;
+ }
+ }
+
+ /**
+ * SortedSet decorator that stays in sync with the multimap values for a key.
+ */
+ private class WrappedSortedSet extends WrappedCollection
+ implements SortedSet<V> {
+ WrappedSortedSet(@Nullable K key, SortedSet<V> delegate,
+ @Nullable WrappedCollection ancestor) {
+ super(key, delegate, ancestor);
+ }
+
+ SortedSet<V> getSortedSetDelegate() {
+ return (SortedSet<V>) getDelegate();
+ }
+
+ @Override
+ public Comparator<? super V> comparator() {
+ return getSortedSetDelegate().comparator();
+ }
+
+ @Override
+ public V first() {
+ refreshIfEmpty();
+ return getSortedSetDelegate().first();
+ }
+
+ @Override
+ public V last() {
+ refreshIfEmpty();
+ return getSortedSetDelegate().last();
+ }
+
+ @Override
+ public SortedSet<V> headSet(V toElement) {
+ refreshIfEmpty();
+ return new WrappedSortedSet(
+ getKey(), getSortedSetDelegate().headSet(toElement),
+ (getAncestor() == null) ? this : getAncestor());
+ }
+
+ @Override
+ public SortedSet<V> subSet(V fromElement, V toElement) {
+ refreshIfEmpty();
+ return new WrappedSortedSet(
+ getKey(), getSortedSetDelegate().subSet(fromElement, toElement),
+ (getAncestor() == null) ? this : getAncestor());
+ }
+
+ @Override
+ public SortedSet<V> tailSet(V fromElement) {
+ refreshIfEmpty();
+ return new WrappedSortedSet(
+ getKey(), getSortedSetDelegate().tailSet(fromElement),
+ (getAncestor() == null) ? this : getAncestor());
+ }
+ }
+
+ /** List decorator that stays in sync with the multimap values for a key. */
+ private class WrappedList extends WrappedCollection implements List<V> {
+ WrappedList(@Nullable K key, List<V> delegate,
+ @Nullable WrappedCollection ancestor) {
+ super(key, delegate, ancestor);
+ }
+
+ List<V> getListDelegate() {
+ return (List<V>) getDelegate();
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends V> c) {
+ if (c.isEmpty()) {
+ return false;
+ }
+ int oldSize = size(); // calls refreshIfEmpty
+ boolean changed = getListDelegate().addAll(index, c);
+ if (changed) {
+ int newSize = getDelegate().size();
+ totalSize += (newSize - oldSize);
+ if (oldSize == 0) {
+ addToMap();
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ public V get(int index) {
+ refreshIfEmpty();
+ return getListDelegate().get(index);
+ }
+
+ @Override
+ public V set(int index, V element) {
+ refreshIfEmpty();
+ return getListDelegate().set(index, element);
+ }
+
+ @Override
+ public void add(int index, V element) {
+ refreshIfEmpty();
+ boolean wasEmpty = getDelegate().isEmpty();
+ getListDelegate().add(index, element);
+ totalSize++;
+ if (wasEmpty) {
+ addToMap();
+ }
+ }
+
+ @Override
+ public V remove(int index) {
+ refreshIfEmpty();
+ V value = getListDelegate().remove(index);
+ totalSize--;
+ removeIfEmpty();
+ return value;
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ refreshIfEmpty();
+ return getListDelegate().indexOf(o);
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ refreshIfEmpty();
+ return getListDelegate().lastIndexOf(o);
+ }
+
+ @Override
+ public ListIterator<V> listIterator() {
+ refreshIfEmpty();
+ return new WrappedListIterator();
+ }
+
+ @Override
+ public ListIterator<V> listIterator(int index) {
+ refreshIfEmpty();
+ return new WrappedListIterator(index);
+ }
+
+ @Override
+ public List<V> subList(int fromIndex, int toIndex) {
+ refreshIfEmpty();
+ return wrapList(getKey(),
+ getListDelegate().subList(fromIndex, toIndex),
+ (getAncestor() == null) ? this : getAncestor());
+ }
+
+ /** ListIterator decorator. */
+ private class WrappedListIterator extends WrappedIterator
+ implements ListIterator<V> {
+ WrappedListIterator() {}
+
+ public WrappedListIterator(int index) {
+ super(getListDelegate().listIterator(index));
+ }
+
+ private ListIterator<V> getDelegateListIterator() {
+ return (ListIterator<V>) getDelegateIterator();
+ }
+
+ @Override
+ public boolean hasPrevious() {
+ return getDelegateListIterator().hasPrevious();
+ }
+
+ @Override
+ public V previous() {
+ return getDelegateListIterator().previous();
+ }
+
+ @Override
+ public int nextIndex() {
+ return getDelegateListIterator().nextIndex();
+ }
+
+ @Override
+ public int previousIndex() {
+ return getDelegateListIterator().previousIndex();
+ }
+
+ @Override
+ public void set(V value) {
+ getDelegateListIterator().set(value);
+ }
+
+ @Override
+ public void add(V value) {
+ boolean wasEmpty = isEmpty();
+ getDelegateListIterator().add(value);
+ totalSize++;
+ if (wasEmpty) {
+ addToMap();
+ }
+ }
+ }
+ }
+
+ /**
+ * List decorator that stays in sync with the multimap values for a key and
+ * supports rapid random access.
+ */
+ private class RandomAccessWrappedList extends WrappedList
+ implements RandomAccess {
+ RandomAccessWrappedList(@Nullable K key, List<V> delegate,
+ @Nullable WrappedCollection ancestor) {
+ super(key, delegate, ancestor);
+ }
+ }
+
+ @Override
+ Set<K> createKeySet() {
+ // TreeMultimap uses NavigableKeySet explicitly, but we don't handle that here for GWT
+ // compatibility reasons
+ return (map instanceof SortedMap)
+ ? new SortedKeySet((SortedMap<K, Collection<V>>) map) : new KeySet(map);
+ }
+
+ private class KeySet extends Maps.KeySet<K, Collection<V>> {
+
+ /**
+ * This is usually the same as map, except when someone requests a
+ * subcollection of a {@link SortedKeySet}.
+ */
+ final Map<K, Collection<V>> subMap;
+
+ KeySet(final Map<K, Collection<V>> subMap) {
+ this.subMap = subMap;
+ }
+
+ @Override
+ Map<K, Collection<V>> map() {
+ return subMap;
+ }
+
+ @Override public Iterator<K> iterator() {
+ final Iterator<Map.Entry<K, Collection<V>>> entryIterator
+ = subMap.entrySet().iterator();
+ return new Iterator<K>() {
+ Map.Entry<K, Collection<V>> entry;
+
+ @Override
+ public boolean hasNext() {
+ return entryIterator.hasNext();
+ }
+ @Override
+ public K next() {
+ entry = entryIterator.next();
+ return entry.getKey();
+ }
+ @Override
+ public void remove() {
+ Iterators.checkRemove(entry != null);
+ Collection<V> collection = entry.getValue();
+ entryIterator.remove();
+ totalSize -= collection.size();
+ collection.clear();
+ }
+ };
+ }
+
+ // The following methods are included for better performance.
+
+ @Override public boolean remove(Object key) {
+ int count = 0;
+ Collection<V> collection = subMap.remove(key);
+ if (collection != null) {
+ count = collection.size();
+ collection.clear();
+ totalSize -= count;
+ }
+ return count > 0;
+ }
+
+ @Override
+ public void clear() {
+ Iterators.clear(iterator());
+ }
+
+ @Override public boolean containsAll(Collection<?> c) {
+ return subMap.keySet().containsAll(c);
+ }
+
+ @Override public boolean equals(@Nullable Object object) {
+ return this == object || this.subMap.keySet().equals(object);
+ }
+
+ @Override public int hashCode() {
+ return subMap.keySet().hashCode();
+ }
+ }
+
+ private class SortedKeySet extends KeySet implements SortedSet<K> {
+
+ SortedKeySet(SortedMap<K, Collection<V>> subMap) {
+ super(subMap);
+ }
+
+ SortedMap<K, Collection<V>> sortedMap() {
+ return (SortedMap<K, Collection<V>>) subMap;
+ }
+
+ @Override
+ public Comparator<? super K> comparator() {
+ return sortedMap().comparator();
+ }
+
+ @Override
+ public K first() {
+ return sortedMap().firstKey();
+ }
+
+ @Override
+ public SortedSet<K> headSet(K toElement) {
+ return new SortedKeySet(sortedMap().headMap(toElement));
+ }
+
+ @Override
+ public K last() {
+ return sortedMap().lastKey();
+ }
+
+ @Override
+ public SortedSet<K> subSet(K fromElement, K toElement) {
+ return new SortedKeySet(sortedMap().subMap(fromElement, toElement));
+ }
+
+ @Override
+ public SortedSet<K> tailSet(K fromElement) {
+ return new SortedKeySet(sortedMap().tailMap(fromElement));
+ }
+ }
+
+ /**
+ * Removes all values for the provided key. Unlike {@link #removeAll}, it
+ * returns the number of removed mappings.
+ */
+ private int removeValuesForKey(Object key) {
+ Collection<V> collection = Maps.safeRemove(map, key);
+
+ int count = 0;
+ if (collection != null) {
+ count = collection.size();
+ collection.clear();
+ totalSize -= count;
+ }
+ return count;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The iterator generated by the returned collection traverses the values
+ * for one key, followed by the values of a second key, and so on.
+ */
+ @Override public Collection<V> values() {
+ return super.values();
+ }
+
+ /*
+ * TODO(kevinb): should we copy this javadoc to each concrete class, so that
+ * classes like LinkedHashMultimap that need to say something different are
+ * still able to {@inheritDoc} all the way from Multimap?
+ */
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The iterator generated by the returned collection traverses the values
+ * for one key, followed by the values of a second key, and so on.
+ *
+ * <p>Each entry is an immutable snapshot of a key-value mapping in the
+ * multimap, taken at the time the entry is returned by a method call to the
+ * collection or its iterator.
+ */
+ @Override
+ public Collection<Map.Entry<K, V>> entries() {
+ return super.entries();
+ }
+
+ /**
+ * Returns an iterator across all key-value map entries, used by {@code
+ * entries().iterator()} and {@code values().iterator()}. The default
+ * behavior, which traverses the values for one key, the values for a second
+ * key, and so on, suffices for most {@code AbstractMapBasedMultimap} implementations.
+ *
+ * @return an iterator across map entries
+ */
+ @Override
+ Iterator<Map.Entry<K, V>> entryIterator() {
+ return new EntryIterator();
+ }
+
+ /** Iterator across all key-value pairs. */
+ private class EntryIterator implements Iterator<Map.Entry<K, V>> {
+ final Iterator<Map.Entry<K, Collection<V>>> keyIterator;
+ K key;
+ Collection<V> collection;
+ Iterator<V> valueIterator;
+
+ EntryIterator() {
+ keyIterator = map.entrySet().iterator();
+ if (keyIterator.hasNext()) {
+ findValueIteratorAndKey();
+ } else {
+ valueIterator = Iterators.emptyModifiableIterator();
+ }
+ }
+
+ void findValueIteratorAndKey() {
+ Map.Entry<K, Collection<V>> entry = keyIterator.next();
+ key = entry.getKey();
+ collection = entry.getValue();
+ valueIterator = collection.iterator();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return keyIterator.hasNext() || valueIterator.hasNext();
+ }
+
+ @Override
+ public Map.Entry<K, V> next() {
+ if (!valueIterator.hasNext()) {
+ findValueIteratorAndKey();
+ }
+ return Maps.immutableEntry(key, valueIterator.next());
+ }
+
+ @Override
+ public void remove() {
+ valueIterator.remove();
+ if (collection.isEmpty()) {
+ keyIterator.remove();
+ }
+ totalSize--;
+ }
+ }
+
+ @Override
+ Map<K, Collection<V>> createAsMap() {
+ // TreeMultimap uses NavigableAsMap explicitly, but we don't handle that here for GWT
+ // compatibility reasons
+ return (map instanceof SortedMap)
+ ? new SortedAsMap((SortedMap<K, Collection<V>>) map) : new AsMap(map);
+ }
+
+ private class AsMap extends AbstractMap<K, Collection<V>> {
+ /**
+ * Usually the same as map, but smaller for the headMap(), tailMap(), or
+ * subMap() of a SortedAsMap.
+ */
+ final transient Map<K, Collection<V>> submap;
+
+ AsMap(Map<K, Collection<V>> submap) {
+ this.submap = submap;
+ }
+
+ transient Set<Map.Entry<K, Collection<V>>> entrySet;
+
+ @Override public Set<Map.Entry<K, Collection<V>>> entrySet() {
+ Set<Map.Entry<K, Collection<V>>> result = entrySet;
+ return (result == null) ? entrySet = new AsMapEntries() : result;
+ }
+
+ // The following methods are included for performance.
+
+ @Override public boolean containsKey(Object key) {
+ return Maps.safeContainsKey(submap, key);
+ }
+
+ @Override public Collection<V> get(Object key) {
+ Collection<V> collection = Maps.safeGet(submap, key);
+ if (collection == null) {
+ return null;
+ }
+ @SuppressWarnings("unchecked")
+ K k = (K) key;
+ return wrapCollection(k, collection);
+ }
+
+ @Override public Set<K> keySet() {
+ return AbstractMapBasedMultimap.this.keySet();
+ }
+
+ @Override
+ public int size() {
+ return submap.size();
+ }
+
+ @Override public Collection<V> remove(Object key) {
+ Collection<V> collection = submap.remove(key);
+ if (collection == null) {
+ return null;
+ }
+
+ Collection<V> output = createCollection();
+ output.addAll(collection);
+ totalSize -= collection.size();
+ collection.clear();
+ return output;
+ }
+
+ @Override public boolean equals(@Nullable Object object) {
+ return this == object || submap.equals(object);
+ }
+
+ @Override public int hashCode() {
+ return submap.hashCode();
+ }
+
+ @Override public String toString() {
+ return submap.toString();
+ }
+
+ @Override
+ public void clear() {
+ if (submap == map) {
+ AbstractMapBasedMultimap.this.clear();
+ } else {
+
+ Iterators.clear(new AsMapIterator());
+ }
+ }
+
+ Entry<K, Collection<V>> wrapEntry(Entry<K, Collection<V>> entry) {
+ K key = entry.getKey();
+ return Maps.immutableEntry(key, wrapCollection(key, entry.getValue()));
+ }
+
+ class AsMapEntries extends Maps.EntrySet<K, Collection<V>> {
+ @Override
+ Map<K, Collection<V>> map() {
+ return AsMap.this;
+ }
+
+ @Override public Iterator<Map.Entry<K, Collection<V>>> iterator() {
+ return new AsMapIterator();
+ }
+
+ // The following methods are included for performance.
+
+ @Override public boolean contains(Object o) {
+ return Collections2.safeContains(submap.entrySet(), o);
+ }
+
+ @Override public boolean remove(Object o) {
+ if (!contains(o)) {
+ return false;
+ }
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
+ removeValuesForKey(entry.getKey());
+ return true;
+ }
+ }
+
+ /** Iterator across all keys and value collections. */
+ class AsMapIterator implements Iterator<Map.Entry<K, Collection<V>>> {
+ final Iterator<Map.Entry<K, Collection<V>>> delegateIterator
+ = submap.entrySet().iterator();
+ Collection<V> collection;
+
+ @Override
+ public boolean hasNext() {
+ return delegateIterator.hasNext();
+ }
+
+ @Override
+ public Map.Entry<K, Collection<V>> next() {
+ Map.Entry<K, Collection<V>> entry = delegateIterator.next();
+ collection = entry.getValue();
+ return wrapEntry(entry);
+ }
+
+ @Override
+ public void remove() {
+ delegateIterator.remove();
+ totalSize -= collection.size();
+ collection.clear();
+ }
+ }
+ }
+
+ private class SortedAsMap extends AsMap
+ implements SortedMap<K, Collection<V>> {
+ SortedAsMap(SortedMap<K, Collection<V>> submap) {
+ super(submap);
+ }
+
+ SortedMap<K, Collection<V>> sortedMap() {
+ return (SortedMap<K, Collection<V>>) submap;
+ }
+
+ @Override
+ public Comparator<? super K> comparator() {
+ return sortedMap().comparator();
+ }
+
+ @Override
+ public K firstKey() {
+ return sortedMap().firstKey();
+ }
+
+ @Override
+ public K lastKey() {
+ return sortedMap().lastKey();
+ }
+
+ @Override
+ public SortedMap<K, Collection<V>> headMap(K toKey) {
+ return new SortedAsMap(sortedMap().headMap(toKey));
+ }
+
+ @Override
+ public SortedMap<K, Collection<V>> subMap(K fromKey, K toKey) {
+ return new SortedAsMap(sortedMap().subMap(fromKey, toKey));
+ }
+
+ @Override
+ public SortedMap<K, Collection<V>> tailMap(K fromKey) {
+ return new SortedAsMap(sortedMap().tailMap(fromKey));
+ }
+
+ SortedSet<K> sortedKeySet;
+
+ // returns a SortedSet, even though returning a Set would be sufficient to
+ // satisfy the SortedMap.keySet() interface
+ @Override public SortedSet<K> keySet() {
+ SortedSet<K> result = sortedKeySet;
+ return (result == null) ? sortedKeySet = createKeySet() : result;
+ }
+
+ SortedSet<K> createKeySet() {
+ return new SortedKeySet(sortedMap());
+ }
+ }
+
+ private static final long serialVersionUID = 2447537837011683357L;
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultiset.java
index 6bccc6db7..3c0f05d5e 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultiset.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultiset.java
@@ -25,7 +25,6 @@ import com.google.common.annotations.GwtCompatible;
import com.google.common.primitives.Ints;
import java.io.Serializable;
-import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
@@ -35,7 +34,7 @@ import javax.annotation.Nullable;
/**
* Basic implementation of {@code Multiset<E>} backed by an instance of {@code
- * Map<E, AtomicInteger>}.
+ * Map<E, Count>}.
*
* <p>For serialization to work, the subclass must specify explicit {@code
* readObject} and {@code writeObject} methods.
@@ -61,10 +60,6 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
this.size = super.size();
}
- Map<E, Count> backingMap() {
- return backingMap;
- }
-
/** Used during deserialization only. The backing map must be empty. */
void setBackingMap(Map<E, Count> backingMap) {
this.backingMap = backingMap;
@@ -121,8 +116,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
@Override
public void remove() {
- checkState(toRemove != null,
- "no calls to next() since the last call to remove()");
+ Iterators.checkRemove(toRemove != null);
size -= toRemove.getValue().getAndSet(0);
backingEntries.remove();
toRemove = null;
@@ -156,7 +150,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
/*
* Not subclassing AbstractMultiset$MultisetIterator because next() needs to
- * retrieve the Map.Entry<E, AtomicInteger> entry, which can then be used for
+ * retrieve the Map.Entry<E, Count> entry, which can then be used for
* a more efficient remove() call.
*/
private class MapBasedMultisetIterator implements Iterator<E> {
@@ -202,14 +196,8 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
}
@Override public int count(@Nullable Object element) {
- try {
- Count frequency = backingMap.get(element);
- return (frequency == null) ? 0 : frequency.get();
- } catch (NullPointerException e) {
- return 0;
- } catch (ClassCastException e) {
- return 0;
- }
+ Count frequency = Maps.safeGet(backingMap, element);
+ return (frequency == null) ? 0 : frequency.get();
}
// Optional Operations - Modification Operations
@@ -270,7 +258,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
}
// Roughly a 33% performance improvement over AbstractMultiset.setCount().
- @Override public int setCount(E element, int count) {
+ @Override public int setCount(@Nullable E element, int count) {
checkNonnegative(count, "count");
Count existingCounter;
@@ -299,97 +287,6 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E>
return i.getAndSet(count);
}
- private int removeAllOccurrences(@Nullable Object element,
- Map<E, Count> map) {
- Count frequency = map.remove(element);
- if (frequency == null) {
- return 0;
- }
- int numberRemoved = frequency.getAndSet(0);
- size -= numberRemoved;
- return numberRemoved;
- }
-
- // Views
-
- @Override Set<E> createElementSet() {
- return new MapBasedElementSet(backingMap);
- }
-
- // TODO(user): once TreeMultiset is replaced with a SortedMultiset
- // implementation, replace this with a subclass of Multisets.ElementSet.
- class MapBasedElementSet extends ForwardingSet<E> {
-
- // This mapping is the usually the same as 'backingMap', but can be a
- // submap in some implementations.
- private final Map<E, Count> map;
- private final Set<E> delegate;
-
- MapBasedElementSet(Map<E, Count> map) {
- this.map = map;
- delegate = map.keySet();
- }
-
- @Override protected Set<E> delegate() {
- return delegate;
- }
-
- @Override public Iterator<E> iterator() {
- final Iterator<Map.Entry<E, Count>> entries
- = map.entrySet().iterator();
- return new Iterator<E>() {
- Map.Entry<E, Count> toRemove;
-
- @Override
- public boolean hasNext() {
- return entries.hasNext();
- }
-
- @Override
- public E next() {
- toRemove = entries.next();
- return toRemove.getKey();
- }
-
- @Override
- public void remove() {
- checkState(toRemove != null,
- "no calls to next() since the last call to remove()");
- size -= toRemove.getValue().getAndSet(0);
- entries.remove();
- toRemove = null;
- }
- };
- }
-
- @Override public boolean remove(Object element) {
- return removeAllOccurrences(element, map) != 0;
- }
-
- @Override public boolean removeAll(Collection<?> elementsToRemove) {
- return Iterators.removeAll(iterator(), elementsToRemove);
- }
-
- @Override public boolean retainAll(Collection<?> elementsToRetain) {
- return Iterators.retainAll(iterator(), elementsToRetain);
- }
-
- @Override public void clear() {
- if (map == backingMap) {
- AbstractMapBasedMultiset.this.clear();
- } else {
- Iterator<E> i = iterator();
- while (i.hasNext()) {
- i.next();
- i.remove();
- }
- }
- }
-
- public Map<E, Count> getMap() {
- return map;
- }
- }
-
// Don't allow default serialization.
}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractSortedMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractSortedMultiset.java
new file mode 100644
index 000000000..3eea73477
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractSortedMultiset.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.SortedSet;
+
+import javax.annotation.Nullable;
+
+/**
+ * This class provides a skeletal implementation of the {@link SortedMultiset} interface.
+ *
+ * <p>The {@link #count} and {@link #size} implementations all iterate across the set returned by
+ * {@link Multiset#entrySet()}, as do many methods acting on the set returned by
+ * {@link #elementSet()}. Override those methods for better performance.
+ *
+ * @author Louis Wasserman
+ */
+@GwtCompatible(emulated = true)
+abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements SortedMultiset<E> {
+ @GwtTransient final Comparator<? super E> comparator;
+
+ // needed for serialization
+ @SuppressWarnings("unchecked")
+ AbstractSortedMultiset() {
+ this((Comparator) Ordering.natural());
+ }
+
+ AbstractSortedMultiset(Comparator<? super E> comparator) {
+ this.comparator = checkNotNull(comparator);
+ }
+
+ @Override
+ public SortedSet<E> elementSet() {
+ return (SortedSet<E>) super.elementSet();
+ }
+
+ @Override
+ SortedSet<E> createElementSet() {
+ return new SortedMultisets.ElementSet<E>(this);
+ }
+
+ @Override
+ public Comparator<? super E> comparator() {
+ return comparator;
+ }
+
+ @Override
+ public Entry<E> firstEntry() {
+ Iterator<Entry<E>> entryIterator = entryIterator();
+ return entryIterator.hasNext() ? entryIterator.next() : null;
+ }
+
+ @Override
+ public Entry<E> lastEntry() {
+ Iterator<Entry<E>> entryIterator = descendingEntryIterator();
+ return entryIterator.hasNext() ? entryIterator.next() : null;
+ }
+
+ @Override
+ public Entry<E> pollFirstEntry() {
+ Iterator<Entry<E>> entryIterator = entryIterator();
+ if (entryIterator.hasNext()) {
+ Entry<E> result = entryIterator.next();
+ result = Multisets.immutableEntry(result.getElement(), result.getCount());
+ entryIterator.remove();
+ return result;
+ }
+ return null;
+ }
+
+ @Override
+ public Entry<E> pollLastEntry() {
+ Iterator<Entry<E>> entryIterator = descendingEntryIterator();
+ if (entryIterator.hasNext()) {
+ Entry<E> result = entryIterator.next();
+ result = Multisets.immutableEntry(result.getElement(), result.getCount());
+ entryIterator.remove();
+ return result;
+ }
+ return null;
+ }
+
+ @Override
+ public SortedMultiset<E> subMultiset(@Nullable E fromElement, BoundType fromBoundType,
+ @Nullable E toElement, BoundType toBoundType) {
+ // These are checked elsewhere, but NullPointerTester wants them checked eagerly.
+ checkNotNull(fromBoundType);
+ checkNotNull(toBoundType);
+ return tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType);
+ }
+
+ abstract Iterator<Entry<E>> descendingEntryIterator();
+
+ Iterator<E> descendingIterator() {
+ return Multisets.iteratorImpl(descendingMultiset());
+ }
+
+ private transient SortedMultiset<E> descendingMultiset;
+
+ @Override
+ public SortedMultiset<E> descendingMultiset() {
+ SortedMultiset<E> result = descendingMultiset;
+ return (result == null) ? descendingMultiset = createDescendingMultiset() : result;
+ }
+
+ SortedMultiset<E> createDescendingMultiset() {
+ return new DescendingMultiset<E>() {
+ @Override
+ SortedMultiset<E> forwardMultiset() {
+ return AbstractSortedMultiset.this;
+ }
+
+ @Override
+ Iterator<Entry<E>> entryIterator() {
+ return descendingEntryIterator();
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return descendingIterator();
+ }
+ };
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayListMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayListMultimap.java
index da5478cce..59d4e7709 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayListMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayListMultimap.java
@@ -50,6 +50,10 @@ import java.util.List;
* multimap. Concurrent read operations will work correctly. To allow concurrent
* update operations, wrap your multimap with a call to {@link
* Multimaps#synchronizedListMultimap}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
+ * {@code Multimap}</a>.
*
* @author Jared Levy
* @since 2.0 (imported from Google Collections Library)
@@ -57,7 +61,7 @@ import java.util.List;
@GwtCompatible(serializable = true, emulated = true)
public final class ArrayListMultimap<K, V> extends AbstractListMultimap<K, V> {
// Default from ArrayList
- private static final int DEFAULT_VALUES_PER_KEY = 10;
+ private static final int DEFAULT_VALUES_PER_KEY = 3;
@VisibleForTesting transient int expectedValuesPerKey;
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayTable.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayTable.java
new file mode 100644
index 000000000..bde92c553
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayTable.java
@@ -0,0 +1,817 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkElementIndex;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Objects;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * Fixed-size {@link Table} implementation backed by a two-dimensional array.
+ *
+ * <p>The allowed row and column keys must be supplied when the table is
+ * created. The table always contains a mapping for every row key / column pair.
+ * The value corresponding to a given row and column is null unless another
+ * value is provided.
+ *
+ * <p>The table's size is constant: the product of the number of supplied row
+ * keys and the number of supplied column keys. The {@code remove} and {@code
+ * clear} methods are not supported by the table or its views. The {@link
+ * #erase} and {@link #eraseAll} methods may be used instead.
+ *
+ * <p>The ordering of the row and column keys provided when the table is
+ * constructed determines the iteration ordering across rows and columns in the
+ * table's views. None of the view iterators support {@link Iterator#remove}.
+ * If the table is modified after an iterator is created, the iterator remains
+ * valid.
+ *
+ * <p>This class requires less memory than the {@link HashBasedTable} and {@link
+ * TreeBasedTable} implementations, except when the table is sparse.
+ *
+ * <p>Null row keys or column keys are not permitted.
+ *
+ * <p>This class provides methods involving the underlying array structure,
+ * where the array indices correspond to the position of a row or column in the
+ * lists of allowed keys and values. See the {@link #at}, {@link #set}, {@link
+ * #toArray}, {@link #rowKeyList}, and {@link #columnKeyList} methods for more
+ * details.
+ *
+ * <p>Note that this implementation is not synchronized. If multiple threads
+ * access the same cell of an {@code ArrayTable} concurrently and one of the
+ * threads modifies its value, there is no guarantee that the new value will be
+ * fully visible to the other threads. To guarantee that modifications are
+ * visible, synchronize access to the table. Unlike other {@code Table}
+ * implementations, synchronization is unnecessary between a thread that writes
+ * to one cell and a thread that reads from another.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table">
+ * {@code Table}</a>.
+ *
+ * @author Jared Levy
+ * @since 10.0
+ */
+@Beta
+@GwtCompatible(emulated = true)
+public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable {
+
+ /**
+ * Creates an empty {@code ArrayTable}.
+ *
+ * @param rowKeys row keys that may be stored in the generated table
+ * @param columnKeys column keys that may be stored in the generated table
+ * @throws NullPointerException if any of the provided keys is null
+ * @throws IllegalArgumentException if {@code rowKeys} or {@code columnKeys}
+ * contains duplicates or is empty
+ */
+ public static <R, C, V> ArrayTable<R, C, V> create(
+ Iterable<? extends R> rowKeys, Iterable<? extends C> columnKeys) {
+ return new ArrayTable<R, C, V>(rowKeys, columnKeys);
+ }
+
+ /*
+ * TODO(jlevy): Add factory methods taking an Enum class, instead of an
+ * iterable, to specify the allowed row keys and/or column keys. Note that
+ * custom serialization logic is needed to support different enum sizes during
+ * serialization and deserialization.
+ */
+
+ /**
+ * Creates an {@code ArrayTable} with the mappings in the provided table.
+ *
+ * <p>If {@code table} includes a mapping with row key {@code r} and a
+ * separate mapping with column key {@code c}, the returned table contains a
+ * mapping with row key {@code r} and column key {@code c}. If that row key /
+ * column key pair in not in {@code table}, the pair maps to {@code null} in
+ * the generated table.
+ *
+ * <p>The returned table allows subsequent {@code put} calls with the row keys
+ * in {@code table.rowKeySet()} and the column keys in {@code
+ * table.columnKeySet()}. Calling {@link #put} with other keys leads to an
+ * {@code IllegalArgumentException}.
+ *
+ * <p>The ordering of {@code table.rowKeySet()} and {@code
+ * table.columnKeySet()} determines the row and column iteration ordering of
+ * the returned table.
+ *
+ * @throws NullPointerException if {@code table} has a null key
+ * @throws IllegalArgumentException if the provided table is empty
+ */
+ public static <R, C, V> ArrayTable<R, C, V> create(Table<R, C, V> table) {
+ return new ArrayTable<R, C, V>(table);
+ }
+
+ /**
+ * Creates an {@code ArrayTable} with the same mappings, allowed keys, and
+ * iteration ordering as the provided {@code ArrayTable}.
+ */
+ public static <R, C, V> ArrayTable<R, C, V> create(
+ ArrayTable<R, C, V> table) {
+ return new ArrayTable<R, C, V>(table);
+ }
+
+ private final ImmutableList<R> rowList;
+ private final ImmutableList<C> columnList;
+
+ // TODO(jlevy): Add getters returning rowKeyToIndex and columnKeyToIndex?
+ private final ImmutableMap<R, Integer> rowKeyToIndex;
+ private final ImmutableMap<C, Integer> columnKeyToIndex;
+ private final V[][] array;
+
+ private ArrayTable(Iterable<? extends R> rowKeys,
+ Iterable<? extends C> columnKeys) {
+ this.rowList = ImmutableList.copyOf(rowKeys);
+ this.columnList = ImmutableList.copyOf(columnKeys);
+ checkArgument(!rowList.isEmpty());
+ checkArgument(!columnList.isEmpty());
+
+ /*
+ * TODO(jlevy): Support empty rowKeys or columnKeys? If we do, when
+ * columnKeys is empty but rowKeys isn't, the table is empty but
+ * containsRow() can return true and rowKeySet() isn't empty.
+ */
+ rowKeyToIndex = index(rowList);
+ columnKeyToIndex = index(columnList);
+
+ @SuppressWarnings("unchecked")
+ V[][] tmpArray
+ = (V[][]) new Object[rowList.size()][columnList.size()];
+ array = tmpArray;
+ // Necessary because in GWT the arrays are initialized with "undefined" instead of null.
+ eraseAll();
+ }
+
+ private static <E> ImmutableMap<E, Integer> index(List<E> list) {
+ ImmutableMap.Builder<E, Integer> columnBuilder = ImmutableMap.builder();
+ for (int i = 0; i < list.size(); i++) {
+ columnBuilder.put(list.get(i), i);
+ }
+ return columnBuilder.build();
+ }
+
+ private ArrayTable(Table<R, C, V> table) {
+ this(table.rowKeySet(), table.columnKeySet());
+ putAll(table);
+ }
+
+ private ArrayTable(ArrayTable<R, C, V> table) {
+ rowList = table.rowList;
+ columnList = table.columnList;
+ rowKeyToIndex = table.rowKeyToIndex;
+ columnKeyToIndex = table.columnKeyToIndex;
+ @SuppressWarnings("unchecked")
+ V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()];
+ array = copy;
+ // Necessary because in GWT the arrays are initialized with "undefined" instead of null.
+ eraseAll();
+ for (int i = 0; i < rowList.size(); i++) {
+ System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length);
+ }
+ }
+
+ private abstract static class ArrayMap<K, V> extends Maps.ImprovedAbstractMap<K, V> {
+ private final ImmutableMap<K, Integer> keyIndex;
+
+ private ArrayMap(ImmutableMap<K, Integer> keyIndex) {
+ this.keyIndex = keyIndex;
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return keyIndex.keySet();
+ }
+
+ K getKey(int index) {
+ return keyIndex.keySet().asList().get(index);
+ }
+
+ abstract String getKeyRole();
+
+ @Nullable abstract V getValue(int index);
+
+ @Nullable abstract V setValue(int index, V newValue);
+
+ @Override
+ public int size() {
+ return keyIndex.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return keyIndex.isEmpty();
+ }
+
+ @Override
+ protected Set<Entry<K, V>> createEntrySet() {
+ return new Maps.EntrySet<K, V>() {
+ @Override
+ Map<K, V> map() {
+ return ArrayMap.this;
+ }
+
+ @Override
+ public Iterator<Entry<K, V>> iterator() {
+ return new AbstractIndexedListIterator<Entry<K, V>>(size()) {
+ @Override
+ protected Entry<K, V> get(final int index) {
+ return new AbstractMapEntry<K, V>() {
+ @Override
+ public K getKey() {
+ return ArrayMap.this.getKey(index);
+ }
+
+ @Override
+ public V getValue() {
+ return ArrayMap.this.getValue(index);
+ }
+
+ @Override
+ public V setValue(V value) {
+ return ArrayMap.this.setValue(index, value);
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public boolean containsKey(@Nullable Object key) {
+ return keyIndex.containsKey(key);
+ }
+
+ @Override
+ public V get(@Nullable Object key) {
+ Integer index = keyIndex.get(key);
+ if (index == null) {
+ return null;
+ } else {
+ return getValue(index);
+ }
+ }
+
+ @Override
+ public V put(K key, V value) {
+ Integer index = keyIndex.get(key);
+ if (index == null) {
+ throw new IllegalArgumentException(
+ getKeyRole() + " " + key + " not in " + keyIndex.keySet());
+ }
+ return setValue(index, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Returns, as an immutable list, the row keys provided when the table was
+ * constructed, including those that are mapped to null values only.
+ */
+ public ImmutableList<R> rowKeyList() {
+ return rowList;
+ }
+
+ /**
+ * Returns, as an immutable list, the column keys provided when the table was
+ * constructed, including those that are mapped to null values only.
+ */
+ public ImmutableList<C> columnKeyList() {
+ return columnList;
+ }
+
+ /**
+ * Returns the value corresponding to the specified row and column indices.
+ * The same value is returned by {@code
+ * get(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex))}, but
+ * this method runs more quickly.
+ *
+ * @param rowIndex position of the row key in {@link #rowKeyList()}
+ * @param columnIndex position of the row key in {@link #columnKeyList()}
+ * @return the value with the specified row and column
+ * @throws IndexOutOfBoundsException if either index is negative, {@code
+ * rowIndex} is greater then or equal to the number of allowed row keys,
+ * or {@code columnIndex} is greater then or equal to the number of
+ * allowed column keys
+ */
+ public V at(int rowIndex, int columnIndex) {
+ // In GWT array access never throws IndexOutOfBoundsException.
+ checkElementIndex(rowIndex, rowList.size());
+ checkElementIndex(columnIndex, columnList.size());
+ return array[rowIndex][columnIndex];
+ }
+
+ /**
+ * Associates {@code value} with the specified row and column indices. The
+ * logic {@code
+ * put(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex), value)}
+ * has the same behavior, but this method runs more quickly.
+ *
+ * @param rowIndex position of the row key in {@link #rowKeyList()}
+ * @param columnIndex position of the row key in {@link #columnKeyList()}
+ * @param value value to store in the table
+ * @return the previous value with the specified row and column
+ * @throws IndexOutOfBoundsException if either index is negative, {@code
+ * rowIndex} is greater then or equal to the number of allowed row keys,
+ * or {@code columnIndex} is greater then or equal to the number of
+ * allowed column keys
+ */
+ public V set(int rowIndex, int columnIndex, @Nullable V value) {
+ // In GWT array access never throws IndexOutOfBoundsException.
+ checkElementIndex(rowIndex, rowList.size());
+ checkElementIndex(columnIndex, columnList.size());
+ V oldValue = array[rowIndex][columnIndex];
+ array[rowIndex][columnIndex] = value;
+ return oldValue;
+ }
+
+ /**
+ * Not supported. Use {@link #eraseAll} instead.
+ *
+ * @throws UnsupportedOperationException always
+ * @deprecated Use {@link #eraseAll}
+ */
+ @Override
+ @Deprecated public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Associates the value {@code null} with every pair of allowed row and column
+ * keys.
+ */
+ public void eraseAll() {
+ for (V[] row : array) {
+ Arrays.fill(row, null);
+ }
+ }
+
+ /**
+ * Returns {@code true} if the provided keys are among the keys provided when
+ * the table was constructed.
+ */
+ @Override
+ public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
+ return containsRow(rowKey) && containsColumn(columnKey);
+ }
+
+ /**
+ * Returns {@code true} if the provided column key is among the column keys
+ * provided when the table was constructed.
+ */
+ @Override
+ public boolean containsColumn(@Nullable Object columnKey) {
+ return columnKeyToIndex.containsKey(columnKey);
+ }
+
+ /**
+ * Returns {@code true} if the provided row key is among the row keys
+ * provided when the table was constructed.
+ */
+ @Override
+ public boolean containsRow(@Nullable Object rowKey) {
+ return rowKeyToIndex.containsKey(rowKey);
+ }
+
+ @Override
+ public boolean containsValue(@Nullable Object value) {
+ for (V[] row : array) {
+ for (V element : row) {
+ if (Objects.equal(value, element)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
+ Integer rowIndex = rowKeyToIndex.get(rowKey);
+ Integer columnIndex = columnKeyToIndex.get(columnKey);
+ return (rowIndex == null || columnIndex == null)
+ ? null : at(rowIndex, columnIndex);
+ }
+
+ /**
+ * Always returns {@code false}.
+ */
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IllegalArgumentException if {@code rowKey} is not in {@link
+ * #rowKeySet()} or {@code columnKey} is not in {@link #columnKeySet()}.
+ */
+ @Override
+ public V put(R rowKey, C columnKey, @Nullable V value) {
+ checkNotNull(rowKey);
+ checkNotNull(columnKey);
+ Integer rowIndex = rowKeyToIndex.get(rowKey);
+ checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList);
+ Integer columnIndex = columnKeyToIndex.get(columnKey);
+ checkArgument(columnIndex != null,
+ "Column %s not in %s", columnKey, columnList);
+ return set(rowIndex, columnIndex, value);
+ }
+
+ /*
+ * TODO(jlevy): Consider creating a merge() method, similar to putAll() but
+ * copying non-null values only.
+ */
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>If {@code table} is an {@code ArrayTable}, its null values will be
+ * stored in this table, possibly replacing values that were previously
+ * non-null.
+ *
+ * @throws NullPointerException if {@code table} has a null key
+ * @throws IllegalArgumentException if any of the provided table's row keys or
+ * column keys is not in {@link #rowKeySet()} or {@link #columnKeySet()}
+ */
+ @Override
+ public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
+ for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
+ put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
+ }
+ }
+
+ /**
+ * Not supported. Use {@link #erase} instead.
+ *
+ * @throws UnsupportedOperationException always
+ * @deprecated Use {@link #erase}
+ */
+ @Override
+ @Deprecated public V remove(Object rowKey, Object columnKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Associates the value {@code null} with the specified keys, assuming both
+ * keys are valid. If either key is null or isn't among the keys provided
+ * during construction, this method has no effect.
+ *
+ * <p>This method is equivalent to {@code put(rowKey, columnKey, null)} when
+ * both provided keys are valid.
+ *
+ * @param rowKey row key of mapping to be erased
+ * @param columnKey column key of mapping to be erased
+ * @return the value previously associated with the keys, or {@code null} if
+ * no mapping existed for the keys
+ */
+ public V erase(@Nullable Object rowKey, @Nullable Object columnKey) {
+ Integer rowIndex = rowKeyToIndex.get(rowKey);
+ Integer columnIndex = columnKeyToIndex.get(columnKey);
+ if (rowIndex == null || columnIndex == null) {
+ return null;
+ }
+ return set(rowIndex, columnIndex, null);
+ }
+
+ // TODO(jlevy): Add eraseRow and eraseColumn methods?
+
+ @Override
+ public int size() {
+ return rowList.size() * columnList.size();
+ }
+
+ @Override public boolean equals(@Nullable Object obj) {
+ if (obj instanceof Table) {
+ Table<?, ?, ?> other = (Table<?, ?, ?>) obj;
+ return cellSet().equals(other.cellSet());
+ }
+ return false;
+ }
+
+ @Override public int hashCode() {
+ return cellSet().hashCode();
+ }
+
+ /**
+ * Returns the string representation {@code rowMap().toString()}.
+ */
+ @Override public String toString() {
+ return rowMap().toString();
+ }
+
+ private transient CellSet cellSet;
+
+ /**
+ * Returns an unmodifiable set of all row key / column key / value
+ * triplets. Changes to the table will update the returned set.
+ *
+ * <p>The returned set's iterator traverses the mappings with the first row
+ * key, the mappings with the second row key, and so on.
+ *
+ * <p>The value in the returned cells may change if the table subsequently
+ * changes.
+ *
+ * @return set of table cells consisting of row key / column key / value
+ * triplets
+ */
+ @Override
+ public Set<Cell<R, C, V>> cellSet() {
+ CellSet set = cellSet;
+ return (set == null) ? cellSet = new CellSet() : set;
+ }
+
+ private class CellSet extends AbstractSet<Cell<R, C, V>> {
+
+ @Override public Iterator<Cell<R, C, V>> iterator() {
+ return new AbstractIndexedListIterator<Cell<R, C, V>>(size()) {
+ @Override protected Cell<R, C, V> get(final int index) {
+ return new Tables.AbstractCell<R, C, V>() {
+ final int rowIndex = index / columnList.size();
+ final int columnIndex = index % columnList.size();
+ @Override
+ public R getRowKey() {
+ return rowList.get(rowIndex);
+ }
+ @Override
+ public C getColumnKey() {
+ return columnList.get(columnIndex);
+ }
+ @Override
+ public V getValue() {
+ return at(rowIndex, columnIndex);
+ }
+ };
+ }
+ };
+ }
+
+ @Override public int size() {
+ return ArrayTable.this.size();
+ }
+
+ @Override public boolean contains(Object obj) {
+ if (obj instanceof Cell) {
+ Cell<?, ?, ?> cell = (Cell<?, ?, ?>) obj;
+ Integer rowIndex = rowKeyToIndex.get(cell.getRowKey());
+ Integer columnIndex = columnKeyToIndex.get(cell.getColumnKey());
+ return rowIndex != null
+ && columnIndex != null
+ && Objects.equal(at(rowIndex, columnIndex), cell.getValue());
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns a view of all mappings that have the given column key. If the
+ * column key isn't in {@link #columnKeySet()}, an empty immutable map is
+ * returned.
+ *
+ * <p>Otherwise, for each row key in {@link #rowKeySet()}, the returned map
+ * associates the row key with the corresponding value in the table. Changes
+ * to the returned map will update the underlying table, and vice versa.
+ *
+ * @param columnKey key of column to search for in the table
+ * @return the corresponding map from row keys to values
+ */
+ @Override
+ public Map<R, V> column(C columnKey) {
+ checkNotNull(columnKey);
+ Integer columnIndex = columnKeyToIndex.get(columnKey);
+ return (columnIndex == null)
+ ? ImmutableMap.<R, V>of() : new Column(columnIndex);
+ }
+
+ private class Column extends ArrayMap<R, V> {
+ final int columnIndex;
+
+ Column(int columnIndex) {
+ super(rowKeyToIndex);
+ this.columnIndex = columnIndex;
+ }
+
+ @Override
+ String getKeyRole() {
+ return "Row";
+ }
+
+ @Override
+ V getValue(int index) {
+ return at(index, columnIndex);
+ }
+
+ @Override
+ V setValue(int index, V newValue) {
+ return set(index, columnIndex, newValue);
+ }
+ }
+
+ /**
+ * Returns an immutable set of the valid column keys, including those that
+ * are associated with null values only.
+ *
+ * @return immutable set of column keys
+ */
+ @Override
+ public ImmutableSet<C> columnKeySet() {
+ return columnKeyToIndex.keySet();
+ }
+
+ private transient ColumnMap columnMap;
+
+ @Override
+ public Map<C, Map<R, V>> columnMap() {
+ ColumnMap map = columnMap;
+ return (map == null) ? columnMap = new ColumnMap() : map;
+ }
+
+ private class ColumnMap extends ArrayMap<C, Map<R, V>> {
+ private ColumnMap() {
+ super(columnKeyToIndex);
+ }
+
+ @Override
+ String getKeyRole() {
+ return "Column";
+ }
+
+ @Override
+ Map<R, V> getValue(int index) {
+ return new Column(index);
+ }
+
+ @Override
+ Map<R, V> setValue(int index, Map<R, V> newValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<R, V> put(C key, Map<R, V> value) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Returns a view of all mappings that have the given row key. If the
+ * row key isn't in {@link #rowKeySet()}, an empty immutable map is
+ * returned.
+ *
+ * <p>Otherwise, for each column key in {@link #columnKeySet()}, the returned
+ * map associates the column key with the corresponding value in the
+ * table. Changes to the returned map will update the underlying table, and
+ * vice versa.
+ *
+ * @param rowKey key of row to search for in the table
+ * @return the corresponding map from column keys to values
+ */
+ @Override
+ public Map<C, V> row(R rowKey) {
+ checkNotNull(rowKey);
+ Integer rowIndex = rowKeyToIndex.get(rowKey);
+ return (rowIndex == null) ? ImmutableMap.<C, V>of() : new Row(rowIndex);
+ }
+
+ private class Row extends ArrayMap<C, V> {
+ final int rowIndex;
+
+ Row(int rowIndex) {
+ super(columnKeyToIndex);
+ this.rowIndex = rowIndex;
+ }
+
+ @Override
+ String getKeyRole() {
+ return "Column";
+ }
+
+ @Override
+ V getValue(int index) {
+ return at(rowIndex, index);
+ }
+
+ @Override
+ V setValue(int index, V newValue) {
+ return set(rowIndex, index, newValue);
+ }
+ }
+
+ /**
+ * Returns an immutable set of the valid row keys, including those that are
+ * associated with null values only.
+ *
+ * @return immutable set of row keys
+ */
+ @Override
+ public ImmutableSet<R> rowKeySet() {
+ return rowKeyToIndex.keySet();
+ }
+
+ private transient RowMap rowMap;
+
+ @Override
+ public Map<R, Map<C, V>> rowMap() {
+ RowMap map = rowMap;
+ return (map == null) ? rowMap = new RowMap() : map;
+ }
+
+ private class RowMap extends ArrayMap<R, Map<C, V>> {
+ private RowMap() {
+ super(rowKeyToIndex);
+ }
+
+ @Override
+ String getKeyRole() {
+ return "Row";
+ }
+
+ @Override
+ Map<C, V> getValue(int index) {
+ return new Row(index);
+ }
+
+ @Override
+ Map<C, V> setValue(int index, Map<C, V> newValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<C, V> put(R key, Map<C, V> value) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private transient Collection<V> values;
+
+ /**
+ * Returns an unmodifiable collection of all values, which may contain
+ * duplicates. Changes to the table will update the returned collection.
+ *
+ * <p>The returned collection's iterator traverses the values of the first row
+ * key, the values of the second row key, and so on.
+ *
+ * @return collection of values
+ */
+ @Override
+ public Collection<V> values() {
+ Collection<V> v = values;
+ return (v == null) ? values = new Values() : v;
+ }
+
+ private class Values extends AbstractCollection<V> {
+ @Override public Iterator<V> iterator() {
+ return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) {
+ @Override
+ V transform(Cell<R, C, V> cell) {
+ return cell.getValue();
+ }
+ };
+ }
+
+ @Override public int size() {
+ return ArrayTable.this.size();
+ }
+ }
+
+ private static final long serialVersionUID = 0;
+}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ContiguousSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ContiguousSet.java
new file mode 100644
index 000000000..5cebc9852
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ContiguousSet.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.NoSuchElementException;
+
+/**
+ * A sorted set of contiguous values in a given {@link DiscreteDomain}.
+ *
+ * <p><b>Warning:</b> Be extremely careful what you do with conceptually large instances (such as
+ * {@code ContiguousSet.create(Range.greaterThan(0), DiscreteDomains.integers()}). Certain
+ * operations on such a set can be performed efficiently, but others (such as {@link Set#hashCode}
+ * or {@link Collections#frequency}) can cause major performance problems.
+ *
+ * @author Gregory Kick
+ * @since 10.0
+ */
+@Beta
+@GwtCompatible(emulated = true)
+@SuppressWarnings("rawtypes") // allow ungenerified Comparable types
+public abstract class ContiguousSet<C extends Comparable> extends ImmutableSortedSet<C> {
+ /**
+ * Returns a {@code ContiguousSet} containing the same values in the given domain
+ * {@linkplain Range#contains contained} by the range.
+ *
+ * @throws IllegalArgumentException if neither range nor the domain has a lower bound, or if
+ * neither has an upper bound
+ *
+ * @since 13.0
+ */
+ public static <C extends Comparable> ContiguousSet<C> create(
+ Range<C> range, DiscreteDomain<C> domain) {
+ checkNotNull(range);
+ checkNotNull(domain);
+ Range<C> effectiveRange = range;
+ try {
+ if (!range.hasLowerBound()) {
+ effectiveRange = effectiveRange.intersection(Range.atLeast(domain.minValue()));
+ }
+ if (!range.hasUpperBound()) {
+ effectiveRange = effectiveRange.intersection(Range.atMost(domain.maxValue()));
+ }
+ } catch (NoSuchElementException e) {
+ throw new IllegalArgumentException(e);
+ }
+
+ // Per class spec, we are allowed to throw CCE if necessary
+ boolean empty = effectiveRange.isEmpty()
+ || Range.compareOrThrow(
+ range.lowerBound.leastValueAbove(domain),
+ range.upperBound.greatestValueBelow(domain)) > 0;
+
+ return empty
+ ? new EmptyContiguousSet<C>(domain)
+ : new RegularContiguousSet<C>(effectiveRange, domain);
+ }
+
+ final DiscreteDomain<C> domain;
+
+ ContiguousSet(DiscreteDomain<C> domain) {
+ super(Ordering.natural());
+ this.domain = domain;
+ }
+
+ @Override public ContiguousSet<C> headSet(C toElement) {
+ return headSetImpl(checkNotNull(toElement), false);
+ }
+
+ @Override public ContiguousSet<C> subSet(C fromElement, C toElement) {
+ checkNotNull(fromElement);
+ checkNotNull(toElement);
+ checkArgument(comparator().compare(fromElement, toElement) <= 0);
+ return subSetImpl(fromElement, true, toElement, false);
+ }
+
+ @Override public ContiguousSet<C> tailSet(C fromElement) {
+ return tailSetImpl(checkNotNull(fromElement), true);
+ }
+
+ /*
+ * These methods perform most headSet, subSet, and tailSet logic, besides parameter validation.
+ */
+ /*@Override*/ abstract ContiguousSet<C> headSetImpl(C toElement, boolean inclusive);
+
+ /*@Override*/ abstract ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive,
+ C toElement, boolean toInclusive);
+
+ /*@Override*/ abstract ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive);
+
+ /**
+ * Returns the set of values that are contained in both this set and the other.
+ *
+ * <p>This method should always be used instead of
+ * {@link Sets#intersection} for {@link ContiguousSet} instances.
+ */
+ public abstract ContiguousSet<C> intersection(ContiguousSet<C> other);
+
+ /**
+ * Returns a range, closed on both ends, whose endpoints are the minimum and maximum values
+ * contained in this set. This is equivalent to {@code range(CLOSED, CLOSED)}.
+ *
+ * @throws NoSuchElementException if this set is empty
+ */
+ public abstract Range<C> range();
+
+ /**
+ * Returns the minimal range with the given boundary types for which all values in this set are
+ * {@linkplain Range#contains(Comparable) contained} within the range.
+ *
+ * <p>Note that this method will return ranges with unbounded endpoints if {@link BoundType#OPEN}
+ * is requested for a domain minimum or maximum. For example, if {@code set} was created from the
+ * range {@code [1..Integer.MAX_VALUE]} then {@code set.range(CLOSED, OPEN)} must return
+ * {@code [1..∞)}.
+ *
+ * @throws NoSuchElementException if this set is empty
+ */
+ public abstract Range<C> range(BoundType lowerBoundType, BoundType upperBoundType);
+
+ /** Returns a short-hand representation of the contents such as {@code "[1..100]"}. */
+ @Override public String toString() {
+ return range().toString();
+ }
+}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/DescendingMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/DescendingMultiset.java
new file mode 100644
index 000000000..a708d9c0b
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/DescendingMultiset.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedSet;
+
+/**
+ * A skeleton implementation of a descending multiset. Only needs
+ * {@code forwardMultiset()} and {@code entryIterator()}.
+ *
+ * @author Louis Wasserman
+ */
+@GwtCompatible(emulated = true)
+abstract class DescendingMultiset<E> extends ForwardingMultiset<E>
+ implements SortedMultiset<E> {
+ abstract SortedMultiset<E> forwardMultiset();
+
+ private transient Comparator<? super E> comparator;
+
+ @Override public Comparator<? super E> comparator() {
+ Comparator<? super E> result = comparator;
+ if (result == null) {
+ return comparator =
+ Ordering.from(forwardMultiset().comparator()).<E>reverse();
+ }
+ return result;
+ }
+
+ private transient SortedSet<E> elementSet;
+
+ @Override public SortedSet<E> elementSet() {
+ SortedSet<E> result = elementSet;
+ if (result == null) {
+ return elementSet = new SortedMultisets.ElementSet<E>(this);
+ }
+ return result;
+ }
+
+ @Override public Entry<E> pollFirstEntry() {
+ return forwardMultiset().pollLastEntry();
+ }
+
+ @Override public Entry<E> pollLastEntry() {
+ return forwardMultiset().pollFirstEntry();
+ }
+
+ @Override public SortedMultiset<E> headMultiset(E toElement,
+ BoundType boundType) {
+ return forwardMultiset().tailMultiset(toElement, boundType)
+ .descendingMultiset();
+ }
+
+ @Override public SortedMultiset<E> subMultiset(E fromElement,
+ BoundType fromBoundType, E toElement, BoundType toBoundType) {
+ return forwardMultiset().subMultiset(toElement, toBoundType, fromElement,
+ fromBoundType).descendingMultiset();
+ }
+
+ @Override public SortedMultiset<E> tailMultiset(E fromElement,
+ BoundType boundType) {
+ return forwardMultiset().headMultiset(fromElement, boundType)
+ .descendingMultiset();
+ }
+
+ @Override protected Multiset<E> delegate() {
+ return forwardMultiset();
+ }
+
+ @Override public SortedMultiset<E> descendingMultiset() {
+ return forwardMultiset();
+ }
+
+ @Override public Entry<E> firstEntry() {
+ return forwardMultiset().lastEntry();
+ }
+
+ @Override public Entry<E> lastEntry() {
+ return forwardMultiset().firstEntry();
+ }
+
+ abstract Iterator<Entry<E>> entryIterator();
+
+ private transient Set<Entry<E>> entrySet;
+
+ @Override public Set<Entry<E>> entrySet() {
+ Set<Entry<E>> result = entrySet;
+ return (result == null) ? entrySet = createEntrySet() : result;
+ }
+
+ Set<Entry<E>> createEntrySet() {
+ return new Multisets.EntrySet<E>() {
+ @Override Multiset<E> multiset() {
+ return DescendingMultiset.this;
+ }
+
+ @Override public Iterator<Entry<E>> iterator() {
+ return entryIterator();
+ }
+
+ @Override public int size() {
+ return forwardMultiset().entrySet().size();
+ }
+ };
+ }
+
+ @Override public Iterator<E> iterator() {
+ return Multisets.iteratorImpl(this);
+ }
+
+ @Override public Object[] toArray() {
+ return standardToArray();
+ }
+
+ @Override public <T> T[] toArray(T[] array) {
+ return standardToArray(array);
+ }
+
+ @Override public String toString() {
+ return entrySet().toString();
+ }
+} \ No newline at end of file
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyContiguousSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyContiguousSet.java
index f8fbe5958..368e856d9 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyContiguousSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyContiguousSet.java
@@ -69,11 +69,6 @@ final class EmptyContiguousSet<C extends Comparable> extends ContiguousSet<C> {
return this;
}
- //Abstract method doesn't exist in GWT emulation
- /* @Override */ int indexOf(Object target) {
- return -1;
- }
-
@Override public UnmodifiableIterator<C> iterator() {
return Iterators.emptyIterator();
}
diff --git a/guava-gwt/src-super/com/google/common/escape/super/com/google/common/escape/Platform.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableBiMap.java
index 36f1b2e25..1520d588f 100644
--- a/guava-gwt/src-super/com/google/common/escape/super/com/google/common/escape/Platform.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableBiMap.java
@@ -14,20 +14,23 @@
* limitations under the License.
*/
-package com.google.common.escape;
+package com.google.common.collect;
+
+import java.util.Collections;
/**
- * @author Jesse Wilson
+ * GWT emulation of {@link EmptyImmutableBiMap}.
+ *
+ * @author Hayward Chan
*/
-class Platform {
+@SuppressWarnings("serial")
+final class EmptyImmutableBiMap extends ImmutableBiMap<Object, Object> {
+ static final EmptyImmutableBiMap INSTANCE = new EmptyImmutableBiMap();
- private static final char[] CHAR_BUFFER = new char[1024];
-
- static char[] charBufferFromThreadLocal() {
- // ThreadLocal is not available to GWT, so we always reuse the same
- // instance. It is always safe to return the same instance because
- // javascript is single-threaded, and only used by blocks that doesn't
- // involve async callbacks.
- return CHAR_BUFFER;
+ private EmptyImmutableBiMap() {
+ super(Collections.emptyMap());
+ }
+ @Override public ImmutableBiMap<Object, Object> inverse() {
+ return this;
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableList.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableList.java
index 435fc260a..24b115144 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableList.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableList.java
@@ -13,14 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.google.common.collect;
+import static java.util.Collections.emptyList;
+
+import java.util.List;
+
/**
* GWT emulated version of EmptyImmutableList.
*
* @author Hayward Chan
*/
-final class EmptyImmutableList extends ImmutableList<Object> {
-
+final class EmptyImmutableList extends ForwardingImmutableList<Object> {
static final EmptyImmutableList INSTANCE = new EmptyImmutableList();
+
+ private EmptyImmutableList() {
+ }
+
+ @Override List<Object> delegateList() {
+ return emptyList();
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSet.java
index 6166c36c4..ae3b3f325 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSet.java
@@ -16,12 +16,17 @@
package com.google.common.collect;
+import java.util.Collections;
+
/**
* GWT emulation of {@link EmptyImmutableSet}.
*
* @author Hayward Chan
*/
-final class EmptyImmutableSet extends ImmutableSet<Object> {
+final class EmptyImmutableSet extends ForwardingImmutableSet<Object> {
+ private EmptyImmutableSet() {
+ super(Collections.emptySet());
+ }
static final EmptyImmutableSet INSTANCE = new EmptyImmutableSet();
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedMap.java
new file mode 100644
index 000000000..4159cd448
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedMap.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import java.util.Comparator;
+import java.util.TreeMap;
+
+/**
+ * GWT emulated version of {@link EmptyImmutableSortedMap}.
+ *
+ * @author Chris Povirk
+ */
+final class EmptyImmutableSortedMap<K, V> extends ImmutableSortedMap<K, V> {
+ private EmptyImmutableSortedMap(Comparator<? super K> comparator) {
+ super(new TreeMap<K, V>(comparator), comparator);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static final ImmutableSortedMap<Object, Object> NATURAL_EMPTY_MAP =
+ new EmptyImmutableSortedMap<Object, Object>(NATURAL_ORDER);
+
+ static <K, V> ImmutableSortedMap<K, V> forComparator(Comparator<? super K> comparator) {
+ if (comparator == NATURAL_ORDER) {
+ return (ImmutableSortedMap) NATURAL_EMPTY_MAP;
+ }
+ return new EmptyImmutableSortedMap<K, V>(comparator);
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedSet.java
index b21896b55..ff45a3961 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedSet.java
@@ -25,6 +25,6 @@ import java.util.Comparator;
*/
class EmptyImmutableSortedSet<E> extends ImmutableSortedSet<E> {
EmptyImmutableSortedSet(Comparator<? super E> comparator) {
- super(comparator);
+ super(Sets.newTreeSet(comparator));
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumBiMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumBiMap.java
index f6d7e9052..46fa64170 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumBiMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumBiMap.java
@@ -17,6 +17,7 @@
package com.google.common.collect;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtCompatible;
@@ -27,6 +28,10 @@ import java.util.Map;
* A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values
* are not permitted. An {@code EnumBiMap} and its inverse are both
* serializable.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap">
+ * {@code BiMap}</a>.
*
* @author Mike Bostock
* @since 2.0 (imported from Google Collections Library)
@@ -101,5 +106,15 @@ public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>>
public Class<V> valueType() {
return valueType;
}
+
+ @Override
+ K checkKey(K key) {
+ return checkNotNull(key);
+ }
+
+ @Override
+ V checkValue(V value) {
+ return checkNotNull(value);
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumHashBiMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumHashBiMap.java
index dce8d66eb..3d9503fd4 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumHashBiMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumHashBiMap.java
@@ -16,6 +16,8 @@
package com.google.common.collect;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.google.common.annotations.GwtCompatible;
import java.util.EnumMap;
@@ -28,6 +30,10 @@ import javax.annotation.Nullable;
* a {@code HashMap} instance for values-to-keys. Null keys are not permitted,
* but null values are. An {@code EnumHashBiMap} and its inverse are both
* serializable.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap">
+ * {@code BiMap}</a>.
*
* @author Mike Bostock
* @since 2.0 (imported from Google Collections Library)
@@ -72,7 +78,12 @@ public final class EnumHashBiMap<K extends Enum<K>, V>
this.keyType = keyType;
}
- // Overriding these two methods to show that values may be null (but not keys)
+ // Overriding these 3 methods to show that values may be null (but not keys)
+
+ @Override
+ K checkKey(K key) {
+ return checkNotNull(key);
+ }
@Override public V put(K key, @Nullable V value) {
return super.put(key, value);
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumMultiset.java
index 589706bd6..4e769fb41 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumMultiset.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EnumMultiset.java
@@ -23,6 +23,10 @@ import java.util.Iterator;
/**
* Multiset implementation backed by an {@link EnumMap}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset">
+ * {@code Multiset}</a>.
*
* @author Jared Levy
* @since 2.0 (imported from Google Collections Library)
@@ -50,6 +54,19 @@ public final class EnumMultiset<E extends Enum<E>> extends AbstractMapBasedMulti
Iterables.addAll(multiset, elements);
return multiset;
}
+
+ /**
+ * Returns a new {@code EnumMultiset} instance containing the given elements. Unlike
+ * {@link EnumMultiset#create(Iterable)}, this method does not produce an exception on an empty
+ * iterable.
+ *
+ * @since 14.0
+ */
+ public static <E extends Enum<E>> EnumMultiset<E> create(Iterable<E> elements, Class<E> type) {
+ EnumMultiset<E> result = create(type);
+ Iterables.addAll(result, elements);
+ return result;
+ }
private transient Class<E> type;
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/FluentIterable.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/FluentIterable.java
new file mode 100644
index 000000000..7c08944ee
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/FluentIterable.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+
+import javax.annotation.Nullable;
+
+/**
+ * {@code FluentIterable} provides a rich interface for manipulating {@code Iterable} instances in a
+ * chained fashion. A {@code FluentIterable} can be created from an {@code Iterable}, or from a set
+ * of elements. The following types of methods are provided on {@code FluentIterable}:
+ * <ul>
+ * <li>chained methods which return a new {@code FluentIterable} based in some way on the contents
+ * of the current one (for example {@link #transform})
+ * <li>conversion methods which copy the {@code FluentIterable}'s contents into a new collection or
+ * array (for example {@link #toList})
+ * <li>element extraction methods which facilitate the retrieval of certain elements (for example
+ * {@link #last})
+ * <li>query methods which answer questions about the {@code FluentIterable}'s contents (for example
+ * {@link #anyMatch})
+ * </ul>
+ *
+ * <p>Here is an example that merges the lists returned by two separate database calls, transforms
+ * it by invoking {@code toString()} on each element, and returns the first 10 elements as an
+ * {@code ImmutableList}: <pre> {@code
+ *
+ * FluentIterable
+ * .from(database.getClientList())
+ * .filter(activeInLastMonth())
+ * .transform(Functions.toStringFunction())
+ * .limit(10)
+ * .toList();}</pre>
+ *
+ * Anything which can be done using {@code FluentIterable} could be done in a different fashion
+ * (often with {@link Iterables}), however the use of {@code FluentIterable} makes many sets of
+ * operations significantly more concise.
+ *
+ * @author Marcin Mikosik
+ * @since 12.0
+ */
+@GwtCompatible(emulated = true)
+public abstract class FluentIterable<E> implements Iterable<E> {
+ // We store 'iterable' and use it instead of 'this' to allow Iterables to perform instanceof
+ // checks on the _original_ iterable when FluentIterable.from is used.
+ private final Iterable<E> iterable;
+
+ /** Constructor for use by subclasses. */
+ protected FluentIterable() {
+ this.iterable = this;
+ }
+
+ FluentIterable(Iterable<E> iterable) {
+ this.iterable = checkNotNull(iterable);
+ }
+
+ /**
+ * Returns a fluent iterable that wraps {@code iterable}, or {@code iterable} itself if it
+ * is already a {@code FluentIterable}.
+ */
+ public static <E> FluentIterable<E> from(final Iterable<E> iterable) {
+ return (iterable instanceof FluentIterable) ? (FluentIterable<E>) iterable
+ : new FluentIterable<E>(iterable) {
+ @Override
+ public Iterator<E> iterator() {
+ return iterable.iterator();
+ }
+ };
+ }
+
+ /**
+ * Construct a fluent iterable from another fluent iterable. This is obviously never necessary,
+ * but is intended to help call out cases where one migration from {@code Iterable} to
+ * {@code FluentIterable} has obviated the need to explicitly convert to a {@code FluentIterable}.
+ *
+ * @deprecated instances of {@code FluentIterable} don't need to be converted to
+ * {@code FluentIterable}
+ */
+ @Deprecated
+ public static <E> FluentIterable<E> from(FluentIterable<E> iterable) {
+ return checkNotNull(iterable);
+ }
+
+ /**
+ * Returns a string representation of this fluent iterable, with the format
+ * {@code [e1, e2, ..., en]}.
+ */
+ @Override
+ public String toString() {
+ return Iterables.toString(iterable);
+ }
+
+ /**
+ * Returns the number of elements in this fluent iterable.
+ */
+ public final int size() {
+ return Iterables.size(iterable);
+ }
+
+ /**
+ * Returns {@code true} if this fluent iterable contains any object for which
+ * {@code equals(element)} is true.
+ */
+ public final boolean contains(@Nullable Object element) {
+ return Iterables.contains(iterable, element);
+ }
+
+ /**
+ * Returns a fluent iterable whose {@code Iterator} cycles indefinitely over the elements of
+ * this fluent iterable.
+ *
+ * <p>That iterator supports {@code remove()} if {@code iterable.iterator()} does. After
+ * {@code remove()} is called, subsequent cycles omit the removed element, which is no longer in
+ * this fluent iterable. The iterator's {@code hasNext()} method returns {@code true} until
+ * this fluent iterable is empty.
+ *
+ * <p><b>Warning:</b> Typical uses of the resulting iterator may produce an infinite loop. You
+ * should use an explicit {@code break} or be certain that you will eventually remove all the
+ * elements.
+ */
+ public final FluentIterable<E> cycle() {
+ return from(Iterables.cycle(iterable));
+ }
+
+ /**
+ * Returns the elements from this fluent iterable that satisfy a predicate. The
+ * resulting fluent iterable's iterator does not support {@code remove()}.
+ */
+ public final FluentIterable<E> filter(Predicate<? super E> predicate) {
+ return from(Iterables.filter(iterable, predicate));
+ }
+
+ /**
+ * Returns {@code true} if any element in this fluent iterable satisfies the predicate.
+ */
+ public final boolean anyMatch(Predicate<? super E> predicate) {
+ return Iterables.any(iterable, predicate);
+ }
+
+ /**
+ * Returns {@code true} if every element in this fluent iterable satisfies the predicate.
+ * If this fluent iterable is empty, {@code true} is returned.
+ */
+ public final boolean allMatch(Predicate<? super E> predicate) {
+ return Iterables.all(iterable, predicate);
+ }
+
+ /**
+ * Returns an {@link Optional} containing the first element in this fluent iterable that
+ * satisfies the given predicate, if such an element exists.
+ *
+ * <p><b>Warning:</b> avoid using a {@code predicate} that matches {@code null}. If {@code null}
+ * is matched in this fluent iterable, a {@link NullPointerException} will be thrown.
+ */
+ public final Optional<E> firstMatch(Predicate<? super E> predicate) {
+ return Iterables.tryFind(iterable, predicate);
+ }
+
+ /**
+ * Returns a fluent iterable that applies {@code function} to each element of this
+ * fluent iterable.
+ *
+ * <p>The returned fluent iterable's iterator supports {@code remove()} if this iterable's
+ * iterator does. After a successful {@code remove()} call, this fluent iterable no longer
+ * contains the corresponding element.
+ */
+ public final <T> FluentIterable<T> transform(Function<? super E, T> function) {
+ return from(Iterables.transform(iterable, function));
+ }
+
+ /**
+ * Applies {@code function} to each element of this fluent iterable and returns
+ * a fluent iterable with the concatenated combination of results. {@code function}
+ * returns an Iterable of results.
+ *
+ * <p>The returned fluent iterable's iterator supports {@code remove()} if this
+ * function-returned iterables' iterator does. After a successful {@code remove()} call,
+ * the returned fluent iterable no longer contains the corresponding element.
+ *
+ * @since 13.0 (required {@code Function<E, Iterable<T>>} until 14.0)
+ */
+ public <T> FluentIterable<T> transformAndConcat(
+ Function<? super E, ? extends Iterable<? extends T>> function) {
+ return from(Iterables.concat(transform(function)));
+ }
+
+ /**
+ * Returns an {@link Optional} containing the first element in this fluent iterable.
+ * If the iterable is empty, {@code Optional.absent()} is returned.
+ *
+ * @throws NullPointerException if the first element is null; if this is a possibility, use
+ * {@code iterator().next()} or {@link Iterables#getFirst} instead.
+ */
+ public final Optional<E> first() {
+ Iterator<E> iterator = iterable.iterator();
+ return iterator.hasNext()
+ ? Optional.of(iterator.next())
+ : Optional.<E>absent();
+ }
+
+ /**
+ * Returns an {@link Optional} containing the last element in this fluent iterable.
+ * If the iterable is empty, {@code Optional.absent()} is returned.
+ *
+ * @throws NullPointerException if the last element is null; if this is a possibility, use
+ * {@link Iterables#getLast} instead.
+ */
+ public final Optional<E> last() {
+ // Iterables#getLast was inlined here so we don't have to throw/catch a NSEE
+
+ // TODO(kevinb): Support a concurrently modified collection?
+ if (iterable instanceof List) {
+ List<E> list = (List<E>) iterable;
+ if (list.isEmpty()) {
+ return Optional.absent();
+ }
+ return Optional.of(list.get(list.size() - 1));
+ }
+ Iterator<E> iterator = iterable.iterator();
+ if (!iterator.hasNext()) {
+ return Optional.absent();
+ }
+
+ /*
+ * TODO(kevinb): consider whether this "optimization" is worthwhile. Users
+ * with SortedSets tend to know they are SortedSets and probably would not
+ * call this method.
+ */
+ if (iterable instanceof SortedSet) {
+ SortedSet<E> sortedSet = (SortedSet<E>) iterable;
+ return Optional.of(sortedSet.last());
+ }
+
+ while (true) {
+ E current = iterator.next();
+ if (!iterator.hasNext()) {
+ return Optional.of(current);
+ }
+ }
+ }
+
+ /**
+ * Returns a view of this fluent iterable that skips its first {@code numberToSkip}
+ * elements. If this fluent iterable contains fewer than {@code numberToSkip} elements,
+ * the returned fluent iterable skips all of its elements.
+ *
+ * <p>Modifications to this fluent iterable before a call to {@code iterator()} are
+ * reflected in the returned fluent iterable. That is, the its iterator skips the first
+ * {@code numberToSkip} elements that exist when the iterator is created, not when {@code skip()}
+ * is called.
+ *
+ * <p>The returned fluent iterable's iterator supports {@code remove()} if the
+ * {@code Iterator} of this fluent iterable supports it. Note that it is <i>not</i>
+ * possible to delete the last skipped element by immediately calling {@code remove()} on the
+ * returned fluent iterable's iterator, as the {@code Iterator} contract states that a call
+ * to {@code * remove()} before a call to {@code next()} will throw an
+ * {@link IllegalStateException}.
+ */
+ public final FluentIterable<E> skip(int numberToSkip) {
+ return from(Iterables.skip(iterable, numberToSkip));
+ }
+
+ /**
+ * Creates a fluent iterable with the first {@code size} elements of this
+ * fluent iterable. If this fluent iterable does not contain that many elements,
+ * the returned fluent iterable will have the same behavior as this fluent iterable.
+ * The returned fluent iterable's iterator supports {@code remove()} if this
+ * fluent iterable's iterator does.
+ *
+ * @param size the maximum number of elements in the returned fluent iterable
+ * @throws IllegalArgumentException if {@code size} is negative
+ */
+ public final FluentIterable<E> limit(int size) {
+ return from(Iterables.limit(iterable, size));
+ }
+
+ /**
+ * Determines whether this fluent iterable is empty.
+ */
+ public final boolean isEmpty() {
+ return !iterable.iterator().hasNext();
+ }
+
+ /**
+ * Returns an {@code ImmutableList} containing all of the elements from this fluent iterable in
+ * proper sequence.
+ *
+ * @since 14.0 (since 12.0 as {@code toImmutableList()}).
+ */
+ public final ImmutableList<E> toList() {
+ return ImmutableList.copyOf(iterable);
+ }
+
+ /**
+ * Returns an {@code ImmutableList} containing all of the elements from this {@code
+ * FluentIterable} in the order specified by {@code comparator}. To produce an {@code
+ * ImmutableList} sorted by its natural ordering, use {@code toSortedList(Ordering.natural())}.
+ *
+ * @param comparator the function by which to sort list elements
+ * @throws NullPointerException if any element is null
+ * @since 14.0 (since 13.0 as {@code toSortedImmutableList()}).
+ */
+ @Beta
+ public final ImmutableList<E> toSortedList(Comparator<? super E> comparator) {
+ return Ordering.from(comparator).immutableSortedCopy(iterable);
+ }
+
+ /**
+ * Returns an {@code ImmutableSet} containing all of the elements from this fluent iterable with
+ * duplicates removed.
+ *
+ * @since 14.0 (since 12.0 as {@code toImmutableSet()}).
+ */
+ public final ImmutableSet<E> toSet() {
+ return ImmutableSet.copyOf(iterable);
+ }
+
+ /**
+ * Returns an {@code ImmutableSortedSet} containing all of the elements from this {@code
+ * FluentIterable} in the order specified by {@code comparator}, with duplicates (determined by
+ * {@code comparator.compare(x, y) == 0}) removed. To produce an {@code ImmutableSortedSet} sorted
+ * by its natural ordering, use {@code toSortedSet(Ordering.natural())}.
+ *
+ * @param comparator the function by which to sort set elements
+ * @throws NullPointerException if any element is null
+ * @since 14.0 (since 12.0 as {@code toImmutableSortedSet()}).
+ */
+ public final ImmutableSortedSet<E> toSortedSet(Comparator<? super E> comparator) {
+ return ImmutableSortedSet.copyOf(comparator, iterable);
+ }
+
+ /**
+ * Returns an immutable map for which the elements of this {@code FluentIterable} are the keys in
+ * the same order, mapped to values by the given function. If this iterable contains duplicate
+ * elements, the returned map will contain each distinct element once in the order it first
+ * appears.
+ *
+ * @throws NullPointerException if any element of this iterable is {@code null}, or if {@code
+ * valueFunction} produces {@code null} for any key
+ * @since 14.0
+ */
+ public final <V> ImmutableMap<E, V> toMap(Function<? super E, V> valueFunction) {
+ return Maps.toMap(iterable, valueFunction);
+ }
+
+ /**
+ * Creates an index {@code ImmutableListMultimap} that contains the results of applying a
+ * specified function to each item in this {@code FluentIterable} of values. Each element of this
+ * iterable will be stored as a value in the resulting multimap, yielding a multimap with the same
+ * size as this iterable. The key used to store that value in the multimap will be the result of
+ * calling the function on that value. The resulting multimap is created as an immutable snapshot.
+ * In the returned multimap, keys appear in the order they are first encountered, and the values
+ * corresponding to each key appear in the same order as they are encountered.
+ *
+ * @param keyFunction the function used to produce the key for each value
+ * @throws NullPointerException if any of the following cases is true:
+ * <ul>
+ * <li>{@code keyFunction} is null
+ * <li>An element in this fluent iterable is null
+ * <li>{@code keyFunction} returns {@code null} for any element of this iterable
+ * </ul>
+ * @since 14.0
+ */
+ public final <K> ImmutableListMultimap<K, E> index(Function<? super E, K> keyFunction) {
+ return Multimaps.index(iterable, keyFunction);
+ }
+
+ /**
+ * Returns an immutable map for which the {@link java.util.Map#values} are the elements of this
+ * {@code FluentIterable} in the given order, and each key is the product of invoking a supplied
+ * function on its corresponding value.
+ *
+ * @param keyFunction the function used to produce the key for each value
+ * @throws IllegalArgumentException if {@code keyFunction} produces the same key for more than one
+ * value in this fluent iterable
+ * @throws NullPointerException if any element of this fluent iterable is null, or if
+ * {@code keyFunction} produces {@code null} for any value
+ * @since 14.0
+ */
+ public final <K> ImmutableMap<K, E> uniqueIndex(Function<? super E, K> keyFunction) {
+ return Maps.uniqueIndex(iterable, keyFunction);
+ }
+
+ /**
+ * Returns an {@code ImmutableList} containing all of the elements from this
+ * fluent iterable in proper sequence.
+ *
+ * @deprecated Use {@link #toList()} instead. This method is scheduled for removal in Guava 15.0.
+ */
+ @Deprecated
+ public final ImmutableList<E> toImmutableList() {
+ return toList();
+ }
+
+ /**
+ * Returns an {@code ImmutableList} containing all of the elements from this
+ * {@code FluentIterable} in the order specified by {@code comparator}. To produce an
+ * {@code ImmutableList} sorted by its natural ordering, use
+ * {@code toSortedImmutableList(Ordering.natural())}.
+ *
+ * @param comparator the function by which to sort list elements
+ * @throws NullPointerException if any element is null
+ * @since 13.0
+ * @deprecated Use {@link #toSortedList(Comparator)} instead. This method is scheduled for removal
+ * in Guava 15.0.
+ */
+ @Deprecated
+ public final ImmutableList<E> toSortedImmutableList(Comparator<? super E> comparator) {
+ return toSortedList(comparator);
+ }
+
+ /**
+ * Returns an {@code ImmutableSet} containing all of the elements from this
+ * fluent iterable with duplicates removed.
+ *
+ * @deprecated Use {@link #toSet()} instead. This method is scheduled for removal in Guava 15.0.
+ */
+ @Deprecated
+ public final ImmutableSet<E> toImmutableSet() {
+ return toSet();
+ }
+
+ /**
+ * Returns an {@code ImmutableSortedSet} containing all of the elements from this
+ * {@code FluentIterable} in the order specified by {@code comparator}, with duplicates
+ * (determined by {@code comparator.compare(x, y) == 0}) removed. To produce an
+ * {@code ImmutableSortedSet} sorted by its natural ordering, use
+ * {@code toImmutableSortedSet(Ordering.natural())}.
+ *
+ * @param comparator the function by which to sort set elements
+ * @throws NullPointerException if any element is null
+ * @deprecated Use {@link #toSortedSet(Comparator)} instead. This method is scheduled for removal
+ * in Guava 15.0.
+ */
+ @Deprecated
+ public final ImmutableSortedSet<E> toImmutableSortedSet(Comparator<? super E> comparator) {
+ return toSortedSet(comparator);
+ }
+
+ /**
+ * Copies all the elements from this fluent iterable to {@code collection}. This is equivalent to
+ * calling {@code Iterables.addAll(collection, this)}.
+ *
+ * @param collection the collection to copy elements to
+ * @return {@code collection}, for convenience
+ * @since 14.0
+ */
+ public final <C extends Collection<? super E>> C copyInto(C collection) {
+ checkNotNull(collection);
+ if (iterable instanceof Collection) {
+ collection.addAll(Collections2.cast(iterable));
+ } else {
+ for (E item : iterable) {
+ collection.add(item);
+ }
+ }
+ return collection;
+ }
+
+ /**
+ * Returns the element at the specified position in this fluent iterable.
+ *
+ * @param position position of the element to return
+ * @return the element at the specified position in this fluent iterable
+ * @throws IndexOutOfBoundsException if {@code position} is negative or greater than or equal to
+ * the size of this fluent iterable
+ */
+ public final E get(int position) {
+ return Iterables.get(iterable, position);
+ }
+
+ /**
+ * Function that transforms {@code Iterable<E>} into a fluent iterable.
+ */
+ private static class FromIterableFunction<E>
+ implements Function<Iterable<E>, FluentIterable<E>> {
+ @Override
+ public FluentIterable<E> apply(Iterable<E> fromObject) {
+ return FluentIterable.from(fromObject);
+ }
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableList.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableList.java
new file mode 100644
index 000000000..1327ba0bb
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableList.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+/**
+ * GWT emulated version of {@link ImmutableList}.
+ * TODO(cpovirk): more doc
+ *
+ * @author Hayward Chan
+ */
+abstract class ForwardingImmutableList<E> extends ImmutableList<E> {
+
+ ForwardingImmutableList() {
+ }
+
+ abstract List<E> delegateList();
+
+ public int indexOf(@Nullable Object object) {
+ return delegateList().indexOf(object);
+ }
+
+ public int lastIndexOf(@Nullable Object object) {
+ return delegateList().lastIndexOf(object);
+ }
+
+ public E get(int index) {
+ return delegateList().get(index);
+ }
+
+ public ImmutableList<E> subList(int fromIndex, int toIndex) {
+ return unsafeDelegateList(delegateList().subList(fromIndex, toIndex));
+ }
+
+ @Override public Object[] toArray() {
+ // Note that ArrayList.toArray() doesn't work here because it returns E[]
+ // instead of Object[].
+ return delegateList().toArray(new Object[size()]);
+ }
+
+ @Override public boolean equals(Object obj) {
+ return delegateList().equals(obj);
+ }
+
+ @Override public int hashCode() {
+ return delegateList().hashCode();
+ }
+
+ @Override public UnmodifiableIterator<E> iterator() {
+ return Iterators.unmodifiableIterator(delegateList().iterator());
+ }
+
+ @Override public boolean contains(@Nullable Object object) {
+ return object != null && delegateList().contains(object);
+ }
+
+ @Override public boolean containsAll(Collection<?> targets) {
+ return delegateList().containsAll(targets);
+ }
+
+ public int size() {
+ return delegateList().size();
+ }
+
+ @Override public boolean isEmpty() {
+ return delegateList().isEmpty();
+ }
+
+ @Override public <T> T[] toArray(T[] other) {
+ return delegateList().toArray(other);
+ }
+
+ @Override public String toString() {
+ return delegateList().toString();
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableMap.java
new file mode 100644
index 000000000..9ef0d96e2
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableMap.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * GWT implementation of {@link ImmutableMap} that forwards to another map.
+ *
+ * @author Hayward Chan
+ */
+public abstract class ForwardingImmutableMap<K, V> extends ImmutableMap<K, V> {
+
+ final transient Map<K, V> delegate;
+
+ ForwardingImmutableMap(Map<? extends K, ? extends V> delegate) {
+ this.delegate = Collections.unmodifiableMap(delegate);
+ }
+
+ @SuppressWarnings("unchecked")
+ ForwardingImmutableMap(Entry<? extends K, ? extends V>... entries) {
+ Map<K, V> delegate = Maps.newLinkedHashMap();
+ for (Entry<? extends K, ? extends V> entry : entries) {
+ K key = checkNotNull(entry.getKey());
+ V previous = delegate.put(key, checkNotNull(entry.getValue()));
+ if (previous != null) {
+ throw new IllegalArgumentException("duplicate key: " + key);
+ }
+ }
+ this.delegate = Collections.unmodifiableMap(delegate);
+ }
+
+ boolean isPartialView() {
+ return false;
+ }
+
+ public final boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ public final boolean containsKey(@Nullable Object key) {
+ return Maps.safeContainsKey(delegate, key);
+ }
+
+ public final boolean containsValue(@Nullable Object value) {
+ return delegate.containsValue(value);
+ }
+
+ public V get(@Nullable Object key) {
+ return (key == null) ? null : Maps.safeGet(delegate, key);
+ }
+
+ @Override ImmutableSet<Entry<K, V>> createEntrySet() {
+ return ImmutableSet.unsafeDelegate(
+ new ForwardingSet<Entry<K, V>>() {
+ @Override protected Set<Entry<K, V>> delegate() {
+ return delegate.entrySet();
+ }
+ @Override public boolean contains(Object object) {
+ if (object instanceof Entry<?, ?>
+ && ((Entry<?, ?>) object).getKey() == null) {
+ return false;
+ }
+ try {
+ return super.contains(object);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+ @Override public <T> T[] toArray(T[] array) {
+ T[] result = super.toArray(array);
+ if (size() < result.length) {
+ // It works around a GWT bug where elements after last is not
+ // properly null'ed.
+ result[size()] = null;
+ }
+ return result;
+ }
+ });
+ }
+
+ @Override ImmutableSet<K> createKeySet() {
+ return ImmutableSet.unsafeDelegate(delegate.keySet());
+ }
+
+ @Override ImmutableCollection<V> createValues() {
+ return ImmutableCollection.unsafeDelegate(delegate.values());
+ }
+
+ @Override public int size() {
+ return delegate.size();
+ }
+
+ @Override public boolean equals(@Nullable Object object) {
+ return delegate.equals(object);
+ }
+
+ @Override public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override public String toString() {
+ return delegate.toString();
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableSet.java
new file mode 100644
index 000000000..6db3339e9
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableSet.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * GWT implementation of {@link ImmutableSet} that forwards to another {@code Set} implementation.
+ *
+ * @author Hayward Chan
+ */
+@SuppressWarnings("serial") // Serialization only done in GWT.
+public abstract class ForwardingImmutableSet<E> extends ImmutableSet<E> {
+ private final transient Set<E> delegate;
+
+ ForwardingImmutableSet(Set<E> delegate) {
+ // TODO(cpovirk): are we over-wrapping?
+ this.delegate = Collections.unmodifiableSet(delegate);
+ }
+
+ @Override public UnmodifiableIterator<E> iterator() {
+ return Iterators.unmodifiableIterator(delegate.iterator());
+ }
+
+ @Override public boolean contains(@Nullable Object object) {
+ return object != null && delegate.contains(object);
+ }
+
+ @Override public boolean containsAll(Collection<?> targets) {
+ return delegate.containsAll(targets);
+ }
+
+ @Override public int size() {
+ return delegate.size();
+ }
+
+ @Override public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override public Object[] toArray() {
+ return delegate.toArray();
+ }
+
+ @Override public <T> T[] toArray(T[] other) {
+ return delegate.toArray(other);
+ }
+
+ @Override public String toString() {
+ return delegate.toString();
+ }
+
+ // TODO(cpovirk): equals(), as well, in case it's any faster than Sets.equalsImpl?
+
+ @Override public int hashCode() {
+ return delegate.hashCode();
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/GenericMapMaker.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/GenericMapMaker.java
index b75f4bff5..b3b431917 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/GenericMapMaker.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/GenericMapMaker.java
@@ -52,28 +52,11 @@ public abstract class GenericMapMaker<K0, V0> {
abstract GenericMapMaker<K0, V0> maximumSize(int maximumSize);
/**
- * See {@link MapMaker#strongKeys}.
- */
- abstract GenericMapMaker<K0, V0> strongKeys();
-
- /**
* See {@link MapMaker#concurrencyLevel}.
*/
public abstract GenericMapMaker<K0, V0> concurrencyLevel(int concurrencyLevel);
/**
- * See {@link MapMaker#strongValues}.
- */
- abstract GenericMapMaker<K0, V0> strongValues();
-
- /**
- * See {@link MapMaker#expiration}.
- */
- @Deprecated
- public
- abstract GenericMapMaker<K0, V0> expiration(long duration, TimeUnit unit);
-
- /**
* See {@link MapMaker#expireAfterWrite}.
*/
abstract GenericMapMaker<K0, V0> expireAfterWrite(long duration, TimeUnit unit);
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashBiMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashBiMap.java
index 15c3f9e9f..10e4e35e9 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashBiMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashBiMap.java
@@ -16,22 +16,16 @@
package com.google.common.collect;
-import com.google.common.annotations.GwtCompatible;
-
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
/**
- * A {@link BiMap} backed by two {@link HashMap} instances. This implementation
- * allows null keys and values. A {@code HashBiMap} and its inverse are both
- * serializable.
+ * GWT emulation of {@code HashBiMap} that just delegates to two HashMaps.
*
* @author Mike Bostock
- * @since 2.0 (imported from Google Collections Library)
*/
-@GwtCompatible(emulated = true)
public final class HashBiMap<K, V> extends AbstractBiMap<K, V> {
/**
@@ -85,4 +79,3 @@ public final class HashBiMap<K, V> extends AbstractBiMap<K, V> {
return super.forcePut(key, value);
}
}
-
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashMultimap.java
index 55ecd4650..582c2f779 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/HashMultimap.java
@@ -43,7 +43,7 @@ import java.util.Set;
*/
@GwtCompatible(serializable = true, emulated = true)
public final class HashMultimap<K, V> extends AbstractSetMultimap<K, V> {
- private static final int DEFAULT_VALUES_PER_KEY = 8;
+ private static final int DEFAULT_VALUES_PER_KEY = 2;
@VisibleForTesting
transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY;
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList.java
index a79048788..1279bee28 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Guava Authors
+ * Copyright (C) 2009 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,15 +16,39 @@
package com.google.common.collect;
-import java.util.List;
+import com.google.common.annotations.GwtCompatible;
/**
- * GWT emulation of {@link ImmutableAsList}.
+ * List returned by {@link ImmutableCollection#asList} that delegates {@code contains} checks
+ * to the backing collection.
*
- * @author Hayward Chan
+ * @author Jared Levy
+ * @author Louis Wasserman
*/
-final class ImmutableAsList<E> extends RegularImmutableList<E> {
- ImmutableAsList(List<E> delegate) {
- super(delegate);
+@GwtCompatible(serializable = true, emulated = true)
+@SuppressWarnings("serial")
+abstract class ImmutableAsList<E> extends ImmutableList<E> {
+ abstract ImmutableCollection<E> delegateCollection();
+
+ @Override public boolean contains(Object target) {
+ // The collection's contains() is at least as fast as ImmutableList's
+ // and is often faster.
+ return delegateCollection().contains(target);
+ }
+
+ @Override
+ public int size() {
+ return delegateCollection().size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegateCollection().isEmpty();
+ }
+
+ @Override
+ boolean isPartialView() {
+ return delegateCollection().isPartialView();
}
}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableBiMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableBiMap.java
index 59cb78887..c8cdebb63 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableBiMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableBiMap.java
@@ -16,7 +16,8 @@
package com.google.common.collect;
-import java.util.Collections;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import java.util.Map;
/**
@@ -24,20 +25,18 @@ import java.util.Map;
*
* @author Hayward Chan
*/
-public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K,V>
+public abstract class ImmutableBiMap<K, V> extends ForwardingImmutableMap<K, V>
implements BiMap<K, V> {
- private static final ImmutableBiMap<Object, Object> EMPTY_IMMUTABLE_BIMAP
- = new EmptyBiMap();
-
// Casting to any type is safe because the set will never hold any elements.
@SuppressWarnings("unchecked")
public static <K, V> ImmutableBiMap<K, V> of() {
- return (ImmutableBiMap<K, V>) EMPTY_IMMUTABLE_BIMAP;
+ return (ImmutableBiMap<K, V>) EmptyImmutableBiMap.INSTANCE;
}
public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1) {
- return new RegularImmutableBiMap<K, V>(ImmutableMap.of(k1, v1));
+ return new SingletonImmutableBiMap<K, V>(
+ checkNotNull(k1), checkNotNull(v1));
}
public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1, K k2, V v2) {
@@ -118,14 +117,4 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K,V>
public final V forcePut(K key, V value) {
throw new UnsupportedOperationException();
}
-
- @SuppressWarnings("serial")
- static class EmptyBiMap extends ImmutableBiMap<Object, Object> {
- EmptyBiMap() {
- super(Collections.emptyMap());
- }
- @Override public ImmutableBiMap<Object, Object> inverse() {
- return this;
- }
- }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableCollection.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableCollection.java
index 04bd0604c..936f900f7 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableCollection.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableCollection.java
@@ -16,16 +16,15 @@
package com.google.common.collect;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import java.io.Serializable;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import javax.annotation.Nullable;
-import static com.google.common.base.Preconditions.checkNotNull;
-
/**
* GWT emulated version of {@link ImmutableCollection}.
*
@@ -89,6 +88,10 @@ public abstract class ImmutableCollection<E>
return size() == 0;
}
+ @Override public String toString() {
+ return Collections2.toStringImpl(this);
+ }
+
public final boolean add(E e) {
throw new UnsupportedOperationException();
}
@@ -127,15 +130,13 @@ public abstract class ImmutableCollection<E>
case 1:
return ImmutableList.of(iterator().next());
default:
- @SuppressWarnings("unchecked")
- E[] castedArray = (E[]) toArray();
- return new ImmutableAsList<E>(Arrays.asList(castedArray));
+ return new RegularImmutableAsList<E>(this, toArray());
}
}
static <E> ImmutableCollection<E> unsafeDelegate(Collection<E> delegate) {
return new ForwardingImmutableCollection<E>(delegate);
}
-
+
boolean isPartialView(){
return false;
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumMap.java
new file mode 100644
index 000000000..86f0c263c
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumMap.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Map;
+
+/**
+ * GWT emulation of {@link ImmutableEnumMap}. The type parameter is not bounded
+ * by {@code Enum<E>} to avoid code-size bloat.
+ *
+ * @author Hayward Chan
+ */
+final class ImmutableEnumMap<K, V> extends ForwardingImmutableMap<K, V> {
+ static <K, V> ImmutableMap<K, V> asImmutable(Map<K, V> map) {
+ for (Map.Entry<K, V> entry : checkNotNull(map).entrySet()) {
+ checkNotNull(entry.getKey());
+ checkNotNull(entry.getValue());
+ }
+ return new ImmutableEnumMap<K, V>(map);
+ }
+
+ ImmutableEnumMap(Map<? extends K, ? extends V> delegate) {
+ super(WellBehavedMap.wrap(delegate));
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumSet.java
index 14506c2d5..a9c21c8b5 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumSet.java
@@ -24,7 +24,18 @@ import java.util.Set;
*
* @author Hayward Chan
*/
-final class ImmutableEnumSet<E> extends ImmutableSet<E> {
+final class ImmutableEnumSet<E> extends ForwardingImmutableSet<E> {
+ static <E> ImmutableSet<E> asImmutable(Set<E> delegate) {
+ switch (delegate.size()) {
+ case 0:
+ return ImmutableSet.of();
+ case 1:
+ return ImmutableSet.of(Iterables.getOnlyElement(delegate));
+ default:
+ return new ImmutableEnumSet<E>(delegate);
+ }
+ }
+
public ImmutableEnumSet(Set<E> delegate) {
super(delegate);
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableList.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableList.java
index db67daa5d..ca5a95aa6 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableList.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableList.java
@@ -26,30 +26,20 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
import java.util.RandomAccess;
import javax.annotation.Nullable;
/**
* GWT emulated version of {@link ImmutableList}.
+ * TODO(cpovirk): more doc
*
* @author Hayward Chan
*/
@SuppressWarnings("serial") // we're overriding default serialization
-public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
+public abstract class ImmutableList<E> extends ImmutableCollection<E>
implements List<E>, RandomAccess {
-
- private transient final List<E> delegate;
-
- ImmutableList(List<E> delegate) {
- super(delegate);
- this.delegate = Collections.unmodifiableList(delegate);
- }
-
- ImmutableList() {
- this(Collections.<E>emptyList());
- }
+ ImmutableList() {}
// Casting to any type is safe because the list will never hold any elements.
@SuppressWarnings("unchecked")
@@ -126,19 +116,6 @@ public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
return new RegularImmutableList<E>(ImmutableList.<E>nullCheckedList(array));
}
- public static <E> ImmutableList<E> of(E[] elements) {
- checkNotNull(elements); // for GWT
- switch (elements.length) {
- case 0:
- return ImmutableList.of();
- case 1:
- return new SingletonImmutableList<E>(elements[0]);
- default:
- return new RegularImmutableList<E>(
- ImmutableList.<E>nullCheckedList(elements));
- }
- }
-
private static void arrayCopy(Object[] dest, int pos, Object... source) {
System.arraycopy(source, 0, dest, pos, source.length);
}
@@ -188,7 +165,7 @@ public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
}
// Factory method that skips the null checks. Used only when the elements
- // are guaranteed to be null.
+ // are guaranteed to be non-null.
static <E> ImmutableList<E> unsafeDelegateList(List<? extends E> list) {
switch (list.size()) {
case 0:
@@ -202,8 +179,14 @@ public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
}
}
- static <E> ImmutableList<E> backedBy(E[] elements) {
- return unsafeDelegateList(Arrays.asList(elements));
+ /**
+ * Views the array as an immutable list. The array must have only {@code E} elements.
+ *
+ * <p>The array must be internally created.
+ */
+ @SuppressWarnings("unchecked") // caller is reponsible for getting this right
+ static <E> ImmutableList<E> asImmutableList(Object[] elements) {
+ return unsafeDelegateList((List) Arrays.asList(elements));
}
private static <E> List<E> nullCheckedList(Object... array) {
@@ -217,12 +200,14 @@ public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
return Arrays.asList(castedArray);
}
+ @Override
public int indexOf(@Nullable Object object) {
- return delegate.indexOf(object);
+ return (object == null) ? -1 : Lists.indexOfImpl(this, object);
}
+ @Override
public int lastIndexOf(@Nullable Object object) {
- return delegate.lastIndexOf(object);
+ return (object == null) ? -1 : Lists.lastIndexOfImpl(this, object);
}
public final boolean addAll(int index, Collection<? extends E> newElements) {
@@ -241,44 +226,45 @@ public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
throw new UnsupportedOperationException();
}
- public E get(int index) {
- return delegate.get(index);
+ @Override public UnmodifiableIterator<E> iterator() {
+ return listIterator();
}
- public ImmutableList<E> subList(int fromIndex, int toIndex) {
- return unsafeDelegateList(delegate.subList(fromIndex, toIndex));
+ @Override public ImmutableList<E> subList(int fromIndex, int toIndex) {
+ return unsafeDelegateList(Lists.subListImpl(this, fromIndex, toIndex));
}
- public ListIterator<E> listIterator() {
- return delegate.listIterator();
+ @Override public UnmodifiableListIterator<E> listIterator() {
+ return listIterator(0);
}
- public ListIterator<E> listIterator(int index) {
- return delegate.listIterator(index);
+ @Override public UnmodifiableListIterator<E> listIterator(int index) {
+ return new AbstractIndexedListIterator<E>(size(), index) {
+ @Override
+ protected E get(int index) {
+ return ImmutableList.this.get(index);
+ }
+ };
}
@Override public ImmutableList<E> asList() {
return this;
}
-
- public ImmutableList<E> reverse(){
- List<E> list = Lists.newArrayList(this);
- Collections.reverse(list);
- return unsafeDelegateList(list);
- }
- @Override public Object[] toArray() {
- // Note that ArrayList.toArray() doesn't work here because it returns E[]
- // instead of Object[].
- return delegate.toArray(new Object[size()]);
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ return Lists.equalsImpl(this, obj);
}
- @Override public boolean equals(Object obj) {
- return delegate.equals(obj);
+ @Override
+ public int hashCode() {
+ return Lists.hashCodeImpl(this);
}
- @Override public int hashCode() {
- return delegate.hashCode();
+ public ImmutableList<E> reverse() {
+ List<E> list = Lists.newArrayList(this);
+ Collections.reverse(list);
+ return unsafeDelegateList(list);
}
public static <E> Builder<E> builder() {
@@ -286,9 +272,15 @@ public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E>
}
public static final class Builder<E> extends ImmutableCollection.Builder<E> {
- private final ArrayList<E> contents = Lists.newArrayList();
+ private final ArrayList<E> contents;
- public Builder() {}
+ public Builder() {
+ contents = Lists.newArrayList();
+ }
+
+ Builder(int capacity) {
+ contents = Lists.newArrayListWithCapacity(capacity);
+ }
@Override public Builder<E> add(E element) {
contents.add(checkNotNull(element));
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableListMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableListMultimap.java
index fe445d9a6..5803848f8 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableListMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableListMultimap.java
@@ -16,7 +16,6 @@
package com.google.common.collect;
-import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import java.util.Collection;
@@ -41,6 +40,10 @@ import javax.annotation.Nullable;
* it has no public or protected constructors. Thus, instances of this class
* are guaranteed to be immutable.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
+ * immutable collections</a>.
+ *
* @author Jared Levy
* @since 2.0 (imported from Google Collections Library)
*/
@@ -192,7 +195,7 @@ public class ImmutableListMultimap<K, V>
*
* @since 8.0
*/
- @Beta @Override
+ @Override
public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) {
super.orderKeysBy(keyComparator);
return this;
@@ -203,7 +206,7 @@ public class ImmutableListMultimap<K, V>
*
* @since 8.0
*/
- @Beta @Override
+ @Override
public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
super.orderValuesBy(valueComparator);
return this;
@@ -283,13 +286,14 @@ public class ImmutableListMultimap<K, V>
/**
* {@inheritDoc}
*
- * <p>Because an inverse of a list multimap can contain multiple pairs with the same key and
- * value, this method returns an {@code ImmutableListMultimap} rather than the
- * {@code ImmutableMultimap} specified in the {@code ImmutableMultimap} class.
+ * <p>Because an inverse of a list multimap can contain multiple pairs with
+ * the same key and value, this method returns an {@code
+ * ImmutableListMultimap} rather than the {@code ImmutableMultimap} specified
+ * in the {@code ImmutableMultimap} class.
*
- * @since 11
+ * @since 11.0
*/
- @Beta
+ @Override
public ImmutableListMultimap<V, K> inverse() {
ImmutableListMultimap<V, K> result = inverse;
return (result == null) ? (inverse = invert()) : result;
@@ -309,8 +313,9 @@ public class ImmutableListMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
- @Override public ImmutableList<V> removeAll(Object key) {
+ @Deprecated @Override public ImmutableList<V> removeAll(Object key) {
throw new UnsupportedOperationException();
}
@@ -318,8 +323,9 @@ public class ImmutableListMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
- @Override public ImmutableList<V> replaceValues(
+ @Deprecated @Override public ImmutableList<V> replaceValues(
K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMap.java
index 84896b533..410538cd3 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMap.java
@@ -21,9 +21,10 @@ import static com.google.common.collect.Iterables.getOnlyElement;
import java.io.Serializable;
import java.util.Collections;
+import java.util.EnumMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import javax.annotation.Nullable;
@@ -39,39 +40,15 @@ import javax.annotation.Nullable;
* @author Hayward Chan
*/
public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
-
- private transient final Map<K, V> delegate;
- ImmutableMap() {
- this.delegate = Collections.emptyMap();
- }
-
- ImmutableMap(Map<? extends K, ? extends V> delegate) {
- this.delegate = Collections.unmodifiableMap(delegate);
- }
-
- @SuppressWarnings("unchecked")
- ImmutableMap(Entry<? extends K, ? extends V>... entries) {
- Map<K, V> delegate = Maps.newLinkedHashMap();
- for (Entry<? extends K, ? extends V> entry : entries) {
- K key = checkNotNull(entry.getKey());
- V previous = delegate.put(key, checkNotNull(entry.getValue()));
- if (previous != null) {
- throw new IllegalArgumentException("duplicate key: " + key);
- }
- }
- this.delegate = Collections.unmodifiableMap(delegate);
- }
+ ImmutableMap() {}
- // Casting to any type is safe because the set will never hold any elements.
- @SuppressWarnings("unchecked")
public static <K, V> ImmutableMap<K, V> of() {
- return (ImmutableMap<K, V>) EmptyImmutableMap.INSTANCE;
+ return ImmutableBiMap.of();
}
public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {
- return new SingletonImmutableMap<K, V>(
- checkNotNull(k1), checkNotNull(v1));
+ return ImmutableBiMap.of(k1, v1);
}
public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) {
@@ -117,7 +94,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
}
public Builder<K, V> put(Entry<? extends K, ? extends V> entry) {
- if (entry instanceof ImmutableEntry<?, ?>) {
+ if (entry instanceof ImmutableEntry) {
checkNotNull(entry.getKey());
checkNotNull(entry.getValue());
@SuppressWarnings("unchecked") // all supported methods are covariant
@@ -148,8 +125,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
return of();
case 1:
Entry<K, V> entry = getOnlyElement(entries);
- return new SingletonImmutableMap<K, V>(
- entry.getKey(), entry.getValue());
+ return of(entry.getKey(), entry.getValue());
default:
@SuppressWarnings("unchecked")
Entry<K, V>[] entryArray
@@ -165,6 +141,16 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
@SuppressWarnings("unchecked") // safe since map is not writable
ImmutableMap<K, V> kvMap = (ImmutableMap<K, V>) map;
return kvMap;
+ } else if (map instanceof EnumMap) {
+ EnumMap<?, ?> enumMap = (EnumMap<?, ?>) map;
+ for (Map.Entry<?, ?> entry : enumMap.entrySet()) {
+ checkNotNull(entry.getKey());
+ checkNotNull(entry.getValue());
+ }
+ @SuppressWarnings("unchecked")
+ // immutable collections are safe for covariant casts
+ ImmutableMap<K, V> result = ImmutableEnumMap.asImmutable(new EnumMap(enumMap));
+ return result;
}
int size = map.size();
@@ -184,10 +170,8 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
return new RegularImmutableMap<K, V>(orderPreservingCopy);
}
}
-
- boolean isPartialView(){
- return false;
- }
+
+ abstract boolean isPartialView();
public final V put(K k, V v) {
throw new UnsupportedOperationException();
@@ -205,20 +189,19 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
throw new UnsupportedOperationException();
}
- public final boolean isEmpty() {
- return delegate.isEmpty();
+ @Override
+ public boolean isEmpty() {
+ return size() == 0;
}
- public final boolean containsKey(@Nullable Object key) {
- return Maps.safeContainsKey(delegate, key);
+ @Override
+ public boolean containsKey(@Nullable Object key) {
+ return get(key) != null;
}
- public final boolean containsValue(@Nullable Object value) {
- return delegate.containsValue(value);
- }
-
- public final V get(@Nullable Object key) {
- return (key == null) ? null : Maps.safeGet(delegate, key);
+ @Override
+ public boolean containsValue(@Nullable Object value) {
+ return value != null && Maps.containsValueImpl(this, value);
}
private transient ImmutableSet<Entry<K, V>> cachedEntrySet = null;
@@ -227,41 +210,22 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
if (cachedEntrySet != null) {
return cachedEntrySet;
}
- return cachedEntrySet = ImmutableSet.unsafeDelegate(
- new ForwardingSet<Entry<K, V>>() {
- @Override protected Set<Entry<K, V>> delegate() {
- return delegate.entrySet();
- }
- @Override public boolean contains(Object object) {
- if (object instanceof Entry<?, ?>
- && ((Entry<?, ?>) object).getKey() == null) {
- return false;
- }
- try {
- return super.contains(object);
- } catch (ClassCastException e) {
- return false;
- }
- }
- @Override public <T> T[] toArray(T[] array) {
- T[] result = super.toArray(array);
- if (size() < result.length) {
- // It works around a GWT bug where elements after last is not
- // properly null'ed.
- result[size()] = null;
- }
- return result;
- }
- });
+ return cachedEntrySet = createEntrySet();
}
+ abstract ImmutableSet<Entry<K, V>> createEntrySet();
+
private transient ImmutableSet<K> cachedKeySet = null;
public ImmutableSet<K> keySet() {
if (cachedKeySet != null) {
return cachedKeySet;
}
- return cachedKeySet = ImmutableSet.unsafeDelegate(delegate.keySet());
+ return cachedKeySet = createKeySet();
+ }
+
+ ImmutableSet<K> createKeySet() {
+ return new ImmutableMapKeySet<K, V>(this);
}
private transient ImmutableCollection<V> cachedValues = null;
@@ -270,22 +234,102 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable {
if (cachedValues != null) {
return cachedValues;
}
- return cachedValues = ImmutableCollection.unsafeDelegate(delegate.values());
+ return cachedValues = createValues();
+ }
+
+ // esnickell is editing here
+
+ // cached so that this.multimapView().inverse() only computes inverse once
+ private transient ImmutableSetMultimap<K, V> multimapView;
+
+ public ImmutableSetMultimap<K, V> asMultimap() {
+ ImmutableSetMultimap<K, V> result = multimapView;
+ return (result == null) ? (multimapView = createMultimapView()) : result;
+ }
+
+ private ImmutableSetMultimap<K, V> createMultimapView() {
+ ImmutableMap<K, ImmutableSet<V>> map = viewValuesAsImmutableSet();
+ return new ImmutableSetMultimap<K, V>(map, map.size(), null);
+ }
+
+ private ImmutableMap<K, ImmutableSet<V>> viewValuesAsImmutableSet() {
+ final Map<K, V> outer = this;
+ return new ImmutableMap<K, ImmutableSet<V>>() {
+ @Override
+ public int size() {
+ return outer.size();
+ }
+
+ @Override
+ public ImmutableSet<V> get(@Nullable Object key) {
+ V outerValue = outer.get(key);
+ return outerValue == null ? null : ImmutableSet.of(outerValue);
+ }
+
+ @Override
+ ImmutableSet<Entry<K, ImmutableSet<V>>> createEntrySet() {
+ return new ImmutableSet<Entry<K, ImmutableSet<V>>>() {
+ @Override
+ public UnmodifiableIterator<Entry<K, ImmutableSet<V>>> iterator() {
+ final Iterator<Entry<K,V>> outerEntryIterator = outer.entrySet().iterator();
+ return new UnmodifiableIterator<Entry<K, ImmutableSet<V>>>() {
+ @Override
+ public boolean hasNext() {
+ return outerEntryIterator.hasNext();
+ }
+
+ @Override
+ public Entry<K, ImmutableSet<V>> next() {
+ final Entry<K, V> outerEntry = outerEntryIterator.next();
+ return new AbstractMapEntry<K, ImmutableSet<V>>() {
+ @Override
+ public K getKey() {
+ return outerEntry.getKey();
+ }
+
+ @Override
+ public ImmutableSet<V> getValue() {
+ return ImmutableSet.of(outerEntry.getValue());
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ boolean isPartialView() {
+ return false;
+ }
+
+ @Override
+ public int size() {
+ return outer.size();
+ }
+ };
+ }
+
+ @Override
+ boolean isPartialView() {
+ return false;
+ }
+ };
}
- public int size() {
- return delegate.size();
+ ImmutableCollection<V> createValues() {
+ return new ImmutableMapValues<K, V>(this);
}
@Override public boolean equals(@Nullable Object object) {
- return delegate.equals(object);
+ return Maps.equalsImpl(this, object);
}
@Override public int hashCode() {
- return delegate.hashCode();
+ // not caching hash code since it could change if map values are mutable
+ // in a way that modifies their hash codes
+ return entrySet().hashCode();
}
@Override public String toString() {
- return delegate.toString();
+ return Maps.toStringImpl(this);
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapEntrySet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapEntrySet.java
new file mode 100644
index 000000000..7a5e580d7
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapEntrySet.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.Map.Entry;
+
+import javax.annotation.Nullable;
+
+/**
+ * {@code entrySet()} implementation for {@link ImmutableMap}.
+ *
+ * @author Jesse Wilson
+ * @author Kevin Bourrillion
+ */
+@GwtCompatible(emulated = true)
+abstract class ImmutableMapEntrySet<K, V> extends ImmutableSet<Entry<K, V>> {
+ ImmutableMapEntrySet() {}
+
+ abstract ImmutableMap<K, V> map();
+
+ @Override
+ public int size() {
+ return map().size();
+ }
+
+ @Override
+ public boolean contains(@Nullable Object object) {
+ if (object instanceof Entry) {
+ Entry<?, ?> entry = (Entry<?, ?>) object;
+ V value = map().get(entry.getKey());
+ return value != null && value.equals(entry.getValue());
+ }
+ return false;
+ }
+
+ @Override
+ boolean isPartialView() {
+ return map().isPartialView();
+ }
+}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapKeySet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapKeySet.java
new file mode 100644
index 000000000..78508f4d7
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapKeySet.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.Map.Entry;
+
+import javax.annotation.Nullable;
+
+/**
+ * {@code keySet()} implementation for {@link ImmutableMap}.
+ *
+ * @author Jesse Wilson
+ * @author Kevin Bourrillion
+ */
+@GwtCompatible(emulated = true)
+final class ImmutableMapKeySet<K, V> extends ImmutableSet<K> {
+ private final ImmutableMap<K, V> map;
+
+ ImmutableMapKeySet(ImmutableMap<K, V> map) {
+ this.map = map;
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public UnmodifiableIterator<K> iterator() {
+ return asList().iterator();
+ }
+
+ @Override
+ public boolean contains(@Nullable Object object) {
+ return map.containsKey(object);
+ }
+
+ @Override
+ ImmutableList<K> createAsList() {
+ final ImmutableList<Entry<K, V>> entryList = map.entrySet().asList();
+ return new ImmutableAsList<K>() {
+
+ @Override
+ public K get(int index) {
+ return entryList.get(index).getKey();
+ }
+
+ @Override
+ ImmutableCollection<K> delegateCollection() {
+ return ImmutableMapKeySet.this;
+ }
+
+ };
+ }
+
+ @Override
+ boolean isPartialView() {
+ return true;
+ }
+}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapValues.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapValues.java
new file mode 100644
index 000000000..ccf4b7dd5
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapValues.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.Map.Entry;
+
+/**
+ * {@code values()} implementation for {@link ImmutableMap}.
+ *
+ * @author Jesse Wilson
+ * @author Kevin Bourrillion
+ */
+@GwtCompatible(emulated = true)
+final class ImmutableMapValues<K, V> extends ImmutableCollection<V> {
+ private final ImmutableMap<K, V> map;
+
+ ImmutableMapValues(ImmutableMap<K, V> map) {
+ this.map = map;
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public UnmodifiableIterator<V> iterator() {
+ return Maps.valueIterator(map.entrySet().iterator());
+ }
+
+ @Override
+ public boolean contains(Object object) {
+ return map.containsValue(object);
+ }
+
+ @Override
+ boolean isPartialView() {
+ return true;
+ }
+
+ @Override
+ ImmutableList<V> createAsList() {
+ final ImmutableList<Entry<K, V>> entryList = map.entrySet().asList();
+ return new ImmutableAsList<V>() {
+ @Override
+ public V get(int index) {
+ return entryList.get(index).getValue();
+ }
+
+ @Override
+ ImmutableCollection<V> delegateCollection() {
+ return ImmutableMapValues.this;
+ }
+ };
+ }
+}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMultimap.java
index a97112956..f02ea4387 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMultimap.java
@@ -18,8 +18,8 @@ package com.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Function;
import java.io.Serializable;
import java.util.Arrays;
@@ -29,8 +29,9 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
-import java.util.TreeMap;
+import java.util.Set;
import javax.annotation.Nullable;
@@ -52,13 +53,16 @@ import javax.annotation.Nullable;
* <p>In addition to methods defined by {@link Multimap}, an {@link #inverse}
* method is also supported.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
+ * immutable collections</a>.
+ *
* @author Jared Levy
* @since 2.0 (imported from Google Collections Library)
*/
@GwtCompatible(emulated = true)
-// TODO(user): If BiMultimap graduates from labs, this class should implement it.
-public abstract class ImmutableMultimap<K, V>
- implements Multimap<K, V>, Serializable {
+public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V>
+ implements Serializable {
/** Returns an empty multimap. */
public static <K, V> ImmutableMultimap<K, V> of() {
@@ -118,7 +122,7 @@ public abstract class ImmutableMultimap<K, V>
* value orderings, allows duplicate values, and performs better than
* {@link LinkedListMultimap}.
*/
- private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> {
+ private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> {
BuilderMultimap() {
super(new LinkedHashMap<K, Collection<V>>());
}
@@ -129,23 +133,6 @@ public abstract class ImmutableMultimap<K, V>
}
/**
- * Multimap for {@link ImmutableMultimap.Builder} that sorts key and allows
- * duplicate values,
- */
- private static class SortedKeyBuilderMultimap<K, V>
- extends AbstractMultimap<K, V> {
- SortedKeyBuilderMultimap(
- Comparator<? super K> keyComparator, Multimap<K, V> multimap) {
- super(new TreeMap<K, Collection<V>>(keyComparator));
- putAll(multimap);
- }
- @Override Collection<V> createCollection() {
- return Lists.newArrayList();
- }
- private static final long serialVersionUID = 0;
- }
-
- /**
* A builder for creating immutable multimap instances, especially
* {@code public static final} multimaps ("constant multimaps"). Example:
* <pre> {@code
@@ -165,6 +152,7 @@ public abstract class ImmutableMultimap<K, V>
*/
public static class Builder<K, V> {
Multimap<K, V> builderMultimap = new BuilderMultimap<K, V>();
+ Comparator<? super K> keyComparator;
Comparator<? super V> valueComparator;
/**
@@ -239,10 +227,8 @@ public abstract class ImmutableMultimap<K, V>
*
* @since 8.0
*/
- @Beta
public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) {
- builderMultimap = new SortedKeyBuilderMultimap<K, V>(
- checkNotNull(keyComparator), builderMultimap);
+ this.keyComparator = checkNotNull(keyComparator);
return this;
}
@@ -251,7 +237,6 @@ public abstract class ImmutableMultimap<K, V>
*
* @since 8.0
*/
- @Beta
public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
this.valueComparator = checkNotNull(valueComparator);
return this;
@@ -267,6 +252,23 @@ public abstract class ImmutableMultimap<K, V>
Collections.sort(list, valueComparator);
}
}
+ if (keyComparator != null) {
+ Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>();
+ List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList(
+ builderMultimap.asMap().entrySet());
+ Collections.sort(
+ entries,
+ Ordering.from(keyComparator).onResultOf(new Function<Entry<K, Collection<V>>, K>() {
+ @Override
+ public K apply(Entry<K, Collection<V>> entry) {
+ return entry.getKey();
+ }
+ }));
+ for (Map.Entry<K, Collection<V>> entry : entries) {
+ sortedCopy.putAll(entry.getKey(), entry.getValue());
+ }
+ builderMultimap = sortedCopy;
+ }
return copyOf(builderMultimap);
}
}
@@ -315,7 +317,9 @@ public abstract class ImmutableMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
+ @Deprecated
@Override
public ImmutableCollection<V> removeAll(Object key) {
throw new UnsupportedOperationException();
@@ -325,7 +329,9 @@ public abstract class ImmutableMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
+ @Deprecated
@Override
public ImmutableCollection<V> replaceValues(K key,
Iterable<? extends V> values) {
@@ -336,7 +342,9 @@ public abstract class ImmutableMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
+ @Deprecated
@Override
public void clear() {
throw new UnsupportedOperationException();
@@ -356,16 +364,17 @@ public abstract class ImmutableMultimap<K, V>
* key-value mapping in the original, the result will have a mapping with
* key and value reversed.
*
- * @since 11
+ * @since 11.0
*/
- @Beta
public abstract ImmutableMultimap<V, K> inverse();
/**
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
+ @Deprecated
@Override
public boolean put(K key, V value) {
throw new UnsupportedOperationException();
@@ -375,7 +384,9 @@ public abstract class ImmutableMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
+ @Deprecated
@Override
public boolean putAll(K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
@@ -385,7 +396,9 @@ public abstract class ImmutableMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
+ @Deprecated
@Override
public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
throw new UnsupportedOperationException();
@@ -395,65 +408,30 @@ public abstract class ImmutableMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
+ @Deprecated
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
- boolean isPartialView(){
+ boolean isPartialView() {
return map.isPartialView();
}
// accessors
@Override
- public boolean containsEntry(@Nullable Object key, @Nullable Object value) {
- Collection<V> values = map.get(key);
- return values != null && values.contains(value);
- }
-
- @Override
public boolean containsKey(@Nullable Object key) {
return map.containsKey(key);
}
@Override
- public boolean containsValue(@Nullable Object value) {
- for (Collection<V> valueCollection : map.values()) {
- if (valueCollection.contains(value)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean isEmpty() {
- return size == 0;
- }
-
- @Override
public int size() {
return size;
}
- @Override public boolean equals(@Nullable Object object) {
- if (object instanceof Multimap) {
- Multimap<?, ?> that = (Multimap<?, ?>) object;
- return this.map.equals(that.asMap());
- }
- return false;
- }
-
- @Override public int hashCode() {
- return map.hashCode();
- }
-
- @Override public String toString() {
- return map.toString();
- }
-
// views
/**
@@ -475,8 +453,11 @@ public abstract class ImmutableMultimap<K, V>
public ImmutableMap<K, Collection<V>> asMap() {
return (ImmutableMap) map;
}
-
- private transient ImmutableCollection<Entry<K, V>> entries;
+
+ @Override
+ Map<K, Collection<V>> createAsMap() {
+ throw new AssertionError("should never be called");
+ }
/**
* Returns an immutable collection of all key-value pairs in the multimap. Its
@@ -485,9 +466,12 @@ public abstract class ImmutableMultimap<K, V>
*/
@Override
public ImmutableCollection<Entry<K, V>> entries() {
- ImmutableCollection<Entry<K, V>> result = entries;
- return (result == null)
- ? (entries = new EntryCollection<K, V>(this)) : result;
+ return (ImmutableCollection<Entry<K, V>>) super.entries();
+ }
+
+ @Override
+ ImmutableCollection<Entry<K, V>> createEntries() {
+ return new EntryCollection<K, V>(this);
}
private static class EntryCollection<K, V>
@@ -499,30 +483,7 @@ public abstract class ImmutableMultimap<K, V>
}
@Override public UnmodifiableIterator<Entry<K, V>> iterator() {
- final Iterator<? extends Entry<K, ? extends ImmutableCollection<V>>>
- mapIterator = this.multimap.map.entrySet().iterator();
-
- return new UnmodifiableIterator<Entry<K, V>>() {
- K key;
- Iterator<V> valueIterator;
-
- @Override
- public boolean hasNext() {
- return (key != null && valueIterator.hasNext())
- || mapIterator.hasNext();
- }
-
- @Override
- public Entry<K, V> next() {
- if (key == null || !valueIterator.hasNext()) {
- Entry<K, ? extends ImmutableCollection<V>> entry
- = mapIterator.next();
- key = entry.getKey();
- valueIterator = entry.getValue().iterator();
- }
- return Maps.immutableEntry(key, valueIterator.next());
- }
- };
+ return multimap.entryIterator();
}
@Override boolean isPartialView() {
@@ -544,8 +505,34 @@ public abstract class ImmutableMultimap<K, V>
private static final long serialVersionUID = 0;
}
+
+ @Override
+ UnmodifiableIterator<Entry<K, V>> entryIterator() {
+ final Iterator<? extends Entry<K, ? extends ImmutableCollection<V>>>
+ mapIterator = map.entrySet().iterator();
+
+ return new UnmodifiableIterator<Entry<K, V>>() {
+ K key;
+ Iterator<V> valueIterator;
+
+ @Override
+ public boolean hasNext() {
+ return (key != null && valueIterator.hasNext())
+ || mapIterator.hasNext();
+ }
- private transient ImmutableMultiset<K> keys;
+ @Override
+ public Entry<K, V> next() {
+ if (key == null || !valueIterator.hasNext()) {
+ Entry<K, ? extends ImmutableCollection<V>> entry
+ = mapIterator.next();
+ key = entry.getKey();
+ valueIterator = entry.getValue().iterator();
+ }
+ return Maps.immutableEntry(key, valueIterator.next());
+ }
+ };
+ }
/**
* Returns a collection, which may contain duplicates, of all keys. The number
@@ -555,20 +542,77 @@ public abstract class ImmutableMultimap<K, V>
*/
@Override
public ImmutableMultiset<K> keys() {
- ImmutableMultiset<K> result = keys;
- return (result == null) ? (keys = createKeys()) : result;
+ return (ImmutableMultiset<K>) super.keys();
}
- private ImmutableMultiset<K> createKeys() {
- ImmutableMultiset.Builder<K> builder = ImmutableMultiset.builder();
- for (Entry<K, ? extends ImmutableCollection<V>> entry
- : map.entrySet()) {
- builder.addCopies(entry.getKey(), entry.getValue().size());
- }
- return builder.build();
+ @Override
+ ImmutableMultiset<K> createKeys() {
+ return new Keys();
}
- private transient ImmutableCollection<V> values;
+ @SuppressWarnings("serial") // Uses writeReplace, not default serialization
+ class Keys extends ImmutableMultiset<K> {
+ @Override
+ public boolean contains(@Nullable Object object) {
+ return containsKey(object);
+ }
+
+ @Override
+ public int count(@Nullable Object element) {
+ Collection<V> values = map.get(element);
+ return (values == null) ? 0 : values.size();
+ }
+
+ @Override
+ public Set<K> elementSet() {
+ return keySet();
+ }
+
+ @Override
+ public int size() {
+ return ImmutableMultimap.this.size();
+ }
+
+ @Override
+ ImmutableSet<Entry<K>> createEntrySet() {
+ return new KeysEntrySet();
+ }
+
+ private class KeysEntrySet extends ImmutableMultiset<K>.EntrySet {
+ @Override
+ public int size() {
+ return keySet().size();
+ }
+
+ @Override
+ public UnmodifiableIterator<Entry<K>> iterator() {
+ return asList().iterator();
+ }
+
+ @Override
+ ImmutableList<Entry<K>> createAsList() {
+ final ImmutableList<? extends Map.Entry<K, ? extends Collection<V>>> mapEntries =
+ map.entrySet().asList();
+ return new ImmutableAsList<Entry<K>>() {
+ @Override
+ public Entry<K> get(int index) {
+ Map.Entry<K, ? extends Collection<V>> entry = mapEntries.get(index);
+ return Multisets.immutableEntry(entry.getKey(), entry.getValue().size());
+ }
+
+ @Override
+ ImmutableCollection<Entry<K>> delegateCollection() {
+ return KeysEntrySet.this;
+ }
+ };
+ }
+ }
+
+ @Override
+ boolean isPartialView() {
+ return true;
+ }
+ }
/**
* Returns an immutable collection of the values in this multimap. Its
@@ -577,8 +621,12 @@ public abstract class ImmutableMultimap<K, V>
*/
@Override
public ImmutableCollection<V> values() {
- ImmutableCollection<V> result = values;
- return (result == null) ? (values = new Values<V>(this)) : result;
+ return (ImmutableCollection<V>) super.values();
+ }
+
+ @Override
+ ImmutableCollection<V> createValues() {
+ return new Values<V>(this);
}
private static class Values<V> extends ImmutableCollection<V> {
@@ -589,18 +637,7 @@ public abstract class ImmutableMultimap<K, V>
}
@Override public UnmodifiableIterator<V> iterator() {
- final Iterator<? extends Entry<?, V>> entryIterator
- = multimap.entries().iterator();
- return new UnmodifiableIterator<V>() {
- @Override
- public boolean hasNext() {
- return entryIterator.hasNext();
- }
- @Override
- public V next() {
- return entryIterator.next().getValue();
- }
- };
+ return Maps.valueIterator(multimap.entries().iterator());
}
@Override
@@ -617,3 +654,4 @@ public abstract class ImmutableMultimap<K, V>
private static final long serialVersionUID = 0;
}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSet.java
index 26bc57f04..97d9dd13e 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSet.java
@@ -37,16 +37,8 @@ import java.util.Set;
* @author Hayward Chan
*/
@SuppressWarnings("serial") // Serialization only done in GWT.
-public abstract class ImmutableSet<E> extends ForwardingImmutableCollection<E>
- implements Set<E> {
-
- ImmutableSet(Set<E> delegate) {
- super(Collections.unmodifiableSet(delegate));
- }
-
- ImmutableSet() {
- this(Collections.<E>emptySet());
- }
+public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements Set<E> {
+ ImmutableSet() {}
// Casting to any type is safe because the set will never hold any elements.
@SuppressWarnings({"unchecked"})
@@ -79,8 +71,7 @@ public abstract class ImmutableSet<E> extends ForwardingImmutableCollection<E>
}
@SuppressWarnings("unchecked")
- public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6,
- E... others) {
+ public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
int size = others.length + 6;
List<E> all = new ArrayList<E>(size);
Collections.addAll(all, e1, e2, e3, e4, e5, e6);
@@ -111,8 +102,7 @@ public abstract class ImmutableSet<E> extends ForwardingImmutableCollection<E>
}
public static <E> ImmutableSet<E> copyOf(Iterable<? extends E> elements) {
- if (elements instanceof ImmutableSet
- && !(elements instanceof ImmutableSortedSet)) {
+ if (elements instanceof ImmutableSet && !(elements instanceof ImmutableSortedSet)) {
@SuppressWarnings("unchecked") // all supported methods are covariant
ImmutableSet<E> set = (ImmutableSet<E>) elements;
return set;
@@ -175,7 +165,7 @@ public abstract class ImmutableSet<E> extends ForwardingImmutableCollection<E>
}
@Override public int hashCode() {
- return delegate.hashCode();
+ return Sets.hashCodeImpl(this);
}
public static <E> Builder<E> builder() {
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSetMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSetMultimap.java
index b5eaabfdd..43e701e7c 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSetMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSetMultimap.java
@@ -18,15 +18,17 @@ package com.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Function;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
-import java.util.TreeMap;
import javax.annotation.Nullable;
@@ -46,6 +48,10 @@ import javax.annotation.Nullable;
* it has no public or protected constructors. Thus, instances of this class
* are guaranteed to be immutable.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
+ * immutable collections</a>.
+ *
* @author Mike Ward
* @since 2.0 (imported from Google Collections Library)
*/
@@ -140,7 +146,7 @@ public class ImmutableSetMultimap<K, V>
* Multimap for {@link ImmutableSetMultimap.Builder} that maintains key
* and value orderings and performs better than {@link LinkedHashMultimap}.
*/
- private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> {
+ private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> {
BuilderMultimap() {
super(new LinkedHashMap<K, Collection<V>>());
}
@@ -151,23 +157,6 @@ public class ImmutableSetMultimap<K, V>
}
/**
- * Multimap for {@link ImmutableSetMultimap.Builder} that sorts keys and
- * maintains value orderings.
- */
- private static class SortedKeyBuilderMultimap<K, V>
- extends AbstractMultimap<K, V> {
- SortedKeyBuilderMultimap(
- Comparator<? super K> keyComparator, Multimap<K, V> multimap) {
- super(new TreeMap<K, Collection<V>>(keyComparator));
- putAll(multimap);
- }
- @Override Collection<V> createCollection() {
- return Sets.newLinkedHashSet();
- }
- private static final long serialVersionUID = 0;
- }
-
- /**
* A builder for creating immutable {@code SetMultimap} instances, especially
* {@code public static final} multimaps ("constant multimaps"). Example:
* <pre> {@code
@@ -192,7 +181,7 @@ public class ImmutableSetMultimap<K, V>
* generated by {@link ImmutableSetMultimap#builder}.
*/
public Builder() {
- builderMultimap = new BuilderMultimap<K, V>();
+ builderMultimap = new BuilderMultimap<K, V>();
}
/**
@@ -241,26 +230,25 @@ public class ImmutableSetMultimap<K, V>
*
* @since 8.0
*/
- @Beta @Override
+ @Override
public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) {
- builderMultimap = new SortedKeyBuilderMultimap<K, V>(
- checkNotNull(keyComparator), builderMultimap);
+ this.keyComparator = checkNotNull(keyComparator);
return this;
}
/**
* Specifies the ordering of the generated multimap's values for each key.
- *
- * <p>If this method is called, the sets returned by the {@code get()}
+ *
+ * <p>If this method is called, the sets returned by the {@code get()}
* method of the generated multimap and its {@link Multimap#asMap()} view
* are {@link ImmutableSortedSet} instances. However, serialization does not
* preserve that property, though it does maintain the key and value
* ordering.
- *
+ *
* @since 8.0
*/
// TODO: Make serialization behavior consistent.
- @Beta @Override
+ @Override
public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) {
super.orderValuesBy(valueComparator);
return this;
@@ -270,6 +258,23 @@ public class ImmutableSetMultimap<K, V>
* Returns a newly-created immutable set multimap.
*/
@Override public ImmutableSetMultimap<K, V> build() {
+ if (keyComparator != null) {
+ Multimap<K, V> sortedCopy = new BuilderMultimap<K, V>();
+ List<Map.Entry<K, Collection<V>>> entries = Lists.newArrayList(
+ builderMultimap.asMap().entrySet());
+ Collections.sort(
+ entries,
+ Ordering.from(keyComparator).onResultOf(new Function<Entry<K, Collection<V>>, K>() {
+ @Override
+ public K apply(Entry<K, Collection<V>> entry) {
+ return entry.getKey();
+ }
+ }));
+ for (Map.Entry<K, Collection<V>> entry : entries) {
+ sortedCopy.putAll(entry.getKey(), entry.getValue());
+ }
+ builderMultimap = sortedCopy;
+ }
return copyOf(builderMultimap, valueComparator);
}
}
@@ -292,7 +297,7 @@ public class ImmutableSetMultimap<K, V>
Multimap<? extends K, ? extends V> multimap) {
return copyOf(multimap, null);
}
-
+
private static <K, V> ImmutableSetMultimap<K, V> copyOf(
Multimap<? extends K, ? extends V> multimap,
Comparator<? super V> valueComparator) {
@@ -318,7 +323,7 @@ public class ImmutableSetMultimap<K, V>
K key = entry.getKey();
Collection<? extends V> values = entry.getValue();
ImmutableSet<V> set = (valueComparator == null)
- ? ImmutableSet.copyOf(values)
+ ? ImmutableSet.copyOf(values)
: ImmutableSortedSet.copyOf(valueComparator, values);
if (!set.isEmpty()) {
builder.put(key, set);
@@ -333,10 +338,10 @@ public class ImmutableSetMultimap<K, V>
// Returned by get() when values are sorted and a missing key is provided.
private final transient ImmutableSortedSet<V> emptySet;
- ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size,
+ ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size,
@Nullable Comparator<? super V> valueComparator) {
super(map, size);
- this.emptySet = (valueComparator == null)
+ this.emptySet = (valueComparator == null)
? null : ImmutableSortedSet.<V>emptySet(valueComparator);
}
@@ -365,13 +370,13 @@ public class ImmutableSetMultimap<K, V>
/**
* {@inheritDoc}
*
- * <p>Because an inverse of a set multimap cannot contain multiple pairs with the same key and
- * value, this method returns an {@code ImmutableSetMultimap} rather than the
- * {@code ImmutableMultimap} specified in the {@code ImmutableMultimap} class.
+ * <p>Because an inverse of a set multimap cannot contain multiple pairs with
+ * the same key and value, this method returns an {@code ImmutableSetMultimap}
+ * rather than the {@code ImmutableMultimap} specified in the {@code
+ * ImmutableMultimap} class.
*
- * @since 11
+ * @since 11.0
*/
- @Beta
public ImmutableSetMultimap<V, K> inverse() {
ImmutableSetMultimap<V, K> result = inverse;
return (result == null) ? (inverse = invert()) : result;
@@ -391,8 +396,9 @@ public class ImmutableSetMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
- @Override public ImmutableSet<V> removeAll(Object key) {
+ @Deprecated @Override public ImmutableSet<V> removeAll(Object key) {
throw new UnsupportedOperationException();
}
@@ -400,8 +406,9 @@ public class ImmutableSetMultimap<K, V>
* Guaranteed to throw an exception and leave the multimap unmodified.
*
* @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
*/
- @Override public ImmutableSet<V> replaceValues(
+ @Deprecated @Override public ImmutableSet<V> replaceValues(
K key, Iterable<? extends V> values) {
throw new UnsupportedOperationException();
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedAsList.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedAsList.java
new file mode 100644
index 000000000..c94e88e99
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedAsList.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.util.Comparator;
+
+/**
+ * List returned by {@code ImmutableSortedSet.asList()} when the set isn't empty.
+ *
+ * @author Jared Levy
+ * @author Louis Wasserman
+ */
+@GwtCompatible(emulated = true)
+@SuppressWarnings("serial")
+final class ImmutableSortedAsList<E> extends RegularImmutableAsList<E>
+ implements SortedIterable<E> {
+ ImmutableSortedAsList(
+ ImmutableSortedSet<E> backingSet, ImmutableList<E> backingList) {
+ super(backingSet, backingList);
+ }
+
+ @Override
+ ImmutableSortedSet<E> delegateCollection() {
+ return (ImmutableSortedSet<E>) super.delegateCollection();
+ }
+
+ @Override public Comparator<? super E> comparator() {
+ return delegateCollection().comparator();
+ }
+
+ // Override indexOf() and lastIndexOf() to be O(log N) instead of O(N).
+
+ @Override
+ public boolean contains(Object target) {
+ // Necessary for ISS's with comparators inconsistent with equals.
+ return indexOf(target) >= 0;
+ }
+}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedMap.java
index 89624d8a4..19139d17c 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedMap.java
@@ -18,10 +18,11 @@ package com.google.common.collect;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Maps.newTreeMap;
+import static java.util.Collections.unmodifiableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
-import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
@@ -33,60 +34,49 @@ import java.util.SortedMap;
*
* @author Hayward Chan
*/
-public class ImmutableSortedMap<K, V>
- extends ImmutableMap<K, V> implements SortedMap<K, V> {
-
- // TODO: Confirm that ImmutableSortedMap is faster to construct and uses less
- // memory than TreeMap; then say so in the class Javadoc.
-
- // TODO: Create separate subclasses for empty, single-entry, and
- // multiple-entry instances.
-
- @SuppressWarnings("unchecked")
- private static final Comparator NATURAL_ORDER = Ordering.natural();
+public abstract class ImmutableSortedMap<K, V>
+ extends ForwardingImmutableMap<K, V> implements SortedMap<K, V> {
@SuppressWarnings("unchecked")
- private static final ImmutableSortedMap<Object, Object> NATURAL_EMPTY_MAP
- = create(NATURAL_ORDER);
+ static final Comparator NATURAL_ORDER = Ordering.natural();
// This reference is only used by GWT compiler to infer the keys and values
// of the map that needs to be serialized.
- private Comparator<K> unusedComparatorForSerialization;
+ private Comparator<? super K> unusedComparatorForSerialization;
private K unusedKeyForSerialization;
private V unusedValueForSerialization;
- private transient final SortedMap<K, V> sortedDelegate;
+ private final transient SortedMap<K, V> sortedDelegate;
// The comparator used by this map. It's the same as that of sortedDelegate,
// except that when sortedDelegate's comparator is null, it points to a
// non-null instance of Ordering.natural().
- private transient final Comparator<K> comparator;
+ // (cpovirk: Is sortedDelegate's comparator really ever null?)
+ // The comparator will likely also differ because of our nullAccepting hack.
+ // See the bottom of the file for more information about it.
+ private final transient Comparator<? super K> comparator;
- // If map has a null comparator, the keys should have a natural ordering,
- // even though K doesn't explicitly implement Comparable.
- @SuppressWarnings("unchecked")
- ImmutableSortedMap(SortedMap<K, ? extends V> delegate) {
+ ImmutableSortedMap(SortedMap<K, V> delegate, Comparator<? super K> comparator) {
super(delegate);
- this.comparator = (delegate.comparator() == null)
- ? NATURAL_ORDER : delegate.comparator();
- this.sortedDelegate = Collections.unmodifiableSortedMap(delegate);
+ this.comparator = comparator;
+ this.sortedDelegate = delegate;
}
private static <K, V> ImmutableSortedMap<K, V> create(
Comparator<? super K> comparator,
Entry<? extends K, ? extends V>... entries) {
checkNotNull(comparator);
- SortedMap<K, V> delegate = Maps.newTreeMap(comparator);
+ SortedMap<K, V> delegate = newModifiableDelegate(comparator);
for (Entry<? extends K, ? extends V> entry : entries) {
delegate.put(entry.getKey(), entry.getValue());
}
- return new ImmutableSortedMap<K, V>(delegate);
+ return newView(unmodifiableSortedMap(delegate), comparator);
}
// Casting to any type is safe because the set will never hold any elements.
@SuppressWarnings("unchecked")
public static <K, V> ImmutableSortedMap<K, V> of() {
- return (ImmutableSortedMap) NATURAL_EMPTY_MAP;
+ return EmptyImmutableSortedMap.forComparator(NATURAL_ORDER);
}
public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V>
@@ -155,11 +145,11 @@ public class ImmutableSortedMap<K, V>
}
}
- SortedMap<K, V> delegate = Maps.newTreeMap(comparator);
+ SortedMap<K, V> delegate = newModifiableDelegate(comparator);
for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
putEntryWithChecks(delegate, entry);
}
- return new ImmutableSortedMap<K, V>(delegate);
+ return newView(unmodifiableSortedMap(delegate), comparator);
}
private static <K, V> void putEntryWithChecks(
@@ -179,7 +169,7 @@ public class ImmutableSortedMap<K, V>
map.put(key, value);
}
- public static <K extends Comparable<K>, V> Builder<K, V> naturalOrder() {
+ public static <K extends Comparable<?>, V> Builder<K, V> naturalOrder() {
return new Builder<K, V>(Ordering.natural());
}
@@ -187,7 +177,7 @@ public class ImmutableSortedMap<K, V>
return new Builder<K, V>(comparator);
}
- public static <K extends Comparable<K>, V> Builder<K, V> reverseOrder() {
+ public static <K extends Comparable<?>, V> Builder<K, V> reverseOrder() {
return new Builder<K, V>(Ordering.natural().reverse());
}
@@ -216,11 +206,11 @@ public class ImmutableSortedMap<K, V>
}
@Override public ImmutableSortedMap<K, V> build() {
- SortedMap<K, V> delegate = Maps.newTreeMap(comparator);
+ SortedMap<K, V> delegate = newModifiableDelegate(comparator);
for (Entry<? extends K, ? extends V> entry : entries) {
putEntryWithChecks(delegate, entry);
}
- return new ImmutableSortedMap<K, V>(delegate);
+ return newView(unmodifiableSortedMap(delegate), comparator);
}
}
@@ -231,7 +221,7 @@ public class ImmutableSortedMap<K, V>
return (ks == null) ? (keySet = createKeySet()) : ks;
}
- private ImmutableSortedSet<K> createKeySet() {
+ @Override ImmutableSortedSet<K> createKeySet() {
// the keySet() of the delegate is only a Set and TreeMap.navigatableKeySet
// is not available in GWT yet. To keep the code simple and code size more,
// we make a copy here, instead of creating a view of it.
@@ -266,7 +256,7 @@ public class ImmutableSortedMap<K, V>
public ImmutableSortedMap<K, V> headMap(K toKey) {
checkNotNull(toKey);
- return new ImmutableSortedMap<K, V>(sortedDelegate.headMap(toKey));
+ return newView(sortedDelegate.headMap(toKey));
}
ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive) {
@@ -285,7 +275,7 @@ public class ImmutableSortedMap<K, V>
checkNotNull(fromKey);
checkNotNull(toKey);
checkArgument(comparator.compare(fromKey, toKey) <= 0);
- return new ImmutableSortedMap<K, V>(sortedDelegate.subMap(fromKey, toKey));
+ return newView(sortedDelegate.subMap(fromKey, toKey));
}
ImmutableSortedMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive){
@@ -294,10 +284,10 @@ public class ImmutableSortedMap<K, V>
checkArgument(comparator.compare(fromKey, toKey) <= 0);
return tailMap(fromKey, fromInclusive).headMap(toKey, toInclusive);
}
-
+
public ImmutableSortedMap<K, V> tailMap(K fromKey) {
checkNotNull(fromKey);
- return new ImmutableSortedMap<K, V>(sortedDelegate.tailMap(fromKey));
+ return newView(sortedDelegate.tailMap(fromKey));
}
public ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) {
@@ -305,16 +295,42 @@ public class ImmutableSortedMap<K, V>
if (!inclusive) {
fromKey = higher(fromKey);
if (fromKey == null) {
- return emptyMap(comparator());
+ return EmptyImmutableSortedMap.forComparator(comparator());
}
}
return tailMap(fromKey);
}
- static <K, V> ImmutableSortedMap<K, V> emptyMap(Comparator<? super K> comparator) {
- if (comparator == NATURAL_ORDER) {
- return (ImmutableSortedMap) NATURAL_EMPTY_MAP;
+ private ImmutableSortedMap<K, V> newView(SortedMap<K, V> delegate) {
+ return newView(delegate, comparator);
+ }
+
+ private static <K, V> ImmutableSortedMap<K, V> newView(
+ SortedMap<K, V> delegate, Comparator<? super K> comparator) {
+ if (delegate.isEmpty()) {
+ return EmptyImmutableSortedMap.forComparator(comparator);
}
- return create(comparator);
+ return new RegularImmutableSortedMap<K, V>(delegate, comparator);
+ }
+
+ /*
+ * We don't permit nulls, but we wrap every comparator with nullsFirst().
+ * Why? We want for queries like containsKey(null) to return false, but the
+ * GWT SortedMap implementation that we delegate to throws
+ * NullPointerException if the comparator does. Since our construction
+ * methods ensure that null is never present in the map, it's OK for the
+ * comparator to look for it wherever it wants.
+ *
+ * Note that we do NOT touch the comparator returned by comparator(), which
+ * should be identical to the one the user passed in. We touch only the
+ * "secret" comparator used by the delegate implementation.
+ */
+
+ private static <K, V> SortedMap<K, V> newModifiableDelegate(Comparator<? super K> comparator) {
+ return newTreeMap(nullAccepting(comparator));
+ }
+
+ private static <E> Comparator<E> nullAccepting(Comparator<E> comparator) {
+ return Ordering.from(comparator).nullsFirst();
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedSet.java
index cfb1da2ea..c3cb7aa44 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedSet.java
@@ -16,8 +16,8 @@
package com.google.common.collect;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
import java.util.ArrayList;
import java.util.Collection;
@@ -36,7 +36,8 @@ import javax.annotation.Nullable;
* @author Hayward Chan
*/
public abstract class ImmutableSortedSet<E>
- extends ImmutableSet<E> implements SortedSet<E>, SortedIterable<E> {
+ extends ForwardingImmutableSet<E> implements SortedSet<E>, SortedIterable<E> {
+ // TODO(cpovirk): split into ImmutableSortedSet/ForwardingImmutableSortedSet?
// In the non-emulated source, this is in ImmutableSortedSetFauxverideShim,
// which overrides ImmutableSet & which ImmutableSortedSet extends.
@@ -246,6 +247,19 @@ public abstract class ImmutableSortedSet<E>
private transient final SortedSet<E> sortedDelegate;
+ /**
+ * Scary constructor for ContiguousSet. This constructor (in this file, the
+ * GWT emulation of ImmutableSortedSet) creates an empty sortedDelegate,
+ * which, in a vacuum, sets this object's contents to empty. By contrast,
+ * the non-GWT constructor with the same signature uses the comparator only
+ * as a comparator. It does NOT assume empty contents. (It requires an
+ * implementation of iterator() to define its contents, and methods like
+ * contains() are implemented in terms of that method (though they will
+ * likely be overridden by subclasses for performance reasons).) This means
+ * that a call to this method have can different behavior in GWT and non-GWT
+ * environments UNLESS subclasses are careful to always override all methods
+ * implemented in terms of sortedDelegate (except comparator()).
+ */
ImmutableSortedSet(Comparator<? super E> comparator) {
this(Sets.newTreeSet(comparator));
}
@@ -371,11 +385,11 @@ public abstract class ImmutableSortedSet<E>
return new Builder<E>(comparator);
}
- public static <E extends Comparable<E>> Builder<E> reverseOrder() {
+ public static <E extends Comparable<?>> Builder<E> reverseOrder() {
return new Builder<E>(Ordering.natural().reverse());
}
- public static <E extends Comparable<E>> Builder<E> naturalOrder() {
+ public static <E extends Comparable<?>> Builder<E> naturalOrder() {
return new Builder<E>(Ordering.natural());
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterables.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterables.java
index 1299a6279..330c5bf18 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterables.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterables.java
@@ -22,7 +22,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
-import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
@@ -49,6 +48,10 @@ import javax.annotation.Nullable;
* produced in this class are <i>lazy</i>, which means that their iterators
* only advance the backing iteration when absolutely necessary.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Iterables">
+ * {@code Iterables}</a>.
+ *
* @author Kevin Bourrillion
* @author Jared Levy
* @since 2.0 (imported from Google Collections Library)
@@ -79,7 +82,7 @@ public final class Iterables {
return checkNotNull(iterable);
}
- private static final class UnmodifiableIterable<T> implements Iterable<T> {
+ private static final class UnmodifiableIterable<T> extends FluentIterable<T> {
private final Iterable<T> iterable;
private UnmodifiableIterable(Iterable<T> iterable) {
@@ -108,20 +111,14 @@ public final class Iterables {
}
/**
- * Returns {@code true} if {@code iterable} contains {@code element}; that is,
- * any object for which {@code equals(element)} is true.
+ * Returns {@code true} if {@code iterable} contains any object for which {@code equals(element)}
+ * is true.
*/
public static boolean contains(Iterable<?> iterable, @Nullable Object element)
{
if (iterable instanceof Collection) {
Collection<?> collection = (Collection<?>) iterable;
- try {
- return collection.contains(element);
- } catch (NullPointerException e) {
- return false;
- } catch (ClassCastException e) {
- return false;
- }
+ return Collections2.safeContains(collection, element);
}
return Iterators.contains(iterable.iterator(), element);
}
@@ -243,6 +240,13 @@ public final class Iterables {
*/
public static boolean elementsEqual(
Iterable<?> iterable1, Iterable<?> iterable2) {
+ if (iterable1 instanceof Collection && iterable2 instanceof Collection) {
+ Collection<?> collection1 = (Collection<?>) iterable1;
+ Collection<?> collection2 = (Collection<?>) iterable2;
+ if (collection1.size() != collection2.size()) {
+ return false;
+ }
+ }
return Iterators.elementsEqual(iterable1.iterator(), iterable2.iterator());
}
@@ -272,8 +276,9 @@ public final class Iterables {
* @throws IllegalArgumentException if the iterator contains multiple
* elements
*/
+ @Nullable
public static <T> T getOnlyElement(
- Iterable<T> iterable, @Nullable T defaultValue) {
+ Iterable<? extends T> iterable, @Nullable T defaultValue) {
return Iterators.getOnlyElement(iterable.iterator(), defaultValue);
}
@@ -350,7 +355,7 @@ public final class Iterables {
*/
public static <T> Iterable<T> cycle(final Iterable<T> iterable) {
checkNotNull(iterable);
- return new Iterable<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return Iterators.cycle(iterable);
@@ -465,7 +470,7 @@ public final class Iterables {
public static <T> Iterable<T> concat(
final Iterable<? extends Iterable<? extends T>> inputs) {
checkNotNull(inputs);
- return new IterableWithToString<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return Iterators.concat(iterators(inputs));
@@ -516,7 +521,7 @@ public final class Iterables {
final Iterable<T> iterable, final int size) {
checkNotNull(iterable);
checkArgument(size > 0);
- return new IterableWithToString<List<T>>() {
+ return new FluentIterable<List<T>>() {
@Override
public Iterator<List<T>> iterator() {
return Iterators.partition(iterable.iterator(), size);
@@ -545,7 +550,7 @@ public final class Iterables {
final Iterable<T> iterable, final int size) {
checkNotNull(iterable);
checkArgument(size > 0);
- return new IterableWithToString<List<T>>() {
+ return new FluentIterable<List<T>>() {
@Override
public Iterator<List<T>> iterator() {
return Iterators.paddedPartition(iterable.iterator(), size);
@@ -561,7 +566,7 @@ public final class Iterables {
final Iterable<T> unfiltered, final Predicate<? super T> predicate) {
checkNotNull(unfiltered);
checkNotNull(predicate);
- return new IterableWithToString<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return Iterators.filter(unfiltered.iterator(), predicate);
@@ -570,8 +575,7 @@ public final class Iterables {
}
/**
- * Returns {@code true} if one or more elements in {@code iterable} satisfy
- * the predicate.
+ * Returns {@code true} if any element in {@code iterable} satisfies the predicate.
*/
public static <T> boolean any(
Iterable<T> iterable, Predicate<? super T> predicate) {
@@ -590,8 +594,8 @@ public final class Iterables {
/**
* Returns the first element in {@code iterable} that satisfies the given
* predicate; use this method only when such an element is known to exist. If
- * it is possible that <i>no</i> element will match, use {@link
- * #tryFind)} or {@link #find(Iterable, Predicate, T)} instead.
+ * it is possible that <i>no</i> element will match, use {@link #tryFind} or
+ * {@link #find(Iterable, Predicate, Object)} instead.
*
* @throws NoSuchElementException if no element in {@code iterable} matches
* the given predicate
@@ -609,7 +613,8 @@ public final class Iterables {
*
* @since 7.0
*/
- public static <T> T find(Iterable<T> iterable,
+ @Nullable
+ public static <T> T find(Iterable<? extends T> iterable,
Predicate<? super T> predicate, @Nullable T defaultValue) {
return Iterators.find(iterable.iterator(), predicate, defaultValue);
}
@@ -661,7 +666,7 @@ public final class Iterables {
final Function<? super F, ? extends T> function) {
checkNotNull(fromIterable);
checkNotNull(function);
- return new IterableWithToString<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return Iterators.transform(fromIterable.iterator(), function);
@@ -714,8 +719,8 @@ public final class Iterables {
* @throws IndexOutOfBoundsException if {@code position} is negative
* @since 4.0
*/
- public static <T> T get(Iterable<T> iterable, int position,
- @Nullable T defaultValue) {
+ @Nullable
+ public static <T> T get(Iterable<? extends T> iterable, int position, @Nullable T defaultValue) {
checkNotNull(iterable);
checkNonnegativeIndex(position);
@@ -731,11 +736,16 @@ public final class Iterables {
* the iterable is empty. The {@link Iterators} analog to this method is
* {@link Iterators#getNext}.
*
+ * <p>If no default value is desired (and the caller instead wants a
+ * {@link NoSuchElementException} to be thrown), it is recommended that
+ * {@code iterable.iterator().next()} is used instead.
+ *
* @param defaultValue the default value to return if the iterable is empty
* @return the first element of {@code iterable} or the default value
* @since 7.0
*/
- public static <T> T getFirst(Iterable<T> iterable, @Nullable T defaultValue) {
+ @Nullable
+ public static <T> T getFirst(Iterable<? extends T> iterable, @Nullable T defaultValue) {
return Iterators.getNext(iterable.iterator(), defaultValue);
}
@@ -776,16 +786,17 @@ public final class Iterables {
* @return the last element of {@code iterable} or the default value
* @since 3.0
*/
- public static <T> T getLast(Iterable<T> iterable, @Nullable T defaultValue) {
+ @Nullable
+ public static <T> T getLast(Iterable<? extends T> iterable, @Nullable T defaultValue) {
if (iterable instanceof Collection) {
- Collection<T> collection = (Collection<T>) iterable;
+ Collection<? extends T> collection = Collections2.cast(iterable);
if (collection.isEmpty()) {
return defaultValue;
}
}
if (iterable instanceof List) {
- List<T> list = (List<T>) iterable;
+ List<? extends T> list = Lists.cast(iterable);
return getLastInNonemptyList(list);
}
@@ -795,7 +806,7 @@ public final class Iterables {
* call this method.
*/
if (iterable instanceof SortedSet) {
- SortedSet<T> sortedSet = (SortedSet<T>) iterable;
+ SortedSet<? extends T> sortedSet = Sets.cast(iterable);
return sortedSet.last();
}
@@ -833,7 +844,7 @@ public final class Iterables {
if (iterable instanceof List) {
final List<T> list = (List<T>) iterable;
- return new IterableWithToString<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
// TODO(kevinb): Support a concurrently modified collection?
@@ -844,12 +855,12 @@ public final class Iterables {
};
}
- return new IterableWithToString<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
final Iterator<T> iterator = iterable.iterator();
- Iterators.skip(iterator, numberToSkip);
+ Iterators.advance(iterator, numberToSkip);
/*
* We can't just return the iterator because an immediate call to its
@@ -905,7 +916,7 @@ public final class Iterables {
final Iterable<T> iterable, final int limitSize) {
checkNotNull(iterable);
checkArgument(limitSize >= 0, "limit is negative");
- return new IterableWithToString<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return Iterators.limit(iterable.iterator(), limitSize);
@@ -934,7 +945,7 @@ public final class Iterables {
*/
public static <T> Iterable<T> consumingIterable(final Iterable<T> iterable) {
if (iterable instanceof Queue) {
- return new Iterable<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return new ConsumingQueueIterator<T>((Queue<T>) iterable);
@@ -944,7 +955,7 @@ public final class Iterables {
checkNotNull(iterable);
- return new Iterable<T>() {
+ return new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return Iterators.consumingIterator(iterable.iterator());
@@ -971,30 +982,6 @@ public final class Iterables {
// Methods only in Iterables, not in Iterators
/**
- * Adapts a list to an iterable with reversed iteration order. It is
- * especially useful in foreach-style loops: <pre> {@code
- *
- * List<String> mylist = ...
- * for (String str : Iterables.reverse(mylist)) {
- * ...
- * }}</pre>
- *
- * There is no corresponding method in {@link Iterators}, since {@link
- * Iterable#iterator} can simply be invoked on the result of calling this
- * method.
- *
- * @return an iterable with the same elements as the list, in reverse
- *
- * @deprecated use {@link Lists#reverse(List)} or {@link
- * ImmutableList#reverse()}. <b>This method is scheduled for deletion in
- * July 2012.</b>
- */
- @Deprecated
- public static <T> Iterable<T> reverse(final List<T> list) {
- return Lists.reverse(list);
- }
-
- /**
* Determines if the given iterable contains no elements.
*
* <p>There is no precise {@link Iterator} equivalent to this method, since
@@ -1010,43 +997,6 @@ public final class Iterables {
return !iterable.iterator().hasNext();
}
- // Non-public
-
- /**
- * Removes the specified element from the specified iterable.
- *
- * <p>This method iterates over the iterable, checking each element returned
- * by the iterator in turn to see if it equals the object {@code o}. If they
- * are equal, it is removed from the iterable with the iterator's
- * {@code remove} method. At most one element is removed, even if the iterable
- * contains multiple members that equal {@code o}.
- *
- * <p><b>Warning:</b> Do not use this method for a collection, such as a
- * {@link HashSet}, that has a fast {@code remove} method.
- *
- * @param iterable the iterable from which to remove
- * @param o an element to remove from the collection
- * @return {@code true} if the iterable changed as a result
- * @throws UnsupportedOperationException if the iterator does not support the
- * {@code remove} method and the iterable contains the object
- */
- static boolean remove(Iterable<?> iterable, @Nullable Object o) {
- Iterator<?> i = iterable.iterator();
- while (i.hasNext()) {
- if (Objects.equal(i.next(), o)) {
- i.remove();
- return true;
- }
- }
- return false;
- }
-
- abstract static class IterableWithToString<E> implements Iterable<E> {
- @Override public String toString() {
- return Iterables.toString(this);
- }
- }
-
/**
* Returns an iterable over the merged contents of all given
* {@code iterables}. Equivalent entries will not be de-duplicated.
@@ -1065,7 +1015,7 @@ public final class Iterables {
final Comparator<? super T> comparator) {
checkNotNull(iterables, "iterables");
checkNotNull(comparator, "comparator");
- Iterable<T> iterable = new Iterable<T>() {
+ Iterable<T> iterable = new FluentIterable<T>() {
@Override
public Iterator<T> iterator() {
return Iterators.mergeSorted(
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterators.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterators.java
index 9da1da615..ead102f5c 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterators.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterators.java
@@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
+import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
@@ -35,6 +36,7 @@ import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
+import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.PriorityQueue;
import java.util.Queue;
@@ -50,6 +52,10 @@ import javax.annotation.Nullable;
* produced in this class are <i>lazy</i>, which means that they only advance
* the backing iteration when absolutely necessary.
*
+ * <p>See the Guava User Guide section on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Iterables">
+ * {@code Iterators}</a>.
+ *
* @author Kevin Bourrillion
* @author Jared Levy
* @since 2.0 (imported from Google Collections Library)
@@ -58,8 +64,8 @@ import javax.annotation.Nullable;
public final class Iterators {
private Iterators() {}
- static final UnmodifiableIterator<Object> EMPTY_ITERATOR
- = new UnmodifiableIterator<Object>() {
+ static final UnmodifiableListIterator<Object> EMPTY_LIST_ITERATOR
+ = new UnmodifiableListIterator<Object>() {
@Override
public boolean hasNext() {
return false;
@@ -68,6 +74,22 @@ public final class Iterators {
public Object next() {
throw new NoSuchElementException();
}
+ @Override
+ public boolean hasPrevious() {
+ return false;
+ }
+ @Override
+ public Object previous() {
+ throw new NoSuchElementException();
+ }
+ @Override
+ public int nextIndex() {
+ return 0;
+ }
+ @Override
+ public int previousIndex() {
+ return -1;
+ }
};
/**
@@ -76,10 +98,20 @@ public final class Iterators {
* <p>The {@link Iterable} equivalent of this method is {@link
* ImmutableSet#of()}.
*/
+ public static <T> UnmodifiableIterator<T> emptyIterator() {
+ return emptyListIterator();
+ }
+
+ /**
+ * Returns the empty iterator.
+ *
+ * <p>The {@link Iterable} equivalent of this method is {@link
+ * ImmutableSet#of()}.
+ */
// Casting to any type is safe since there are no actual elements.
@SuppressWarnings("unchecked")
- public static <T> UnmodifiableIterator<T> emptyIterator() {
- return (UnmodifiableIterator<T>) EMPTY_ITERATOR;
+ static <T> UnmodifiableListIterator<T> emptyListIterator() {
+ return (UnmodifiableListIterator<T>) EMPTY_LIST_ITERATOR;
}
private static final Iterator<Object> EMPTY_MODIFIABLE_ITERATOR =
@@ -273,15 +305,11 @@ public final class Iterators {
* {@code hasNext()} method will return {@code false}.
*/
public static String toString(Iterator<?> iterator) {
- if (!iterator.hasNext()) {
- return "[]";
- }
- StringBuilder builder = new StringBuilder();
- builder.append('[').append(iterator.next());
- while (iterator.hasNext()) {
- builder.append(", ").append(iterator.next());
- }
- return builder.append(']').toString();
+ return Joiner.on(", ")
+ .useForNull("null")
+ .appendTo(new StringBuilder().append('['), iterator)
+ .append(']')
+ .toString();
}
/**
@@ -317,8 +345,8 @@ public final class Iterators {
* @throws IllegalArgumentException if the iterator contains multiple
* elements. The state of the iterator is unspecified.
*/
- public static <T> T getOnlyElement(
- Iterator<T> iterator, @Nullable T defaultValue) {
+ @Nullable
+ public static <T> T getOnlyElement(Iterator<? extends T> iterator, @Nullable T defaultValue) {
return iterator.hasNext() ? getOnlyElement(iterator) : defaultValue;
}
@@ -413,8 +441,8 @@ public final class Iterators {
/**
* Returns an iterator that cycles indefinitely over the provided elements.
*
- * <p>The returned iterator supports {@code remove()} if the provided iterator
- * does. After {@code remove()} is called, subsequent cycles omit the removed
+ * <p>The returned iterator supports {@code remove()}. After {@code remove()}
+ * is called, subsequent cycles omit the removed
* element, but {@code elements} does not change. The iterator's
* {@code hasNext()} method returns {@code true} until all of the original
* elements have been removed.
@@ -434,6 +462,11 @@ public final class Iterators {
*
* <p>The returned iterator supports {@code remove()} when the corresponding
* input iterator supports it.
+ *
+ * <p><b>Note:</b> the current implementation is not suitable for nested
+ * concatenated iterators, i.e. the following should be avoided when in a loop:
+ * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the
+ * resulting iterator has a cubic complexity to the depth of the nesting.
*/
@SuppressWarnings("unchecked")
public static <T> Iterator<T> concat(Iterator<? extends T> a,
@@ -451,6 +484,11 @@ public final class Iterators {
*
* <p>The returned iterator supports {@code remove()} when the corresponding
* input iterator supports it.
+ *
+ * <p><b>Note:</b> the current implementation is not suitable for nested
+ * concatenated iterators, i.e. the following should be avoided when in a loop:
+ * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the
+ * resulting iterator has a cubic complexity to the depth of the nesting.
*/
@SuppressWarnings("unchecked")
public static <T> Iterator<T> concat(Iterator<? extends T> a,
@@ -469,6 +507,11 @@ public final class Iterators {
*
* <p>The returned iterator supports {@code remove()} when the corresponding
* input iterator supports it.
+ *
+ * <p><b>Note:</b> the current implementation is not suitable for nested
+ * concatenated iterators, i.e. the following should be avoided when in a loop:
+ * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the
+ * resulting iterator has a cubic complexity to the depth of the nesting.
*/
@SuppressWarnings("unchecked")
public static <T> Iterator<T> concat(Iterator<? extends T> a,
@@ -489,6 +532,11 @@ public final class Iterators {
* <p>The returned iterator supports {@code remove()} when the corresponding
* input iterator supports it.
*
+ * <p><b>Note:</b> the current implementation is not suitable for nested
+ * concatenated iterators, i.e. the following should be avoided when in a loop:
+ * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the
+ * resulting iterator has a cubic complexity to the depth of the nesting.
+ *
* @throws NullPointerException if any of the provided iterators is null
*/
public static <T> Iterator<T> concat(Iterator<? extends T>... inputs) {
@@ -503,6 +551,11 @@ public final class Iterators {
* <p>The returned iterator supports {@code remove()} when the corresponding
* input iterator supports it. The methods of the returned iterator may throw
* {@code NullPointerException} if any of the input iterators is null.
+ *
+ * <p><b>Note:</b> the current implementation is not suitable for nested
+ * concatenated iterators, i.e. the following should be avoided when in a loop:
+ * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the
+ * resulting iterator has a cubic complexity to the depth of the nesting.
*/
public static <T> Iterator<T> concat(
final Iterator<? extends Iterator<? extends T>> inputs) {
@@ -675,8 +728,8 @@ public final class Iterators {
* predicate; use this method only when such an element is known to exist. If
* no such element is found, the iterator will be left exhausted: its {@code
* hasNext()} method will return {@code false}. If it is possible that
- * <i>no</i> element will match, use {@link #tryFind)} or {@link
- * #find(Iterator, Predicate, T)} instead.
+ * <i>no</i> element will match, use {@link #tryFind} or {@link
+ * #find(Iterator, Predicate, Object)} instead.
*
* @throws NoSuchElementException if no element in {@code iterator} matches
* the given predicate
@@ -696,9 +749,10 @@ public final class Iterators {
*
* @since 7.0
*/
- public static <T> T find(Iterator<T> iterator, Predicate<? super T> predicate,
+ @Nullable
+ public static <T> T find(Iterator<? extends T> iterator, Predicate<? super T> predicate,
@Nullable T defaultValue) {
- UnmodifiableIterator<T> filteredIterator = filter(iterator, predicate);
+ UnmodifiableIterator<? extends T> filteredIterator = filter(iterator, predicate);
return filteredIterator.hasNext() ? filteredIterator.next() : defaultValue;
}
@@ -763,22 +817,12 @@ public final class Iterators {
*/
public static <F, T> Iterator<T> transform(final Iterator<F> fromIterator,
final Function<? super F, ? extends T> function) {
- checkNotNull(fromIterator);
checkNotNull(function);
- return new Iterator<T>() {
- @Override
- public boolean hasNext() {
- return fromIterator.hasNext();
- }
+ return new TransformedIterator<F, T>(fromIterator) {
@Override
- public T next() {
- F from = fromIterator.next();
+ T transform(F from) {
return function.apply(from);
}
- @Override
- public void remove() {
- fromIterator.remove();
- }
};
}
@@ -830,8 +874,8 @@ public final class Iterators {
* @throws IndexOutOfBoundsException if {@code position} is negative
* @since 4.0
*/
- public static <T> T get(Iterator<T> iterator, int position,
- @Nullable T defaultValue) {
+ @Nullable
+ public static <T> T get(Iterator<? extends T> iterator, int position, @Nullable T defaultValue) {
checkNonnegative(position);
try {
@@ -850,7 +894,8 @@ public final class Iterators {
* @return the next element of {@code iterator} or the default value
* @since 7.0
*/
- public static <T> T getNext(Iterator<T> iterator, @Nullable T defaultValue) {
+ @Nullable
+ public static <T> T getNext(Iterator<? extends T> iterator, @Nullable T defaultValue) {
return iterator.hasNext() ? iterator.next() : defaultValue;
}
@@ -877,24 +922,24 @@ public final class Iterators {
* @return the last element of {@code iterator}
* @since 3.0
*/
- public static <T> T getLast(Iterator<T> iterator, @Nullable T defaultValue) {
+ @Nullable
+ public static <T> T getLast(Iterator<? extends T> iterator, @Nullable T defaultValue) {
return iterator.hasNext() ? getLast(iterator) : defaultValue;
}
/**
- * Calls {@code next()} on {@code iterator}, either {@code numberToSkip} times
+ * Calls {@code next()} on {@code iterator}, either {@code numberToAdvance} times
* or until {@code hasNext()} returns {@code false}, whichever comes first.
*
- * @return the number of elements skipped
- * @since 3.0
+ * @return the number of elements the iterator was advanced
+ * @since 13.0 (since 3.0 as {@code Iterators.skip})
*/
- @Beta
- public static <T> int skip(Iterator<T> iterator, int numberToSkip) {
+ public static int advance(Iterator<?> iterator, int numberToAdvance) {
checkNotNull(iterator);
- checkArgument(numberToSkip >= 0, "number to skip cannot be negative");
+ checkArgument(numberToAdvance >= 0, "number to advance cannot be negative");
int i;
- for (i = 0; i < numberToSkip && iterator.hasNext(); i++) {
+ for (i = 0; i < numberToAdvance && iterator.hasNext(); i++) {
iterator.next();
}
return i;
@@ -970,6 +1015,21 @@ public final class Iterators {
};
}
+ /**
+ * Deletes and returns the next value from the iterator, or returns
+ * {@code defaultValue} if there is no such value.
+ */
+ @Nullable
+ static <T> T pollNext(Iterator<T> iterator) {
+ if (iterator.hasNext()) {
+ T result = iterator.next();
+ iterator.remove();
+ return result;
+ } else {
+ return null;
+ }
+ }
+
// Methods only in Iterators, not in Iterables
/**
@@ -1007,21 +1067,14 @@ public final class Iterators {
}
/**
- * Returns an iterator containing the elements in the specified range of
- * {@code array} in order. The returned iterator is a view of the array;
- * subsequent changes to the array will be reflected in the iterator.
+ * Returns a list iterator containing the elements in the specified range of
+ * {@code array} in order, starting at the specified index.
*
* <p>The {@code Iterable} equivalent of this method is {@code
- * Arrays.asList(array).subList(offset, offset + length)}.
- *
- * @param array array to read elements out of
- * @param offset index of first array element to retrieve
- * @param length number of elements in iteration
- * @throws IndexOutOfBoundsException if {@code offset} is negative, {@code
- * length} is negative, or {@code offset + length > array.length}
+ * Arrays.asList(array).subList(offset, offset + length).listIterator(index)}.
*/
- static <T> UnmodifiableIterator<T> forArray(
- final T[] array, final int offset, int length) {
+ static <T> UnmodifiableListIterator<T> forArray(
+ final T[] array, final int offset, int length, int index) {
checkArgument(length >= 0);
int end = offset + length;
@@ -1033,7 +1086,7 @@ public final class Iterators {
* because the returned Iterator is a ListIterator that may be moved back
* past the beginning of the iteration.
*/
- return new AbstractIndexedListIterator<T>(length) {
+ return new AbstractIndexedListIterator<T>(length, index) {
@Override protected T get(int index) {
return array[offset + index];
}
@@ -1290,4 +1343,19 @@ public final class Iterators {
return next;
}
}
+
+ /**
+ * Precondition tester for {@code Iterator.remove()} that throws an exception with a consistent
+ * error message.
+ */
+ static void checkRemove(boolean canRemove) {
+ checkState(canRemove, "no calls to next() since the last call to remove()");
+ }
+
+ /**
+ * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557
+ */
+ static <T> ListIterator<T> cast(Iterator<T> iterator) {
+ return (ListIterator<T>) iterator;
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultimap.java
index d4268935d..74e0ff171 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultimap.java
@@ -16,16 +16,20 @@
package com.google.common.collect;
+import static com.google.common.base.Preconditions.checkArgument;
+
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.primitives.Ints;
+import com.google.common.base.Objects;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Set;
import javax.annotation.Nullable;
@@ -61,29 +65,23 @@ import javax.annotation.Nullable;
* update operations, wrap your multimap with a call to {@link
* Multimaps#synchronizedSetMultimap}.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
+ * {@code Multimap}</a>.
+ *
* @author Jared Levy
+ * @author Louis Wasserman
* @since 2.0 (imported from Google Collections Library)
*/
@GwtCompatible(serializable = true, emulated = true)
public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
- private static final int DEFAULT_VALUES_PER_KEY = 8;
-
- @VisibleForTesting
- transient int expectedValuesPerKey = DEFAULT_VALUES_PER_KEY;
-
- /**
- * Map entries with an iteration order corresponding to the order in which the
- * key-value pairs were added to the multimap.
- */
- // package-private for GWT deserialization
- transient Collection<Map.Entry<K, V>> linkedEntries;
/**
* Creates a new, empty {@code LinkedHashMultimap} with the default initial
* capacities.
*/
public static <K, V> LinkedHashMultimap<K, V> create() {
- return new LinkedHashMultimap<K, V>();
+ return new LinkedHashMultimap<K, V>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY);
}
/**
@@ -97,7 +95,9 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
*/
public static <K, V> LinkedHashMultimap<K, V> create(
int expectedKeys, int expectedValuesPerKey) {
- return new LinkedHashMultimap<K, V>(expectedKeys, expectedValuesPerKey);
+ return new LinkedHashMultimap<K, V>(
+ Maps.capacity(expectedKeys),
+ Maps.capacity(expectedValuesPerKey));
}
/**
@@ -111,201 +111,158 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
*/
public static <K, V> LinkedHashMultimap<K, V> create(
Multimap<? extends K, ? extends V> multimap) {
- return new LinkedHashMultimap<K, V>(multimap);
+ LinkedHashMultimap<K, V> result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY);
+ result.putAll(multimap);
+ return result;
}
- private LinkedHashMultimap() {
- super(new LinkedHashMap<K, Collection<V>>());
- linkedEntries = Sets.newLinkedHashSet();
+ private interface ValueSetLink<K, V> {
+ ValueSetLink<K, V> getPredecessorInValueSet();
+ ValueSetLink<K, V> getSuccessorInValueSet();
+
+ void setPredecessorInValueSet(ValueSetLink<K, V> entry);
+ void setSuccessorInValueSet(ValueSetLink<K, V> entry);
}
- private LinkedHashMultimap(int expectedKeys, int expectedValuesPerKey) {
- super(new LinkedHashMap<K, Collection<V>>(expectedKeys));
- Preconditions.checkArgument(expectedValuesPerKey >= 0);
- this.expectedValuesPerKey = expectedValuesPerKey;
- linkedEntries = new LinkedHashSet<Map.Entry<K, V>>(
- (int) Math.min(Ints.MAX_POWER_OF_TWO,
- ((long) expectedKeys) * expectedValuesPerKey));
+ private static <K, V> void succeedsInValueSet(ValueSetLink<K, V> pred, ValueSetLink<K, V> succ) {
+ pred.setSuccessorInValueSet(succ);
+ succ.setPredecessorInValueSet(pred);
}
- private LinkedHashMultimap(Multimap<? extends K, ? extends V> multimap) {
- super(new LinkedHashMap<K, Collection<V>>(
- Maps.capacity(multimap.keySet().size())));
- linkedEntries
- = new LinkedHashSet<Map.Entry<K, V>>(Maps.capacity(multimap.size()));
- putAll(multimap);
+ private static <K, V> void succeedsInMultimap(
+ ValueEntry<K, V> pred, ValueEntry<K, V> succ) {
+ pred.setSuccessorInMultimap(succ);
+ succ.setPredecessorInMultimap(pred);
}
- /**
- * {@inheritDoc}
- *
- * <p>Creates an empty {@code LinkedHashSet} for a collection of values for
- * one key.
- *
- * @return a new {@code LinkedHashSet} containing a collection of values for
- * one key
- */
- @Override Set<V> createCollection() {
- return new LinkedHashSet<V>(Maps.capacity(expectedValuesPerKey));
+ private static <K, V> void deleteFromValueSet(ValueSetLink<K, V> entry) {
+ succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet());
}
- /**
- * {@inheritDoc}
- *
- * <p>Creates a decorated {@code LinkedHashSet} that also keeps track of the
- * order in which key-value pairs are added to the multimap.
- *
- * @param key key to associate with values in the collection
- * @return a new decorated {@code LinkedHashSet} containing a collection of
- * values for one key
- */
- @Override Collection<V> createCollection(@Nullable K key) {
- return new SetDecorator(key, createCollection());
+ private static <K, V> void deleteFromMultimap(ValueEntry<K, V> entry) {
+ succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap());
}
- private class SetDecorator extends ForwardingSet<V> {
- final Set<V> delegate;
+ /**
+ * LinkedHashMultimap entries are in no less than three coexisting linked lists:
+ * a row in the hash table for a Set<V> associated with a key, the linked list
+ * of insertion-ordered entries in that Set<V>, and the linked list of entries
+ * in the LinkedHashMultimap as a whole.
+ */
+ @VisibleForTesting
+ static final class ValueEntry<K, V> extends AbstractMapEntry<K, V>
+ implements ValueSetLink<K, V> {
final K key;
+ final V value;
+ final int valueHash;
+
+ @Nullable ValueEntry<K, V> nextInValueSetHashRow;
+
+ ValueSetLink<K, V> predecessorInValueSet;
+ ValueSetLink<K, V> successorInValueSet;
+
+ ValueEntry<K, V> predecessorInMultimap;
+ ValueEntry<K, V> successorInMultimap;
- SetDecorator(@Nullable K key, Set<V> delegate) {
- this.delegate = delegate;
+ ValueEntry(@Nullable K key, @Nullable V value, int valueHash,
+ @Nullable ValueEntry<K, V> nextInValueSetHashRow) {
this.key = key;
+ this.value = value;
+ this.valueHash = valueHash;
+ this.nextInValueSetHashRow = nextInValueSetHashRow;
}
- @Override protected Set<V> delegate() {
- return delegate;
+ @Override
+ public K getKey() {
+ return key;
}
- <E> Map.Entry<K, E> createEntry(@Nullable E value) {
- return Maps.immutableEntry(key, value);
+ @Override
+ public V getValue() {
+ return value;
}
- <E> Collection<Map.Entry<K, E>> createEntries(Collection<E> values) {
- // converts a collection of values into a list of key/value map entries
- Collection<Map.Entry<K, E>> entries
- = Lists.newArrayListWithExpectedSize(values.size());
- for (E value : values) {
- entries.add(createEntry(value));
- }
- return entries;
+ @Override
+ public ValueSetLink<K, V> getPredecessorInValueSet() {
+ return predecessorInValueSet;
}
- @Override public boolean add(@Nullable V value) {
- boolean changed = delegate.add(value);
- if (changed) {
- linkedEntries.add(createEntry(value));
- }
- return changed;
+ @Override
+ public ValueSetLink<K, V> getSuccessorInValueSet() {
+ return successorInValueSet;
}
- @Override public boolean addAll(Collection<? extends V> values) {
- boolean changed = delegate.addAll(values);
- if (changed) {
- linkedEntries.addAll(createEntries(delegate()));
- }
- return changed;
+ @Override
+ public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
+ predecessorInValueSet = entry;
}
- @Override public void clear() {
- for (V value : delegate) {
- linkedEntries.remove(createEntry(value));
- }
- delegate.clear();
+ @Override
+ public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
+ successorInValueSet = entry;
}
- @Override public Iterator<V> iterator() {
- final Iterator<V> delegateIterator = delegate.iterator();
- return new Iterator<V>() {
- V value;
-
- @Override
- public boolean hasNext() {
- return delegateIterator.hasNext();
- }
- @Override
- public V next() {
- value = delegateIterator.next();
- return value;
- }
- @Override
- public void remove() {
- delegateIterator.remove();
- linkedEntries.remove(createEntry(value));
- }
- };
+ public ValueEntry<K, V> getPredecessorInMultimap() {
+ return predecessorInMultimap;
}
- @Override public boolean remove(@Nullable Object value) {
- boolean changed = delegate.remove(value);
- if (changed) {
- /*
- * linkedEntries.remove() will return false when this method is called
- * by entries().iterator().remove()
- */
- linkedEntries.remove(createEntry(value));
- }
- return changed;
+ public ValueEntry<K, V> getSuccessorInMultimap() {
+ return successorInMultimap;
}
- @Override public boolean removeAll(Collection<?> values) {
- boolean changed = delegate.removeAll(values);
- if (changed) {
- linkedEntries.removeAll(createEntries(values));
- }
- return changed;
+ public void setSuccessorInMultimap(ValueEntry<K, V> multimapSuccessor) {
+ this.successorInMultimap = multimapSuccessor;
}
- @Override public boolean retainAll(Collection<?> values) {
- /*
- * Calling linkedEntries.retainAll() would incorrectly remove values
- * with other keys.
- */
- boolean changed = false;
- Iterator<V> iterator = delegate.iterator();
- while (iterator.hasNext()) {
- V value = iterator.next();
- if (!values.contains(value)) {
- iterator.remove();
- linkedEntries.remove(Maps.immutableEntry(key, value));
- changed = true;
- }
- }
- return changed;
+ public void setPredecessorInMultimap(ValueEntry<K, V> multimapPredecessor) {
+ this.predecessorInMultimap = multimapPredecessor;
}
}
+ private static final int DEFAULT_KEY_CAPACITY = 16;
+ private static final int DEFAULT_VALUE_SET_CAPACITY = 2;
+ @VisibleForTesting static final double VALUE_SET_LOAD_FACTOR = 1.0;
+
+ @VisibleForTesting transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY;
+ private transient ValueEntry<K, V> multimapHeaderEntry;
+
+ private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) {
+ super(new LinkedHashMap<K, Collection<V>>(keyCapacity));
+
+ checkArgument(valueSetCapacity >= 0,
+ "expectedValuesPerKey must be >= 0 but was %s", valueSetCapacity);
+
+ this.valueSetCapacity = valueSetCapacity;
+ this.multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null);
+ succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
+ }
+
/**
* {@inheritDoc}
*
- * <p>Generates an iterator across map entries that follows the ordering in
- * which the key-value pairs were added to the multimap.
+ * <p>Creates an empty {@code LinkedHashSet} for a collection of values for
+ * one key.
*
- * @return a key-value iterator with the correct ordering
+ * @return a new {@code LinkedHashSet} containing a collection of values for
+ * one key
*/
- @Override Iterator<Map.Entry<K, V>> createEntryIterator() {
- final Iterator<Map.Entry<K, V>> delegateIterator = linkedEntries.iterator();
-
- return new Iterator<Map.Entry<K, V>>() {
- Map.Entry<K, V> entry;
-
- @Override
- public boolean hasNext() {
- return delegateIterator.hasNext();
- }
-
- @Override
- public Map.Entry<K, V> next() {
- entry = delegateIterator.next();
- return entry;
- }
+ @Override
+ Set<V> createCollection() {
+ return new LinkedHashSet<V>(valueSetCapacity);
+ }
- @Override
- public void remove() {
- // Remove from iterator first to keep iterator valid.
- delegateIterator.remove();
- LinkedHashMultimap.this.remove(entry.getKey(), entry.getValue());
- }
- };
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Creates a decorated insertion-ordered set that also keeps track of the
+ * order in which key-value pairs are added to the multimap.
+ *
+ * @param key key to associate with values in the collection
+ * @return a new decorated set containing a collection of values for one key
+ */
+ @Override
+ Collection<V> createCollection(K key) {
+ return new ValueSet(key, valueSetCapacity);
}
/**
@@ -316,8 +273,8 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
* However, the provided values always come last in the {@link #entries()} and
* {@link #values()} iteration orderings.
*/
- @Override public Set<V> replaceValues(
- @Nullable K key, Iterable<? extends V> values) {
+ @Override
+ public Set<V> replaceValues(@Nullable K key, Iterable<? extends V> values) {
return super.replaceValues(key, values);
}
@@ -348,7 +305,249 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> {
return super.values();
}
- // Unfortunately, the entries() ordering does not determine the key ordering;
- // see the example in the LinkedListMultimap class Javadoc.
+ @VisibleForTesting
+ final class ValueSet extends Sets.ImprovedAbstractSet<V> implements ValueSetLink<K, V> {
+ /*
+ * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory
+ * consumption.
+ */
+
+ private final K key;
+ @VisibleForTesting ValueEntry<K, V>[] hashTable;
+ private int size = 0;
+ private int modCount = 0;
+
+ // We use the set object itself as the end of the linked list, avoiding an unnecessary
+ // entry object per key.
+ private ValueSetLink<K, V> firstEntry;
+ private ValueSetLink<K, V> lastEntry;
+
+ ValueSet(K key, int expectedValues) {
+ this.key = key;
+ this.firstEntry = this;
+ this.lastEntry = this;
+ // Round expected values up to a power of 2 to get the table size.
+ int tableSize = Hashing.closedTableSize(expectedValues, VALUE_SET_LOAD_FACTOR);
+
+ @SuppressWarnings("unchecked")
+ ValueEntry<K, V>[] hashTable = new ValueEntry[tableSize];
+ this.hashTable = hashTable;
+ }
+
+ @Override
+ public ValueSetLink<K, V> getPredecessorInValueSet() {
+ return lastEntry;
+ }
+
+ @Override
+ public ValueSetLink<K, V> getSuccessorInValueSet() {
+ return firstEntry;
+ }
+
+ @Override
+ public void setPredecessorInValueSet(ValueSetLink<K, V> entry) {
+ lastEntry = entry;
+ }
+
+ @Override
+ public void setSuccessorInValueSet(ValueSetLink<K, V> entry) {
+ firstEntry = entry;
+ }
+
+ @Override
+ public Iterator<V> iterator() {
+ return new Iterator<V>() {
+ ValueSetLink<K, V> nextEntry = firstEntry;
+ ValueEntry<K, V> toRemove;
+ int expectedModCount = modCount;
+
+ private void checkForComodification() {
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ checkForComodification();
+ return nextEntry != ValueSet.this;
+ }
+
+ @Override
+ public V next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ValueEntry<K, V> entry = (ValueEntry<K, V>) nextEntry;
+ V result = entry.getValue();
+ toRemove = entry;
+ nextEntry = entry.getSuccessorInValueSet();
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ checkForComodification();
+ Iterators.checkRemove(toRemove != null);
+ Object o = toRemove.getValue();
+ int hash = (o == null) ? 0 : o.hashCode();
+ int row = Hashing.smear(hash) & (hashTable.length - 1);
+ ValueEntry<K, V> prev = null;
+ for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
+ prev = entry, entry = entry.nextInValueSetHashRow) {
+ if (entry == toRemove) {
+ if (prev == null) {
+ // first entry in row
+ hashTable[row] = entry.nextInValueSetHashRow;
+ } else {
+ prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
+ }
+ deleteFromValueSet(toRemove);
+ deleteFromMultimap(toRemove);
+ size--;
+ expectedModCount = ++modCount;
+ break;
+ }
+ }
+ toRemove = null;
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public boolean contains(@Nullable Object o) {
+ int hash = (o == null) ? 0 : o.hashCode();
+ int row = Hashing.smear(hash) & (hashTable.length - 1);
+
+ for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
+ entry = entry.nextInValueSetHashRow) {
+ if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean add(@Nullable V value) {
+ int hash = (value == null) ? 0 : value.hashCode();
+ int row = Hashing.smear(hash) & (hashTable.length - 1);
+
+ ValueEntry<K, V> rowHead = hashTable[row];
+ for (ValueEntry<K, V> entry = rowHead; entry != null;
+ entry = entry.nextInValueSetHashRow) {
+ if (hash == entry.valueHash && Objects.equal(value, entry.getValue())) {
+ return false;
+ }
+ }
+
+ ValueEntry<K, V> newEntry = new ValueEntry<K, V>(key, value, hash, rowHead);
+ succeedsInValueSet(lastEntry, newEntry);
+ succeedsInValueSet(newEntry, this);
+ succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry);
+ succeedsInMultimap(newEntry, multimapHeaderEntry);
+ hashTable[row] = newEntry;
+ size++;
+ modCount++;
+ rehashIfNecessary();
+ return true;
+ }
+
+ private void rehashIfNecessary() {
+ if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) {
+ @SuppressWarnings("unchecked")
+ ValueEntry<K, V>[] hashTable = new ValueEntry[this.hashTable.length * 2];
+ this.hashTable = hashTable;
+ int mask = hashTable.length - 1;
+ for (ValueSetLink<K, V> entry = firstEntry;
+ entry != this; entry = entry.getSuccessorInValueSet()) {
+ ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
+ int row = Hashing.smear(valueEntry.valueHash) & mask;
+ valueEntry.nextInValueSetHashRow = hashTable[row];
+ hashTable[row] = valueEntry;
+ }
+ }
+ }
+
+ @Override
+ public boolean remove(@Nullable Object o) {
+ int hash = (o == null) ? 0 : o.hashCode();
+ int row = Hashing.smear(hash) & (hashTable.length - 1);
+
+ ValueEntry<K, V> prev = null;
+ for (ValueEntry<K, V> entry = hashTable[row]; entry != null;
+ prev = entry, entry = entry.nextInValueSetHashRow) {
+ if (hash == entry.valueHash && Objects.equal(o, entry.getValue())) {
+ if (prev == null) {
+ // first entry in the row
+ hashTable[row] = entry.nextInValueSetHashRow;
+ } else {
+ prev.nextInValueSetHashRow = entry.nextInValueSetHashRow;
+ }
+ deleteFromValueSet(entry);
+ deleteFromMultimap(entry);
+ size--;
+ modCount++;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void clear() {
+ Arrays.fill(hashTable, null);
+ size = 0;
+ for (ValueSetLink<K, V> entry = firstEntry;
+ entry != this; entry = entry.getSuccessorInValueSet()) {
+ ValueEntry<K, V> valueEntry = (ValueEntry<K, V>) entry;
+ deleteFromMultimap(valueEntry);
+ }
+ succeedsInValueSet(this, this);
+ modCount++;
+ }
+ }
+
+ @Override
+ Iterator<Map.Entry<K, V>> entryIterator() {
+ return new Iterator<Map.Entry<K, V>>() {
+ ValueEntry<K, V> nextEntry = multimapHeaderEntry.successorInMultimap;
+ ValueEntry<K, V> toRemove;
+
+ @Override
+ public boolean hasNext() {
+ return nextEntry != multimapHeaderEntry;
+ }
+
+ @Override
+ public Map.Entry<K, V> next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ValueEntry<K, V> result = nextEntry;
+ toRemove = result;
+ nextEntry = nextEntry.successorInMultimap;
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ Iterators.checkRemove(toRemove != null);
+ LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue());
+ toRemove = null;
+ }
+ };
+ }
+
+ @Override
+ public void clear() {
+ super.clear();
+ succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry);
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultiset.java
index 77157840d..da93ead83 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultiset.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedHashMultiset.java
@@ -27,6 +27,10 @@ import java.util.LinkedHashMap;
* element, those instances are consecutive in the iteration order. If all
* occurrences of an element are removed, after which that element is added to
* the multiset, the element will appear at the end of the iteration.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset">
+ * {@code Multiset}</a>.
*
* @author Kevin Bourrillion
* @author Jared Levy
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedListMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedListMultimap.java
index 22735354b..f51d292ea 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedListMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/LinkedListMultimap.java
@@ -17,9 +17,7 @@
package com.google.common.collect;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Multisets.setCountImpl;
import static java.util.Collections.unmodifiableList;
import com.google.common.annotations.GwtCompatible;
@@ -27,11 +25,10 @@ import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import java.io.Serializable;
-import java.util.AbstractCollection;
-import java.util.AbstractMap;
import java.util.AbstractSequentialList;
-import java.util.AbstractSet;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -91,6 +88,10 @@ import javax.annotation.Nullable;
* update operations, wrap your multimap with a call to {@link
* Multimaps#synchronizedListMultimap}.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
+ * {@code Multimap}</a>.
+ *
* @author Mike Bostock
* @since 2.0 (imported from Google Collections Library)
*/
@@ -121,12 +122,32 @@ public class LinkedListMultimap<K, V>
return key + "=" + value;
}
}
+
+ private static class KeyList<K, V> {
+ Node<K, V> head;
+ Node<K, V> tail;
+ int count;
+
+ KeyList(Node<K, V> firstNode) {
+ this.head = firstNode;
+ this.tail = firstNode;
+ firstNode.previousSibling = null;
+ firstNode.nextSibling = null;
+ this.count = 1;
+ }
+ }
private transient Node<K, V> head; // the head for all keys
private transient Node<K, V> tail; // the tail for all keys
- private transient Multiset<K> keyCount; // the number of values for each key
- private transient Map<K, Node<K, V>> keyToKeyHead; // the head for a given key
- private transient Map<K, Node<K, V>> keyToKeyTail; // the tail for a given key
+ private transient Map<K, KeyList<K, V>> keyToKeyList;
+ private transient int size;
+
+ /*
+ * Tracks modifications to keyToKeyList so that addition or removal of keys invalidates
+ * preexisting iterators. This does *not* track simple additions and removals of values
+ * that are not the first to be added or last to be removed for their key.
+ */
+ private transient int modCount;
/**
* Creates a new, empty {@code LinkedListMultimap} with the default initial
@@ -160,15 +181,11 @@ public class LinkedListMultimap<K, V>
}
LinkedListMultimap() {
- keyCount = LinkedHashMultiset.create();
- keyToKeyHead = Maps.newHashMap();
- keyToKeyTail = Maps.newHashMap();
+ keyToKeyList = Maps.newHashMap();
}
private LinkedListMultimap(int expectedKeys) {
- keyCount = LinkedHashMultiset.create(expectedKeys);
- keyToKeyHead = Maps.newHashMapWithExpectedSize(expectedKeys);
- keyToKeyTail = Maps.newHashMapWithExpectedSize(expectedKeys);
+ keyToKeyList = new HashMap<K, KeyList<K, V>>(expectedKeys);
}
private LinkedListMultimap(Multimap<? extends K, ? extends V> multimap) {
@@ -187,27 +204,32 @@ public class LinkedListMultimap<K, V>
Node<K, V> node = new Node<K, V>(key, value);
if (head == null) { // empty list
head = tail = node;
- keyToKeyHead.put(key, node);
- keyToKeyTail.put(key, node);
+ keyToKeyList.put(key, new KeyList<K, V>(node));
+ modCount++;
} else if (nextSibling == null) { // non-empty list, add to tail
tail.next = node;
node.previous = tail;
- Node<K, V> keyTail = keyToKeyTail.get(key);
- if (keyTail == null) { // first for this key
- keyToKeyHead.put(key, node);
+ tail = node;
+ KeyList<K, V> keyList = keyToKeyList.get(key);
+ if (keyList == null) {
+ keyToKeyList.put(key, keyList = new KeyList<K, V>(node));
+ modCount++;
} else {
+ keyList.count++;
+ Node<K, V> keyTail = keyList.tail;
keyTail.nextSibling = node;
node.previousSibling = keyTail;
+ keyList.tail = node;
}
- keyToKeyTail.put(key, node);
- tail = node;
} else { // non-empty list, insert before nextSibling
+ KeyList<K, V> keyList = keyToKeyList.get(key);
+ keyList.count++;
node.previous = nextSibling.previous;
node.previousSibling = nextSibling.previousSibling;
node.next = nextSibling;
node.nextSibling = nextSibling;
if (nextSibling.previousSibling == null) { // nextSibling was key head
- keyToKeyHead.put(key, node);
+ keyToKeyList.get(key).head = node;
} else {
nextSibling.previousSibling.nextSibling = node;
}
@@ -219,7 +241,7 @@ public class LinkedListMultimap<K, V>
nextSibling.previous = node;
nextSibling.previousSibling = node;
}
- keyCount.add(key);
+ size++;
return node;
}
@@ -239,21 +261,27 @@ public class LinkedListMultimap<K, V>
} else { // node was tail
tail = node.previous;
}
- if (node.previousSibling != null) {
- node.previousSibling.nextSibling = node.nextSibling;
- } else if (node.nextSibling != null) { // node was key head
- keyToKeyHead.put(node.key, node.nextSibling);
+ if (node.previousSibling == null && node.nextSibling == null) {
+ KeyList<K, V> keyList = keyToKeyList.remove(node.key);
+ keyList.count = 0;
+ modCount++;
} else {
- keyToKeyHead.remove(node.key); // don't leak a key-null entry
- }
- if (node.nextSibling != null) {
- node.nextSibling.previousSibling = node.previousSibling;
- } else if (node.previousSibling != null) { // node was key tail
- keyToKeyTail.put(node.key, node.previousSibling);
- } else {
- keyToKeyTail.remove(node.key); // don't leak a key-null entry
+ KeyList<K, V> keyList = keyToKeyList.get(node.key);
+ keyList.count--;
+
+ if (node.previousSibling == null) {
+ keyList.head = node.nextSibling;
+ } else {
+ node.previousSibling.nextSibling = node.nextSibling;
+ }
+
+ if (node.nextSibling == null) {
+ keyList.tail = node.previousSibling;
+ } else {
+ node.nextSibling.previousSibling = node.previousSibling;
+ }
}
- keyCount.remove(node.key);
+ size--;
}
/** Removes all nodes for the specified key. */
@@ -277,6 +305,7 @@ public class LinkedListMultimap<K, V>
Node<K, V> next;
Node<K, V> current;
Node<K, V> previous;
+ int expectedModCount = modCount;
NodeIterator() {
next = head;
@@ -298,12 +327,19 @@ public class LinkedListMultimap<K, V>
}
current = null;
}
+ private void checkForConcurrentModification() {
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
@Override
public boolean hasNext() {
+ checkForConcurrentModification();
return next != null;
}
@Override
public Node<K, V> next() {
+ checkForConcurrentModification();
checkElement(next);
previous = current = next;
next = next.next;
@@ -312,6 +348,7 @@ public class LinkedListMultimap<K, V>
}
@Override
public void remove() {
+ checkForConcurrentModification();
checkState(current != null);
if (current != next) { // after call to next()
previous = current.previous;
@@ -321,13 +358,16 @@ public class LinkedListMultimap<K, V>
}
removeNode(current);
current = null;
+ expectedModCount = modCount;
}
@Override
public boolean hasPrevious() {
+ checkForConcurrentModification();
return previous != null;
}
@Override
public Node<K, V> previous() {
+ checkForConcurrentModification();
checkElement(previous);
next = current = previous;
previous = previous.previous;
@@ -361,13 +401,21 @@ public class LinkedListMultimap<K, V>
final Set<K> seenKeys = Sets.<K>newHashSetWithExpectedSize(keySet().size());
Node<K, V> next = head;
Node<K, V> current;
-
+ int expectedModCount = modCount;
+
+ private void checkForConcurrentModification() {
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
@Override
public boolean hasNext() {
+ checkForConcurrentModification();
return next != null;
}
@Override
public K next() {
+ checkForConcurrentModification();
checkElement(next);
current = next;
seenKeys.add(current.key);
@@ -378,9 +426,11 @@ public class LinkedListMultimap<K, V>
}
@Override
public void remove() {
+ checkForConcurrentModification();
checkState(current != null);
removeAllNodes(current.key);
current = null;
+ expectedModCount = modCount;
}
}
@@ -395,7 +445,8 @@ public class LinkedListMultimap<K, V>
/** Constructs a new iterator over all values for the specified key. */
ValueForKeyIterator(@Nullable Object key) {
this.key = key;
- next = keyToKeyHead.get(key);
+ KeyList<K, V> keyList = keyToKeyList.get(key);
+ next = (keyList == null) ? null : keyList.head;
}
/**
@@ -408,16 +459,17 @@ public class LinkedListMultimap<K, V>
* @throws IndexOutOfBoundsException if index is invalid
*/
public ValueForKeyIterator(@Nullable Object key, int index) {
- int size = keyCount.count(key);
+ KeyList<K, V> keyList = keyToKeyList.get(key);
+ int size = (keyList == null) ? 0 : keyList.count;
Preconditions.checkPositionIndex(index, size);
if (index >= (size / 2)) {
- previous = keyToKeyTail.get(key);
+ previous = (keyList == null) ? null : keyList.tail;
nextIndex = size;
while (index++ < size) {
previous();
}
} else {
- next = keyToKeyHead.get(key);
+ next = (keyList == null) ? null : keyList.head;
while (index-- > 0) {
next();
}
@@ -496,7 +548,7 @@ public class LinkedListMultimap<K, V>
@Override
public int size() {
- return keyCount.size();
+ return size;
}
@Override
@@ -506,7 +558,7 @@ public class LinkedListMultimap<K, V>
@Override
public boolean containsKey(@Nullable Object key) {
- return keyToKeyHead.containsKey(key);
+ return keyToKeyList.containsKey(key);
}
@Override
@@ -633,9 +685,9 @@ public class LinkedListMultimap<K, V>
public void clear() {
head = null;
tail = null;
- keyCount.clear();
- keyToKeyHead.clear();
- keyToKeyTail.clear();
+ keyToKeyList.clear();
+ size = 0;
+ modCount++;
}
// Views
@@ -653,7 +705,8 @@ public class LinkedListMultimap<K, V>
public List<V> get(final @Nullable K key) {
return new AbstractSequentialList<V>() {
@Override public int size() {
- return keyCount.count(key);
+ KeyList<K, V> keyList = keyToKeyList.get(key);
+ return (keyList == null) ? 0 : keyList.count;
}
@Override public ListIterator<V> listIterator(int index) {
return new ValueForKeyIterator(key, index);
@@ -673,19 +726,19 @@ public class LinkedListMultimap<K, V>
public Set<K> keySet() {
Set<K> result = keySet;
if (result == null) {
- keySet = result = new AbstractSet<K>() {
+ keySet = result = new Sets.ImprovedAbstractSet<K>() {
@Override public int size() {
- return keyCount.elementSet().size();
+ return keyToKeyList.size();
}
@Override public Iterator<K> iterator() {
return new DistinctKeyIterator();
}
@Override public boolean contains(Object key) { // for performance
- return keyCount.contains(key);
+ return containsKey(key);
}
- @Override public boolean removeAll(Collection<?> c) {
- checkNotNull(c); // eager for GWT
- return super.removeAll(c);
+ @Override
+ public boolean remove(Object o) { // for performance
+ return !LinkedListMultimap.this.removeAll(o).isEmpty();
}
};
}
@@ -703,39 +756,50 @@ public class LinkedListMultimap<K, V>
return result;
}
- private class MultisetView extends AbstractCollection<K>
- implements Multiset<K> {
+ private class MultisetView extends AbstractMultiset<K> {
+ @Override
+ public int size() {
+ return size;
+ }
- @Override public int size() {
- return keyCount.size();
+ @Override
+ public int count(Object element) {
+ KeyList<K, V> keyList = keyToKeyList.get(element);
+ return (keyList == null) ? 0 : keyList.count;
}
- @Override public Iterator<K> iterator() {
- final Iterator<Node<K, V>> nodes = new NodeIterator();
- return new Iterator<K>() {
- @Override
- public boolean hasNext() {
- return nodes.hasNext();
- }
- @Override
- public K next() {
- return nodes.next().key;
- }
+ @Override
+ Iterator<Entry<K>> entryIterator() {
+ return new TransformedIterator<K, Entry<K>>(new DistinctKeyIterator()) {
@Override
- public void remove() {
- nodes.remove();
+ Entry<K> transform(final K key) {
+ return new Multisets.AbstractEntry<K>() {
+ @Override
+ public K getElement() {
+ return key;
+ }
+
+ @Override
+ public int getCount() {
+ return keyToKeyList.get(key).count;
+ }
+ };
}
};
}
@Override
- public int count(@Nullable Object key) {
- return keyCount.count(key);
+ int distinctElements() {
+ return elementSet().size();
}
- @Override
- public int add(@Nullable K key, int occurrences) {
- throw new UnsupportedOperationException();
+ @Override public Iterator<K> iterator() {
+ return new TransformedIterator<Node<K, V>, K>(new NodeIterator()) {
+ @Override
+ K transform(Node<K, V> node) {
+ return node.key;
+ }
+ };
}
@Override
@@ -751,77 +815,9 @@ public class LinkedListMultimap<K, V>
}
@Override
- public int setCount(K element, int count) {
- return setCountImpl(this, element, count);
- }
-
- @Override
- public boolean setCount(K element, int oldCount, int newCount) {
- return setCountImpl(this, element, oldCount, newCount);
- }
-
- @Override public boolean removeAll(Collection<?> c) {
- return Iterators.removeAll(iterator(), c);
- }
-
- @Override public boolean retainAll(Collection<?> c) {
- return Iterators.retainAll(iterator(), c);
- }
-
- @Override
public Set<K> elementSet() {
return keySet();
}
-
- @Override
- public Set<Entry<K>> entrySet() {
- // TODO(jlevy): lazy init?
- return new AbstractSet<Entry<K>>() {
- @Override public int size() {
- return keyCount.elementSet().size();
- }
-
- @Override public Iterator<Entry<K>> iterator() {
- final Iterator<K> keyIterator = new DistinctKeyIterator();
- return new Iterator<Entry<K>>() {
- @Override
- public boolean hasNext() {
- return keyIterator.hasNext();
- }
- @Override
- public Entry<K> next() {
- final K key = keyIterator.next();
- return new Multisets.AbstractEntry<K>() {
- @Override
- public K getElement() {
- return key;
- }
- @Override
- public int getCount() {
- return keyCount.count(key);
- }
- };
- }
- @Override
- public void remove() {
- keyIterator.remove();
- }
- };
- }
- };
- }
-
- @Override public boolean equals(@Nullable Object object) {
- return keyCount.equals(object);
- }
-
- @Override public int hashCode() {
- return keyCount.hashCode();
- }
-
- @Override public String toString() {
- return keyCount.toString(); // XXX observe order?
- }
}
private transient List<V> valuesList;
@@ -841,47 +837,20 @@ public class LinkedListMultimap<K, V>
if (result == null) {
valuesList = result = new AbstractSequentialList<V>() {
@Override public int size() {
- return keyCount.size();
+ return size;
}
@Override
public ListIterator<V> listIterator(int index) {
final NodeIterator nodes = new NodeIterator(index);
- return new ListIterator<V>() {
- @Override
- public boolean hasNext() {
- return nodes.hasNext();
- }
+ return new TransformedListIterator<Node<K, V>, V>(nodes) {
@Override
- public V next() {
- return nodes.next().value;
- }
- @Override
- public boolean hasPrevious() {
- return nodes.hasPrevious();
- }
- @Override
- public V previous() {
- return nodes.previous().value;
- }
- @Override
- public int nextIndex() {
- return nodes.nextIndex();
- }
- @Override
- public int previousIndex() {
- return nodes.previousIndex();
- }
- @Override
- public void remove() {
- nodes.remove();
- }
- @Override
- public void set(V e) {
- nodes.setValue(e);
+ V transform(Node<K, V> node) {
+ return node.value;
}
+
@Override
- public void add(V e) {
- throw new UnsupportedOperationException();
+ public void set(V value) {
+ nodes.setValue(value);
}
};
}
@@ -932,55 +901,14 @@ public class LinkedListMultimap<K, V>
if (result == null) {
entries = result = new AbstractSequentialList<Entry<K, V>>() {
@Override public int size() {
- return keyCount.size();
+ return size;
}
@Override public ListIterator<Entry<K, V>> listIterator(int index) {
- final ListIterator<Node<K, V>> nodes = new NodeIterator(index);
- return new ListIterator<Entry<K, V>>() {
- @Override
- public boolean hasNext() {
- return nodes.hasNext();
- }
-
- @Override
- public Entry<K, V> next() {
- return createEntry(nodes.next());
- }
-
- @Override
- public void remove() {
- nodes.remove();
- }
-
- @Override
- public boolean hasPrevious() {
- return nodes.hasPrevious();
- }
-
- @Override
- public Map.Entry<K, V> previous() {
- return createEntry(nodes.previous());
- }
-
- @Override
- public int nextIndex() {
- return nodes.nextIndex();
- }
-
- @Override
- public int previousIndex() {
- return nodes.previousIndex();
- }
-
- @Override
- public void set(Map.Entry<K, V> e) {
- throw new UnsupportedOperationException();
- }
-
+ return new TransformedListIterator<Node<K, V>, Entry<K, V>>(new NodeIterator(index)) {
@Override
- public void add(Map.Entry<K, V> e) {
- throw new UnsupportedOperationException();
+ Entry<K, V> transform(Node<K, V> node) {
+ return createEntry(node);
}
};
}
@@ -989,75 +917,39 @@ public class LinkedListMultimap<K, V>
return result;
}
- private class AsMapEntries extends AbstractSet<Entry<K, Collection<V>>> {
- @Override public int size() {
- return keyCount.elementSet().size();
- }
-
- @Override public Iterator<Entry<K, Collection<V>>> iterator() {
- final Iterator<K> keyIterator = new DistinctKeyIterator();
- return new Iterator<Entry<K, Collection<V>>>() {
- @Override
- public boolean hasNext() {
- return keyIterator.hasNext();
- }
-
- @Override
- public Entry<K, Collection<V>> next() {
- final K key = keyIterator.next();
- return new AbstractMapEntry<K, Collection<V>>() {
- @Override public K getKey() {
- return key;
- }
-
- @Override public Collection<V> getValue() {
- return LinkedListMultimap.this.get(key);
- }
- };
- }
-
- @Override
- public void remove() {
- keyIterator.remove();
- }
- };
- }
-
- // TODO(jlevy): Override contains() and remove() for better performance.
- }
-
private transient Map<K, Collection<V>> map;
@Override
public Map<K, Collection<V>> asMap() {
Map<K, Collection<V>> result = map;
if (result == null) {
- map = result = new AbstractMap<K, Collection<V>>() {
- Set<Entry<K, Collection<V>>> entrySet;
-
- @Override public Set<Entry<K, Collection<V>>> entrySet() {
- Set<Entry<K, Collection<V>>> result = entrySet;
- if (result == null) {
- entrySet = result = new AsMapEntries();
- }
- return result;
+ map = result = new Multimaps.AsMap<K, V>() {
+ @Override
+ public int size() {
+ return keyToKeyList.size();
}
- // The following methods are included for performance.
-
- @Override public boolean containsKey(@Nullable Object key) {
- return LinkedListMultimap.this.containsKey(key);
+ @Override
+ Multimap<K, V> multimap() {
+ return LinkedListMultimap.this;
}
- @SuppressWarnings("unchecked")
- @Override public Collection<V> get(@Nullable Object key) {
- Collection<V> collection = LinkedListMultimap.this.get((K) key);
- return collection.isEmpty() ? null : collection;
- }
+ @Override
+ Iterator<Entry<K, Collection<V>>> entryIterator() {
+ return new TransformedIterator<K, Entry<K, Collection<V>>>(new DistinctKeyIterator()) {
+ @Override
+ Entry<K, Collection<V>> transform(final K key) {
+ return new AbstractMapEntry<K, Collection<V>>() {
+ @Override public K getKey() {
+ return key;
+ }
- @Override public Collection<V> remove(@Nullable Object key) {
- Collection<V> collection = removeAll(key);
- return collection.isEmpty() ? null : collection;
+ @Override public Collection<V> getValue() {
+ return LinkedListMultimap.this.get(key);
+ }
+ };
+ }
+ };
}
};
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Lists.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Lists.java
new file mode 100644
index 000000000..0bdb38a73
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Lists.java
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (C) 2007 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkElementIndex;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkPositionIndex;
+import static com.google.common.base.Preconditions.checkPositionIndexes;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.primitives.Ints;
+
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.AbstractSequentialList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.RandomAccess;
+
+import javax.annotation.Nullable;
+
+/**
+ * Static utility methods pertaining to {@link List} instances. Also see this
+ * class's counterparts {@link Sets}, {@link Maps} and {@link Queues}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Lists">
+ * {@code Lists}</a>.
+ *
+ * @author Kevin Bourrillion
+ * @author Mike Bostock
+ * @author Louis Wasserman
+ * @since 2.0 (imported from Google Collections Library)
+ */
+@GwtCompatible(emulated = true)
+public final class Lists {
+ private Lists() {}
+
+ // ArrayList
+
+ /**
+ * Creates a <i>mutable</i>, empty {@code ArrayList} instance.
+ *
+ * <p><b>Note:</b> if mutability is not required, use {@link
+ * ImmutableList#of()} instead.
+ *
+ * @return a new, empty {@code ArrayList}
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> ArrayList<E> newArrayList() {
+ return new ArrayList<E>();
+ }
+
+ /**
+ * Creates a <i>mutable</i> {@code ArrayList} instance containing the given
+ * elements.
+ *
+ * <p><b>Note:</b> if mutability is not required and the elements are
+ * non-null, use an overload of {@link ImmutableList#of()} (for varargs) or
+ * {@link ImmutableList#copyOf(Object[])} (for an array) instead.
+ *
+ * @param elements the elements that the list should contain, in order
+ * @return a new {@code ArrayList} containing those elements
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> ArrayList<E> newArrayList(E... elements) {
+ checkNotNull(elements); // for GWT
+ // Avoid integer overflow when a large array is passed in
+ int capacity = computeArrayListCapacity(elements.length);
+ ArrayList<E> list = new ArrayList<E>(capacity);
+ Collections.addAll(list, elements);
+ return list;
+ }
+
+ @VisibleForTesting static int computeArrayListCapacity(int arraySize) {
+ checkArgument(arraySize >= 0);
+
+ // TODO(kevinb): Figure out the right behavior, and document it
+ return Ints.saturatedCast(5L + arraySize + (arraySize / 10));
+ }
+
+ /**
+ * Creates a <i>mutable</i> {@code ArrayList} instance containing the given
+ * elements.
+ *
+ * <p><b>Note:</b> if mutability is not required and the elements are
+ * non-null, use {@link ImmutableList#copyOf(Iterator)} instead.
+ *
+ * @param elements the elements that the list should contain, in order
+ * @return a new {@code ArrayList} containing those elements
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> ArrayList<E> newArrayList(Iterable<? extends E> elements) {
+ checkNotNull(elements); // for GWT
+ // Let ArrayList's sizing logic work, if possible
+ return (elements instanceof Collection)
+ ? new ArrayList<E>(Collections2.cast(elements))
+ : newArrayList(elements.iterator());
+ }
+
+ /**
+ * Creates a <i>mutable</i> {@code ArrayList} instance containing the given
+ * elements.
+ *
+ * <p><b>Note:</b> if mutability is not required and the elements are
+ * non-null, use {@link ImmutableList#copyOf(Iterator)} instead.
+ *
+ * @param elements the elements that the list should contain, in order
+ * @return a new {@code ArrayList} containing those elements
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> ArrayList<E> newArrayList(Iterator<? extends E> elements) {
+ checkNotNull(elements); // for GWT
+ ArrayList<E> list = newArrayList();
+ while (elements.hasNext()) {
+ list.add(elements.next());
+ }
+ return list;
+ }
+
+ /**
+ * Creates an {@code ArrayList} instance backed by an array of the
+ * <i>exact</i> size specified; equivalent to
+ * {@link ArrayList#ArrayList(int)}.
+ *
+ * <p><b>Note:</b> if you know the exact size your list will be, consider
+ * using a fixed-size list ({@link Arrays#asList(Object[])}) or an {@link
+ * ImmutableList} instead of a growable {@link ArrayList}.
+ *
+ * <p><b>Note:</b> If you have only an <i>estimate</i> of the eventual size of
+ * the list, consider padding this estimate by a suitable amount, or simply
+ * use {@link #newArrayListWithExpectedSize(int)} instead.
+ *
+ * @param initialArraySize the exact size of the initial backing array for
+ * the returned array list ({@code ArrayList} documentation calls this
+ * value the "capacity")
+ * @return a new, empty {@code ArrayList} which is guaranteed not to resize
+ * itself unless its size reaches {@code initialArraySize + 1}
+ * @throws IllegalArgumentException if {@code initialArraySize} is negative
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> ArrayList<E> newArrayListWithCapacity(
+ int initialArraySize) {
+ checkArgument(initialArraySize >= 0); // for GWT.
+ return new ArrayList<E>(initialArraySize);
+ }
+
+ /**
+ * Creates an {@code ArrayList} instance sized appropriately to hold an
+ * <i>estimated</i> number of elements without resizing. A small amount of
+ * padding is added in case the estimate is low.
+ *
+ * <p><b>Note:</b> If you know the <i>exact</i> number of elements the list
+ * will hold, or prefer to calculate your own amount of padding, refer to
+ * {@link #newArrayListWithCapacity(int)}.
+ *
+ * @param estimatedSize an estimate of the eventual {@link List#size()} of
+ * the new list
+ * @return a new, empty {@code ArrayList}, sized appropriately to hold the
+ * estimated number of elements
+ * @throws IllegalArgumentException if {@code estimatedSize} is negative
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> ArrayList<E> newArrayListWithExpectedSize(
+ int estimatedSize) {
+ return new ArrayList<E>(computeArrayListCapacity(estimatedSize));
+ }
+
+ // LinkedList
+
+ /**
+ * Creates an empty {@code LinkedList} instance.
+ *
+ * <p><b>Note:</b> if you need an immutable empty {@link List}, use
+ * {@link ImmutableList#of()} instead.
+ *
+ * @return a new, empty {@code LinkedList}
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> LinkedList<E> newLinkedList() {
+ return new LinkedList<E>();
+ }
+
+ /**
+ * Creates a {@code LinkedList} instance containing the given elements.
+ *
+ * @param elements the elements that the list should contain, in order
+ * @return a new {@code LinkedList} containing those elements
+ */
+ @GwtCompatible(serializable = true)
+ public static <E> LinkedList<E> newLinkedList(
+ Iterable<? extends E> elements) {
+ LinkedList<E> list = newLinkedList();
+ for (E element : elements) {
+ list.add(element);
+ }
+ return list;
+ }
+
+ /**
+ * Returns an unmodifiable list containing the specified first element and
+ * backed by the specified array of additional elements. Changes to the {@code
+ * rest} array will be reflected in the returned list. Unlike {@link
+ * Arrays#asList}, the returned list is unmodifiable.
+ *
+ * <p>This is useful when a varargs method needs to use a signature such as
+ * {@code (Foo firstFoo, Foo... moreFoos)}, in order to avoid overload
+ * ambiguity or to enforce a minimum argument count.
+ *
+ * <p>The returned list is serializable and implements {@link RandomAccess}.
+ *
+ * @param first the first element
+ * @param rest an array of additional elements, possibly empty
+ * @return an unmodifiable list containing the specified elements
+ */
+ public static <E> List<E> asList(@Nullable E first, E[] rest) {
+ return new OnePlusArrayList<E>(first, rest);
+ }
+
+ /** @see Lists#asList(Object, Object[]) */
+ private static class OnePlusArrayList<E> extends AbstractList<E>
+ implements Serializable, RandomAccess {
+ final E first;
+ final E[] rest;
+
+ OnePlusArrayList(@Nullable E first, E[] rest) {
+ this.first = first;
+ this.rest = checkNotNull(rest);
+ }
+ @Override public int size() {
+ return rest.length + 1;
+ }
+ @Override public E get(int index) {
+ // check explicitly so the IOOBE will have the right message
+ checkElementIndex(index, size());
+ return (index == 0) ? first : rest[index - 1];
+ }
+ private static final long serialVersionUID = 0;
+ }
+
+ /**
+ * Returns an unmodifiable list containing the specified first and second
+ * element, and backed by the specified array of additional elements. Changes
+ * to the {@code rest} array will be reflected in the returned list. Unlike
+ * {@link Arrays#asList}, the returned list is unmodifiable.
+ *
+ * <p>This is useful when a varargs method needs to use a signature such as
+ * {@code (Foo firstFoo, Foo secondFoo, Foo... moreFoos)}, in order to avoid
+ * overload ambiguity or to enforce a minimum argument count.
+ *
+ * <p>The returned list is serializable and implements {@link RandomAccess}.
+ *
+ * @param first the first element
+ * @param second the second element
+ * @param rest an array of additional elements, possibly empty
+ * @return an unmodifiable list containing the specified elements
+ */
+ public static <E> List<E> asList(
+ @Nullable E first, @Nullable E second, E[] rest) {
+ return new TwoPlusArrayList<E>(first, second, rest);
+ }
+
+ /** @see Lists#asList(Object, Object, Object[]) */
+ private static class TwoPlusArrayList<E> extends AbstractList<E>
+ implements Serializable, RandomAccess {
+ final E first;
+ final E second;
+ final E[] rest;
+
+ TwoPlusArrayList(@Nullable E first, @Nullable E second, E[] rest) {
+ this.first = first;
+ this.second = second;
+ this.rest = checkNotNull(rest);
+ }
+ @Override public int size() {
+ return rest.length + 2;
+ }
+ @Override public E get(int index) {
+ switch (index) {
+ case 0:
+ return first;
+ case 1:
+ return second;
+ default:
+ // check explicitly so the IOOBE will have the right message
+ checkElementIndex(index, size());
+ return rest[index - 2];
+ }
+ }
+ private static final long serialVersionUID = 0;
+ }
+
+ /**
+ * Returns every possible list that can be formed by choosing one element
+ * from each of the given lists in order; the "n-ary
+ * <a href="http://en.wikipedia.org/wiki/Cartesian_product">Cartesian
+ * product</a>" of the lists. For example: <pre> {@code
+ *
+ * Lists.cartesianProduct(ImmutableList.of(
+ * ImmutableList.of(1, 2),
+ * ImmutableList.of("A", "B", "C")))}</pre>
+ *
+ * returns a list containing six lists in the following order:
+ *
+ * <ul>
+ * <li>{@code ImmutableList.of(1, "A")}
+ * <li>{@code ImmutableList.of(1, "B")}
+ * <li>{@code ImmutableList.of(1, "C")}
+ * <li>{@code ImmutableList.of(2, "A")}
+ * <li>{@code ImmutableList.of(2, "B")}
+ * <li>{@code ImmutableList.of(2, "C")}
+ * </ul>
+ *
+ * The result is guaranteed to be in the "traditional", lexicographical
+ * order for Cartesian products that you would get from nesting for loops:
+ * <pre> {@code
+ *
+ * for (B b0 : lists.get(0)) {
+ * for (B b1 : lists.get(1)) {
+ * ...
+ * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...);
+ * // operate on tuple
+ * }
+ * }}</pre>
+ *
+ * Note that if any input list is empty, the Cartesian product will also be
+ * empty. If no lists at all are provided (an empty list), the resulting
+ * Cartesian product has one element, an empty list (counter-intuitive, but
+ * mathematically consistent).
+ *
+ * <p><i>Performance notes:</i> while the cartesian product of lists of size
+ * {@code m, n, p} is a list of size {@code m x n x p}, its actual memory
+ * consumption is much smaller. When the cartesian product is constructed, the
+ * input lists are merely copied. Only as the resulting list is iterated are
+ * the individual lists created, and these are not retained after iteration.
+ *
+ * @param lists the lists to choose elements from, in the order that
+ * the elements chosen from those lists should appear in the resulting
+ * lists
+ * @param <B> any common base class shared by all axes (often just {@link
+ * Object})
+ * @return the Cartesian product, as an immutable list containing immutable
+ * lists
+ * @throws IllegalArgumentException if the size of the cartesian product would
+ * be greater than {@link Integer#MAX_VALUE}
+ * @throws NullPointerException if {@code lists}, any one of the {@code lists},
+ * or any element of a provided list is null
+ */
+ static <B> List<List<B>> cartesianProduct(
+ List<? extends List<? extends B>> lists) {
+ return CartesianList.create(lists);
+ }
+
+ /**
+ * Returns every possible list that can be formed by choosing one element
+ * from each of the given lists in order; the "n-ary
+ * <a href="http://en.wikipedia.org/wiki/Cartesian_product">Cartesian
+ * product</a>" of the lists. For example: <pre> {@code
+ *
+ * Lists.cartesianProduct(ImmutableList.of(
+ * ImmutableList.of(1, 2),
+ * ImmutableList.of("A", "B", "C")))}</pre>
+ *
+ * returns a list containing six lists in the following order:
+ *
+ * <ul>
+ * <li>{@code ImmutableList.of(1, "A")}
+ * <li>{@code ImmutableList.of(1, "B")}
+ * <li>{@code ImmutableList.of(1, "C")}
+ * <li>{@code ImmutableList.of(2, "A")}
+ * <li>{@code ImmutableList.of(2, "B")}
+ * <li>{@code ImmutableList.of(2, "C")}
+ * </ul>
+ *
+ * The result is guaranteed to be in the "traditional", lexicographical
+ * order for Cartesian products that you would get from nesting for loops:
+ * <pre> {@code
+ *
+ * for (B b0 : lists.get(0)) {
+ * for (B b1 : lists.get(1)) {
+ * ...
+ * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...);
+ * // operate on tuple
+ * }
+ * }}</pre>
+ *
+ * Note that if any input list is empty, the Cartesian product will also be
+ * empty. If no lists at all are provided (an empty list), the resulting
+ * Cartesian product has one element, an empty list (counter-intuitive, but
+ * mathematically consistent).
+ *
+ * <p><i>Performance notes:</i> while the cartesian product of lists of size
+ * {@code m, n, p} is a list of size {@code m x n x p}, its actual memory
+ * consumption is much smaller. When the cartesian product is constructed, the
+ * input lists are merely copied. Only as the resulting list is iterated are
+ * the individual lists created, and these are not retained after iteration.
+ *
+ * @param lists the lists to choose elements from, in the order that
+ * the elements chosen from those lists should appear in the resulting
+ * lists
+ * @param <B> any common base class shared by all axes (often just {@link
+ * Object})
+ * @return the Cartesian product, as an immutable list containing immutable
+ * lists
+ * @throws IllegalArgumentException if the size of the cartesian product would
+ * be greater than {@link Integer#MAX_VALUE}
+ * @throws NullPointerException if {@code lists}, any one of the
+ * {@code lists}, or any element of a provided list is null
+ */
+ static <B> List<List<B>> cartesianProduct(List<? extends B>... lists) {
+ return cartesianProduct(Arrays.asList(lists));
+ }
+
+ /**
+ * Returns a list that applies {@code function} to each element of {@code
+ * fromList}. The returned list is a transformed view of {@code fromList};
+ * changes to {@code fromList} will be reflected in the returned list and vice
+ * versa.
+ *
+ * <p>Since functions are not reversible, the transform is one-way and new
+ * items cannot be stored in the returned list. The {@code add},
+ * {@code addAll} and {@code set} methods are unsupported in the returned
+ * list.
+ *
+ * <p>The function is applied lazily, invoked when needed. This is necessary
+ * for the returned list to be a view, but it means that the function will be
+ * applied many times for bulk operations like {@link List#contains} and
+ * {@link List#hashCode}. For this to perform well, {@code function} should be
+ * fast. To avoid lazy evaluation when the returned list doesn't need to be a
+ * view, copy the returned list into a new list of your choosing.
+ *
+ * <p>If {@code fromList} implements {@link RandomAccess}, so will the
+ * returned list. The returned list is threadsafe if the supplied list and
+ * function are.
+ *
+ * <p>If only a {@code Collection} or {@code Iterable} input is available, use
+ * {@link Collections2#transform} or {@link Iterables#transform}.
+ *
+ * <p><b>Note:</b> serializing the returned list is implemented by serializing
+ * {@code fromList}, its contents, and {@code function} -- <i>not</i> by
+ * serializing the transformed values. This can lead to surprising behavior,
+ * so serializing the returned list is <b>not recommended</b>. Instead,
+ * copy the list using {@link ImmutableList#copyOf(Collection)} (for example),
+ * then serialize the copy. Other methods similar to this do not implement
+ * serialization at all for this reason.
+ */
+ public static <F, T> List<T> transform(
+ List<F> fromList, Function<? super F, ? extends T> function) {
+ return (fromList instanceof RandomAccess)
+ ? new TransformingRandomAccessList<F, T>(fromList, function)
+ : new TransformingSequentialList<F, T>(fromList, function);
+ }
+
+ /**
+ * Implementation of a sequential transforming list.
+ *
+ * @see Lists#transform
+ */
+ private static class TransformingSequentialList<F, T>
+ extends AbstractSequentialList<T> implements Serializable {
+ final List<F> fromList;
+ final Function<? super F, ? extends T> function;
+
+ TransformingSequentialList(
+ List<F> fromList, Function<? super F, ? extends T> function) {
+ this.fromList = checkNotNull(fromList);
+ this.function = checkNotNull(function);
+ }
+ /**
+ * The default implementation inherited is based on iteration and removal of
+ * each element which can be overkill. That's why we forward this call
+ * directly to the backing list.
+ */
+ @Override public void clear() {
+ fromList.clear();
+ }
+ @Override public int size() {
+ return fromList.size();
+ }
+ @Override public ListIterator<T> listIterator(final int index) {
+ return new TransformedListIterator<F, T>(fromList.listIterator(index)) {
+ @Override
+ T transform(F from) {
+ return function.apply(from);
+ }
+ };
+ }
+
+ private static final long serialVersionUID = 0;
+ }
+
+ /**
+ * Implementation of a transforming random access list. We try to make as many
+ * of these methods pass-through to the source list as possible so that the
+ * performance characteristics of the source list and transformed list are
+ * similar.
+ *
+ * @see Lists#transform
+ */
+ private static class TransformingRandomAccessList<F, T>
+ extends AbstractList<T> implements RandomAccess, Serializable {
+ final List<F> fromList;
+ final Function<? super F, ? extends T> function;
+
+ TransformingRandomAccessList(
+ List<F> fromList, Function<? super F, ? extends T> function) {
+ this.fromList = checkNotNull(fromList);
+ this.function = checkNotNull(function);
+ }
+ @Override public void clear() {
+ fromList.clear();
+ }
+ @Override public T get(int index) {
+ return function.apply(fromList.get(index));
+ }
+ @Override public boolean isEmpty() {
+ return fromList.isEmpty();
+ }
+ @Override public T remove(int index) {
+ return function.apply(fromList.remove(index));
+ }
+ @Override public int size() {
+ return fromList.size();
+ }
+ private static final long serialVersionUID = 0;
+ }
+
+ /**
+ * Returns consecutive {@linkplain List#subList(int, int) sublists} of a list,
+ * each of the same size (the final list may be smaller). For example,
+ * partitioning a list containing {@code [a, b, c, d, e]} with a partition
+ * size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer list containing
+ * two inner lists of three and two elements, all in the original order.
+ *
+ * <p>The outer list is unmodifiable, but reflects the latest state of the
+ * source list. The inner lists are sublist views of the original list,
+ * produced on demand using {@link List#subList(int, int)}, and are subject
+ * to all the usual caveats about modification as explained in that API.
+ *
+ * @param list the list to return consecutive sublists of
+ * @param size the desired size of each sublist (the last may be
+ * smaller)
+ * @return a list of consecutive sublists
+ * @throws IllegalArgumentException if {@code partitionSize} is nonpositive
+ */
+ public static <T> List<List<T>> partition(List<T> list, int size) {
+ checkNotNull(list);
+ checkArgument(size > 0);
+ return (list instanceof RandomAccess)
+ ? new RandomAccessPartition<T>(list, size)
+ : new Partition<T>(list, size);
+ }
+
+ private static class Partition<T> extends AbstractList<List<T>> {
+ final List<T> list;
+ final int size;
+
+ Partition(List<T> list, int size) {
+ this.list = list;
+ this.size = size;
+ }
+
+ @Override public List<T> get(int index) {
+ int listSize = size();
+ checkElementIndex(index, listSize);
+ int start = index * size;
+ int end = Math.min(start + size, list.size());
+ return list.subList(start, end);
+ }
+
+ @Override public int size() {
+ // TODO(user): refactor to common.math.IntMath.divide
+ int result = list.size() / size;
+ if (result * size != list.size()) {
+ result++;
+ }
+ return result;
+ }
+
+ @Override public boolean isEmpty() {
+ return list.isEmpty();
+ }
+ }
+
+ private static class RandomAccessPartition<T> extends Partition<T>
+ implements RandomAccess {
+ RandomAccessPartition(List<T> list, int size) {
+ super(list, size);
+ }
+ }
+
+ /**
+ * Returns a view of the specified string as an immutable list of {@code
+ * Character} values.
+ *
+ * @since 7.0
+ */
+ @Beta public static ImmutableList<Character> charactersOf(String string) {
+ return new StringAsImmutableList(checkNotNull(string));
+ }
+
+ @SuppressWarnings("serial") // serialized using ImmutableList serialization
+ private static final class StringAsImmutableList
+ extends ImmutableList<Character> {
+
+ private final String string;
+
+ StringAsImmutableList(String string) {
+ this.string = string;
+ }
+
+ @Override public int indexOf(@Nullable Object object) {
+ return (object instanceof Character)
+ ? string.indexOf((Character) object) : -1;
+ }
+
+ @Override public int lastIndexOf(@Nullable Object object) {
+ return (object instanceof Character)
+ ? string.lastIndexOf((Character) object) : -1;
+ }
+
+ @Override public ImmutableList<Character> subList(
+ int fromIndex, int toIndex) {
+ checkPositionIndexes(fromIndex, toIndex, size()); // for GWT
+ return charactersOf(string.substring(fromIndex, toIndex));
+ }
+
+ @Override boolean isPartialView() {
+ return false;
+ }
+
+ @Override public Character get(int index) {
+ checkElementIndex(index, size()); // for GWT
+ return string.charAt(index);
+ }
+
+ @Override public int size() {
+ return string.length();
+ }
+
+ @Override public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof List)) {
+ return false;
+ }
+ List<?> list = (List<?>) obj;
+ int n = string.length();
+ if (n != list.size()) {
+ return false;
+ }
+ Iterator<?> iterator = list.iterator();
+ for (int i = 0; i < n; i++) {
+ Object elem = iterator.next();
+ if (!(elem instanceof Character)
+ || ((Character) elem).charValue() != string.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ int hash = 0;
+
+ @Override public int hashCode() {
+ int h = hash;
+ if (h == 0) {
+ h = 1;
+ for (int i = 0; i < string.length(); i++) {
+ h = h * 31 + string.charAt(i);
+ }
+ hash = h;
+ }
+ return h;
+ }
+ }
+
+ /**
+ * Returns a view of the specified {@code CharSequence} as a {@code
+ * List<Character>}, viewing {@code sequence} as a sequence of Unicode code
+ * units. The view does not support any modification operations, but reflects
+ * any changes to the underlying character sequence.
+ *
+ * @param sequence the character sequence to view as a {@code List} of
+ * characters
+ * @return an {@code List<Character>} view of the character sequence
+ * @since 7.0
+ */
+ @Beta public static List<Character> charactersOf(CharSequence sequence) {
+ return new CharSequenceAsList(checkNotNull(sequence));
+ }
+
+ private static final class CharSequenceAsList
+ extends AbstractList<Character> {
+ private final CharSequence sequence;
+
+ CharSequenceAsList(CharSequence sequence) {
+ this.sequence = sequence;
+ }
+
+ @Override public Character get(int index) {
+ checkElementIndex(index, size()); // for GWT
+ return sequence.charAt(index);
+ }
+
+ @Override public boolean contains(@Nullable Object o) {
+ return indexOf(o) >= 0;
+ }
+
+ @Override public int indexOf(@Nullable Object o) {
+ if (o instanceof Character) {
+ char c = (Character) o;
+ for (int i = 0; i < sequence.length(); i++) {
+ if (sequence.charAt(i) == c) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ @Override public int lastIndexOf(@Nullable Object o) {
+ if (o instanceof Character) {
+ char c = ((Character) o).charValue();
+ for (int i = sequence.length() - 1; i >= 0; i--) {
+ if (sequence.charAt(i) == c) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ @Override public int size() {
+ return sequence.length();
+ }
+
+ @Override public List<Character> subList(int fromIndex, int toIndex) {
+ checkPositionIndexes(fromIndex, toIndex, size()); // for GWT
+ return charactersOf(sequence.subSequence(fromIndex, toIndex));
+ }
+
+ @Override public int hashCode() {
+ int hash = 1;
+ for (int i = 0; i < sequence.length(); i++) {
+ hash = hash * 31 + sequence.charAt(i);
+ }
+ return hash;
+ }
+
+ @Override public boolean equals(@Nullable Object o) {
+ if (!(o instanceof List)) {
+ return false;
+ }
+ List<?> list = (List<?>) o;
+ int n = sequence.length();
+ if (n != list.size()) {
+ return false;
+ }
+ Iterator<?> iterator = list.iterator();
+ for (int i = 0; i < n; i++) {
+ Object elem = iterator.next();
+ if (!(elem instanceof Character)
+ || ((Character) elem).charValue() != sequence.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Returns a reversed view of the specified list. For example, {@code
+ * Lists.reverse(Arrays.asList(1, 2, 3))} returns a list containing {@code 3,
+ * 2, 1}. The returned list is backed by this list, so changes in the returned
+ * list are reflected in this list, and vice-versa. The returned list supports
+ * all of the optional list operations supported by this list.
+ *
+ * <p>The returned list is random-access if the specified list is random
+ * access.
+ *
+ * @since 7.0
+ */
+ public static <T> List<T> reverse(List<T> list) {
+ if (list instanceof ReverseList) {
+ return ((ReverseList<T>) list).getForwardList();
+ } else if (list instanceof RandomAccess) {
+ return new RandomAccessReverseList<T>(list);
+ } else {
+ return new ReverseList<T>(list);
+ }
+ }
+
+ private static class ReverseList<T> extends AbstractList<T> {
+ private final List<T> forwardList;
+
+ ReverseList(List<T> forwardList) {
+ this.forwardList = checkNotNull(forwardList);
+ }
+
+ List<T> getForwardList() {
+ return forwardList;
+ }
+
+ private int reverseIndex(int index) {
+ int size = size();
+ checkElementIndex(index, size);
+ return (size - 1) - index;
+ }
+
+ private int reversePosition(int index) {
+ int size = size();
+ checkPositionIndex(index, size);
+ return size - index;
+ }
+
+ @Override public void add(int index, @Nullable T element) {
+ forwardList.add(reversePosition(index), element);
+ }
+
+ @Override public void clear() {
+ forwardList.clear();
+ }
+
+ @Override public T remove(int index) {
+ return forwardList.remove(reverseIndex(index));
+ }
+
+ @Override protected void removeRange(int fromIndex, int toIndex) {
+ subList(fromIndex, toIndex).clear();
+ }
+
+ @Override public T set(int index, @Nullable T element) {
+ return forwardList.set(reverseIndex(index), element);
+ }
+
+ @Override public T get(int index) {
+ return forwardList.get(reverseIndex(index));
+ }
+
+ @Override public boolean isEmpty() {
+ return forwardList.isEmpty();
+ }
+
+ @Override public int size() {
+ return forwardList.size();
+ }
+
+ @Override public boolean contains(@Nullable Object o) {
+ return forwardList.contains(o);
+ }
+
+ @Override public boolean containsAll(Collection<?> c) {
+ return forwardList.containsAll(c);
+ }
+
+ @Override public List<T> subList(int fromIndex, int toIndex) {
+ checkPositionIndexes(fromIndex, toIndex, size());
+ return reverse(forwardList.subList(
+ reversePosition(toIndex), reversePosition(fromIndex)));
+ }
+
+ @Override public int indexOf(@Nullable Object o) {
+ int index = forwardList.lastIndexOf(o);
+ return (index >= 0) ? reverseIndex(index) : -1;
+ }
+
+ @Override public int lastIndexOf(@Nullable Object o) {
+ int index = forwardList.indexOf(o);
+ return (index >= 0) ? reverseIndex(index) : -1;
+ }
+
+ @Override public Iterator<T> iterator() {
+ return listIterator();
+ }
+
+ @Override public ListIterator<T> listIterator(int index) {
+ int start = reversePosition(index);
+ final ListIterator<T> forwardIterator = forwardList.listIterator(start);
+ return new ListIterator<T>() {
+
+ boolean canRemove;
+ boolean canSet;
+
+ @Override public void add(T e) {
+ forwardIterator.add(e);
+ forwardIterator.previous();
+ canSet = canRemove = false;
+ }
+
+ @Override public boolean hasNext() {
+ return forwardIterator.hasPrevious();
+ }
+
+ @Override public boolean hasPrevious() {
+ return forwardIterator.hasNext();
+ }
+
+ @Override public T next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ canSet = canRemove = true;
+ return forwardIterator.previous();
+ }
+
+ @Override public int nextIndex() {
+ return reversePosition(forwardIterator.nextIndex());
+ }
+
+ @Override public T previous() {
+ if (!hasPrevious()) {
+ throw new NoSuchElementException();
+ }
+ canSet = canRemove = true;
+ return forwardIterator.next();
+ }
+
+ @Override public int previousIndex() {
+ return nextIndex() - 1;
+ }
+
+ @Override public void remove() {
+ checkState(canRemove);
+ forwardIterator.remove();
+ canRemove = canSet = false;
+ }
+
+ @Override public void set(T e) {
+ checkState(canSet);
+ forwardIterator.set(e);
+ }
+ };
+ }
+ }
+
+ private static class RandomAccessReverseList<T> extends ReverseList<T>
+ implements RandomAccess {
+ RandomAccessReverseList(List<T> forwardList) {
+ super(forwardList);
+ }
+ }
+
+ /**
+ * An implementation of {@link List#hashCode()}.
+ */
+ static int hashCodeImpl(List<?> list) {
+ int hashCode = 1;
+ for (Object o : list) {
+ hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode());
+
+ hashCode = ~~hashCode;
+ // needed to deal with GWT integer overflow
+ }
+ return hashCode;
+ }
+
+ /**
+ * An implementation of {@link List#equals(Object)}.
+ */
+ static boolean equalsImpl(List<?> list, @Nullable Object object) {
+ if (object == checkNotNull(list)) {
+ return true;
+ }
+ if (!(object instanceof List)) {
+ return false;
+ }
+
+ List<?> o = (List<?>) object;
+
+ return list.size() == o.size()
+ && Iterators.elementsEqual(list.iterator(), o.iterator());
+ }
+
+ /**
+ * An implementation of {@link List#addAll(int, Collection)}.
+ */
+ static <E> boolean addAllImpl(
+ List<E> list, int index, Iterable<? extends E> elements) {
+ boolean changed = false;
+ ListIterator<E> listIterator = list.listIterator(index);
+ for (E e : elements) {
+ listIterator.add(e);
+ changed = true;
+ }
+ return changed;
+ }
+
+ /**
+ * An implementation of {@link List#indexOf(Object)}.
+ */
+ static int indexOfImpl(List<?> list, @Nullable Object element){
+ ListIterator<?> listIterator = list.listIterator();
+ while (listIterator.hasNext()) {
+ if (Objects.equal(element, listIterator.next())) {
+ return listIterator.previousIndex();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * An implementation of {@link List#lastIndexOf(Object)}.
+ */
+ static int lastIndexOfImpl(List<?> list, @Nullable Object element){
+ ListIterator<?> listIterator = list.listIterator(list.size());
+ while (listIterator.hasPrevious()) {
+ if (Objects.equal(element, listIterator.previous())) {
+ return listIterator.nextIndex();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns an implementation of {@link List#listIterator(int)}.
+ */
+ static <E> ListIterator<E> listIteratorImpl(List<E> list, int index) {
+ return new AbstractListWrapper<E>(list).listIterator(index);
+ }
+
+ /**
+ * An implementation of {@link List#subList(int, int)}.
+ */
+ static <E> List<E> subListImpl(
+ final List<E> list, int fromIndex, int toIndex) {
+ List<E> wrapper;
+ if (list instanceof RandomAccess) {
+ wrapper = new RandomAccessListWrapper<E>(list) {
+ @Override public ListIterator<E> listIterator(int index) {
+ return backingList.listIterator(index);
+ }
+
+ private static final long serialVersionUID = 0;
+ };
+ } else {
+ wrapper = new AbstractListWrapper<E>(list) {
+ @Override public ListIterator<E> listIterator(int index) {
+ return backingList.listIterator(index);
+ }
+
+ private static final long serialVersionUID = 0;
+ };
+ }
+ return wrapper.subList(fromIndex, toIndex);
+ }
+
+ private static class AbstractListWrapper<E> extends AbstractList<E> {
+ final List<E> backingList;
+
+ AbstractListWrapper(List<E> backingList) {
+ this.backingList = checkNotNull(backingList);
+ }
+
+ @Override public void add(int index, E element) {
+ backingList.add(index, element);
+ }
+
+ @Override public boolean addAll(int index, Collection<? extends E> c) {
+ return backingList.addAll(index, c);
+ }
+
+ @Override public E get(int index) {
+ return backingList.get(index);
+ }
+
+ @Override public E remove(int index) {
+ return backingList.remove(index);
+ }
+
+ @Override public E set(int index, E element) {
+ return backingList.set(index, element);
+ }
+
+ @Override public boolean contains(Object o) {
+ return backingList.contains(o);
+ }
+
+ @Override public int size() {
+ return backingList.size();
+ }
+ }
+
+ private static class RandomAccessListWrapper<E>
+ extends AbstractListWrapper<E> implements RandomAccess {
+ RandomAccessListWrapper(List<E> backingList) {
+ super(backingList);
+ }
+ }
+
+ /**
+ * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557
+ */
+ static <T> List<T> cast(Iterable<T> iterable) {
+ return (List<T>) iterable;
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/MapMaker.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/MapMaker.java
index 52c1a9871..6f88f8922 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/MapMaker.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/MapMaker.java
@@ -42,8 +42,8 @@ public class MapMaker extends GenericMapMaker<Object, Object> {
private final Function<? super K, ? extends V> computer;
private final int maximumSize;
- ExpiringComputingMap(long expirationMillis, int maximumSize, int initialCapacity,
- float loadFactor) {
+ ExpiringComputingMap(
+ long expirationMillis, int maximumSize, int initialCapacity, float loadFactor) {
this(expirationMillis, null, maximumSize, initialCapacity, loadFactor);
}
@@ -184,12 +184,6 @@ public class MapMaker extends GenericMapMaker<Object, Object> {
}
@Override
- public
- MapMaker expiration(long duration, TimeUnit unit) {
- return expireAfterWrite(duration, unit);
- }
-
- @Override
MapMaker expireAfterWrite(long duration, TimeUnit unit) {
if (expirationMillis != 0) {
throw new IllegalStateException(
@@ -227,26 +221,16 @@ public class MapMaker extends GenericMapMaker<Object, Object> {
}
@Override
- MapMaker strongKeys() {
- return this;
- }
-
- @Override
- MapMaker strongValues() {
- return this;
- }
-
- @Override
public <K, V> ConcurrentMap<K, V> makeMap() {
return useCustomMap
- ? new ExpiringComputingMap<K, V>(expirationMillis, null, maximumSize, initialCapacity,
- loadFactor)
+ ? new ExpiringComputingMap<K, V>(
+ expirationMillis, null, maximumSize, initialCapacity, loadFactor)
: new ConcurrentHashMap<K, V>(initialCapacity, loadFactor);
}
@Override
public <K, V> ConcurrentMap<K, V> makeComputingMap(Function<? super K, ? extends V> computer) {
- return new ExpiringComputingMap<K, V>(expirationMillis, computer, maximumSize, initialCapacity,
- loadFactor);
+ return new ExpiringComputingMap<K, V>(
+ expirationMillis, computer, maximumSize, initialCapacity, loadFactor);
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Maps.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Maps.java
index 78165de7a..fd6909da7 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Maps.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Maps.java
@@ -22,7 +22,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Equivalence;
-import com.google.common.base.Equivalences;
import com.google.common.base.Function;
import com.google.common.base.Joiner.MapJoiner;
import com.google.common.base.Objects;
@@ -35,7 +34,6 @@ import com.google.common.primitives.Ints;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
-import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -48,14 +46,20 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
+import java.util.SortedSet;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nullable;
/**
- * Static utility methods pertaining to {@link Map} instances. Also see this
- * class's counterparts {@link Lists} and {@link Sets}.
+ * Static utility methods pertaining to {@link Map} instances (including instances of
+ * {@link SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts
+ * {@link Lists}, {@link Sets} and {@link Queues}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Maps">
+ * {@code Maps}</a>.
*
* @author Kevin Bourrillion
* @author Mike Bostock
@@ -67,6 +71,60 @@ import javax.annotation.Nullable;
public final class Maps {
private Maps() {}
+ private enum EntryFunction implements Function<Entry, Object> {
+ KEY {
+ @Override
+ @Nullable
+ public Object apply(Entry entry) {
+ return entry.getKey();
+ }
+ },
+ VALUE {
+ @Override
+ @Nullable
+ public Object apply(Entry entry) {
+ return entry.getValue();
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ static <K> Function<Entry<K, ?>, K> keyFunction() {
+ return (Function) EntryFunction.KEY;
+ }
+
+ static <V> Function<Entry<?, V>, V> valueFunction() {
+ return (Function) EntryFunction.VALUE;
+ }
+
+ /**
+ * Returns an immutable map instance containing the given entries.
+ * Internally, the returned set will be backed by an {@link EnumMap}.
+ *
+ * <p>The iteration order of the returned map follows the enum's iteration
+ * order, not the order in which the elements appear in the given map.
+ *
+ * @param map the map to make an immutable copy of
+ * @return an immutable map containing those entries
+ * @since 14.0
+ */
+ @GwtCompatible(serializable = true)
+ @Beta
+ public static <K extends Enum<K>, V> ImmutableMap<K, V> immutableEnumMap(
+ Map<K, V> map) {
+ if (map instanceof ImmutableEnumMap) {
+ return (ImmutableEnumMap<K, V>) map;
+ } else if (map.isEmpty()) {
+ return ImmutableMap.of();
+ } else {
+ for (Map.Entry<K, V> entry : map.entrySet()) {
+ checkNotNull(entry.getKey());
+ checkNotNull(entry.getValue());
+ }
+ return ImmutableEnumMap.asImmutable(new EnumMap<K, V>(map));
+ }
+ }
+
/**
* Creates a <i>mutable</i>, empty {@code HashMap} instance.
*
@@ -266,38 +324,6 @@ public final class Maps {
}
/**
- * Returns a synchronized (thread-safe) bimap backed by the specified bimap.
- * In order to guarantee serial access, it is critical that <b>all</b> access
- * to the backing bimap is accomplished through the returned bimap.
- *
- * <p>It is imperative that the user manually synchronize on the returned map
- * when accessing any of its collection views: <pre> {@code
- *
- * BiMap<Long, String> map = Maps.synchronizedBiMap(
- * HashBiMap.<Long, String>create());
- * ...
- * Set<Long> set = map.keySet(); // Needn't be in synchronized block
- * ...
- * synchronized (map) { // Synchronizing on map, not set!
- * Iterator<Long> it = set.iterator(); // Must be in synchronized block
- * while (it.hasNext()) {
- * foo(it.next());
- * }
- * }}</pre>
- *
- * Failure to follow this advice may result in non-deterministic behavior.
- *
- * <p>The returned bimap will be serializable if the specified bimap is
- * serializable.
- *
- * @param bimap the bimap to be wrapped in a synchronized view
- * @return a sychronized view of the specified bimap
- */
- public static <K, V> BiMap<K, V> synchronizedBiMap(BiMap<K, V> bimap) {
- return Synchronized.biMap(bimap, null);
- }
-
- /**
* Computes the difference between two maps. This difference is an immutable
* snapshot of the state of the maps at the time this method is called. It
* will never change, even if the maps change at a later time.
@@ -321,7 +347,7 @@ public final class Maps {
SortedMapDifference<K, V> result = difference(sortedLeft, right);
return result;
}
- return difference(left, right, Equivalences.equals());
+ return difference(left, right, Equivalence.equals());
}
/**
@@ -493,7 +519,7 @@ public final class Maps {
}
@Override public boolean equals(@Nullable Object object) {
- if (object instanceof MapDifference.ValueDifference<?>) {
+ if (object instanceof MapDifference.ValueDifference) {
MapDifference.ValueDifference<?> that =
(MapDifference.ValueDifference<?>) object;
return Objects.equal(this.left, that.leftValue())
@@ -530,7 +556,6 @@ public final class Maps {
* @return the difference between the two maps
* @since 11.0
*/
- @Beta
public static <K, V> SortedMapDifference<K, V> difference(
SortedMap<K, ? extends V> left, Map<? extends K, ? extends V> right) {
checkNotNull(left);
@@ -615,6 +640,313 @@ public final class Maps {
}
return (Comparator<E>) Ordering.natural();
}
+
+ /**
+ * Returns a view of the set as a map, mapping keys from the set according to
+ * the specified function.
+ *
+ * <p>Specifically, for each {@code k} in the backing set, the returned map
+ * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code
+ * keySet}, {@code values}, and {@code entrySet} views of the returned map
+ * iterate in the same order as the backing set.
+ *
+ * <p>Modifications to the backing set are read through to the returned map.
+ * The returned map supports removal operations if the backing set does.
+ * Removal operations write through to the backing set. The returned map
+ * does not support put operations.
+ *
+ * <p><b>Warning</b>: If the function rejects {@code null}, caution is
+ * required to make sure the set does not contain {@code null}, because the
+ * view cannot stop {@code null} from being added to the set.
+ *
+ * <p><b>Warning:</b> This method assumes that for any instance {@code k} of
+ * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also
+ * of type {@code K}. Using a key type for which this may not hold, such as
+ * {@code ArrayList}, may risk a {@code ClassCastException} when calling
+ * methods on the resulting map view.
+ *
+ * @since 14.0
+ */
+ @Beta
+ public static <K, V> Map<K, V> asMap(
+ Set<K> set, Function<? super K, V> function) {
+ if (set instanceof SortedSet) {
+ return asMap((SortedSet<K>) set, function);
+ } else {
+ return new AsMapView<K, V>(set, function);
+ }
+ }
+
+ /**
+ * Returns a view of the sorted set as a map, mapping keys from the set
+ * according to the specified function.
+ *
+ * <p>Specifically, for each {@code k} in the backing set, the returned map
+ * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code
+ * keySet}, {@code values}, and {@code entrySet} views of the returned map
+ * iterate in the same order as the backing set.
+ *
+ * <p>Modifications to the backing set are read through to the returned map.
+ * The returned map supports removal operations if the backing set does.
+ * Removal operations write through to the backing set. The returned map does
+ * not support put operations.
+ *
+ * <p><b>Warning</b>: If the function rejects {@code null}, caution is
+ * required to make sure the set does not contain {@code null}, because the
+ * view cannot stop {@code null} from being added to the set.
+ *
+ * <p><b>Warning:</b> This method assumes that for any instance {@code k} of
+ * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of
+ * type {@code K}. Using a key type for which this may not hold, such as
+ * {@code ArrayList}, may risk a {@code ClassCastException} when calling
+ * methods on the resulting map view.
+ *
+ * @since 14.0
+ */
+ @Beta
+ public static <K, V> SortedMap<K, V> asMap(
+ SortedSet<K> set, Function<? super K, V> function) {
+ return Platform.mapsAsMapSortedSet(set, function);
+ }
+
+ static <K, V> SortedMap<K, V> asMapSortedIgnoreNavigable(SortedSet<K> set,
+ Function<? super K, V> function) {
+ return new SortedAsMapView<K, V>(set, function);
+ }
+
+ private static class AsMapView<K, V> extends ImprovedAbstractMap<K, V> {
+
+ private final Set<K> set;
+ final Function<? super K, V> function;
+
+ Set<K> backingSet() {
+ return set;
+ }
+
+ AsMapView(Set<K> set, Function<? super K, V> function) {
+ this.set = checkNotNull(set);
+ this.function = checkNotNull(function);
+ }
+
+ @Override
+ public Set<K> keySet() {
+ // probably not worth caching
+ return removeOnlySet(backingSet());
+ }
+
+ @Override
+ public Collection<V> values() {
+ // probably not worth caching
+ return Collections2.transform(set, function);
+ }
+
+ @Override
+ public int size() {
+ return backingSet().size();
+ }
+
+ @Override
+ public boolean containsKey(@Nullable Object key) {
+ return backingSet().contains(key);
+ }
+
+ @Override
+ public V get(@Nullable Object key) {
+ if (backingSet().contains(key)) {
+ @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it
+ K k = (K) key;
+ return function.apply(k);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public V remove(@Nullable Object key) {
+ if (backingSet().remove(key)) {
+ @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it
+ K k = (K) key;
+ return function.apply(k);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void clear() {
+ backingSet().clear();
+ }
+
+ @Override
+ protected Set<Entry<K, V>> createEntrySet() {
+ return new EntrySet<K, V>() {
+ @Override
+ Map<K, V> map() {
+ return AsMapView.this;
+ }
+
+ @Override
+ public Iterator<Entry<K, V>> iterator() {
+ return asSetEntryIterator(backingSet(), function);
+ }
+ };
+ }
+ }
+
+ private static <K, V> Iterator<Entry<K, V>> asSetEntryIterator(
+ Set<K> set, final Function<? super K, V> function) {
+ return new TransformedIterator<K, Entry<K,V>>(set.iterator()) {
+ @Override
+ Entry<K, V> transform(K key) {
+ return Maps.immutableEntry(key, function.apply(key));
+ }
+ };
+ }
+
+ private static class SortedAsMapView<K, V> extends AsMapView<K, V>
+ implements SortedMap<K, V> {
+
+ SortedAsMapView(SortedSet<K> set, Function<? super K, V> function) {
+ super(set, function);
+ }
+
+ @Override
+ SortedSet<K> backingSet() {
+ return (SortedSet<K>) super.backingSet();
+ }
+
+ @Override
+ public Comparator<? super K> comparator() {
+ return backingSet().comparator();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return removeOnlySortedSet(backingSet());
+ }
+
+ @Override
+ public SortedMap<K, V> subMap(K fromKey, K toKey) {
+ return asMap(backingSet().subSet(fromKey, toKey), function);
+ }
+
+ @Override
+ public SortedMap<K, V> headMap(K toKey) {
+ return asMap(backingSet().headSet(toKey), function);
+ }
+
+ @Override
+ public SortedMap<K, V> tailMap(K fromKey) {
+ return asMap(backingSet().tailSet(fromKey), function);
+ }
+
+ @Override
+ public K firstKey() {
+ return backingSet().first();
+ }
+
+ @Override
+ public K lastKey() {
+ return backingSet().last();
+ }
+ }
+
+ private static <E> Set<E> removeOnlySet(final Set<E> set) {
+ return new ForwardingSet<E>() {
+ @Override
+ protected Set<E> delegate() {
+ return set;
+ }
+
+ @Override
+ public boolean add(E element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> es) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ private static <E> SortedSet<E> removeOnlySortedSet(final SortedSet<E> set) {
+ return new ForwardingSortedSet<E>() {
+ @Override
+ protected SortedSet<E> delegate() {
+ return set;
+ }
+
+ @Override
+ public boolean add(E element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> es) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SortedSet<E> headSet(E toElement) {
+ return removeOnlySortedSet(super.headSet(toElement));
+ }
+
+ @Override
+ public SortedSet<E> subSet(E fromElement, E toElement) {
+ return removeOnlySortedSet(super.subSet(fromElement, toElement));
+ }
+
+ @Override
+ public SortedSet<E> tailSet(E fromElement) {
+ return removeOnlySortedSet(super.tailSet(fromElement));
+ }
+ };
+ }
+
+ /**
+ * Returns an immutable map for which the given {@code keys} are mapped to
+ * values by the given function in the order they appear in the original
+ * iterable. If {@code keys} contains duplicate elements, the returned map
+ * will contain each distinct key once in the order it first appears in
+ * {@code keys}.
+ *
+ * @throws NullPointerException if any element of {@code keys} is
+ * {@code null}, or if {@code valueFunction} produces {@code null}
+ * for any key
+ * @since 14.0
+ */
+ @Beta
+ public static <K, V> ImmutableMap<K, V> toMap(Iterable<K> keys,
+ Function<? super K, V> valueFunction) {
+ return toMap(keys.iterator(), valueFunction);
+ }
+
+ /**
+ * Returns an immutable map for which the given {@code keys} are mapped to
+ * values by the given function in the order they appear in the original
+ * iterator. If {@code keys} contains duplicate elements, the returned map
+ * will contain each distinct key once in the order it first appears in
+ * {@code keys}.
+ *
+ * @throws NullPointerException if any element of {@code keys} is
+ * {@code null}, or if {@code valueFunction} produces {@code null}
+ * for any key
+ * @since 14.0
+ */
+ @Beta
+ public static <K, V> ImmutableMap<K, V> toMap(Iterator<K> keys,
+ Function<? super K, V> valueFunction) {
+ checkNotNull(valueFunction);
+ // Using LHM instead of a builder so as not to fail on duplicate keys
+ Map<K, V> builder = newLinkedHashMap();
+ while (keys.hasNext()) {
+ K key = keys.next();
+ builder.put(key, valueFunction.apply(key));
+ }
+ return ImmutableMap.copyOf(builder);
+ }
+
/**
* Returns an immutable map for which the {@link Map#values} are the given
* elements in the given order, and each key is the product of invoking a
@@ -635,24 +967,6 @@ public final class Maps {
}
/**
- * <b>Deprecated.</b>
- *
- * @since 10.0
- * @deprecated use {@link #uniqueIndex(Iterator, Function)} by casting {@code
- * values} to {@code Iterator<V>}, or better yet, by implementing only
- * {@code Iterator} and not {@code Iterable}. <b>This method is scheduled
- * for deletion in March 2012.</b>
- */
- @Beta
- @Deprecated
- public static <K, V, I extends Object & Iterable<V> & Iterator<V>>
- ImmutableMap<K, V> uniqueIndex(
- I values, Function<? super V, K> keyFunction) {
- Iterable<V> valuesIterable = checkNotNull(values);
- return uniqueIndex(valuesIterable, keyFunction);
- }
-
- /**
* Returns an immutable map for which the {@link Map#values} are the given
* elements in the given order, and each key is the product of invoking a
* supplied function on its corresponding value.
@@ -814,6 +1128,38 @@ public final class Maps {
}
/**
+ * Returns a synchronized (thread-safe) bimap backed by the specified bimap.
+ * In order to guarantee serial access, it is critical that <b>all</b> access
+ * to the backing bimap is accomplished through the returned bimap.
+ *
+ * <p>It is imperative that the user manually synchronize on the returned map
+ * when accessing any of its collection views: <pre> {@code
+ *
+ * BiMap<Long, String> map = Maps.synchronizedBiMap(
+ * HashBiMap.<Long, String>create());
+ * ...
+ * Set<Long> set = map.keySet(); // Needn't be in synchronized block
+ * ...
+ * synchronized (map) { // Synchronizing on map, not set!
+ * Iterator<Long> it = set.iterator(); // Must be in synchronized block
+ * while (it.hasNext()) {
+ * foo(it.next());
+ * }
+ * }}</pre>
+ *
+ * Failure to follow this advice may result in non-deterministic behavior.
+ *
+ * <p>The returned bimap will be serializable if the specified bimap is
+ * serializable.
+ *
+ * @param bimap the bimap to be wrapped in a synchronized view
+ * @return a sychronized view of the specified bimap
+ */
+ public static <K, V> BiMap<K, V> synchronizedBiMap(BiMap<K, V> bimap) {
+ return Synchronized.biMap(bimap, null);
+ }
+
+ /**
* Returns an unmodifiable view of the specified bimap. This method allows
* modules to provide users with "read-only" access to internal bimaps. Query
* operations on the returned bimap "read through" to the specified bimap, and
@@ -836,7 +1182,7 @@ public final class Maps {
extends ForwardingMap<K, V> implements BiMap<K, V>, Serializable {
final Map<K, V> unmodifiableMap;
final BiMap<? extends K, ? extends V> delegate;
- transient BiMap<V, K> inverse;
+ BiMap<V, K> inverse;
transient Set<V> values;
UnmodifiableBiMap(BiMap<? extends K, ? extends V> delegate,
@@ -910,16 +1256,8 @@ public final class Maps {
* a view, copy the returned map into a new map of your choosing.
*/
public static <K, V1, V2> Map<K, V2> transformValues(
- Map<K, V1> fromMap, final Function<? super V1, V2> function) {
- checkNotNull(function);
- EntryTransformer<K, V1, V2> transformer =
- new EntryTransformer<K, V1, V2>() {
- @Override
- public V2 transformEntry(K key, V1 value) {
- return function.apply(value);
- }
- };
- return transformEntries(fromMap, transformer);
+ Map<K, V1> fromMap, Function<? super V1, V2> function) {
+ return transformEntries(fromMap, asEntryTransformer(function));
}
/**
@@ -961,18 +1299,20 @@ public final class Maps {
*
* @since 11.0
*/
- @Beta
public static <K, V1, V2> SortedMap<K, V2> transformValues(
- SortedMap<K, V1> fromMap, final Function<? super V1, V2> function) {
+ SortedMap<K, V1> fromMap, Function<? super V1, V2> function) {
+ return transformEntries(fromMap, asEntryTransformer(function));
+ }
+
+ private static <K, V1, V2> EntryTransformer<K, V1, V2>
+ asEntryTransformer(final Function<? super V1, V2> function) {
checkNotNull(function);
- EntryTransformer<K, V1, V2> transformer =
- new EntryTransformer<K, V1, V2>() {
- @Override
- public V2 transformEntry(K key, V1 value) {
- return function.apply(value);
- }
- };
- return transformEntries(fromMap, transformer);
+ return new EntryTransformer<K, V1, V2>() {
+ @Override
+ public V2 transformEntry(K key, V1 value) {
+ return function.apply(value);
+ }
+ };
}
/**
@@ -1087,9 +1427,14 @@ public final class Maps {
*
* @since 11.0
*/
- @Beta
public static <K, V1, V2> SortedMap<K, V2> transformEntries(
- final SortedMap<K, V1> fromMap,
+ SortedMap<K, V1> fromMap,
+ EntryTransformer<? super K, ? super V1, V2> transformer) {
+ return Platform.mapsTransformEntriesSortedMap(fromMap, transformer);
+ }
+
+ static <K, V1, V2> SortedMap<K, V2> transformEntriesIgnoreNavigable(
+ SortedMap<K, V1> fromMap,
EntryTransformer<? super K, ? super V1, V2> transformer) {
return new TransformedEntriesSortedMap<K, V1, V2>(fromMap, transformer);
}
@@ -1181,17 +1526,23 @@ public final class Maps {
}
@Override public Iterator<Entry<K, V2>> iterator() {
- final Iterator<Entry<K, V1>> backingIterator =
- fromMap.entrySet().iterator();
- return Iterators.transform(backingIterator,
- new Function<Entry<K, V1>, Entry<K, V2>>() {
- @Override public Entry<K, V2> apply(Entry<K, V1> entry) {
- return immutableEntry(
- entry.getKey(),
- transformer.transformEntry(entry.getKey(),
- entry.getValue()));
+ return new TransformedIterator<Entry<K, V1>, Entry<K, V2>>(
+ fromMap.entrySet().iterator()) {
+ @Override
+ Entry<K, V2> transform(final Entry<K, V1> entry) {
+ return new AbstractMapEntry<K, V2>() {
+ @Override
+ public K getKey() {
+ return entry.getKey();
}
- });
+
+ @Override
+ public V2 getValue() {
+ return transformer.transformEntry(entry.getKey(), entry.getValue());
+ }
+ };
+ }
+ };
}
};
}
@@ -1249,7 +1600,32 @@ public final class Maps {
@Override public SortedMap<K, V2> tailMap(K fromKey) {
return transformEntries(fromMap().tailMap(fromKey), transformer);
}
+ }
+
+ private static final class KeyPredicate<K, V> implements Predicate<Entry<K, V>> {
+ private final Predicate<? super K> keyPredicate;
+
+ KeyPredicate(Predicate<? super K> keyPredicate) {
+ this.keyPredicate = checkNotNull(keyPredicate);
+ }
+ @Override
+ public boolean apply(Entry<K, V> input) {
+ return keyPredicate.apply(input.getKey());
+ }
+ }
+
+ private static final class ValuePredicate<K, V> implements Predicate<Entry<K, V>> {
+ private final Predicate<? super V> valuePredicate;
+
+ ValuePredicate(Predicate<? super V> valuePredicate) {
+ this.valuePredicate = checkNotNull(valuePredicate);
+ }
+
+ @Override
+ public boolean apply(Entry<K, V> input) {
+ return valuePredicate.apply(input.getValue());
+ }
}
/**
@@ -1284,15 +1660,11 @@ public final class Maps {
Map<K, V> unfiltered, final Predicate<? super K> keyPredicate) {
if (unfiltered instanceof SortedMap) {
return filterKeys((SortedMap<K, V>) unfiltered, keyPredicate);
+ } else if (unfiltered instanceof BiMap) {
+ return filterKeys((BiMap<K, V>) unfiltered, keyPredicate);
}
checkNotNull(keyPredicate);
- Predicate<Entry<K, V>> entryPredicate =
- new Predicate<Entry<K, V>>() {
- @Override
- public boolean apply(Entry<K, V> input) {
- return keyPredicate.apply(input.getKey());
- }
- };
+ Predicate<Entry<K, V>> entryPredicate = new KeyPredicate<K, V>(keyPredicate);
return (unfiltered instanceof AbstractFilteredMap)
? filterFiltered((AbstractFilteredMap<K, V>) unfiltered, entryPredicate)
: new FilteredKeyMap<K, V>(
@@ -1329,19 +1701,42 @@ public final class Maps {
*
* @since 11.0
*/
- @Beta
public static <K, V> SortedMap<K, V> filterKeys(
SortedMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) {
- // TODO: Return a subclass of Maps.FilteredKeyMap for slightly better
+ // TODO(user): Return a subclass of Maps.FilteredKeyMap for slightly better
// performance.
+ return filterEntries(unfiltered, new KeyPredicate<K, V>(keyPredicate));
+ }
+
+ /**
+ * Returns a bimap containing the mappings in {@code unfiltered} whose keys satisfy a predicate.
+ * The returned bimap is a live view of {@code unfiltered}; changes to one affect the other.
+ *
+ * <p>The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have
+ * iterators that don't support {@code remove()}, but all other methods are supported by the
+ * bimap and its views. When given a key that doesn't satisfy the predicate, the bimap's {@code
+ * put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link
+ * IllegalArgumentException}.
+ *
+ * <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
+ * bimap or its views, only mappings that satisfy the filter will be removed from the underlying
+ * bimap.
+ *
+ * <p>The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is.
+ *
+ * <p>Many of the filtered bimap's methods, such as {@code size()}, iterate across every key in
+ * the underlying bimap and determine which satisfy the filter. When a live view is <i>not</i>
+ * needed, it may be faster to copy the filtered bimap and use the copy.
+ *
+ * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals </i>, as
+ * documented at {@link Predicate#apply}.
+ *
+ * @since 14.0
+ */
+ public static <K, V> BiMap<K, V> filterKeys(
+ BiMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) {
checkNotNull(keyPredicate);
- Predicate<Entry<K, V>> entryPredicate = new Predicate<Entry<K, V>>() {
- @Override
- public boolean apply(Entry<K, V> input) {
- return keyPredicate.apply(input.getKey());
- }
- };
- return filterEntries(unfiltered, entryPredicate);
+ return filterEntries(unfiltered, new KeyPredicate<K, V>(keyPredicate));
}
/**
@@ -1377,16 +1772,10 @@ public final class Maps {
Map<K, V> unfiltered, final Predicate<? super V> valuePredicate) {
if (unfiltered instanceof SortedMap) {
return filterValues((SortedMap<K, V>) unfiltered, valuePredicate);
+ } else if (unfiltered instanceof BiMap) {
+ return filterValues((BiMap<K, V>) unfiltered, valuePredicate);
}
- checkNotNull(valuePredicate);
- Predicate<Entry<K, V>> entryPredicate =
- new Predicate<Entry<K, V>>() {
- @Override
- public boolean apply(Entry<K, V> input) {
- return valuePredicate.apply(input.getValue());
- }
- };
- return filterEntries(unfiltered, entryPredicate);
+ return filterEntries(unfiltered, new ValuePredicate<K, V>(valuePredicate));
}
/**
@@ -1420,18 +1809,42 @@ public final class Maps {
*
* @since 11.0
*/
- @Beta
public static <K, V> SortedMap<K, V> filterValues(
SortedMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) {
- checkNotNull(valuePredicate);
- Predicate<Entry<K, V>> entryPredicate =
- new Predicate<Entry<K, V>>() {
- @Override
- public boolean apply(Entry<K, V> input) {
- return valuePredicate.apply(input.getValue());
- }
- };
- return filterEntries(unfiltered, entryPredicate);
+ return filterEntries(unfiltered, new ValuePredicate<K, V>(valuePredicate));
+ }
+
+ /**
+ * Returns a bimap containing the mappings in {@code unfiltered} whose values satisfy a
+ * predicate. The returned bimap is a live view of {@code unfiltered}; changes to one affect the
+ * other.
+ *
+ * <p>The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have
+ * iterators that don't support {@code remove()}, but all other methods are supported by the
+ * bimap and its views. When given a value that doesn't satisfy the predicate, the bimap's
+ * {@code put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link
+ * IllegalArgumentException}. Similarly, the map's entries have a {@link Entry#setValue} method
+ * that throws an {@link IllegalArgumentException} when the provided value doesn't satisfy the
+ * predicate.
+ *
+ * <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
+ * bimap or its views, only mappings that satisfy the filter will be removed from the underlying
+ * bimap.
+ *
+ * <p>The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is.
+ *
+ * <p>Many of the filtered bimap's methods, such as {@code size()}, iterate across every value in
+ * the underlying bimap and determine which satisfy the filter. When a live view is <i>not</i>
+ * needed, it may be faster to copy the filtered bimap and use the copy.
+ *
+ * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals </i>, as
+ * documented at {@link Predicate#apply}.
+ *
+ * @since 14.0
+ */
+ public static <K, V> BiMap<K, V> filterValues(
+ BiMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) {
+ return filterEntries(unfiltered, new ValuePredicate<K, V>(valuePredicate));
}
/**
@@ -1467,6 +1880,8 @@ public final class Maps {
Map<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) {
if (unfiltered instanceof SortedMap) {
return filterEntries((SortedMap<K, V>) unfiltered, entryPredicate);
+ } else if (unfiltered instanceof BiMap) {
+ return filterEntries((BiMap<K, V>) unfiltered, entryPredicate);
}
checkNotNull(entryPredicate);
return (unfiltered instanceof AbstractFilteredMap)
@@ -1505,10 +1920,15 @@ public final class Maps {
*
* @since 11.0
*/
- @Beta
public static <K, V> SortedMap<K, V> filterEntries(
SortedMap<K, V> unfiltered,
Predicate<? super Entry<K, V>> entryPredicate) {
+ return Platform.mapsFilterSortedMap(unfiltered, entryPredicate);
+ }
+
+ static <K, V> SortedMap<K, V> filterSortedIgnoreNavigable(
+ SortedMap<K, V> unfiltered,
+ Predicate<? super Entry<K, V>> entryPredicate) {
checkNotNull(entryPredicate);
return (unfiltered instanceof FilteredEntrySortedMap)
? filterFiltered((FilteredEntrySortedMap<K, V>) unfiltered, entryPredicate)
@@ -1516,6 +1936,42 @@ public final class Maps {
}
/**
+ * Returns a bimap containing the mappings in {@code unfiltered} that satisfy a predicate. The
+ * returned bimap is a live view of {@code unfiltered}; changes to one affect the other.
+ *
+ * <p>The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have
+ * iterators that don't support {@code remove()}, but all other methods are supported by the bimap
+ * and its views. When given a key/value pair that doesn't satisfy the predicate, the bimap's
+ * {@code put()}, {@code forcePut()} and {@code putAll()} methods throw an
+ * {@link IllegalArgumentException}. Similarly, the map's entries have an {@link Entry#setValue}
+ * method that throws an {@link IllegalArgumentException} when the existing key and the provided
+ * value don't satisfy the predicate.
+ *
+ * <p>When methods such as {@code removeAll()} and {@code clear()} are called on the filtered
+ * bimap or its views, only mappings that satisfy the filter will be removed from the underlying
+ * bimap.
+ *
+ * <p>The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is.
+ *
+ * <p>Many of the filtered bimap's methods, such as {@code size()}, iterate across every
+ * key/value mapping in the underlying bimap and determine which satisfy the filter. When a live
+ * view is <i>not</i> needed, it may be faster to copy the filtered bimap and use the copy.
+ *
+ * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with equals </i>, as
+ * documented at {@link Predicate#apply}.
+ *
+ * @since 14.0
+ */
+ public static <K, V> BiMap<K, V> filterEntries(
+ BiMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) {
+ checkNotNull(unfiltered);
+ checkNotNull(entryPredicate);
+ return (unfiltered instanceof FilteredEntryBiMap)
+ ? filterFiltered((FilteredEntryBiMap<K, V>) unfiltered, entryPredicate)
+ : new FilteredEntryBiMap<K, V>(unfiltered, entryPredicate);
+ }
+
+ /**
* Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when
* filtering a filtered map.
*/
@@ -1660,6 +2116,7 @@ public final class Maps {
}
}
}
+
/**
* Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when
* filtering a filtered sorted map.
@@ -1720,6 +2177,66 @@ public final class Maps {
}
}
+ /**
+ * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when
+ * filtering a filtered map.
+ */
+ private static <K, V> BiMap<K, V> filterFiltered(
+ FilteredEntryBiMap<K, V> map, Predicate<? super Entry<K, V>> entryPredicate) {
+ Predicate<Entry<K, V>> predicate = Predicates.and(map.predicate, entryPredicate);
+ return new FilteredEntryBiMap<K, V>(map.unfiltered(), predicate);
+ }
+
+ static final class FilteredEntryBiMap<K, V> extends FilteredEntryMap<K, V>
+ implements BiMap<K, V> {
+ private final BiMap<V, K> inverse;
+
+ private static <K, V> Predicate<Entry<V, K>> inversePredicate(
+ final Predicate<? super Entry<K, V>> forwardPredicate) {
+ return new Predicate<Entry<V, K>>() {
+ @Override
+ public boolean apply(Entry<V, K> input) {
+ return forwardPredicate.apply(
+ Maps.immutableEntry(input.getValue(), input.getKey()));
+ }
+ };
+ }
+
+ FilteredEntryBiMap(BiMap<K, V> delegate,
+ Predicate<? super Entry<K, V>> predicate) {
+ super(delegate, predicate);
+ this.inverse = new FilteredEntryBiMap<V, K>(
+ delegate.inverse(), inversePredicate(predicate), this);
+ }
+
+ private FilteredEntryBiMap(
+ BiMap<K, V> delegate, Predicate<? super Entry<K, V>> predicate,
+ BiMap<V, K> inverse) {
+ super(delegate, predicate);
+ this.inverse = inverse;
+ }
+
+ BiMap<K, V> unfiltered() {
+ return (BiMap<K, V>) unfiltered;
+ }
+
+ @Override
+ public V forcePut(@Nullable K key, @Nullable V value) {
+ checkArgument(predicate.apply(Maps.immutableEntry(key, value)));
+ return unfiltered().forcePut(key, value);
+ }
+
+ @Override
+ public BiMap<V, K> inverse() {
+ return inverse;
+ }
+
+ @Override
+ public Set<V> values() {
+ return inverse.keySet();
+ }
+ }
+
private static class FilteredKeyMap<K, V> extends AbstractFilteredMap<K, V> {
Predicate<? super K> keyPredicate;
@@ -1811,10 +2328,14 @@ public final class Maps {
@Override public Set<K> keySet() {
Set<K> result = keySet;
- return (result == null) ? keySet = new KeySet() : result;
+ return (result == null) ? keySet = createKeySet() : result;
+ }
+
+ Set<K> createKeySet() {
+ return new KeySet();
}
- private class KeySet extends AbstractSet<K> {
+ private class KeySet extends Sets.ImprovedAbstractSet<K> {
@Override public Iterator<K> iterator() {
final Iterator<Entry<K, V>> iterator = filteredEntrySet.iterator();
return new UnmodifiableIterator<K>() {
@@ -1850,22 +2371,13 @@ public final class Maps {
return false;
}
- @Override public boolean removeAll(Collection<?> collection) {
- checkNotNull(collection); // for GWT
- boolean changed = false;
- for (Object obj : collection) {
- changed |= remove(obj);
- }
- return changed;
- }
-
@Override public boolean retainAll(Collection<?> collection) {
checkNotNull(collection); // for GWT
boolean changed = false;
Iterator<Entry<K, V>> iterator = unfiltered.entrySet().iterator();
while (iterator.hasNext()) {
Entry<K, V> entry = iterator.next();
- if (!collection.contains(entry.getKey()) && predicate.apply(entry)) {
+ if (predicate.apply(entry) && !collection.contains(entry.getKey())) {
iterator.remove();
changed = true;
}
@@ -1884,6 +2396,10 @@ public final class Maps {
}
}
+ @Nullable private static <K, V> Entry<K, V> unmodifiableOrNull(@Nullable Entry<K, V> entry) {
+ return (entry == null) ? null : Maps.unmodifiableEntry(entry);
+ }
+
/**
* {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code
* entrySet().isEmpty()} instead of {@code size() == 0} to speed up
@@ -1892,7 +2408,7 @@ public final class Maps {
* implementation.
*/
@GwtCompatible
- static abstract class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> {
+ abstract static class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> {
/**
* Creates the entry set to be returned by {@link #entrySet()}. This method
* is invoked at most once on a given map, at the time when {@code entrySet}
@@ -1929,7 +2445,7 @@ public final class Maps {
@Override public Collection<V> values() {
Collection<V> result = values;
if (result == null) {
- return values = new Values<K, V>(){
+ return values = new Values<K, V>() {
@Override Map<K, V> map() {
return ImprovedAbstractMap.this;
}
@@ -1937,17 +2453,6 @@ public final class Maps {
}
return result;
}
-
- /**
- * Returns {@code true} if this map contains no key-value mappings.
- *
- * <p>The implementation returns {@code entrySet().isEmpty()}.
- *
- * @return {@code true} if this map contains no key-value mappings
- */
- @Override public boolean isEmpty() {
- return entrySet().isEmpty();
- }
}
static final MapJoiner STANDARD_JOINER =
@@ -1955,25 +2460,46 @@ public final class Maps {
/**
* Delegates to {@link Map#get}. Returns {@code null} on {@code
- * ClassCastException}.
+ * ClassCastException} and {@code NullPointerException}.
*/
static <V> V safeGet(Map<?, V> map, Object key) {
+ checkNotNull(map);
try {
return map.get(key);
} catch (ClassCastException e) {
return null;
+ } catch (NullPointerException e) {
+ return null;
}
}
/**
* Delegates to {@link Map#containsKey}. Returns {@code false} on {@code
- * ClassCastException}
+ * ClassCastException} and {@code NullPointerException}.
*/
static boolean safeContainsKey(Map<?, ?> map, Object key) {
+ checkNotNull(map);
try {
return map.containsKey(key);
} catch (ClassCastException e) {
return false;
+ } catch (NullPointerException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Delegates to {@link Map#remove}. Returns {@code null} on {@code
+ * ClassCastException} and {@code NullPointerException}.
+ */
+ static <V> V safeRemove(Map<?, V> map, Object key) {
+ checkNotNull(map);
+ try {
+ return map.remove(key);
+ } catch (ClassCastException e) {
+ return null;
+ } catch (NullPointerException e) {
+ return null;
}
}
@@ -2032,13 +2558,6 @@ public final class Maps {
}
/**
- * An implementation of {@link Map#hashCode}.
- */
- static int hashCodeImpl(Map<?, ?> map) {
- return Sets.hashCodeImpl(map.entrySet());
- }
-
- /**
* An implementation of {@link Map#toString}.
*/
static String toStringImpl(Map<?, ?> map) {
@@ -2062,36 +2581,30 @@ public final class Maps {
* An admittedly inefficient implementation of {@link Map#containsKey}.
*/
static boolean containsKeyImpl(Map<?, ?> map, @Nullable Object key) {
- for (Entry<?, ?> entry : map.entrySet()) {
- if (Objects.equal(entry.getKey(), key)) {
- return true;
- }
- }
- return false;
+ return Iterators.contains(keyIterator(map.entrySet().iterator()), key);
}
/**
* An implementation of {@link Map#containsValue}.
*/
static boolean containsValueImpl(Map<?, ?> map, @Nullable Object value) {
- for (Entry<?, ?> entry : map.entrySet()) {
- if (Objects.equal(entry.getValue(), value)) {
- return true;
+ return Iterators.contains(valueIterator(map.entrySet().iterator()), value);
+ }
+
+ static <K, V> Iterator<K> keyIterator(Iterator<Entry<K, V>> entryIterator) {
+ return new TransformedIterator<Entry<K, V>, K>(entryIterator) {
+ @Override
+ K transform(Entry<K, V> entry) {
+ return entry.getKey();
}
- }
- return false;
+ };
}
- abstract static class KeySet<K, V> extends AbstractSet<K> {
+ abstract static class KeySet<K, V> extends Sets.ImprovedAbstractSet<K> {
abstract Map<K, V> map();
@Override public Iterator<K> iterator() {
- return Iterators.transform(map().entrySet().iterator(),
- new Function<Map.Entry<K, V>, K>() {
- @Override public K apply(Entry<K, V> entry) {
- return entry.getKey();
- }
- });
+ return keyIterator(map().entrySet().iterator());
}
@Override public int size() {
@@ -2114,27 +2627,50 @@ public final class Maps {
return false;
}
- @Override
- public boolean removeAll(Collection<?> c) {
- // TODO(user): find out why this is necessary to make GWT tests pass.
- return super.removeAll(checkNotNull(c));
- }
-
@Override public void clear() {
map().clear();
}
}
+ @Nullable
+ static <K> K keyOrNull(@Nullable Entry<K, ?> entry) {
+ return (entry == null) ? null : entry.getKey();
+ }
+
+ @Nullable
+ static <V> V valueOrNull(@Nullable Entry<?, V> entry) {
+ return (entry == null) ? null : entry.getValue();
+ }
+
+ static <K, V> Iterator<V> valueIterator(Iterator<Entry<K, V>> entryIterator) {
+ return new TransformedIterator<Entry<K, V>, V>(entryIterator) {
+ @Override
+ V transform(Entry<K, V> entry) {
+ return entry.getValue();
+ }
+ };
+ }
+
+ static <K, V> UnmodifiableIterator<V> valueIterator(
+ final UnmodifiableIterator<Entry<K, V>> entryIterator) {
+ return new UnmodifiableIterator<V>() {
+ @Override
+ public boolean hasNext() {
+ return entryIterator.hasNext();
+ }
+
+ @Override
+ public V next() {
+ return entryIterator.next().getValue();
+ }
+ };
+ }
+
abstract static class Values<K, V> extends AbstractCollection<V> {
abstract Map<K, V> map();
@Override public Iterator<V> iterator() {
- return Iterators.transform(map().entrySet().iterator(),
- new Function<Entry<K, V>, V>() {
- @Override public V apply(Entry<K, V> entry) {
- return entry.getValue();
- }
- });
+ return valueIterator(map().entrySet().iterator());
}
@Override public boolean remove(Object o) {
@@ -2196,7 +2732,8 @@ public final class Maps {
}
}
- abstract static class EntrySet<K, V> extends AbstractSet<Entry<K, V>> {
+ abstract static class EntrySet<K, V>
+ extends Sets.ImprovedAbstractSet<Entry<K, V>> {
abstract Map<K, V> map();
@Override public int size() {
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Multimaps.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Multimaps.java
index 7e8e2f735..c2d630f84 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Multimaps.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Multimaps.java
@@ -20,21 +20,17 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Joiner.MapJoiner;
-import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
-import com.google.common.collect.Collections2.TransformedCollection;
import com.google.common.collect.Maps.EntryTransformer;
import java.io.Serializable;
import java.util.AbstractCollection;
-import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -52,6 +48,10 @@ import javax.annotation.Nullable;
/**
* Provides static methods acting on or generating a {@code Multimap}.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Multimaps">
+ * {@code Multimaps}</a>.
+ *
* @author Jared Levy
* @author Robert Konigsberg
* @author Mike Bostock
@@ -63,9 +63,14 @@ public final class Multimaps {
private Multimaps() {}
/**
- * Creates a new {@code Multimap} that uses the provided map and factory. It
- * can generate a multimap based on arbitrary {@link Map} and
- * {@link Collection} classes.
+ * Creates a new {@code Multimap} backed by {@code map}, whose internal value
+ * collections are generated by {@code factory}.
+ *
+ * <b>Warning: do not use</b> this method when the collections returned by
+ * {@code factory} implement either {@link List} or {@code Set}! Use the more
+ * specific method {@link #newListMultimap}, {@link #newSetMultimap} or {@link
+ * #newSortedSetMultimap} instead, to avoid very surprising behavior from
+ * {@link Multimap#equals}.
*
* <p>The {@code factory}-generated and {@code map} classes determine the
* multimap iteration order. They also specify the behavior of the
@@ -105,7 +110,7 @@ public final class Multimaps {
return new CustomMultimap<K, V>(map, factory);
}
- private static class CustomMultimap<K, V> extends AbstractMultimap<K, V> {
+ private static class CustomMultimap<K, V> extends AbstractMapBasedMultimap<K, V> {
transient Supplier<? extends Collection<V>> factory;
CustomMultimap(Map<K, Collection<V>> map,
@@ -329,13 +334,13 @@ public final class Multimaps {
* <p>It is imperative that the user manually synchronize on the returned
* multimap when accessing any of its collection views: <pre> {@code
*
- * Multimap<K, V> m = Multimaps.synchronizedMultimap(
+ * Multimap<K, V> multimap = Multimaps.synchronizedMultimap(
* HashMultimap.<K, V>create());
* ...
- * Set<K> s = m.keySet(); // Needn't be in synchronized block
+ * Collection<V> values = multimap.get(key); // Needn't be in synchronized block
* ...
- * synchronized (m) { // Synchronizing on m, not s!
- * Iterator<K> i = s.iterator(); // Must be in synchronized block
+ * synchronized (multimap) { // Synchronizing on multimap, not values!
+ * Iterator<V> i = values.iterator(); // Must be in synchronized block
* while (i.hasNext()) {
* foo(i.next());
* }
@@ -536,7 +541,7 @@ public final class Multimaps {
}
@Override public Iterator<Collection<V>> iterator() {
final Iterator<Collection<V>> iterator = delegate.iterator();
- return new Iterator<Collection<V>>() {
+ return new UnmodifiableIterator<Collection<V>>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
@@ -545,10 +550,6 @@ public final class Multimaps {
public Collection<V> next() {
return unmodifiableValueCollection(iterator.next());
}
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
};
}
@Override public Object[] toArray() {
@@ -971,7 +972,7 @@ public final class Multimaps {
@Override
public Set<V> get(final K key) {
- return new AbstractSet<V>() {
+ return new Sets.ImprovedAbstractSet<V>() {
@Override public Iterator<V> iterator() {
return new Iterator<V>() {
int i;
@@ -1052,7 +1053,7 @@ public final class Multimaps {
@Override
public Multiset<K> keys() {
- return Multisets.forSet(map.keySet());
+ return new Multimaps.Keys<K, V>(this);
}
@Override
@@ -1103,35 +1104,27 @@ public final class Multimaps {
}
/** @see MapMultimap#asMap */
- class AsMapEntries extends AbstractSet<Entry<K, Collection<V>>> {
+ class AsMapEntries extends Sets.ImprovedAbstractSet<Entry<K, Collection<V>>> {
@Override public int size() {
return map.size();
}
@Override public Iterator<Entry<K, Collection<V>>> iterator() {
- return new Iterator<Entry<K, Collection<V>>>() {
- final Iterator<K> keys = map.keySet().iterator();
-
+ return new TransformedIterator<K, Entry<K, Collection<V>>>(map.keySet().iterator()) {
@Override
- public boolean hasNext() {
- return keys.hasNext();
- }
- @Override
- public Entry<K, Collection<V>> next() {
- final K key = keys.next();
+ Entry<K, Collection<V>> transform(final K key) {
return new AbstractMapEntry<K, Collection<V>>() {
- @Override public K getKey() {
+ @Override
+ public K getKey() {
return key;
}
- @Override public Collection<V> getValue() {
+
+ @Override
+ public Collection<V> getValue() {
return get(key);
}
};
}
- @Override
- public void remove() {
- keys.remove();
- }
};
}
@@ -1233,7 +1226,6 @@ public final class Multimaps {
*
* @since 7.0
*/
- @Beta
public static <K, V1, V2> Multimap<K, V2> transformValues(
Multimap<K, V1> fromMultimap, final Function<? super V1, V2> function) {
checkNotNull(function);
@@ -1302,15 +1294,29 @@ public final class Multimaps {
*
* @since 7.0
*/
- @Beta
public static <K, V1, V2> Multimap<K, V2> transformEntries(
Multimap<K, V1> fromMap,
EntryTransformer<? super K, ? super V1, V2> transformer) {
return new TransformedEntriesMultimap<K, V1, V2>(fromMap, transformer);
}
+
+ static final class ValueFunction<K, V1, V2> implements Function<V1, V2> {
+ private final K key;
+ private final EntryTransformer<? super K, ? super V1, V2> transformer;
+
+ ValueFunction(K key, EntryTransformer<? super K, ? super V1, V2> transformer) {
+ this.key = key;
+ this.transformer = transformer;
+ }
+
+ @Override
+ public V2 apply(@Nullable V1 value) {
+ return transformer.transformEntry(key, value);
+ }
+ }
private static class TransformedEntriesMultimap<K, V1, V2>
- implements Multimap<K, V2> {
+ extends AbstractMultimap<K, V2> {
final Multimap<K, V1> fromMultimap;
final EntryTransformer<? super K, ? super V1, V2> transformer;
@@ -1320,30 +1326,25 @@ public final class Multimaps {
this.transformer = checkNotNull(transformer);
}
- Collection<V2> transform(final K key, Collection<V1> values) {
- return Collections2.transform(values, new Function<V1, V2>() {
- @Override public V2 apply(V1 value) {
- return transformer.transformEntry(key, value);
- }
- });
+ Collection<V2> transform(K key, Collection<V1> values) {
+ Function<V1, V2> function = new ValueFunction<K, V1, V2>(key, transformer);
+ if (values instanceof List) {
+ return Lists.transform((List<V1>) values, function);
+ } else {
+ return Collections2.transform(values, function);
+ }
}
- private transient Map<K, Collection<V2>> asMap;
-
- @Override public Map<K, Collection<V2>> asMap() {
- if (asMap == null) {
- Map<K, Collection<V2>> aM = Maps.transformEntries(fromMultimap.asMap(),
- new EntryTransformer<K, Collection<V1>, Collection<V2>>() {
+ @Override
+ Map<K, Collection<V2>> createAsMap() {
+ return Maps.transformEntries(fromMultimap.asMap(),
+ new EntryTransformer<K, Collection<V1>, Collection<V2>>() {
- @Override public Collection<V2> transformEntry(
- K key, Collection<V1> value) {
- return transform(key, value);
- }
- });
- asMap = aM;
- return aM;
- }
- return asMap;
+ @Override public Collection<V2> transformEntry(
+ K key, Collection<V1> value) {
+ return transform(key, value);
+ }
+ });
}
@Override public void clear() {
@@ -1364,58 +1365,25 @@ public final class Multimaps {
return values().contains(value);
}
- private transient Collection<Entry<K, V2>> entries;
-
- @Override public Collection<Entry<K, V2>> entries() {
- if (entries == null) {
- Collection<Entry<K, V2>> es = new TransformedEntries(transformer);
- entries = es;
- return es;
- }
- return entries;
- }
-
- private class TransformedEntries
- extends TransformedCollection<Entry<K, V1>, Entry<K, V2>> {
-
- TransformedEntries(
- final EntryTransformer<? super K, ? super V1, V2> transformer) {
- super(fromMultimap.entries(),
- new Function<Entry<K, V1>, Entry<K, V2>>() {
- @Override public Entry<K, V2> apply(final Entry<K, V1> entry) {
- return new AbstractMapEntry<K, V2>() {
-
- @Override public K getKey() {
- return entry.getKey();
- }
-
- @Override public V2 getValue() {
- return transformer.transformEntry(
- entry.getKey(), entry.getValue());
- }
- };
- }
- });
- }
-
- @Override public boolean contains(Object o) {
- if (o instanceof Entry) {
- Entry<?, ?> entry = (Entry<?, ?>) o;
- return containsEntry(entry.getKey(), entry.getValue());
- }
- return false;
- }
-
- @SuppressWarnings("unchecked")
- @Override public boolean remove(Object o) {
- if (o instanceof Entry) {
- Entry<?, ?> entry = (Entry<?, ?>) o;
- Collection<V2> values = get((K) entry.getKey());
- return values.remove(entry.getValue());
- }
- return false;
- }
+ @Override
+ Iterator<Entry<K, V2>> entryIterator() {
+ return Iterators.transform(
+ fromMultimap.entries().iterator(), new Function<Entry<K, V1>, Entry<K, V2>>() {
+ @Override
+ public Entry<K, V2> apply(final Entry<K, V1> entry) {
+ return new AbstractMapEntry<K, V2>() {
+ @Override
+ public K getKey() {
+ return entry.getKey();
+ }
+ @Override
+ public V2 getValue() {
+ return transformer.transformEntry(entry.getKey(), entry.getValue());
+ }
+ };
+ }
+ });
}
@Override public Collection<V2> get(final K key) {
@@ -1465,39 +1433,16 @@ public final class Multimaps {
@Override public int size() {
return fromMultimap.size();
}
-
- private transient Collection<V2> values;
-
- @Override public Collection<V2> values() {
- if (values == null) {
- Collection<V2> vs = Collections2.transform(
- fromMultimap.entries(), new Function<Entry<K, V1>, V2>() {
-
- @Override public V2 apply(Entry<K, V1> entry) {
- return transformer.transformEntry(
- entry.getKey(), entry.getValue());
- }
- });
- values = vs;
- return vs;
- }
- return values;
- }
-
- @Override public boolean equals(Object obj) {
- if (obj instanceof Multimap) {
- Multimap<?, ?> other = (Multimap<?, ?>) obj;
- return asMap().equals(other.asMap());
- }
- return false;
- }
-
- @Override public int hashCode() {
- return asMap().hashCode();
- }
-
- @Override public String toString() {
- return asMap().toString();
+
+ @Override
+ Collection<V2> createValues() {
+ return Collections2.transform(
+ fromMultimap.entries(), new Function<Entry<K, V1>, V2>() {
+ @Override public V2 apply(Entry<K, V1> entry) {
+ return transformer.transformEntry(
+ entry.getKey(), entry.getValue());
+ }
+ });
}
}
@@ -1542,7 +1487,6 @@ public final class Multimaps {
*
* @since 7.0
*/
- @Beta
public static <K, V1, V2> ListMultimap<K, V2> transformValues(
ListMultimap<K, V1> fromMultimap,
final Function<? super V1, V2> function) {
@@ -1609,7 +1553,6 @@ public final class Multimaps {
*
* @since 7.0
*/
- @Beta
public static <K, V1, V2> ListMultimap<K, V2> transformEntries(
ListMultimap<K, V1> fromMap,
EntryTransformer<? super K, ? super V1, V2> transformer) {
@@ -1696,24 +1639,6 @@ public final class Multimaps {
}
/**
- * <b>Deprecated.</b>
- *
- * @since 10.0
- * @deprecated use {@link #index(Iterator, Function)} by casting {@code
- * values} to {@code Iterator<V>}, or better yet, by implementing only
- * {@code Iterator} and not {@code Iterable}. <b>This method is scheduled
- * for deletion in March 2012.</b>
- */
- @Beta
- @Deprecated
- public static <K, V, I extends Object & Iterable<V> & Iterator<V>>
- ImmutableListMultimap<K, V> index(
- I values, Function<? super V, K> keyFunction) {
- Iterable<V> valuesIterable = checkNotNull(values);
- return index(valuesIterable, keyFunction);
- }
-
- /**
* Creates an index {@code ImmutableListMultimap} that contains the results of
* applying a specified function to each item in an {@code Iterator} of
* values. Each value will be stored as a value in the resulting multimap,
@@ -1769,39 +1694,36 @@ public final class Multimaps {
return builder.build();
}
- static abstract class Keys<K, V> extends AbstractMultiset<K> {
- abstract Multimap<K, V> multimap();
+ static class Keys<K, V> extends AbstractMultiset<K> {
+ final Multimap<K, V> multimap;
+
+ Keys(Multimap<K, V> multimap) {
+ this.multimap = multimap;
+ }
@Override Iterator<Multiset.Entry<K>> entryIterator() {
- final Iterator<Map.Entry<K, Collection<V>>> backingIterator =
- multimap().asMap().entrySet().iterator();
- return new Iterator<Multiset.Entry<K>>() {
- @Override public boolean hasNext() {
- return backingIterator.hasNext();
- }
-
- @Override public Multiset.Entry<K> next() {
- final Map.Entry<K, Collection<V>> backingEntry =
- backingIterator.next();
+ return new TransformedIterator<Map.Entry<K, Collection<V>>, Multiset.Entry<K>>(
+ multimap.asMap().entrySet().iterator()) {
+ @Override
+ Multiset.Entry<K> transform(
+ final Map.Entry<K, Collection<V>> backingEntry) {
return new Multisets.AbstractEntry<K>() {
- @Override public K getElement() {
+ @Override
+ public K getElement() {
return backingEntry.getKey();
}
- @Override public int getCount() {
+ @Override
+ public int getCount() {
return backingEntry.getValue().size();
}
};
}
-
- @Override public void remove() {
- backingIterator.remove();
- }
};
}
@Override int distinctElements() {
- return multimap().asMap().size();
+ return multimap.asMap().size();
}
@Override Set<Multiset.Entry<K>> createEntrySet() {
@@ -1822,22 +1744,22 @@ public final class Multimaps {
}
@Override public boolean isEmpty() {
- return multimap().isEmpty();
+ return multimap.isEmpty();
}
@Override public boolean contains(@Nullable Object o) {
- if (o instanceof Multiset.Entry<?>) {
+ if (o instanceof Multiset.Entry) {
Multiset.Entry<?> entry = (Multiset.Entry<?>) o;
- Collection<V> collection = multimap().asMap().get(entry.getElement());
+ Collection<V> collection = multimap.asMap().get(entry.getElement());
return collection != null && collection.size() == entry.getCount();
}
return false;
}
@Override public boolean remove(@Nullable Object o) {
- if (o instanceof Multiset.Entry<?>) {
+ if (o instanceof Multiset.Entry) {
Multiset.Entry<?> entry = (Multiset.Entry<?>) o;
- Collection<V> collection = multimap().asMap().get(entry.getElement());
+ Collection<V> collection = multimap.asMap().get(entry.getElement());
if (collection != null && collection.size() == entry.getCount()) {
collection.clear();
return true;
@@ -1848,30 +1770,16 @@ public final class Multimaps {
}
@Override public boolean contains(@Nullable Object element) {
- return multimap().containsKey(element);
+ return multimap.containsKey(element);
}
@Override public Iterator<K> iterator() {
- return Iterators.transform(multimap().entries().iterator(),
- new Function<Map.Entry<K, V>, K>() {
- @Override public K apply(Map.Entry<K, V> entry) {
- return entry.getKey();
- }
- });
+ return Maps.keyIterator(multimap.entries().iterator());
}
@Override public int count(@Nullable Object element) {
- try {
- if (multimap().containsKey(element)) {
- Collection<V> values = multimap().asMap().get(element);
- return (values == null) ? 0 : values.size();
- }
- return 0;
- } catch (ClassCastException e) {
- return 0;
- } catch (NullPointerException e) {
- return 0;
- }
+ Collection<V> values = Maps.safeGet(multimap.asMap(), element);
+ return (values == null) ? 0 : values.size();
}
@Override public int remove(@Nullable Object element, int occurrences) {
@@ -1880,14 +1788,7 @@ public final class Multimaps {
return count(element);
}
- Collection<V> values;
- try {
- values = multimap().asMap().get(element);
- } catch (ClassCastException e) {
- return 0;
- } catch (NullPointerException e) {
- return 0;
- }
+ Collection<V> values = Maps.safeGet(multimap.asMap(), element);
if (values == null) {
return 0;
@@ -1907,45 +1808,35 @@ public final class Multimaps {
}
@Override public void clear() {
- multimap().clear();
+ multimap.clear();
}
@Override public Set<K> elementSet() {
- return multimap().keySet();
+ return multimap.keySet();
}
}
- static abstract class Values<K, V> extends AbstractCollection<V> {
- abstract Multimap<K, V> multimap();
+ static class Values<K, V> extends AbstractCollection<V> {
+ final Multimap<K, V> multimap;
+
+ Values(Multimap<K, V> multimap) {
+ this.multimap = multimap;
+ }
@Override public Iterator<V> iterator() {
- final Iterator<Map.Entry<K, V>> backingIterator =
- multimap().entries().iterator();
- return new Iterator<V>() {
- @Override public boolean hasNext() {
- return backingIterator.hasNext();
- }
-
- @Override public V next() {
- return backingIterator.next().getValue();
- }
-
- @Override public void remove() {
- backingIterator.remove();
- }
- };
+ return Maps.valueIterator(multimap.entries().iterator());
}
@Override public int size() {
- return multimap().size();
+ return multimap.size();
}
@Override public boolean contains(@Nullable Object o) {
- return multimap().containsValue(o);
+ return multimap.containsValue(o);
}
@Override public void clear() {
- multimap().clear();
+ multimap.clear();
}
}
@@ -1961,7 +1852,7 @@ public final class Multimaps {
}
@Override public boolean contains(@Nullable Object o) {
- if (o instanceof Map.Entry<?, ?>) {
+ if (o instanceof Map.Entry) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
return multimap().containsEntry(entry.getKey(), entry.getValue());
}
@@ -1969,7 +1860,7 @@ public final class Multimaps {
}
@Override public boolean remove(@Nullable Object o) {
- if (o instanceof Map.Entry<?, ?>) {
+ if (o instanceof Map.Entry) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
return multimap().remove(entry.getKey(), entry.getValue());
}
@@ -2062,476 +1953,15 @@ public final class Multimaps {
/**
* Support removal operations when filtering a filtered multimap. Since a
* filtered multimap has iterators that don't support remove, passing one to
- * the FilteredMultimap constructor would lead to a multimap whose removal
+ * the FilteredEntryMultimap constructor would lead to a multimap whose removal
* operations would fail. This method combines the predicates to avoid that
* problem.
*/
- private static <K, V> Multimap<K, V> filterFiltered(FilteredMultimap<K, V> map,
+ private static <K, V> Multimap<K, V> filterFiltered(FilteredMultimap<K, V> multimap,
Predicate<? super Entry<K, V>> entryPredicate) {
Predicate<Entry<K, V>> predicate
- = Predicates.and(map.predicate, entryPredicate);
- return new FilteredMultimap<K, V>(map.unfiltered, predicate);
- }
-
- private static class FilteredMultimap<K, V> implements Multimap<K, V> {
- final Multimap<K, V> unfiltered;
- final Predicate<? super Entry<K, V>> predicate;
-
- FilteredMultimap(Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> predicate) {
- this.unfiltered = unfiltered;
- this.predicate = predicate;
- }
-
- @Override public int size() {
- return entries().size();
- }
-
- @Override public boolean isEmpty() {
- return entries().isEmpty();
- }
-
- @Override public boolean containsKey(Object key) {
- return asMap().containsKey(key);
- }
-
- @Override public boolean containsValue(Object value) {
- return values().contains(value);
- }
-
- // This method should be called only when key is a K and value is a V.
- @SuppressWarnings("unchecked")
- boolean satisfiesPredicate(Object key, Object value) {
- return predicate.apply(Maps.immutableEntry((K) key, (V) value));
- }
-
- @Override public boolean containsEntry(Object key, Object value) {
- return unfiltered.containsEntry(key, value) && satisfiesPredicate(key, value);
- }
-
- @Override public boolean put(K key, V value) {
- checkArgument(satisfiesPredicate(key, value));
- return unfiltered.put(key, value);
- }
-
- @Override public boolean remove(Object key, Object value) {
- return containsEntry(key, value) ? unfiltered.remove(key, value) : false;
- }
-
- @Override public boolean putAll(K key, Iterable<? extends V> values) {
- for (V value : values) {
- checkArgument(satisfiesPredicate(key, value));
- }
- return unfiltered.putAll(key, values);
- }
-
- @Override public boolean putAll(Multimap<? extends K, ? extends V> multimap) {
- for (Entry<? extends K, ? extends V> entry : multimap.entries()) {
- checkArgument(satisfiesPredicate(entry.getKey(), entry.getValue()));
- }
- return unfiltered.putAll(multimap);
- }
-
- @Override public Collection<V> replaceValues(K key, Iterable<? extends V> values) {
- for (V value : values) {
- checkArgument(satisfiesPredicate(key, value));
- }
- // Not calling unfiltered.replaceValues() since values that don't satisify
- // the filter should remain in the multimap.
- Collection<V> oldValues = removeAll(key);
- unfiltered.putAll(key, values);
- return oldValues;
- }
-
- @Override public Collection<V> removeAll(Object key) {
- List<V> removed = Lists.newArrayList();
- Collection<V> values = unfiltered.asMap().get(key);
- if (values != null) {
- Iterator<V> iterator = values.iterator();
- while (iterator.hasNext()) {
- V value = iterator.next();
- if (satisfiesPredicate(key, value)) {
- removed.add(value);
- iterator.remove();
- }
- }
- }
- if (unfiltered instanceof SetMultimap) {
- return Collections.unmodifiableSet(Sets.newLinkedHashSet(removed));
- } else {
- return Collections.unmodifiableList(removed);
- }
- }
-
- @Override public void clear() {
- entries().clear();
- }
-
- @Override public boolean equals(@Nullable Object object) {
- if (object == this) {
- return true;
- }
- if (object instanceof Multimap) {
- Multimap<?, ?> that = (Multimap<?, ?>) object;
- return asMap().equals(that.asMap());
- }
- return false;
- }
-
- @Override public int hashCode() {
- return asMap().hashCode();
- }
-
- @Override public String toString() {
- return asMap().toString();
- }
-
- class ValuePredicate implements Predicate<V> {
- final K key;
- ValuePredicate(K key) {
- this.key = key;
- }
- @Override public boolean apply(V value) {
- return satisfiesPredicate(key, value);
- }
- }
-
- Collection<V> filterCollection(Collection<V> collection, Predicate<V> predicate) {
- if (collection instanceof Set) {
- return Sets.filter((Set<V>) collection, predicate);
- } else {
- return Collections2.filter(collection, predicate);
- }
- }
-
- @Override public Collection<V> get(K key) {
- return filterCollection(unfiltered.get(key), new ValuePredicate(key));
- }
-
- @Override public Set<K> keySet() {
- return asMap().keySet();
- }
-
- Collection<V> values;
-
- @Override public Collection<V> values() {
- return (values == null) ? values = new Values() : values;
- }
-
- class Values extends Multimaps.Values<K, V> {
- @Override Multimap<K, V> multimap() {
- return FilteredMultimap.this;
- }
-
- @Override public boolean contains(@Nullable Object o) {
- return Iterators.contains(iterator(), o);
- }
-
- // Override remove methods since iterator doesn't support remove.
-
- @Override public boolean remove(Object o) {
- Iterator<Entry<K, V>> iterator = unfiltered.entries().iterator();
- while (iterator.hasNext()) {
- Entry<K, V> entry = iterator.next();
- if (Objects.equal(o, entry.getValue()) && predicate.apply(entry)) {
- iterator.remove();
- return true;
- }
- }
- return false;
- }
-
- @Override public boolean removeAll(Collection<?> c) {
- boolean changed = false;
- Iterator<Entry<K, V>> iterator = unfiltered.entries().iterator();
- while (iterator.hasNext()) {
- Entry<K, V> entry = iterator.next();
- if (c.contains(entry.getValue()) && predicate.apply(entry)) {
- iterator.remove();
- changed = true;
- }
- }
- return changed;
- }
-
- @Override public boolean retainAll(Collection<?> c) {
- boolean changed = false;
- Iterator<Entry<K, V>> iterator = unfiltered.entries().iterator();
- while (iterator.hasNext()) {
- Entry<K, V> entry = iterator.next();
- if (!c.contains(entry.getValue()) && predicate.apply(entry)) {
- iterator.remove();
- changed = true;
- }
- }
- return changed;
- }
- }
-
- Collection<Entry<K, V>> entries;
-
- @Override public Collection<Entry<K, V>> entries() {
- return (entries == null)
- ? entries = Collections2.filter(unfiltered.entries(), predicate)
- : entries;
- }
-
- /**
- * Remove all filtered asMap() entries that satisfy the predicate.
- */
- boolean removeEntriesIf(Predicate<Map.Entry<K, Collection<V>>> removalPredicate) {
- Iterator<Map.Entry<K, Collection<V>>> iterator = unfiltered.asMap().entrySet().iterator();
- boolean changed = false;
- while (iterator.hasNext()) {
- // Determine whether to remove the filtered values with this key.
- Map.Entry<K, Collection<V>> entry = iterator.next();
- K key = entry.getKey();
- Collection<V> collection = entry.getValue();
- Predicate<V> valuePredicate = new ValuePredicate(key);
- Collection<V> filteredCollection = filterCollection(collection, valuePredicate);
- Map.Entry<K, Collection<V>> filteredEntry = Maps.immutableEntry(key, filteredCollection);
- if (removalPredicate.apply(filteredEntry) && !filteredCollection.isEmpty()) {
- changed = true;
- if (Iterables.all(collection, valuePredicate)) {
- iterator.remove(); // Remove all values for the key.
- } else {
- filteredCollection.clear(); // Remove the filtered values only.
- }
- }
- }
- return changed;
- }
-
- Map<K, Collection<V>> asMap;
-
- @Override public Map<K, Collection<V>> asMap() {
- return (asMap == null) ? asMap = createAsMap() : asMap;
- }
-
- static final Predicate<Collection<?>> NOT_EMPTY = new Predicate<Collection<?>>() {
- @Override public boolean apply(Collection<?> input) {
- return !input.isEmpty();
- }
- };
-
- Map<K, Collection<V>> createAsMap() {
- // Select the values that satisify the predicate.
- EntryTransformer<K, Collection<V>, Collection<V>> transformer
- = new EntryTransformer<K, Collection<V>, Collection<V>>() {
- @Override public Collection<V> transformEntry(K key, Collection<V> collection) {
- return filterCollection(collection, new ValuePredicate(key));
- }
- };
- Map<K, Collection<V>> transformed
- = Maps.transformEntries(unfiltered.asMap(), transformer);
-
- // Select the keys that have at least one value remaining.
- Map<K, Collection<V>> filtered = Maps.filterValues(transformed, NOT_EMPTY);
-
- // Override the removal methods, since removing a map entry should not
- // affect values that don't satisfy the filter.
- return new AsMap(filtered);
- }
-
- class AsMap extends ForwardingMap<K, Collection<V>> {
- final Map<K, Collection<V>> delegate;
-
- AsMap(Map<K, Collection<V>> delegate) {
- this.delegate = delegate;
- }
-
- @Override protected Map<K, Collection<V>> delegate() {
- return delegate;
- }
-
- @Override public Collection<V> remove(Object o) {
- Collection<V> output = FilteredMultimap.this.removeAll(o);
- return output.isEmpty() ? null : output;
- }
-
- @Override public void clear() {
- FilteredMultimap.this.clear();
- }
-
- Set<K> keySet;
-
- @Override public Set<K> keySet() {
- return (keySet == null) ? keySet = new KeySet() : keySet;
- }
-
- class KeySet extends Maps.KeySet<K, Collection<V>> {
- @Override Map<K, Collection<V>> map() {
- return AsMap.this;
- }
-
- @Override public boolean remove(Object o) {
- Collection<V> collection = delegate.get(o);
- if (collection == null) {
- return false;
- }
- collection.clear();
- return true;
- }
-
- @Override public boolean removeAll(Collection<?> c) {
- return Sets.removeAllImpl(this, c);
- }
-
- @Override public boolean retainAll(final Collection<?> c) {
- Predicate<Map.Entry<K, Collection<V>>> removalPredicate
- = new Predicate<Map.Entry<K, Collection<V>>>() {
- @Override public boolean apply(Map.Entry<K, Collection<V>> entry) {
- return !c.contains(entry.getKey());
- }
- };
- return removeEntriesIf(removalPredicate);
- }
- }
-
- Values asMapValues;
-
- @Override public Collection<Collection<V>> values() {
- return (asMapValues == null) ? asMapValues = new Values() : asMapValues;
- }
-
- class Values extends Maps.Values<K, Collection<V>> {
- @Override Map<K, Collection<V>> map() {
- return AsMap.this;
- }
-
- @Override public boolean remove(Object o) {
- for (Collection<V> collection : this) {
- if (collection.equals(o)) {
- collection.clear();
- return true;
- }
- }
- return false;
- }
-
- @Override public boolean removeAll(final Collection<?> c) {
- Predicate<Map.Entry<K, Collection<V>>> removalPredicate
- = new Predicate<Map.Entry<K, Collection<V>>>() {
- @Override public boolean apply(Map.Entry<K, Collection<V>> entry) {
- return c.contains(entry.getValue());
- }
- };
- return removeEntriesIf(removalPredicate);
- }
-
- @Override public boolean retainAll(final Collection<?> c) {
- Predicate<Map.Entry<K, Collection<V>>> removalPredicate
- = new Predicate<Map.Entry<K, Collection<V>>>() {
- @Override public boolean apply(Map.Entry<K, Collection<V>> entry) {
- return !c.contains(entry.getValue());
- }
- };
- return removeEntriesIf(removalPredicate);
- }
- }
-
- EntrySet entrySet;
-
- @Override public Set<Map.Entry<K, Collection<V>>> entrySet() {
- return (entrySet == null) ? entrySet = new EntrySet(super.entrySet()) : entrySet;
- }
-
- class EntrySet extends Maps.EntrySet<K, Collection<V>> {
- Set<Map.Entry<K, Collection<V>>> delegateEntries;
-
- public EntrySet(Set<Map.Entry<K, Collection<V>>> delegateEntries) {
- this.delegateEntries = delegateEntries;
- }
-
- @Override Map<K, Collection<V>> map() {
- return AsMap.this;
- }
-
- @Override public Iterator<Map.Entry<K, Collection<V>>> iterator() {
- return delegateEntries.iterator();
- }
-
- @Override public boolean remove(Object o) {
- if (o instanceof Entry<?, ?>) {
- Entry<?, ?> entry = (Entry<?, ?>) o;
- Collection<V> collection = delegate.get(entry.getKey());
- if (collection != null && collection.equals(entry.getValue())) {
- collection.clear();
- return true;
- }
- }
- return false;
- }
-
- @Override public boolean removeAll(Collection<?> c) {
- return Sets.removeAllImpl(this, c);
- }
-
- @Override public boolean retainAll(final Collection<?> c) {
- Predicate<Map.Entry<K, Collection<V>>> removalPredicate
- = new Predicate<Map.Entry<K, Collection<V>>>() {
- @Override public boolean apply(Map.Entry<K, Collection<V>> entry) {
- return !c.contains(entry);
- }
- };
- return removeEntriesIf(removalPredicate);
- }
- }
- }
-
- AbstractMultiset<K> keys;
-
- @Override public Multiset<K> keys() {
- return (keys == null) ? keys = new Keys() : keys;
- }
-
- class Keys extends Multimaps.Keys<K, V> {
- @Override Multimap<K, V> multimap() {
- return FilteredMultimap.this;
- }
-
- @Override public int remove(Object o, int occurrences) {
- checkArgument(occurrences >= 0);
- Collection<V> values = unfiltered.asMap().get(o);
- if (values == null) {
- return 0;
- }
- int priorCount = 0;
- int removed = 0;
- Iterator<V> iterator = values.iterator();
- while (iterator.hasNext()) {
- if (satisfiesPredicate(o, iterator.next())) {
- priorCount++;
- if (removed < occurrences) {
- iterator.remove();
- removed++;
- }
- }
- }
- return priorCount;
- }
-
- @Override Set<Multiset.Entry<K>> createEntrySet() {
- return new EntrySet();
- }
-
- class EntrySet extends Multimaps.Keys<K, V>.KeysEntrySet {
- @Override public boolean removeAll(Collection<?> c) {
- return Sets.removeAllImpl(this, c);
- }
-
- @Override public boolean retainAll(final Collection<?> c) {
- Predicate<Map.Entry<K, Collection<V>>> removalPredicate
- = new Predicate<Map.Entry<K, Collection<V>>>() {
- @Override public boolean apply(Map.Entry<K, Collection<V>> entry) {
- Multiset.Entry<K> multisetEntry
- = Multisets.immutableEntry(entry.getKey(), entry.getValue().size());
- return !c.contains(multisetEntry);
- }
- };
- return removeEntriesIf(removalPredicate);
- }
- }
- }
+ = Predicates.and(multimap.entryPredicate(), entryPredicate);
+ return new FilteredEntryMultimap<K, V>(multimap.unfiltered, predicate);
}
// TODO(jlevy): Create methods that filter a SetMultimap or SortedSetMultimap.
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ObjectArrays.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ObjectArrays.java
index 087fd178e..6465ee740 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ObjectArrays.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ObjectArrays.java
@@ -30,6 +30,8 @@ import javax.annotation.Nullable;
*/
@GwtCompatible(emulated = true)
public final class ObjectArrays {
+ static final Object[] EMPTY_ARRAY = new Object[0];
+
private ObjectArrays() {}
/**
@@ -55,7 +57,7 @@ public final class ObjectArrays {
public static <T> T[] concat(@Nullable T element, T[] array) {
T[] result = newArray(array, array.length + 1);
result[0] = element;
- Platform.unsafeArrayCopy(array, 0, result, 1, array.length);
+ System.arraycopy(array, 0, result, 1, array.length);
return result;
}
@@ -77,7 +79,7 @@ public final class ObjectArrays {
/** GWT safe version of Arrays.copyOf. */
static <T> T[] arraysCopyOf(T[] original, int newLength) {
T[] copy = newArray(original, newLength);
- Platform.unsafeArrayCopy(
+ System.arraycopy(
original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
@@ -152,5 +154,14 @@ public final class ObjectArrays {
array[i] = array[j];
array[j] = temp;
}
+
+ // We do this instead of Preconditions.checkNotNull to save boxing and array
+ // creation cost.
+ static Object checkElementNotNull(Object element, int index) {
+ if (element == null) {
+ throw new NullPointerException("at index " + index);
+ }
+ return element;
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Platform.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Platform.java
index 4fbdde014..3fe815044 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Platform.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Platform.java
@@ -16,6 +16,14 @@
package com.google.common.collect;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Maps.EntryTransformer;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.SortedSet;
+
/**
* Minimal GWT emulation of {@code com.google.common.collect.Platform}.
*
@@ -29,19 +37,6 @@ class Platform {
return GwtPlatform.clone(array);
}
- // TODO: Fix System.arraycopy in GWT so that it isn't necessary.
- static void unsafeArrayCopy(
- Object[] src, int srcPos, Object[] dest, int destPos, int length) {
- for (int i = 0; i < length; i++) {
- dest[destPos + i] = src[srcPos + i];
- }
- }
-
- static <T> T[] newArray(Class<T> type, int length) {
- throw new UnsupportedOperationException(
- "Platform.newArray is not supported in GWT yet.");
- }
-
static <T> T[] newArray(T[] reference, int length) {
return GwtPlatform.newArray(reference, length);
}
@@ -49,4 +44,25 @@ class Platform {
static MapMaker tryWeakKeys(MapMaker mapMaker) {
return mapMaker;
}
+
+ static <K, V1, V2> SortedMap<K, V2> mapsTransformEntriesSortedMap(
+ SortedMap<K, V1> fromMap,
+ EntryTransformer<? super K, ? super V1, V2> transformer) {
+ return Maps.transformEntriesIgnoreNavigable(fromMap, transformer);
+ }
+
+ static <K, V> SortedMap<K, V> mapsAsMapSortedSet(
+ SortedSet<K> set, Function<? super K, V> function) {
+ return Maps.asMapSortedIgnoreNavigable(set, function);
+ }
+
+ static <E> SortedSet<E> setsFilterSortedSet(
+ SortedSet<E> unfiltered, Predicate<? super E> predicate) {
+ return Sets.filterSortedIgnoreNavigable(unfiltered, predicate);
+ }
+
+ static <K, V> SortedMap<K, V> mapsFilterSortedMap(
+ SortedMap<K, V> unfiltered, Predicate<? super Map.Entry<K, V>> predicate) {
+ return Maps.filterSortedIgnoreNavigable(unfiltered, predicate);
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularContiguousSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularContiguousSet.java
index a63b391d9..999f6eec4 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularContiguousSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularContiguousSet.java
@@ -39,32 +39,33 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C>
this.range = range;
}
- // Abstract method doesn't exist in GWT emulation
- /* @Override */ ContiguousSet<C> headSetImpl(C toElement, boolean inclusive) {
- return range.intersection(Ranges.upTo(toElement, BoundType.forBoolean(inclusive)))
- .asSet(domain);
+ private ContiguousSet<C> intersectionInCurrentDomain(Range<C> other) {
+ return (range.isConnected(other))
+ ? ContiguousSet.create(range.intersection(other), domain)
+ : new EmptyContiguousSet<C>(domain);
}
- // Abstract method doesn't exist in GWT emulation
- /* @Override */ int indexOf(Object target) {
- return contains(target) ? (int) domain.distance(first(), (C) target) : -1;
+ @Override ContiguousSet<C> headSetImpl(C toElement, boolean inclusive) {
+ return intersectionInCurrentDomain(Range.upTo(toElement, BoundType.forBoolean(inclusive)));
}
- // Abstract method doesn't exist in GWT emulation
- /* @Override */ ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive, C toElement,
+ @Override ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive, C toElement,
boolean toInclusive) {
- return range.intersection(Ranges.range(fromElement, BoundType.forBoolean(fromInclusive),
- toElement, BoundType.forBoolean(toInclusive))).asSet(domain);
+ if (fromElement.compareTo(toElement) == 0 && !fromInclusive && !toInclusive) {
+ // Range would reject our attempt to create (x, x).
+ return new EmptyContiguousSet<C>(domain);
+ }
+ return intersectionInCurrentDomain(Range.range(
+ fromElement, BoundType.forBoolean(fromInclusive),
+ toElement, BoundType.forBoolean(toInclusive)));
}
- // Abstract method doesn't exist in GWT emulation
- /* @Override */ ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive) {
- return range.intersection(Ranges.downTo(fromElement, BoundType.forBoolean(inclusive)))
- .asSet(domain);
+ @Override ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive) {
+ return intersectionInCurrentDomain(Range.downTo(fromElement, BoundType.forBoolean(inclusive)));
}
@Override public UnmodifiableIterator<C> iterator() {
- return new AbstractLinkedIterator<C>(first()) {
+ return new AbstractSequentialIterator<C>(first()) {
final C last = last();
@Override
@@ -95,7 +96,10 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C>
return (distance >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) distance + 1;
}
- @Override public boolean contains(Object object) {
+ @Override public boolean contains(@Nullable Object object) {
+ if (object == null) {
+ return false;
+ }
try {
return range.contains((C) object);
} catch (ClassCastException e) {
@@ -104,11 +108,7 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C>
}
@Override public boolean containsAll(Collection<?> targets) {
- try {
- return range.containsAll((Iterable<? extends C>) targets);
- } catch (ClassCastException e) {
- return false;
- }
+ return Collections2.containsAllImpl(this, targets);
}
@Override public boolean isEmpty() {
@@ -134,7 +134,7 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C>
C lowerEndpoint = Ordering.natural().max(this.first(), other.first());
C upperEndpoint = Ordering.natural().min(this.last(), other.last());
return (lowerEndpoint.compareTo(upperEndpoint) < 0)
- ? Ranges.closed(lowerEndpoint, upperEndpoint).asSet(domain)
+ ? ContiguousSet.create(Range.closed(lowerEndpoint, upperEndpoint), domain)
: new EmptyContiguousSet<C>(domain);
}
}
@@ -144,14 +144,14 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C>
}
@Override public Range<C> range(BoundType lowerBoundType, BoundType upperBoundType) {
- return Ranges.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain),
+ return Range.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain),
range.upperBound.withUpperBoundType(upperBoundType, domain));
}
- @Override public boolean equals(Object object) {
+ @Override public boolean equals(@Nullable Object object) {
if (object == this) {
return true;
- } else if (object instanceof RegularContiguousSet<?>) {
+ } else if (object instanceof RegularContiguousSet) {
RegularContiguousSet<?> that = (RegularContiguousSet<?>) object;
if (this.domain.equals(that.domain)) {
return this.first().equals(that.first())
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableList.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableList.java
index 5cdddfff2..f82a6b6c7 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableList.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableList.java
@@ -16,6 +16,8 @@
package com.google.common.collect;
+import static java.util.Collections.unmodifiableList;
+
import java.util.List;
/**
@@ -23,8 +25,15 @@ import java.util.List;
*
* @author Hayward Chan
*/
-class RegularImmutableList<E> extends ImmutableList<E> {
+class RegularImmutableList<E> extends ForwardingImmutableList<E> {
+ private final List<E> delegate;
+
RegularImmutableList(List<E> delegate) {
- super(delegate);
+ // TODO(cpovirk): avoid redundant unmodifiableList wrapping
+ this.delegate = unmodifiableList(delegate);
+ }
+
+ @Override List<E> delegateList() {
+ return delegate;
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableMap.java
index bdf536bb7..b79ce805e 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableMap.java
@@ -23,7 +23,7 @@ import java.util.Map;
*
* @author Hayward Chan
*/
-final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
+final class RegularImmutableMap<K, V> extends ForwardingImmutableMap<K, V> {
RegularImmutableMap(Map<? extends K, ? extends V> delegate) {
super(delegate);
@@ -31,5 +31,5 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
RegularImmutableMap(Entry<? extends K, ? extends V>... entries) {
super(entries);
- }
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSet.java
index f462ade08..72da3e435 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSet.java
@@ -25,7 +25,7 @@ import java.util.Set;
*
* @author Hayward Chan
*/
-final class RegularImmutableSet<E> extends ImmutableSet<E> {
+final class RegularImmutableSet<E> extends ForwardingImmutableSet<E> {
RegularImmutableSet(Set<E> delegate) {
super(delegate);
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedMap.java
new file mode 100644
index 000000000..2e36f395a
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedMap.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import java.util.Comparator;
+import java.util.SortedMap;
+
+/**
+ * GWT emulated version of {@link RegularImmutableSortedMap}.
+ *
+ * @author Chris Povirk
+ */
+final class RegularImmutableSortedMap<K, V> extends ImmutableSortedMap<K, V> {
+ RegularImmutableSortedMap(SortedMap<K, V> delegate, Comparator<? super K> comparator) {
+ super(delegate, comparator);
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedSet.java
index 8256e981d..32794499e 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedSet.java
@@ -32,4 +32,8 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> {
super(delegate);
this.isSubset = isSubset;
}
+
+ @Override ImmutableList<E> createAsList() {
+ return new ImmutableSortedAsList<E>(this, ImmutableList.<E>asImmutableList(toArray()));
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Sets.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Sets.java
index be31363bc..bb06a9c18 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Sets.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Sets.java
@@ -19,15 +19,10 @@ package com.google.common.collect;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
-import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2.FilteredCollection;
-import com.google.common.math.IntMath;
import java.io.Serializable;
import java.util.AbstractSet;
@@ -50,7 +45,11 @@ import javax.annotation.Nullable;
/**
* Static utility methods pertaining to {@link Set} instances. Also see this
- * class's counterparts {@link Lists} and {@link Maps}.
+ * class's counterparts {@link Lists}, {@link Maps} and {@link Queues}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Sets">
+ * {@code Sets}</a>.
*
* @author Kevin Bourrillion
* @author Jared Levy
@@ -62,6 +61,22 @@ public final class Sets {
private Sets() {}
/**
+ * {@link AbstractSet} substitute without the potentially-quadratic
+ * {@code removeAll} implementation.
+ */
+ abstract static class ImprovedAbstractSet<E> extends AbstractSet<E> {
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return removeAllImpl(this, c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return super.retainAll(checkNotNull(c)); // GWT compatibility
+ }
+ }
+
+ /**
* Returns an immutable set instance containing the given enum elements.
* Internally, the returned set will be backed by an {@link EnumSet}.
*
@@ -76,7 +91,7 @@ public final class Sets {
@GwtCompatible(serializable = true)
public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet(
E anElement, E... otherElements) {
- return new ImmutableEnumSet<E>(EnumSet.of(anElement, otherElements));
+ return ImmutableEnumSet.asImmutable(EnumSet.of(anElement, otherElements));
}
/**
@@ -94,20 +109,25 @@ public final class Sets {
@GwtCompatible(serializable = true)
public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet(
Iterable<E> elements) {
- Iterator<E> iterator = elements.iterator();
- if (!iterator.hasNext()) {
- return ImmutableSet.of();
- }
- if (elements instanceof EnumSet) {
- EnumSet<E> enumSetClone = EnumSet.copyOf((EnumSet<E>) elements);
- return new ImmutableEnumSet<E>(enumSetClone);
- }
- E first = iterator.next();
- EnumSet<E> set = EnumSet.of(first);
- while (iterator.hasNext()) {
- set.add(iterator.next());
+ if (elements instanceof ImmutableEnumSet) {
+ return (ImmutableEnumSet<E>) elements;
+ } else if (elements instanceof Collection) {
+ Collection<E> collection = (Collection<E>) elements;
+ if (collection.isEmpty()) {
+ return ImmutableSet.of();
+ } else {
+ return ImmutableEnumSet.asImmutable(EnumSet.copyOf(collection));
+ }
+ } else {
+ Iterator<E> itr = elements.iterator();
+ if (itr.hasNext()) {
+ EnumSet<E> enumSet = EnumSet.of(itr.next());
+ Iterators.addAll(enumSet, itr);
+ return ImmutableEnumSet.asImmutable(enumSet);
+ } else {
+ return ImmutableSet.of();
+ }
}
- return new ImmutableEnumSet<E>(set);
}
/**
@@ -118,20 +138,6 @@ public final class Sets {
*/
public static <E extends Enum<E>> EnumSet<E> newEnumSet(Iterable<E> iterable,
Class<E> elementType) {
- /*
- * TODO(cpovirk): noneOf() and addAll() will both throw
- * NullPointerExceptions when appropriate. However, NullPointerTester will
- * fail on this method because it passes in Class.class instead of an enum
- * type. This means that, when iterable is null but elementType is not,
- * noneOf() will throw a ClassCastException before addAll() has a chance to
- * throw a NullPointerException. NullPointerTester considers this a failure.
- * Ideally the test would be fixed, but it would require a special case for
- * Class<E> where E extends Enum. Until that happens (if ever), leave
- * checkNotNull() here. For now, contemplate the irony that checking
- * elementType, the problem argument, is harmful, while checking iterable,
- * the innocent bystander, is effective.
- */
- checkNotNull(iterable);
EnumSet<E> set = EnumSet.noneOf(elementType);
Iterables.addAll(set, iterable);
return set;
@@ -566,6 +572,11 @@ public final class Sets {
* <p><b>Note:</b> The returned view performs better when {@code set1} is the
* smaller of the two sets. If you have reason to believe one of your sets
* will generally be smaller than the other, pass it first.
+ *
+ * <p>Further, note that the current implementation is not suitable for nested
+ * {@code union} views, i.e. the following should be avoided when in a loop:
+ * {@code union = Sets.union(union, anotherSet);}, since iterating over the resulting
+ * set has a cubic complexity to the depth of the nesting.
*/
public static <E> SetView<E> union(
final Set<? extends E> set1, final Set<? extends E> set2) {
@@ -796,10 +807,13 @@ public final class Sets {
*
* @since 11.0
*/
- @Beta
- @SuppressWarnings("unchecked")
public static <E> SortedSet<E> filter(
SortedSet<E> unfiltered, Predicate<? super E> predicate) {
+ return Platform.setsFilterSortedSet(unfiltered, predicate);
+ }
+
+ static <E> SortedSet<E> filterSortedIgnoreNavigable(
+ SortedSet<E> unfiltered, Predicate<? super E> predicate) {
if (unfiltered instanceof FilteredSet) {
// Support clear(), removeAll(), and retainAll() when filtering a filtered
// collection.
@@ -814,21 +828,13 @@ public final class Sets {
checkNotNull(unfiltered), checkNotNull(predicate));
}
- private static class FilteredSortedSet<E> extends FilteredCollection<E>
+ private static class FilteredSortedSet<E> extends FilteredSet<E>
implements SortedSet<E> {
FilteredSortedSet(SortedSet<E> unfiltered, Predicate<? super E> predicate) {
super(unfiltered, predicate);
}
- @Override public boolean equals(@Nullable Object object) {
- return equalsImpl(this, object);
- }
-
- @Override public int hashCode() {
- return hashCodeImpl(this);
- }
-
@Override
public Comparator<? super E> comparator() {
return ((SortedSet<E>) unfiltered).comparator();
@@ -889,12 +895,22 @@ public final class Sets {
* <li>{@code ImmutableList.of(2, "C")}
* </ul>
*
- * The order in which these lists are returned is not guaranteed, however the
- * position of an element inside a tuple always corresponds to the position of
- * the set from which it came in the input list. Note that if any input set is
- * empty, the Cartesian product will also be empty. If no sets at all are
- * provided (an empty list), the resulting Cartesian product has one element,
- * an empty list (counter-intuitive, but mathematically consistent).
+ * The result is guaranteed to be in the "traditional", lexicographical
+ * order for Cartesian products that you would get from nesting for loops:
+ * <pre> {@code
+ *
+ * for (B b0 : sets.get(0)) {
+ * for (B b1 : sets.get(1)) {
+ * ...
+ * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...);
+ * // operate on tuple
+ * }
+ * }}</pre>
+ *
+ * Note that if any input set is empty, the Cartesian product will also be
+ * empty. If no sets at all are provided (an empty list), the resulting
+ * Cartesian product has one element, an empty list (counter-intuitive, but
+ * mathematically consistent).
*
* <p><i>Performance notes:</i> while the cartesian product of sets of size
* {@code m, n, p} is a set of size {@code m x n x p}, its actual memory
@@ -920,8 +936,7 @@ public final class Sets {
return ImmutableSet.of();
}
}
- CartesianSet<B> cartesianSet = new CartesianSet<B>(sets);
- return cartesianSet;
+ return CartesianSet.create(sets);
}
/**
@@ -945,12 +960,22 @@ public final class Sets {
* <li>{@code ImmutableList.of(2, "C")}
* </ul>
*
- * The order in which these lists are returned is not guaranteed, however the
- * position of an element inside a tuple always corresponds to the position of
- * the set from which it came in the input list. Note that if any input set is
- * empty, the Cartesian product will also be empty. If no sets at all are
- * provided, the resulting Cartesian product has one element, an empty list
- * (counter-intuitive, but mathematically consistent).
+ * The result is guaranteed to be in the "traditional", lexicographical
+ * order for Cartesian products that you would get from nesting for loops:
+ * <pre> {@code
+ *
+ * for (B b0 : sets.get(0)) {
+ * for (B b1 : sets.get(1)) {
+ * ...
+ * ImmutableList<B> tuple = ImmutableList.of(b0, b1, ...);
+ * // operate on tuple
+ * }
+ * }}</pre>
+ *
+ * Note that if any input set is empty, the Cartesian product will also be
+ * empty. If no sets at all are provided (an empty list), the resulting
+ * Cartesian product has one element, an empty list (counter-intuitive, but
+ * mathematically consistent).
*
* <p><i>Performance notes:</i> while the cartesian product of sets of size
* {@code m, n, p} is a set of size {@code m x n x p}, its actual memory
@@ -974,73 +999,51 @@ public final class Sets {
return cartesianProduct(Arrays.asList(sets));
}
- private static class CartesianSet<B> extends AbstractSet<List<B>> {
- final ImmutableList<Axis> axes;
- final int size;
-
- CartesianSet(List<? extends Set<? extends B>> sets) {
- int dividend = 1;
- ImmutableList.Builder<Axis> builder = ImmutableList.builder();
- try {
- for (Set<? extends B> set : sets) {
- Axis axis = new Axis(set, dividend);
- builder.add(axis);
- dividend = IntMath.checkedMultiply(dividend, axis.size());
+ private static final class CartesianSet<E>
+ extends ForwardingCollection<List<E>> implements Set<List<E>> {
+ private transient final ImmutableList<ImmutableSet<E>> axes;
+ private transient final CartesianList<E> delegate;
+
+ static <E> Set<List<E>> create(List<? extends Set<? extends E>> sets) {
+ ImmutableList.Builder<ImmutableSet<E>> axesBuilder =
+ new ImmutableList.Builder<ImmutableSet<E>>(sets.size());
+ for (Set<? extends E> set : sets) {
+ ImmutableSet<E> copy = ImmutableSet.copyOf(set);
+ if (copy.isEmpty()) {
+ return ImmutableSet.of();
}
- } catch (ArithmeticException overflow) {
- throw new IllegalArgumentException("cartesian product too big");
+ axesBuilder.add(copy);
}
- this.axes = builder.build();
- size = dividend;
- }
+ final ImmutableList<ImmutableSet<E>> axes = axesBuilder.build();
+ ImmutableList<List<E>> listAxes = new ImmutableList<List<E>>() {
- @Override public int size() {
- return size;
- }
-
- @Override public UnmodifiableIterator<List<B>> iterator() {
- return new UnmodifiableIterator<List<B>>() {
- int index;
+ @Override
+ public int size() {
+ return axes.size();
+ }
@Override
- public boolean hasNext() {
- return index < size;
+ public List<E> get(int index) {
+ return axes.get(index).asList();
}
@Override
- public List<B> next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
-
- Object[] tuple = new Object[axes.size()];
- for (int i = 0 ; i < tuple.length; i++) {
- tuple[i] = axes.get(i).getForIndex(index);
- }
- index++;
-
- @SuppressWarnings("unchecked") // only B's are put in here
- List<B> result = (ImmutableList<B>) ImmutableList.copyOf(tuple);
- return result;
+ boolean isPartialView() {
+ return true;
}
};
+ return new CartesianSet<E>(axes, new CartesianList<E>(listAxes));
}
- @Override public boolean contains(Object element) {
- if (!(element instanceof List<?>)) {
- return false;
- }
- List<?> tuple = (List<?>) element;
- int dimensions = axes.size();
- if (tuple.size() != dimensions) {
- return false;
- }
- for (int i = 0; i < dimensions; i++) {
- if (!axes.get(i).contains(tuple.get(i))) {
- return false;
- }
- }
- return true;
+ private CartesianSet(
+ ImmutableList<ImmutableSet<E>> axes, CartesianList<E> delegate) {
+ this.axes = axes;
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected Collection<List<E>> delegate() {
+ return delegate;
}
@Override public boolean equals(@Nullable Object object) {
@@ -1053,56 +1056,26 @@ public final class Sets {
return super.equals(object);
}
- @Override public int hashCode() {
+ @Override
+ public int hashCode() {
// Warning: this is broken if size() == 0, so it is critical that we
// substitute an empty ImmutableSet to the user in place of this
// It's a weird formula, but tests prove it works.
- int adjust = size - 1;
+ int adjust = size() - 1;
for (int i = 0; i < axes.size(); i++) {
adjust *= 31;
+ adjust = ~~adjust;
+ // in GWT, we have to deal with integer overflow carefully
}
- return axes.hashCode() + adjust;
- }
+ int hash = 1;
+ for (Set<E> axis : axes) {
+ hash = 31 * hash + (size() / axis.size() * axis.hashCode());
- private class Axis {
- final ImmutableSet<? extends B> choices;
- final ImmutableList<? extends B> choicesList;
- final int dividend;
-
- Axis(Set<? extends B> set, int dividend) {
- choices = ImmutableSet.copyOf(set);
- choicesList = choices.asList();
- this.dividend = dividend;
- }
-
- int size() {
- return choices.size();
- }
-
- B getForIndex(int index) {
- return choicesList.get(index / dividend % size());
- }
-
- boolean contains(Object target) {
- return choices.contains(target);
- }
-
- @Override public boolean equals(Object obj) {
- if (obj instanceof CartesianSet.Axis) {
- CartesianSet.Axis that = (CartesianSet.Axis) obj;
- return this.choices.equals(that.choices);
- // dividends must be equal or we wouldn't have gotten this far
- }
- return false;
- }
-
- @Override public int hashCode() {
- // Because Axis instances are not exposed, we can
- // opportunistically choose whatever bizarre formula happens
- // to make CartesianSet.hashCode() as simple as possible.
- return size / choices.size() * choices.hashCode();
+ hash = ~~hash;
}
+ hash += adjust;
+ return ~~hash;
}
}
@@ -1240,6 +1213,9 @@ public final class Sets {
int hashCode = 0;
for (Object o : s) {
hashCode += o != null ? o.hashCode() : 0;
+
+ hashCode = ~~hashCode;
+ // Needed to deal with unusual integer overflow in GWT.
}
return hashCode;
}
@@ -1266,117 +1242,47 @@ public final class Sets {
}
/**
- * Creates a view of Set<B> for a Set<A>, given a bijection between A and B.
- * (Modelled for now as InvertibleFunction<A, B>, can't be Converter<A, B>
- * because that's not in Guava, though both designs are less than optimal).
- * Note that the bijection is treated as undefined for values not in the
- * given Set<A> - it doesn't have to define a true bijection for those.
- *
- * <p>Note that the returned Set's contains method is unsafe -
- * you *must* pass an instance of B to it, since the bijection
- * can only invert B's (not any Object) back to A, so we can
- * then delegate the call to the original Set<A>.
- */
- static <A, B> Set<B> transform(
- Set<A> set, InvertibleFunction<A, B> bijection) {
- return new TransformedSet<A, B>(
- Preconditions.checkNotNull(set, "set"),
- Preconditions.checkNotNull(bijection, "bijection")
- );
- }
-
- /**
- * Stop-gap measure since there is no bijection related type in Guava.
+ * Remove each element in an iterable from a set.
*/
- abstract static class InvertibleFunction<A, B> implements Function<A, B> {
- abstract A invert(B b);
-
- public InvertibleFunction<B, A> inverse() {
- return new InvertibleFunction<B, A>() {
- @Override public A apply(B b) {
- return InvertibleFunction.this.invert(b);
- }
-
- @Override B invert(A a) {
- return InvertibleFunction.this.apply(a);
- }
-
- // Not required per se, but just for good karma.
- @Override public InvertibleFunction<A, B> inverse() {
- return InvertibleFunction.this;
- }
- };
+ static boolean removeAllImpl(Set<?> set, Iterator<?> iterator) {
+ boolean changed = false;
+ while (iterator.hasNext()) {
+ changed |= set.remove(iterator.next());
}
+ return changed;
}
- private static class TransformedSet<A, B> extends AbstractSet<B> {
- final Set<A> delegate;
- final InvertibleFunction<A, B> bijection;
-
- TransformedSet(Set<A> delegate, InvertibleFunction<A, B> bijection) {
- this.delegate = delegate;
- this.bijection = bijection;
- }
-
- @Override public Iterator<B> iterator() {
- return Iterators.transform(delegate.iterator(), bijection);
- }
-
- @Override public int size() {
- return delegate.size();
- }
-
- @SuppressWarnings("unchecked") // unsafe, passed object *must* be B
- @Override public boolean contains(Object o) {
- B b = (B) o;
- A a = bijection.invert(b);
- /*
- * Mathematically, Converter<A, B> defines a bijection between ALL A's
- * on ALL B's. Here we concern ourselves with a subset
- * of this relation: we only want the part that is defined by a *subset*
- * of all A's (defined by that Set<A> delegate), and the image
- * of *that* on B (which is this set). We don't care whether
- * the converter is *not* a bijection for A's that are not in Set<A>
- * or B's not in this Set<B>.
- *
- * We only want to return true if and only f the user passes a B instance
- * that is contained in precisely in the image of Set<A>.
- *
- * The first test is whether the inverse image of this B is indeed
- * in Set<A>. But we don't know whether that B belongs in this Set<B>
- * or not; if not, the converter is free to return
- * anything it wants, even an element of Set<A> (and this relationship
- * is not part of the Set<A> <--> Set<B> bijection), and we must not
- * be confused by that. So we have to do a final check to see if the
- * image of that A is really equivalent to the passed B, which proves
- * that the given B belongs indeed in the image of Set<A>.
- */
- return delegate.contains(a) && Objects.equal(bijection.apply(a), o);
- }
-
- @Override public boolean add(B b) {
- return delegate.add(bijection.invert(b));
- }
-
- @SuppressWarnings("unchecked") // unsafe, passed object *must* be B
- @Override public boolean remove(Object o) {
- return contains(o) && delegate.remove(bijection.invert((B) o));
+ static boolean removeAllImpl(Set<?> set, Collection<?> collection) {
+ checkNotNull(collection); // for GWT
+ if (collection instanceof Multiset) {
+ collection = ((Multiset<?>) collection).elementSet();
}
-
- @Override public void clear() {
- delegate.clear();
+ /*
+ * AbstractSet.removeAll(List) has quadratic behavior if the list size
+ * is just less than the set's size. We augment the test by
+ * assuming that sets have fast contains() performance, and other
+ * collections don't. See
+ * http://code.google.com/p/guava-libraries/issues/detail?id=1013
+ */
+ if (collection instanceof Set && collection.size() > set.size()) {
+ Iterator<?> setIterator = set.iterator();
+ boolean changed = false;
+ while (setIterator.hasNext()) {
+ if (collection.contains(setIterator.next())) {
+ changed = true;
+ setIterator.remove();
+ }
+ }
+ return changed;
+ } else {
+ return removeAllImpl(set, collection.iterator());
}
}
/**
- * Remove each element in an iterable from a set.
+ * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557
*/
- static boolean removeAllImpl(Set<?> set, Iterable<?> iterable) {
- // TODO(jlevy): Have ForwardingSet.standardRemoveAll() call this method.
- boolean changed = false;
- for (Object o : iterable) {
- changed |= set.remove(o);
- }
- return changed;
+ static <T> SortedSet<T> cast(Iterable<T> iterable) {
+ return (SortedSet<T>) iterable;
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableBiMap.java
index 236d76ebe..f086bf3b2 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableMap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableBiMap.java
@@ -21,11 +21,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collections;
/**
- * GWT emulation of {@link SingletonImmutableMap}.
+ * GWT emulation of {@link SingletonImmutableBiMap}.
*
* @author Hayward Chan
*/
-final class SingletonImmutableMap<K, V> extends ImmutableMap<K, V> {
+final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> {
// These references are used both by the custom field serializer, and by the
// GWT compiler to infer the keys and values of the map that needs to be
@@ -36,9 +36,34 @@ final class SingletonImmutableMap<K, V> extends ImmutableMap<K, V> {
K singleKey;
V singleValue;
- SingletonImmutableMap(K key, V value) {
+ transient SingletonImmutableBiMap<V, K> inverse;
+
+ SingletonImmutableBiMap(K key, V value) {
super(Collections.singletonMap(checkNotNull(key), checkNotNull(value)));
this.singleKey = key;
this.singleValue = value;
}
+
+ private SingletonImmutableBiMap(
+ K key, V value, SingletonImmutableBiMap<V, K> inverse) {
+ super(Collections.singletonMap(checkNotNull(key), checkNotNull(value)));
+ this.singleKey = singleKey;
+ this.singleValue = singleValue;
+ this.inverse = inverse;
+ }
+
+ @Override
+ public ImmutableBiMap<V, K> inverse() {
+ ImmutableBiMap<V, K> result = inverse;
+ if (result == null) {
+ return inverse = new SingletonImmutableBiMap<V, K>(singleValue, singleKey, this);
+ } else {
+ return result;
+ }
+ }
+
+ @Override
+ public ImmutableSet<V> values() {
+ return ImmutableSet.of(singleValue);
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableList.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableList.java
index 617219127..e570f0099 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableList.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableList.java
@@ -17,23 +17,29 @@
package com.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Collections.singletonList;
-import java.util.Collections;
+import java.util.List;
/**
* GWT emulated version of {@link SingletonImmutableList}.
*
* @author Hayward Chan
*/
-final class SingletonImmutableList<E> extends ImmutableList<E> {
+final class SingletonImmutableList<E> extends ForwardingImmutableList<E> {
+ final transient List<E> delegate;
// This reference is used both by the custom field serializer, and by the
// GWT compiler to infer the elements of the lists that needs to be
// serialized.
E element;
SingletonImmutableList(E element) {
- super(Collections.singletonList(checkNotNull(element)));
+ this.delegate = singletonList(checkNotNull(element));
this.element = element;
}
+
+ @Override List<E> delegateList() {
+ return delegate;
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableSet.java
index b51d4aa2b..d1aa6b2e0 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableSet.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableSet.java
@@ -18,7 +18,8 @@ package com.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
-import java.util.Collections;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.UnmodifiableIterator;
/**
* GWT emulation of {@link SingletonImmutableSet}.
@@ -26,7 +27,7 @@ import java.util.Collections;
* @author Hayward Chan
*/
final class SingletonImmutableSet<E> extends ImmutableSet<E> {
-
+
// This reference is used both by the custom field serializer, and by the
// GWT compiler to infer the elements of the lists that needs to be
// serialized.
@@ -35,7 +36,21 @@ final class SingletonImmutableSet<E> extends ImmutableSet<E> {
E element;
SingletonImmutableSet(E element) {
- super(Collections.singleton(checkNotNull(element)));
- this.element = element;
+ this.element = checkNotNull(element);
+ }
+
+ @Override
+ public int size() {
+ return 1;
+ }
+
+ @Override
+ public UnmodifiableIterator<E> iterator() {
+ return Iterators.singletonIterator(element);
+ }
+
+ @Override
+ public boolean contains(Object object) {
+ return element.equals(object);
}
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultiset.java
new file mode 100644
index 000000000..d9248ddf9
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultiset.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.annotations.Beta;
+
+import java.util.Comparator;
+import java.util.SortedSet;
+
+/**
+ * GWT emulation of {@code SortedMultiset}, with {@code elementSet} reduced
+ * to returning a {@code SortedSet} for GWT compatibility.
+ *
+ * @author Louis Wasserman
+ * @since 11.0
+ */
+@Beta
+public interface SortedMultiset<E> extends Multiset<E>, SortedIterable<E> {
+ Comparator<? super E> comparator();
+
+ Entry<E> firstEntry();
+
+ Entry<E> lastEntry();
+
+ Entry<E> pollFirstEntry();
+
+ Entry<E> pollLastEntry();
+
+ /**
+ * Returns a {@link SortedSet} view of the distinct elements in this multiset.
+ * (Outside GWT, this returns a {@code NavigableSet}.)
+ */
+ @Override SortedSet<E> elementSet();
+
+ SortedMultiset<E> descendingMultiset();
+
+ SortedMultiset<E> headMultiset(E upperBound, BoundType boundType);
+
+ SortedMultiset<E> subMultiset(E lowerBound, BoundType lowerBoundType,
+ E upperBound, BoundType upperBoundType);
+
+ SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType);
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultisets.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultisets.java
new file mode 100644
index 000000000..e3955b54f
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultisets.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import static com.google.common.collect.BoundType.CLOSED;
+import static com.google.common.collect.BoundType.OPEN;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.collect.Multiset.Entry;
+
+import java.util.Comparator;
+import java.util.NoSuchElementException;
+import java.util.SortedSet;
+
+import javax.annotation.Nullable;
+
+/**
+ * Provides static utility methods for creating and working with
+ * {@link SortedMultiset} instances.
+ *
+ * @author Louis Wasserman
+ */
+@GwtCompatible(emulated = true)
+final class SortedMultisets {
+ private SortedMultisets() {
+ }
+
+ /**
+ * A skeleton implementation for {@link SortedMultiset#elementSet}.
+ */
+ static class ElementSet<E> extends Multisets.ElementSet<E> implements
+ SortedSet<E> {
+ private final SortedMultiset<E> multiset;
+
+ ElementSet(SortedMultiset<E> multiset) {
+ this.multiset = multiset;
+ }
+
+ @Override final SortedMultiset<E> multiset() {
+ return multiset;
+ }
+
+ @Override public Comparator<? super E> comparator() {
+ return multiset().comparator();
+ }
+
+ @Override public SortedSet<E> subSet(E fromElement, E toElement) {
+ return multiset().subMultiset(fromElement, CLOSED, toElement, OPEN).elementSet();
+ }
+
+ @Override public SortedSet<E> headSet(E toElement) {
+ return multiset().headMultiset(toElement, OPEN).elementSet();
+ }
+
+ @Override public SortedSet<E> tailSet(E fromElement) {
+ return multiset().tailMultiset(fromElement, CLOSED).elementSet();
+ }
+
+ @Override public E first() {
+ return getElementOrThrow(multiset().firstEntry());
+ }
+
+ @Override public E last() {
+ return getElementOrThrow(multiset().lastEntry());
+ }
+ }
+
+ private static <E> E getElementOrThrow(Entry<E> entry) {
+ if (entry == null) {
+ throw new NoSuchElementException();
+ }
+ return entry.getElement();
+ }
+
+ private static <E> E getElementOrNull(@Nullable Entry<E> entry) {
+ return (entry == null) ? null : entry.getElement();
+ }
+}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Synchronized.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Synchronized.java
index fd09c97fe..c809da8b4 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Synchronized.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Synchronized.java
@@ -28,6 +28,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
import java.util.RandomAccess;
import java.util.Set;
import java.util.SortedMap;
@@ -194,7 +196,7 @@ final class Synchronized {
static class SynchronizedSet<E>
extends SynchronizedCollection<E> implements Set<E> {
-
+
SynchronizedSet(Set<E> delegate, @Nullable Object mutex) {
super(delegate, mutex);
}
@@ -834,7 +836,7 @@ final class Synchronized {
}
@Override public Map.Entry<K, Collection<V>> next() {
- final Map.Entry<K, Collection<V>> entry = iterator.next();
+ final Map.Entry<K, Collection<V>> entry = super.next();
return new ForwardingMapEntry<K, Collection<V>>() {
@Override protected Map.Entry<K, Collection<V>> delegate() {
return entry;
@@ -1026,12 +1028,12 @@ final class Synchronized {
private static final long serialVersionUID = 0;
}
-
+
static <K, V> SortedMap<K, V> sortedMap(
SortedMap<K, V> sortedMap, @Nullable Object mutex) {
return new SynchronizedSortedMap<K, V>(sortedMap, mutex);
}
-
+
static class SynchronizedSortedMap<K, V> extends SynchronizedMap<K, V>
implements SortedMap<K, V> {
@@ -1195,12 +1197,67 @@ final class Synchronized {
return iterator;
}
@Override public Collection<V> next() {
- return typePreservingCollection(iterator.next(), mutex);
+ return typePreservingCollection(super.next(), mutex);
}
};
}
private static final long serialVersionUID = 0;
}
+
+ static <E> Queue<E> queue(Queue<E> queue, @Nullable Object mutex) {
+ return (queue instanceof SynchronizedQueue)
+ ? queue
+ : new SynchronizedQueue<E>(queue, mutex);
+ }
+
+ private static class SynchronizedQueue<E> extends SynchronizedCollection<E>
+ implements Queue<E> {
+
+ SynchronizedQueue(Queue<E> delegate, @Nullable Object mutex) {
+ super(delegate, mutex);
+ }
+
+ @Override Queue<E> delegate() {
+ return (Queue<E>) super.delegate();
+ }
+
+ @Override
+ public E element() {
+ synchronized (mutex) {
+ return delegate().element();
+ }
+ }
+
+ @Override
+ public boolean offer(E e) {
+ synchronized (mutex) {
+ return delegate().offer(e);
+ }
+ }
+
+ @Override
+ public E peek() {
+ synchronized (mutex) {
+ return delegate().peek();
+ }
+ }
+
+ @Override
+ public E poll() {
+ synchronized (mutex) {
+ return delegate().poll();
+ }
+ }
+
+ @Override
+ public E remove() {
+ synchronized (mutex) {
+ return delegate().remove();
+ }
+ }
+
+ private static final long serialVersionUID = 0;
+ }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultimap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultimap.java
index 228dc2d93..5023a0c51 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultimap.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultimap.java
@@ -22,11 +22,12 @@ import com.google.common.annotations.GwtCompatible;
import java.util.Collection;
import java.util.Comparator;
-import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
+import javax.annotation.Nullable;
+
/**
* Implementation of {@code Multimap} whose keys and values are ordered by
* their natural ordering or by supplied comparators. In all cases, this
@@ -60,11 +61,16 @@ import java.util.TreeSet;
* update operations, wrap your multimap with a call to {@link
* Multimaps#synchronizedSortedSetMultimap}.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap">
+ * {@code Multimap}</a>.
+ *
* @author Jared Levy
+ * @author Louis Wasserman
* @since 2.0 (imported from Google Collections Library)
*/
@GwtCompatible(serializable = true, emulated = true)
-public class TreeMultimap<K, V> extends AbstractSortedSetMultimap<K, V> {
+public class TreeMultimap<K, V> extends AbstractSortedKeySortedSetMultimap<K, V> {
private transient Comparator<? super K> keyComparator;
private transient Comparator<? super V> valueComparator;
@@ -130,6 +136,14 @@ public class TreeMultimap<K, V> extends AbstractSortedSetMultimap<K, V> {
return new TreeSet<V>(valueComparator);
}
+ @Override
+ Collection<V> createCollection(@Nullable K key) {
+ if (key == null) {
+ keyComparator().compare(key, key);
+ }
+ return super.createCollection(key);
+ }
+
/**
* Returns the comparator that orders the multimap keys.
*/
@@ -142,26 +156,10 @@ public class TreeMultimap<K, V> extends AbstractSortedSetMultimap<K, V> {
return valueComparator;
}
- /**
- * {@inheritDoc}
- *
- * <p>Because a {@code TreeMultimap} has unique sorted keys, this method
- * returns a {@link SortedSet}, instead of the {@link java.util.Set} specified
- * in the {@link Multimap} interface.
+ /*
+ * The following @GwtIncompatible methods override the methods in
+ * AbstractSortedKeySortedSetMultimap, so GWT will fall back to the ASKSSM implementations,
+ * which return SortedSets and SortedMaps.
*/
- @Override public SortedSet<K> keySet() {
- return (SortedSet<K>) super.keySet();
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>Because a {@code TreeMultimap} has unique sorted keys, this method
- * returns a {@link SortedMap}, instead of the {@link java.util.Map} specified
- * in the {@link Multimap} interface.
- */
- @Override public SortedMap<K, Collection<V>> asMap() {
- return (SortedMap<K, Collection<V>>) super.asMap();
- }
}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultiset.java
index 622454d61..b45d12772 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultiset.java
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/TreeMultiset.java
@@ -17,168 +17,217 @@
package com.google.common.collect;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.BstSide.LEFT;
-import static com.google.common.collect.BstSide.RIGHT;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Objects;
+import com.google.common.primitives.Ints;
import java.io.Serializable;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
+import java.util.NoSuchElementException;
import javax.annotation.Nullable;
-import com.google.common.annotations.GwtCompatible;
-import com.google.common.primitives.Ints;
-
/**
- * A multiset which maintains the ordering of its elements, according to either
- * their natural order or an explicit {@link Comparator}. In all cases, this
- * implementation uses {@link Comparable#compareTo} or {@link
- * Comparator#compare} instead of {@link Object#equals} to determine
- * equivalence of instances.
+ * A multiset which maintains the ordering of its elements, according to either their natural order
+ * or an explicit {@link Comparator}. In all cases, this implementation uses
+ * {@link Comparable#compareTo} or {@link Comparator#compare} instead of {@link Object#equals} to
+ * determine equivalence of instances.
*
- * <p><b>Warning:</b> The comparison must be <i>consistent with equals</i> as
- * explained by the {@link Comparable} class specification. Otherwise, the
- * resulting multiset will violate the {@link java.util.Collection} contract,
- * which is specified in terms of {@link Object#equals}.
+ * <p><b>Warning:</b> The comparison must be <i>consistent with equals</i> as explained by the
+ * {@link Comparable} class specification. Otherwise, the resulting multiset will violate the
+ * {@link java.util.Collection} contract, which is specified in terms of {@link Object#equals}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset">
+ * {@code Multiset}</a>.
*
* @author Louis Wasserman
* @author Jared Levy
* @since 2.0 (imported from Google Collections Library)
*/
@GwtCompatible(emulated = true)
-public final class TreeMultiset<E> extends AbstractSortedMultiset<E>
- implements Serializable {
+public final class TreeMultiset<E> extends AbstractSortedMultiset<E> implements Serializable {
/**
- * Creates a new, empty multiset, sorted according to the elements' natural
- * order. All elements inserted into the multiset must implement the
- * {@code Comparable} interface. Furthermore, all such elements must be
- * <i>mutually comparable</i>: {@code e1.compareTo(e2)} must not throw a
- * {@code ClassCastException} for any elements {@code e1} and {@code e2} in
- * the multiset. If the user attempts to add an element to the multiset that
- * violates this constraint (for example, the user attempts to add a string
- * element to a set whose elements are integers), the {@code add(Object)}
- * call will throw a {@code ClassCastException}.
+ * Creates a new, empty multiset, sorted according to the elements' natural order. All elements
+ * inserted into the multiset must implement the {@code Comparable} interface. Furthermore, all
+ * such elements must be <i>mutually comparable</i>: {@code e1.compareTo(e2)} must not throw a
+ * {@code ClassCastException} for any elements {@code e1} and {@code e2} in the multiset. If the
+ * user attempts to add an element to the multiset that violates this constraint (for example,
+ * the user attempts to add a string element to a set whose elements are integers), the
+ * {@code add(Object)} call will throw a {@code ClassCastException}.
*
- * <p>The type specification is {@code <E extends Comparable>}, instead of the
- * more specific {@code <E extends Comparable<? super E>>}, to support
- * classes defined without generics.
+ * <p>The type specification is {@code <E extends Comparable>}, instead of the more specific
+ * {@code <E extends Comparable<? super E>>}, to support classes defined without generics.
*/
public static <E extends Comparable> TreeMultiset<E> create() {
return new TreeMultiset<E>(Ordering.natural());
}
/**
- * Creates a new, empty multiset, sorted according to the specified
- * comparator. All elements inserted into the multiset must be <i>mutually
- * comparable</i> by the specified comparator: {@code comparator.compare(e1,
- * e2)} must not throw a {@code ClassCastException} for any elements {@code
- * e1} and {@code e2} in the multiset. If the user attempts to add an element
- * to the multiset that violates this constraint, the {@code add(Object)} call
- * will throw a {@code ClassCastException}.
+ * Creates a new, empty multiset, sorted according to the specified comparator. All elements
+ * inserted into the multiset must be <i>mutually comparable</i> by the specified comparator:
+ * {@code comparator.compare(e1,
+ * e2)} must not throw a {@code ClassCastException} for any elements {@code e1} and {@code e2} in
+ * the multiset. If the user attempts to add an element to the multiset that violates this
+ * constraint, the {@code add(Object)} call will throw a {@code ClassCastException}.
*
- * @param comparator the comparator that will be used to sort this multiset. A
- * null value indicates that the elements' <i>natural ordering</i> should
- * be used.
+ * @param comparator
+ * the comparator that will be used to sort this multiset. A null value indicates that
+ * the elements' <i>natural ordering</i> should be used.
*/
@SuppressWarnings("unchecked")
- public static <E> TreeMultiset<E> create(
- @Nullable Comparator<? super E> comparator) {
+ public static <E> TreeMultiset<E> create(@Nullable Comparator<? super E> comparator) {
return (comparator == null)
- ? new TreeMultiset<E>((Comparator) Ordering.natural())
- : new TreeMultiset<E>(comparator);
+ ? new TreeMultiset<E>((Comparator) Ordering.natural())
+ : new TreeMultiset<E>(comparator);
}
/**
- * Creates an empty multiset containing the given initial elements, sorted
- * according to the elements' natural order.
+ * Creates an empty multiset containing the given initial elements, sorted according to the
+ * elements' natural order.
*
- * <p>This implementation is highly efficient when {@code elements} is itself
- * a {@link Multiset}.
+ * <p>This implementation is highly efficient when {@code elements} is itself a {@link Multiset}.
*
- * <p>The type specification is {@code <E extends Comparable>}, instead of the
- * more specific {@code <E extends Comparable<? super E>>}, to support
- * classes defined without generics.
+ * <p>The type specification is {@code <E extends Comparable>}, instead of the more specific
+ * {@code <E extends Comparable<? super E>>}, to support classes defined without generics.
*/
- public static <E extends Comparable> TreeMultiset<E> create(
- Iterable<? extends E> elements) {
+ public static <E extends Comparable> TreeMultiset<E> create(Iterable<? extends E> elements) {
TreeMultiset<E> multiset = create();
Iterables.addAll(multiset, elements);
return multiset;
}
- /**
- * Returns an iterator over the elements contained in this collection.
- */
- @Override
- public Iterator<E> iterator() {
- // Needed to avoid Javadoc bug.
- return super.iterator();
- }
-
- private TreeMultiset(Comparator<? super E> comparator) {
- super(comparator);
- this.range = GeneralRange.all(comparator);
- this.rootReference = new Reference<Node<E>>();
- }
+ private final transient Reference<AvlNode<E>> rootReference;
+ private final transient GeneralRange<E> range;
+ private final transient AvlNode<E> header;
- private TreeMultiset(GeneralRange<E> range, Reference<Node<E>> root) {
+ TreeMultiset(Reference<AvlNode<E>> rootReference, GeneralRange<E> range, AvlNode<E> endLink) {
super(range.comparator());
+ this.rootReference = rootReference;
this.range = range;
- this.rootReference = root;
+ this.header = endLink;
}
- @SuppressWarnings("unchecked")
- E checkElement(Object o) {
- return (E) o;
+ TreeMultiset(Comparator<? super E> comparator) {
+ super(comparator);
+ this.range = GeneralRange.all(comparator);
+ this.header = new AvlNode<E>(null, 1);
+ successor(header, header);
+ this.rootReference = new Reference<AvlNode<E>>();
}
- private transient final GeneralRange<E> range;
+ /**
+ * A function which can be summed across a subtree.
+ */
+ private enum Aggregate {
+ SIZE {
+ @Override
+ int nodeAggregate(AvlNode<?> node) {
+ return node.elemCount;
+ }
+
+ @Override
+ long treeAggregate(@Nullable AvlNode<?> root) {
+ return (root == null) ? 0 : root.totalCount;
+ }
+ },
+ DISTINCT {
+ @Override
+ int nodeAggregate(AvlNode<?> node) {
+ return 1;
+ }
- private transient final Reference<Node<E>> rootReference;
+ @Override
+ long treeAggregate(@Nullable AvlNode<?> root) {
+ return (root == null) ? 0 : root.distinctElements;
+ }
+ };
+ abstract int nodeAggregate(AvlNode<?> node);
- static final class Reference<T> {
- T value;
+ abstract long treeAggregate(@Nullable AvlNode<?> root);
+ }
- public Reference() {}
+ private long aggregateForEntries(Aggregate aggr) {
+ AvlNode<E> root = rootReference.get();
+ long total = aggr.treeAggregate(root);
+ if (range.hasLowerBound()) {
+ total -= aggregateBelowRange(aggr, root);
+ }
+ if (range.hasUpperBound()) {
+ total -= aggregateAboveRange(aggr, root);
+ }
+ return total;
+ }
- public T get() {
- return value;
+ private long aggregateBelowRange(Aggregate aggr, @Nullable AvlNode<E> node) {
+ if (node == null) {
+ return 0;
}
+ int cmp = comparator().compare(range.getLowerEndpoint(), node.elem);
+ if (cmp < 0) {
+ return aggregateBelowRange(aggr, node.left);
+ } else if (cmp == 0) {
+ switch (range.getLowerBoundType()) {
+ case OPEN:
+ return aggr.nodeAggregate(node) + aggr.treeAggregate(node.left);
+ case CLOSED:
+ return aggr.treeAggregate(node.left);
+ default:
+ throw new AssertionError();
+ }
+ } else {
+ return aggr.treeAggregate(node.left) + aggr.nodeAggregate(node)
+ + aggregateBelowRange(aggr, node.right);
+ }
+ }
- public boolean compareAndSet(T expected, T newValue) {
- if (value == expected) {
- value = newValue;
- return true;
+ private long aggregateAboveRange(Aggregate aggr, @Nullable AvlNode<E> node) {
+ if (node == null) {
+ return 0;
+ }
+ int cmp = comparator().compare(range.getUpperEndpoint(), node.elem);
+ if (cmp > 0) {
+ return aggregateAboveRange(aggr, node.right);
+ } else if (cmp == 0) {
+ switch (range.getUpperBoundType()) {
+ case OPEN:
+ return aggr.nodeAggregate(node) + aggr.treeAggregate(node.right);
+ case CLOSED:
+ return aggr.treeAggregate(node.right);
+ default:
+ throw new AssertionError();
}
- return false;
+ } else {
+ return aggr.treeAggregate(node.right) + aggr.nodeAggregate(node)
+ + aggregateAboveRange(aggr, node.left);
}
}
@Override
- int distinctElements() {
- Node<E> root = rootReference.get();
- return Ints.checkedCast(BstRangeOps.totalInRange(distinctAggregate(), range, root));
+ public int size() {
+ return Ints.saturatedCast(aggregateForEntries(Aggregate.SIZE));
}
@Override
- public int size() {
- Node<E> root = rootReference.get();
- return Ints.saturatedCast(BstRangeOps.totalInRange(sizeAggregate(), range, root));
+ int distinctElements() {
+ return Ints.saturatedCast(aggregateForEntries(Aggregate.DISTINCT));
}
@Override
public int count(@Nullable Object element) {
try {
- E e = checkElement(element);
- if (range.contains(e)) {
- Node<E> node = BstOperations.seek(comparator(), rootReference.get(), e);
- return countOrZero(node);
+ @SuppressWarnings("unchecked")
+ E e = (E) element;
+ AvlNode<E> root = rootReference.get();
+ if (!range.contains(e) || root == null) {
+ return 0;
}
- return 0;
+ return root.count(comparator(), e);
} catch (ClassCastException e) {
return 0;
} catch (NullPointerException e) {
@@ -186,354 +235,713 @@ public final class TreeMultiset<E> extends AbstractSortedMultiset<E>
}
}
- private int mutate(@Nullable E e, MultisetModifier modifier) {
- BstMutationRule<E, Node<E>> mutationRule = BstMutationRule.createRule(
- modifier,
- BstCountBasedBalancePolicies.
- <E, Node<E>>singleRebalancePolicy(distinctAggregate()),
- nodeFactory());
- BstMutationResult<E, Node<E>> mutationResult =
- BstOperations.mutate(comparator(), mutationRule, rootReference.get(), e);
- if (!rootReference.compareAndSet(
- mutationResult.getOriginalRoot(), mutationResult.getChangedRoot())) {
- throw new ConcurrentModificationException();
- }
- Node<E> original = mutationResult.getOriginalTarget();
- return countOrZero(original);
- }
-
@Override
- public int add(E element, int occurrences) {
- checkElement(element);
+ public int add(@Nullable E element, int occurrences) {
+ checkArgument(occurrences >= 0, "occurrences must be >= 0 but was %s", occurrences);
if (occurrences == 0) {
return count(element);
}
checkArgument(range.contains(element));
- return mutate(element, new AddModifier(occurrences));
+ AvlNode<E> root = rootReference.get();
+ if (root == null) {
+ comparator().compare(element, element);
+ AvlNode<E> newRoot = new AvlNode<E>(element, occurrences);
+ successor(header, newRoot, header);
+ rootReference.checkAndSet(root, newRoot);
+ return 0;
+ }
+ int[] result = new int[1]; // used as a mutable int reference to hold result
+ AvlNode<E> newRoot = root.add(comparator(), element, occurrences, result);
+ rootReference.checkAndSet(root, newRoot);
+ return result[0];
}
@Override
public int remove(@Nullable Object element, int occurrences) {
- if (element == null) {
- return 0;
- } else if (occurrences == 0) {
+ checkArgument(occurrences >= 0, "occurrences must be >= 0 but was %s", occurrences);
+ if (occurrences == 0) {
return count(element);
}
+ AvlNode<E> root = rootReference.get();
+ int[] result = new int[1]; // used as a mutable int reference to hold result
+ AvlNode<E> newRoot;
try {
- E e = checkElement(element);
- return range.contains(e) ? mutate(e, new RemoveModifier(occurrences)) : 0;
+ @SuppressWarnings("unchecked")
+ E e = (E) element;
+ if (!range.contains(e) || root == null) {
+ return 0;
+ }
+ newRoot = root.remove(comparator(), e, occurrences, result);
} catch (ClassCastException e) {
return 0;
+ } catch (NullPointerException e) {
+ return 0;
}
+ rootReference.checkAndSet(root, newRoot);
+ return result[0];
}
@Override
- public boolean setCount(E element, int oldCount, int newCount) {
- checkElement(element);
- checkArgument(range.contains(element));
- return mutate(element, new ConditionalSetCountModifier(oldCount, newCount))
- == oldCount;
+ public int setCount(@Nullable E element, int count) {
+ checkArgument(count >= 0);
+ if (!range.contains(element)) {
+ checkArgument(count == 0);
+ return 0;
+ }
+
+ AvlNode<E> root = rootReference.get();
+ if (root == null) {
+ if (count > 0) {
+ add(element, count);
+ }
+ return 0;
+ }
+ int[] result = new int[1]; // used as a mutable int reference to hold result
+ AvlNode<E> newRoot = root.setCount(comparator(), element, count, result);
+ rootReference.checkAndSet(root, newRoot);
+ return result[0];
}
@Override
- public int setCount(E element, int count) {
- checkElement(element);
+ public boolean setCount(@Nullable E element, int oldCount, int newCount) {
+ checkArgument(newCount >= 0);
+ checkArgument(oldCount >= 0);
checkArgument(range.contains(element));
- return mutate(element, new SetCountModifier(count));
+
+ AvlNode<E> root = rootReference.get();
+ if (root == null) {
+ if (oldCount == 0) {
+ if (newCount > 0) {
+ add(element, newCount);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ int[] result = new int[1]; // used as a mutable int reference to hold result
+ AvlNode<E> newRoot = root.setCount(comparator(), element, oldCount, newCount, result);
+ rootReference.checkAndSet(root, newRoot);
+ return result[0] == oldCount;
}
- private BstPathFactory<Node<E>, BstInOrderPath<Node<E>>> pathFactory() {
- return BstInOrderPath.inOrderFactory();
+ private Entry<E> wrapEntry(final AvlNode<E> baseEntry) {
+ return new Multisets.AbstractEntry<E>() {
+ @Override
+ public E getElement() {
+ return baseEntry.getElement();
+ }
+
+ @Override
+ public int getCount() {
+ int result = baseEntry.getCount();
+ if (result == 0) {
+ return count(getElement());
+ } else {
+ return result;
+ }
+ }
+ };
}
- @Override
- Iterator<Entry<E>> entryIterator() {
- Node<E> root = rootReference.get();
- final BstInOrderPath<Node<E>> startingPath =
- BstRangeOps.furthestPath(range, LEFT, pathFactory(), root);
- return iteratorInDirection(startingPath, RIGHT);
+ /**
+ * Returns the first node in the tree that is in range.
+ */
+ @Nullable private AvlNode<E> firstNode() {
+ AvlNode<E> root = rootReference.get();
+ if (root == null) {
+ return null;
+ }
+ AvlNode<E> node;
+ if (range.hasLowerBound()) {
+ E endpoint = range.getLowerEndpoint();
+ node = rootReference.get().ceiling(comparator(), endpoint);
+ if (node == null) {
+ return null;
+ }
+ if (range.getLowerBoundType() == BoundType.OPEN
+ && comparator().compare(endpoint, node.getElement()) == 0) {
+ node = node.succ;
+ }
+ } else {
+ node = header.succ;
+ }
+ return (node == header || !range.contains(node.getElement())) ? null : node;
}
- @Override
- Iterator<Entry<E>> descendingEntryIterator() {
- Node<E> root = rootReference.get();
- final BstInOrderPath<Node<E>> startingPath =
- BstRangeOps.furthestPath(range, RIGHT, pathFactory(), root);
- return iteratorInDirection(startingPath, LEFT);
+ @Nullable private AvlNode<E> lastNode() {
+ AvlNode<E> root = rootReference.get();
+ if (root == null) {
+ return null;
+ }
+ AvlNode<E> node;
+ if (range.hasUpperBound()) {
+ E endpoint = range.getUpperEndpoint();
+ node = rootReference.get().floor(comparator(), endpoint);
+ if (node == null) {
+ return null;
+ }
+ if (range.getUpperBoundType() == BoundType.OPEN
+ && comparator().compare(endpoint, node.getElement()) == 0) {
+ node = node.pred;
+ }
+ } else {
+ node = header.pred;
+ }
+ return (node == header || !range.contains(node.getElement())) ? null : node;
}
- private Iterator<Entry<E>> iteratorInDirection(
- @Nullable BstInOrderPath<Node<E>> start, final BstSide direction) {
- final Iterator<BstInOrderPath<Node<E>>> pathIterator =
- new AbstractLinkedIterator<BstInOrderPath<Node<E>>>(start) {
- @Override
- protected BstInOrderPath<Node<E>> computeNext(BstInOrderPath<Node<E>> previous) {
- if (!previous.hasNext(direction)) {
- return null;
- }
- BstInOrderPath<Node<E>> next = previous.next(direction);
- // TODO(user): only check against one side
- return range.contains(next.getTip().getKey()) ? next : null;
- }
- };
+ @Override
+ Iterator<Entry<E>> entryIterator() {
return new Iterator<Entry<E>>() {
- E toRemove = null;
+ AvlNode<E> current = firstNode();
+ Entry<E> prevEntry;
@Override
public boolean hasNext() {
- return pathIterator.hasNext();
+ if (current == null) {
+ return false;
+ } else if (range.tooHigh(current.getElement())) {
+ current = null;
+ return false;
+ } else {
+ return true;
+ }
}
@Override
public Entry<E> next() {
- BstInOrderPath<Node<E>> path = pathIterator.next();
- return new LiveEntry(
- toRemove = path.getTip().getKey(), path.getTip().elemCount());
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Entry<E> result = wrapEntry(current);
+ prevEntry = result;
+ if (current.succ == header) {
+ current = null;
+ } else {
+ current = current.succ;
+ }
+ return result;
}
@Override
public void remove() {
- checkState(toRemove != null);
- setCount(toRemove, 0);
- toRemove = null;
+ checkState(prevEntry != null);
+ setCount(prevEntry.getElement(), 0);
+ prevEntry = null;
}
};
}
- class LiveEntry extends Multisets.AbstractEntry<E> {
- private Node<E> expectedRoot;
- private final E element;
- private int count;
+ @Override
+ Iterator<Entry<E>> descendingEntryIterator() {
+ return new Iterator<Entry<E>>() {
+ AvlNode<E> current = lastNode();
+ Entry<E> prevEntry = null;
- private LiveEntry(E element, int count) {
- this.expectedRoot = rootReference.get();
- this.element = element;
- this.count = count;
- }
+ @Override
+ public boolean hasNext() {
+ if (current == null) {
+ return false;
+ } else if (range.tooLow(current.getElement())) {
+ current = null;
+ return false;
+ } else {
+ return true;
+ }
+ }
- @Override
- public E getElement() {
- return element;
- }
+ @Override
+ public Entry<E> next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ Entry<E> result = wrapEntry(current);
+ prevEntry = result;
+ if (current.pred == header) {
+ current = null;
+ } else {
+ current = current.pred;
+ }
+ return result;
+ }
- @Override
- public int getCount() {
- if (rootReference.get() == expectedRoot) {
- return count;
- } else {
- // check for updates
- expectedRoot = rootReference.get();
- return count = TreeMultiset.this.count(element);
+ @Override
+ public void remove() {
+ checkState(prevEntry != null);
+ setCount(prevEntry.getElement(), 0);
+ prevEntry = null;
}
- }
+ };
}
@Override
- public void clear() {
- Node<E> root = rootReference.get();
- Node<E> cleared = BstRangeOps.minusRange(range,
- BstCountBasedBalancePolicies.<E, Node<E>>fullRebalancePolicy(distinctAggregate()),
- nodeFactory(), root);
- if (!rootReference.compareAndSet(root, cleared)) {
- throw new ConcurrentModificationException();
- }
+ public SortedMultiset<E> headMultiset(@Nullable E upperBound, BoundType boundType) {
+ return new TreeMultiset<E>(rootReference, range.intersect(GeneralRange.upTo(
+ comparator(),
+ upperBound,
+ boundType)), header);
}
@Override
- public SortedMultiset<E> headMultiset(E upperBound, BoundType boundType) {
- checkNotNull(upperBound);
- return new TreeMultiset<E>(
- range.intersect(GeneralRange.upTo(comparator, upperBound, boundType)), rootReference);
+ public SortedMultiset<E> tailMultiset(@Nullable E lowerBound, BoundType boundType) {
+ return new TreeMultiset<E>(rootReference, range.intersect(GeneralRange.downTo(
+ comparator(),
+ lowerBound,
+ boundType)), header);
}
- @Override
- public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) {
- checkNotNull(lowerBound);
- return new TreeMultiset<E>(
- range.intersect(GeneralRange.downTo(comparator, lowerBound, boundType)), rootReference);
+ static int distinctElements(@Nullable AvlNode<?> node) {
+ return (node == null) ? 0 : node.distinctElements;
}
- /**
- * {@inheritDoc}
- *
- * @since 11.0
- */
- @Override
- public Comparator<? super E> comparator() {
- return super.comparator();
+ private static final class Reference<T> {
+ @Nullable private T value;
+
+ @Nullable public T get() {
+ return value;
+ }
+
+ public void checkAndSet(@Nullable T expected, T newValue) {
+ if (value != expected) {
+ throw new ConcurrentModificationException();
+ }
+ value = newValue;
+ }
}
- private static final class Node<E> extends BstNode<E, Node<E>> implements Serializable {
- private final long size;
- private final int distinct;
+ private static final class AvlNode<E> extends Multisets.AbstractEntry<E> {
+ @Nullable private final E elem;
- private Node(E key, int elemCount, @Nullable Node<E> left,
- @Nullable Node<E> right) {
- super(key, left, right);
+ // elemCount is 0 iff this node has been deleted.
+ private int elemCount;
+
+ private int distinctElements;
+ private long totalCount;
+ private int height;
+ private AvlNode<E> left;
+ private AvlNode<E> right;
+ private AvlNode<E> pred;
+ private AvlNode<E> succ;
+
+ AvlNode(@Nullable E elem, int elemCount) {
checkArgument(elemCount > 0);
- this.size = (long) elemCount + sizeOrZero(left) + sizeOrZero(right);
- this.distinct = 1 + distinctOrZero(left) + distinctOrZero(right);
+ this.elem = elem;
+ this.elemCount = elemCount;
+ this.totalCount = elemCount;
+ this.distinctElements = 1;
+ this.height = 1;
+ this.left = null;
+ this.right = null;
}
- int elemCount() {
- long result = size - sizeOrZero(childOrNull(LEFT))
- - sizeOrZero(childOrNull(RIGHT));
- return Ints.checkedCast(result);
+ public int count(Comparator<? super E> comparator, E e) {
+ int cmp = comparator.compare(e, elem);
+ if (cmp < 0) {
+ return (left == null) ? 0 : left.count(comparator, e);
+ } else if (cmp > 0) {
+ return (right == null) ? 0 : right.count(comparator, e);
+ } else {
+ return elemCount;
+ }
}
- private Node(E key, int elemCount) {
- this(key, elemCount, null, null);
+ private AvlNode<E> addRightChild(E e, int count) {
+ right = new AvlNode<E>(e, count);
+ successor(this, right, succ);
+ height = Math.max(2, height);
+ distinctElements++;
+ totalCount += count;
+ return this;
}
- private static final long serialVersionUID = 0;
- }
-
- private static long sizeOrZero(@Nullable Node<?> node) {
- return (node == null) ? 0 : node.size;
- }
+ private AvlNode<E> addLeftChild(E e, int count) {
+ left = new AvlNode<E>(e, count);
+ successor(pred, left, this);
+ height = Math.max(2, height);
+ distinctElements++;
+ totalCount += count;
+ return this;
+ }
- private static int distinctOrZero(@Nullable Node<?> node) {
- return (node == null) ? 0 : node.distinct;
- }
+ AvlNode<E> add(Comparator<? super E> comparator, @Nullable E e, int count, int[] result) {
+ /*
+ * It speeds things up considerably to unconditionally add count to totalCount here,
+ * but that destroys failure atomicity in the case of count overflow. =(
+ */
+ int cmp = comparator.compare(e, elem);
+ if (cmp < 0) {
+ AvlNode<E> initLeft = left;
+ if (initLeft == null) {
+ result[0] = 0;
+ return addLeftChild(e, count);
+ }
+ int initHeight = initLeft.height;
- private static int countOrZero(@Nullable Node<?> entry) {
- return (entry == null) ? 0 : entry.elemCount();
- }
+ left = initLeft.add(comparator, e, count, result);
+ if (result[0] == 0) {
+ distinctElements++;
+ }
+ this.totalCount += count;
+ return (left.height == initHeight) ? this : rebalance();
+ } else if (cmp > 0) {
+ AvlNode<E> initRight = right;
+ if (initRight == null) {
+ result[0] = 0;
+ return addRightChild(e, count);
+ }
+ int initHeight = initRight.height;
- @SuppressWarnings("unchecked")
- private BstAggregate<Node<E>> distinctAggregate() {
- return (BstAggregate) DISTINCT_AGGREGATE;
- }
+ right = initRight.add(comparator, e, count, result);
+ if (result[0] == 0) {
+ distinctElements++;
+ }
+ this.totalCount += count;
+ return (right.height == initHeight) ? this : rebalance();
+ }
- private static final BstAggregate<Node<Object>> DISTINCT_AGGREGATE =
- new BstAggregate<Node<Object>>() {
- @Override
- public int entryValue(Node<Object> entry) {
- return 1;
+ // adding count to me! No rebalance possible.
+ result[0] = elemCount;
+ long resultCount = (long) elemCount + count;
+ checkArgument(resultCount <= Integer.MAX_VALUE);
+ this.elemCount += count;
+ this.totalCount += count;
+ return this;
}
- @Override
- public long treeValue(@Nullable Node<Object> tree) {
- return distinctOrZero(tree);
+ AvlNode<E> remove(Comparator<? super E> comparator, @Nullable E e, int count, int[] result) {
+ int cmp = comparator.compare(e, elem);
+ if (cmp < 0) {
+ AvlNode<E> initLeft = left;
+ if (initLeft == null) {
+ result[0] = 0;
+ return this;
+ }
+
+ left = initLeft.remove(comparator, e, count, result);
+
+ if (result[0] > 0) {
+ if (count >= result[0]) {
+ this.distinctElements--;
+ this.totalCount -= result[0];
+ } else {
+ this.totalCount -= count;
+ }
+ }
+ return (result[0] == 0) ? this : rebalance();
+ } else if (cmp > 0) {
+ AvlNode<E> initRight = right;
+ if (initRight == null) {
+ result[0] = 0;
+ return this;
+ }
+
+ right = initRight.remove(comparator, e, count, result);
+
+ if (result[0] > 0) {
+ if (count >= result[0]) {
+ this.distinctElements--;
+ this.totalCount -= result[0];
+ } else {
+ this.totalCount -= count;
+ }
+ }
+ return rebalance();
+ }
+
+ // removing count from me!
+ result[0] = elemCount;
+ if (count >= elemCount) {
+ return deleteMe();
+ } else {
+ this.elemCount -= count;
+ this.totalCount -= count;
+ return this;
+ }
}
- };
- @SuppressWarnings("unchecked")
- private BstAggregate<Node<E>> sizeAggregate() {
- return (BstAggregate) SIZE_AGGREGATE;
- }
+ AvlNode<E> setCount(Comparator<? super E> comparator, @Nullable E e, int count, int[] result) {
+ int cmp = comparator.compare(e, elem);
+ if (cmp < 0) {
+ AvlNode<E> initLeft = left;
+ if (initLeft == null) {
+ result[0] = 0;
+ return (count > 0) ? addLeftChild(e, count) : this;
+ }
- private static final BstAggregate<Node<Object>> SIZE_AGGREGATE =
- new BstAggregate<Node<Object>>() {
- @Override
- public int entryValue(Node<Object> entry) {
- return entry.elemCount();
+ left = initLeft.setCount(comparator, e, count, result);
+
+ if (count == 0 && result[0] != 0) {
+ this.distinctElements--;
+ } else if (count > 0 && result[0] == 0) {
+ this.distinctElements++;
}
- @Override
- public long treeValue(@Nullable Node<Object> tree) {
- return sizeOrZero(tree);
+ this.totalCount += count - result[0];
+ return rebalance();
+ } else if (cmp > 0) {
+ AvlNode<E> initRight = right;
+ if (initRight == null) {
+ result[0] = 0;
+ return (count > 0) ? addRightChild(e, count) : this;
}
- };
- @SuppressWarnings("unchecked")
- private BstNodeFactory<Node<E>> nodeFactory() {
- return (BstNodeFactory) NODE_FACTORY;
- }
+ right = initRight.setCount(comparator, e, count, result);
- private static final BstNodeFactory<Node<Object>> NODE_FACTORY =
- new BstNodeFactory<Node<Object>>() {
- @Override
- public Node<Object> createNode(Node<Object> source, @Nullable Node<Object> left,
- @Nullable Node<Object> right) {
- return new Node<Object>(source.getKey(), source.elemCount(), left, right);
+ if (count == 0 && result[0] != 0) {
+ this.distinctElements--;
+ } else if (count > 0 && result[0] == 0) {
+ this.distinctElements++;
}
- };
- private abstract class MultisetModifier implements BstModifier<E, Node<E>> {
- abstract int newCount(int oldCount);
+ this.totalCount += count - result[0];
+ return rebalance();
+ }
- @Nullable
- @Override
- public BstModificationResult<Node<E>> modify(E key, @Nullable Node<E> originalEntry) {
- int oldCount = countOrZero(originalEntry);
- int newCount = newCount(oldCount);
- if (oldCount == newCount) {
- return BstModificationResult.identity(originalEntry);
- } else if (newCount == 0) {
- return BstModificationResult.rebalancingChange(originalEntry, null);
- } else if (oldCount == 0) {
- return BstModificationResult.rebalancingChange(null, new Node<E>(key, newCount));
+ // setting my count
+ result[0] = elemCount;
+ if (count == 0) {
+ return deleteMe();
+ }
+ this.totalCount += count - elemCount;
+ this.elemCount = count;
+ return this;
+ }
+
+ AvlNode<E> setCount(
+ Comparator<? super E> comparator,
+ @Nullable E e,
+ int expectedCount,
+ int newCount,
+ int[] result) {
+ int cmp = comparator.compare(e, elem);
+ if (cmp < 0) {
+ AvlNode<E> initLeft = left;
+ if (initLeft == null) {
+ result[0] = 0;
+ if (expectedCount == 0 && newCount > 0) {
+ return addLeftChild(e, newCount);
+ }
+ return this;
+ }
+
+ left = initLeft.setCount(comparator, e, expectedCount, newCount, result);
+
+ if (result[0] == expectedCount) {
+ if (newCount == 0 && result[0] != 0) {
+ this.distinctElements--;
+ } else if (newCount > 0 && result[0] == 0) {
+ this.distinctElements++;
+ }
+ this.totalCount += newCount - result[0];
+ }
+ return rebalance();
+ } else if (cmp > 0) {
+ AvlNode<E> initRight = right;
+ if (initRight == null) {
+ result[0] = 0;
+ if (expectedCount == 0 && newCount > 0) {
+ return addRightChild(e, newCount);
+ }
+ return this;
+ }
+
+ right = initRight.setCount(comparator, e, expectedCount, newCount, result);
+
+ if (result[0] == expectedCount) {
+ if (newCount == 0 && result[0] != 0) {
+ this.distinctElements--;
+ } else if (newCount > 0 && result[0] == 0) {
+ this.distinctElements++;
+ }
+ this.totalCount += newCount - result[0];
+ }
+ return rebalance();
+ }
+
+ // setting my count
+ result[0] = elemCount;
+ if (expectedCount == elemCount) {
+ if (newCount == 0) {
+ return deleteMe();
+ }
+ this.totalCount += newCount - elemCount;
+ this.elemCount = newCount;
+ }
+ return this;
+ }
+
+ private AvlNode<E> deleteMe() {
+ int oldElemCount = this.elemCount;
+ this.elemCount = 0;
+ successor(pred, succ);
+ if (left == null) {
+ return right;
+ } else if (right == null) {
+ return left;
+ } else if (left.height >= right.height) {
+ AvlNode<E> newTop = pred;
+ // newTop is the maximum node in my left subtree
+ newTop.left = left.removeMax(newTop);
+ newTop.right = right;
+ newTop.distinctElements = distinctElements - 1;
+ newTop.totalCount = totalCount - oldElemCount;
+ return newTop.rebalance();
} else {
- return BstModificationResult.rebuildingChange(originalEntry,
- new Node<E>(originalEntry.getKey(), newCount));
+ AvlNode<E> newTop = succ;
+ newTop.right = right.removeMin(newTop);
+ newTop.left = left;
+ newTop.distinctElements = distinctElements - 1;
+ newTop.totalCount = totalCount - oldElemCount;
+ return newTop.rebalance();
}
}
- }
- private final class AddModifier extends MultisetModifier {
- private final int countToAdd;
+ // Removes the minimum node from this subtree to be reused elsewhere
+ private AvlNode<E> removeMin(AvlNode<E> node) {
+ if (left == null) {
+ return right;
+ } else {
+ left = left.removeMin(node);
+ distinctElements--;
+ totalCount -= node.elemCount;
+ return rebalance();
+ }
+ }
- private AddModifier(int countToAdd) {
- checkArgument(countToAdd > 0);
- this.countToAdd = countToAdd;
+ // Removes the maximum node from this subtree to be reused elsewhere
+ private AvlNode<E> removeMax(AvlNode<E> node) {
+ if (right == null) {
+ return left;
+ } else {
+ right = right.removeMax(node);
+ distinctElements--;
+ totalCount -= node.elemCount;
+ return rebalance();
+ }
}
- @Override
- int newCount(int oldCount) {
- checkArgument(countToAdd <= Integer.MAX_VALUE - oldCount, "Cannot add this many elements");
- return oldCount + countToAdd;
+ private void recomputeMultiset() {
+ this.distinctElements = 1 + TreeMultiset.distinctElements(left)
+ + TreeMultiset.distinctElements(right);
+ this.totalCount = elemCount + totalCount(left) + totalCount(right);
}
- }
- private final class RemoveModifier extends MultisetModifier {
- private final int countToRemove;
+ private void recomputeHeight() {
+ this.height = 1 + Math.max(height(left), height(right));
+ }
- private RemoveModifier(int countToRemove) {
- checkArgument(countToRemove > 0);
- this.countToRemove = countToRemove;
+ private void recompute() {
+ recomputeMultiset();
+ recomputeHeight();
}
- @Override
- int newCount(int oldCount) {
- return Math.max(0, oldCount - countToRemove);
+ private AvlNode<E> rebalance() {
+ switch (balanceFactor()) {
+ case -2:
+ if (right.balanceFactor() > 0) {
+ right = right.rotateRight();
+ }
+ return rotateLeft();
+ case 2:
+ if (left.balanceFactor() < 0) {
+ left = left.rotateLeft();
+ }
+ return rotateRight();
+ default:
+ recomputeHeight();
+ return this;
+ }
}
- }
- private final class SetCountModifier extends MultisetModifier {
- private final int countToSet;
+ private int balanceFactor() {
+ return height(left) - height(right);
+ }
- private SetCountModifier(int countToSet) {
- checkArgument(countToSet >= 0);
- this.countToSet = countToSet;
+ private AvlNode<E> rotateLeft() {
+ checkState(right != null);
+ AvlNode<E> newTop = right;
+ this.right = newTop.left;
+ newTop.left = this;
+ newTop.totalCount = this.totalCount;
+ newTop.distinctElements = this.distinctElements;
+ this.recompute();
+ newTop.recomputeHeight();
+ return newTop;
}
- @Override
- int newCount(int oldCount) {
- return countToSet;
+ private AvlNode<E> rotateRight() {
+ checkState(left != null);
+ AvlNode<E> newTop = left;
+ this.left = newTop.right;
+ newTop.right = this;
+ newTop.totalCount = this.totalCount;
+ newTop.distinctElements = this.distinctElements;
+ this.recompute();
+ newTop.recomputeHeight();
+ return newTop;
}
- }
- private final class ConditionalSetCountModifier extends MultisetModifier {
- private final int expectedCount;
- private final int setCount;
+ private static long totalCount(@Nullable AvlNode<?> node) {
+ return (node == null) ? 0 : node.totalCount;
+ }
- private ConditionalSetCountModifier(int expectedCount, int setCount) {
- checkArgument(setCount >= 0 & expectedCount >= 0);
- this.expectedCount = expectedCount;
- this.setCount = setCount;
+ private static int height(@Nullable AvlNode<?> node) {
+ return (node == null) ? 0 : node.height;
+ }
+
+ @Nullable private AvlNode<E> ceiling(Comparator<? super E> comparator, E e) {
+ int cmp = comparator.compare(e, elem);
+ if (cmp < 0) {
+ return (left == null) ? this : Objects.firstNonNull(left.ceiling(comparator, e), this);
+ } else if (cmp == 0) {
+ return this;
+ } else {
+ return (right == null) ? null : right.ceiling(comparator, e);
+ }
+ }
+
+ @Nullable private AvlNode<E> floor(Comparator<? super E> comparator, E e) {
+ int cmp = comparator.compare(e, elem);
+ if (cmp > 0) {
+ return (right == null) ? this : Objects.firstNonNull(right.floor(comparator, e), this);
+ } else if (cmp == 0) {
+ return this;
+ } else {
+ return (left == null) ? null : left.floor(comparator, e);
+ }
+ }
+
+ @Override
+ public E getElement() {
+ return elem;
+ }
+
+ @Override
+ public int getCount() {
+ return elemCount;
}
@Override
- int newCount(int oldCount) {
- return (oldCount == expectedCount) ? setCount : oldCount;
+ public String toString() {
+ return Multisets.immutableEntry(getElement(), getCount()).toString();
}
}
+ private static <T> void successor(AvlNode<T> a, AvlNode<T> b) {
+ a.succ = b;
+ b.pred = a;
+ }
+
+ private static <T> void successor(AvlNode<T> a, AvlNode<T> b, AvlNode<T> c) {
+ successor(a, b);
+ successor(b, c);
+ }
+
/*
- * TODO(jlevy): Decide whether entrySet() should return entries with an
- * equals() method that calls the comparator to compare the two keys. If that
- * change is made, AbstractMultiset.equals() can simply check whether two
- * multisets have equal entry sets.
+ * TODO(jlevy): Decide whether entrySet() should return entries with an equals() method that
+ * calls the comparator to compare the two keys. If that change is made,
+ * AbstractMultiset.equals() can simply check whether two multisets have equal entry sets.
*/
}
+
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/UnmodifiableSortedMultiset.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/UnmodifiableSortedMultiset.java
new file mode 100644
index 000000000..87937fe1d
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/UnmodifiableSortedMultiset.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.collect.Multisets.UnmodifiableMultiset;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.SortedSet;
+
+/**
+ * Implementation of {@link Multisets#unmodifiableSortedMultiset(SortedMultiset)}
+ * for GWT.
+ *
+ * @author Louis Wasserman
+ */
+final class UnmodifiableSortedMultiset<E>
+ extends UnmodifiableMultiset<E> implements SortedMultiset<E> {
+ UnmodifiableSortedMultiset(SortedMultiset<E> delegate) {
+ super(delegate);
+ }
+
+ @Override
+ protected SortedMultiset<E> delegate() {
+ return (SortedMultiset<E>) super.delegate();
+ }
+
+ @Override
+ public Comparator<? super E> comparator() {
+ return delegate().comparator();
+ }
+
+ @Override
+ SortedSet<E> createElementSet() {
+ return Collections.unmodifiableSortedSet(delegate().elementSet());
+ }
+
+ @Override
+ public SortedSet<E> elementSet() {
+ return (SortedSet<E>) super.elementSet();
+ }
+
+ private transient UnmodifiableSortedMultiset<E> descendingMultiset;
+
+ @Override
+ public SortedMultiset<E> descendingMultiset() {
+ UnmodifiableSortedMultiset<E> result = descendingMultiset;
+ if (result == null) {
+ result = new UnmodifiableSortedMultiset<E>(
+ delegate().descendingMultiset());
+ result.descendingMultiset = this;
+ return descendingMultiset = result;
+ }
+ return result;
+ }
+
+ @Override
+ public Entry<E> firstEntry() {
+ return delegate().firstEntry();
+ }
+
+ @Override
+ public Entry<E> lastEntry() {
+ return delegate().lastEntry();
+ }
+
+ @Override
+ public Entry<E> pollFirstEntry() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Entry<E> pollLastEntry() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SortedMultiset<E> headMultiset(E upperBound, BoundType boundType) {
+ return Multisets.unmodifiableSortedMultiset(
+ delegate().headMultiset(upperBound, boundType));
+ }
+
+ @Override
+ public SortedMultiset<E> subMultiset(
+ E lowerBound, BoundType lowerBoundType,
+ E upperBound, BoundType upperBoundType) {
+ return Multisets.unmodifiableSortedMultiset(delegate().subMultiset(
+ lowerBound, lowerBoundType, upperBound, upperBoundType));
+ }
+
+ @Override
+ public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) {
+ return Multisets.unmodifiableSortedMultiset(
+ delegate().tailMultiset(lowerBound, boundType));
+ }
+
+ private static final long serialVersionUID = 0;
+} \ No newline at end of file
diff --git a/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/BaseEncoding.java b/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/BaseEncoding.java
new file mode 100644
index 000000000..dee5b8935
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/BaseEncoding.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.io;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkPositionIndexes;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.io.GwtWorkarounds.asCharInput;
+import static com.google.common.io.GwtWorkarounds.stringBuilderOutput;
+import static com.google.common.math.IntMath.divide;
+import static com.google.common.math.IntMath.log2;
+import static java.math.RoundingMode.CEILING;
+import static java.math.RoundingMode.FLOOR;
+import static java.math.RoundingMode.UNNECESSARY;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Ascii;
+import com.google.common.base.CharMatcher;
+import com.google.common.io.GwtWorkarounds.ByteInput;
+import com.google.common.io.GwtWorkarounds.ByteOutput;
+import com.google.common.io.GwtWorkarounds.CharInput;
+import com.google.common.io.GwtWorkarounds.CharOutput;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nullable;
+
+/**
+ * A binary encoding scheme for reversibly translating between byte sequences and printable ASCII
+ * strings. This class includes several constants for encoding schemes specified by <a
+ * href="http://tools.ietf.org/html/rfc4648">RFC 4648</a>. For example, the expression:
+ * <pre> {@code
+ *
+ * BaseEncoding.base32().encode("foo".getBytes(Charsets.US_ASCII))
+ * }</pre>
+ * returns the string {@code "MZXW6==="}, and <pre> {@code
+ *
+ * byte[] decoded = BaseEncoding.base32().decode("MZXW6===");
+ * }</pre>
+ *
+ * ...returns the ASCII bytes of the string {@code "foo"}.
+ *
+ * <p>By default, {@code BaseEncoding}'s behavior is relatively strict and in accordance with
+ * RFC 4648. Decoding rejects characters in the wrong case, though padding is optional.
+ * To modify encoding and decoding behavior, use configuration methods to obtain a new encoding
+ * with modified behavior: <pre> {@code
+ *
+ * BaseEncoding.base16().lowerCase().decode("deadbeef");
+ * }</pre>
+ *
+ * <p>Warning: BaseEncoding instances are immutable. Invoking a configuration method has no effect
+ * on the receiving instance; you must store and use the new encoding instance it returns, instead.
+ * <pre> {@code
+ *
+ * // Do NOT do this
+ * BaseEncoding hex = BaseEncoding.base16();
+ * hex.lowerCase(); // does nothing!
+ * return hex.decode("deadbeef"); // throws an IllegalArgumentException
+ * }</pre>
+ *
+ * <p>It is guaranteed that {@code encoding.decode(encoding.encode(x))} is always equal to
+ * {@code x}, but the reverse does not necessarily hold.
+ *
+ * <p>
+ * <table>
+ * <tr>
+ * <th>Encoding
+ * <th>Alphabet
+ * <th>{@code char:byte} ratio
+ * <th>Default padding
+ * <th>Comments
+ * <tr>
+ * <td>{@link #base16()}
+ * <td>0-9 A-F
+ * <td>2.00
+ * <td>N/A
+ * <td>Traditional hexadecimal. Defaults to upper case.
+ * <tr>
+ * <td>{@link #base32()}
+ * <td>A-Z 2-7
+ * <td>1.60
+ * <td>=
+ * <td>Human-readable; no possibility of mixing up 0/O or 1/I. Defaults to upper case.
+ * <tr>
+ * <td>{@link #base32Hex()}
+ * <td>0-9 A-V
+ * <td>1.60
+ * <td>=
+ * <td>"Numerical" base 32; extended from the traditional hex alphabet. Defaults to upper case.
+ * <tr>
+ * <td>{@link #base64()}
+ * <td>A-Z a-z 0-9 + /
+ * <td>1.33
+ * <td>=
+ * <td>
+ * <tr>
+ * <td>{@link #base64Url()}
+ * <td>A-Z a-z 0-9 - _
+ * <td>1.33
+ * <td>=
+ * <td>Safe to use as filenames, or to pass in URLs without escaping
+ * </table>
+ *
+ * <p>
+ * All instances of this class are immutable, so they may be stored safely as static constants.
+ *
+ * @author Louis Wasserman
+ * @since 14.0
+ */
+@Beta
+@GwtCompatible(emulated = true)
+public abstract class BaseEncoding {
+ // TODO(user): consider adding encodeTo(Appendable, byte[], [int, int])
+
+ BaseEncoding() {}
+
+ /**
+ * Encodes the specified byte array, and returns the encoded {@code String}.
+ */
+ public String encode(byte[] bytes) {
+ return encode(checkNotNull(bytes), 0, bytes.length);
+ }
+
+ /**
+ * Encodes the specified range of the specified byte array, and returns the encoded
+ * {@code String}.
+ */
+ public final String encode(byte[] bytes, int off, int len) {
+ checkNotNull(bytes);
+ checkPositionIndexes(off, off + len, bytes.length);
+ CharOutput result = stringBuilderOutput(maxEncodedSize(len));
+ ByteOutput byteOutput = encodingStream(result);
+ try {
+ for (int i = 0; i < len; i++) {
+ byteOutput.write(bytes[off + i]);
+ }
+ byteOutput.close();
+ } catch (IOException impossible) {
+ throw new AssertionError("impossible");
+ }
+ return result.toString();
+ }
+
+ // TODO(user): document the extent of leniency, probably after adding ignore(CharMatcher)
+
+ private static byte[] extract(byte[] result, int length) {
+ if (length == result.length) {
+ return result;
+ } else {
+ byte[] trunc = new byte[length];
+ System.arraycopy(result, 0, trunc, 0, length);
+ return trunc;
+ }
+ }
+
+ /**
+ * Decodes the specified character sequence, and returns the resulting {@code byte[]}.
+ * This is the inverse operation to {@link #encode(byte[])}.
+ *
+ * @throws IllegalArgumentException if the input is not a valid encoded string according to this
+ * encoding.
+ */
+ public final byte[] decode(CharSequence chars) {
+ chars = padding().trimTrailingFrom(chars);
+ ByteInput decodedInput = decodingStream(asCharInput(chars));
+ byte[] tmp = new byte[maxDecodedSize(chars.length())];
+ int index = 0;
+ try {
+ for (int i = decodedInput.read(); i != -1; i = decodedInput.read()) {
+ tmp[index++] = (byte) i;
+ }
+ } catch (IOException badInput) {
+ throw new IllegalArgumentException(badInput);
+ }
+ return extract(tmp, index);
+ }
+
+ // Implementations for encoding/decoding
+
+ abstract int maxEncodedSize(int bytes);
+
+ abstract ByteOutput encodingStream(CharOutput charOutput);
+
+ abstract int maxDecodedSize(int chars);
+
+ abstract ByteInput decodingStream(CharInput charInput);
+
+ abstract CharMatcher padding();
+
+ // Modified encoding generators
+
+ /**
+ * Returns an encoding that behaves equivalently to this encoding, but omits any padding
+ * characters as specified by <a href="http://tools.ietf.org/html/rfc4648#section-3.2">RFC 4648
+ * section 3.2</a>, Padding of Encoded Data.
+ */
+ @CheckReturnValue
+ public abstract BaseEncoding omitPadding();
+
+ /**
+ * Returns an encoding that behaves equivalently to this encoding, but uses an alternate character
+ * for padding.
+ *
+ * @throws IllegalArgumentException if this padding character is already used in the alphabet or a
+ * separator
+ */
+ @CheckReturnValue
+ public abstract BaseEncoding withPadChar(char padChar);
+
+ /**
+ * Returns an encoding that behaves equivalently to this encoding, but adds a separator string
+ * after every {@code n} characters. Any occurrences of any characters that occur in the separator
+ * are skipped over in decoding.
+ *
+ * @throws IllegalArgumentException if any alphabet or padding characters appear in the separator
+ * string, or if {@code n <= 0}
+ * @throws UnsupportedOperationException if this encoding already uses a separator
+ */
+ @CheckReturnValue
+ public abstract BaseEncoding withSeparator(String separator, int n);
+
+ /**
+ * Returns an encoding that behaves equivalently to this encoding, but encodes and decodes with
+ * uppercase letters. Padding and separator characters remain in their original case.
+ *
+ * @throws IllegalStateException if the alphabet used by this encoding contains mixed upper- and
+ * lower-case characters
+ */
+ @CheckReturnValue
+ public abstract BaseEncoding upperCase();
+
+ /**
+ * Returns an encoding that behaves equivalently to this encoding, but encodes and decodes with
+ * lowercase letters. Padding and separator characters remain in their original case.
+ *
+ * @throws IllegalStateException if the alphabet used by this encoding contains mixed upper- and
+ * lower-case characters
+ */
+ @CheckReturnValue
+ public abstract BaseEncoding lowerCase();
+
+ private static final BaseEncoding BASE64 = new StandardBaseEncoding(
+ "base64()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", '=');
+
+ /**
+ * The "base64" base encoding specified by <a
+ * href="http://tools.ietf.org/html/rfc4648#section-4">RFC 4648 section 4</a>, Base 64 Encoding.
+ * (This is the same as the base 64 encoding from <a
+ * href="http://tools.ietf.org/html/rfc3548#section-3">RFC 3548</a>.)
+ *
+ * <p>The character {@code '='} is used for padding, but can be {@linkplain #omitPadding()
+ * omitted} or {@linkplain #withPadChar(char) replaced}.
+ *
+ * <p>No line feeds are added by default, as per <a
+ * href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section 3.1</a>, Line Feeds in
+ * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}.
+ */
+ public static BaseEncoding base64() {
+ return BASE64;
+ }
+
+ private static final BaseEncoding BASE64_URL = new StandardBaseEncoding(
+ "base64Url()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", '=');
+
+ /**
+ * The "base64url" encoding specified by <a
+ * href="http://tools.ietf.org/html/rfc4648#section-5">RFC 4648 section 5</a>, Base 64 Encoding
+ * with URL and Filename Safe Alphabet, also sometimes referred to as the "web safe Base64."
+ * (This is the same as the base 64 encoding with URL and filename safe alphabet from <a
+ * href="http://tools.ietf.org/html/rfc3548#section-4">RFC 3548</a>.)
+ *
+ * <p>The character {@code '='} is used for padding, but can be {@linkplain #omitPadding()
+ * omitted} or {@linkplain #withPadChar(char) replaced}.
+ *
+ * <p>No line feeds are added by default, as per <a
+ * href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section 3.1</a>, Line Feeds in
+ * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}.
+ */
+ public static BaseEncoding base64Url() {
+ return BASE64_URL;
+ }
+
+ private static final BaseEncoding BASE32 =
+ new StandardBaseEncoding("base32()", "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", '=');
+
+ /**
+ * The "base32" encoding specified by <a
+ * href="http://tools.ietf.org/html/rfc4648#section-6">RFC 4648 section 6</a>, Base 32 Encoding.
+ * (This is the same as the base 32 encoding from <a
+ * href="http://tools.ietf.org/html/rfc3548#section-5">RFC 3548</a>.)
+ *
+ * <p>The character {@code '='} is used for padding, but can be {@linkplain #omitPadding()
+ * omitted} or {@linkplain #withPadChar(char) replaced}.
+ *
+ * <p>No line feeds are added by default, as per <a
+ * href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section 3.1</a>, Line Feeds in
+ * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}.
+ */
+ public static BaseEncoding base32() {
+ return BASE32;
+ }
+
+ private static final BaseEncoding BASE32_HEX =
+ new StandardBaseEncoding("base32Hex()", "0123456789ABCDEFGHIJKLMNOPQRSTUV", '=');
+
+ /**
+ * The "base32hex" encoding specified by <a
+ * href="http://tools.ietf.org/html/rfc4648#section-7">RFC 4648 section 7</a>, Base 32 Encoding
+ * with Extended Hex Alphabet. There is no corresponding encoding in RFC 3548.
+ *
+ * <p>The character {@code '='} is used for padding, but can be {@linkplain #omitPadding()
+ * omitted} or {@linkplain #withPadChar(char) replaced}.
+ *
+ * <p>No line feeds are added by default, as per <a
+ * href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section 3.1</a>, Line Feeds in
+ * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}.
+ */
+ public static BaseEncoding base32Hex() {
+ return BASE32_HEX;
+ }
+
+ private static final BaseEncoding BASE16 =
+ new StandardBaseEncoding("base16()", "0123456789ABCDEF", null);
+
+ /**
+ * The "base16" encoding specified by <a
+ * href="http://tools.ietf.org/html/rfc4648#section-8">RFC 4648 section 8</a>, Base 16 Encoding.
+ * (This is the same as the base 16 encoding from <a
+ * href="http://tools.ietf.org/html/rfc3548#section-6">RFC 3548</a>.) This is commonly known as
+ * "hexadecimal" format.
+ *
+ * <p>No padding is necessary in base 16, so {@link #withPadChar(char)} and
+ * {@link #omitPadding()} have no effect.
+ *
+ * <p>No line feeds are added by default, as per <a
+ * href="http://tools.ietf.org/html/rfc4648#section-3.1"> RFC 4648 section 3.1</a>, Line Feeds in
+ * Encoded Data. Line feeds may be added using {@link #withSeparator(String, int)}.
+ */
+ public static BaseEncoding base16() {
+ return BASE16;
+ }
+
+ private static final class Alphabet extends CharMatcher {
+ private final String name;
+ // this is meant to be immutable -- don't modify it!
+ private final char[] chars;
+ final int mask;
+ final int bitsPerChar;
+ final int charsPerChunk;
+ final int bytesPerChunk;
+ private final byte[] decodabet;
+ private final boolean[] validPadding;
+
+ Alphabet(String name, char[] chars) {
+ this.name = checkNotNull(name);
+ this.chars = checkNotNull(chars);
+ try {
+ this.bitsPerChar = log2(chars.length, UNNECESSARY);
+ } catch (ArithmeticException e) {
+ throw new IllegalArgumentException("Illegal alphabet length " + chars.length, e);
+ }
+
+ /*
+ * e.g. for base64, bitsPerChar == 6, charsPerChunk == 4, and bytesPerChunk == 3. This makes
+ * for the smallest chunk size that still has charsPerChunk * bitsPerChar be a multiple of 8.
+ */
+ int gcd = Math.min(8, Integer.lowestOneBit(bitsPerChar));
+ this.charsPerChunk = 8 / gcd;
+ this.bytesPerChunk = bitsPerChar / gcd;
+
+ this.mask = chars.length - 1;
+
+ byte[] decodabet = new byte[Ascii.MAX + 1];
+ Arrays.fill(decodabet, (byte) -1);
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+ checkArgument(CharMatcher.ASCII.matches(c), "Non-ASCII character: %s", c);
+ checkArgument(decodabet[c] == -1, "Duplicate character: %s", c);
+ decodabet[c] = (byte) i;
+ }
+ this.decodabet = decodabet;
+
+ boolean[] validPadding = new boolean[charsPerChunk];
+ for (int i = 0; i < bytesPerChunk; i++) {
+ validPadding[divide(i * 8, bitsPerChar, CEILING)] = true;
+ }
+ this.validPadding = validPadding;
+ }
+
+ char encode(int bits) {
+ return chars[bits];
+ }
+
+ boolean isValidPaddingStartPosition(int index) {
+ return validPadding[index % charsPerChunk];
+ }
+
+ int decode(char ch) throws IOException {
+ if (ch > Ascii.MAX || decodabet[ch] == -1) {
+ throw new IOException("Unrecognized character: " + ch);
+ }
+ return decodabet[ch];
+ }
+
+ private boolean hasLowerCase() {
+ for (char c : chars) {
+ if (Ascii.isLowerCase(c)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasUpperCase() {
+ for (char c : chars) {
+ if (Ascii.isUpperCase(c)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Alphabet upperCase() {
+ if (!hasLowerCase()) {
+ return this;
+ } else {
+ checkState(!hasUpperCase(), "Cannot call upperCase() on a mixed-case alphabet");
+ char[] upperCased = new char[chars.length];
+ for (int i = 0; i < chars.length; i++) {
+ upperCased[i] = Ascii.toUpperCase(chars[i]);
+ }
+ return new Alphabet(name + ".upperCase()", upperCased);
+ }
+ }
+
+ Alphabet lowerCase() {
+ if (!hasUpperCase()) {
+ return this;
+ } else {
+ checkState(!hasLowerCase(), "Cannot call lowerCase() on a mixed-case alphabet");
+ char[] lowerCased = new char[chars.length];
+ for (int i = 0; i < chars.length; i++) {
+ lowerCased[i] = Ascii.toLowerCase(chars[i]);
+ }
+ return new Alphabet(name + ".lowerCase()", lowerCased);
+ }
+ }
+
+ @Override
+ public boolean matches(char c) {
+ return CharMatcher.ASCII.matches(c) && decodabet[c] != -1;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ static final class StandardBaseEncoding extends BaseEncoding {
+ // TODO(user): provide a useful toString
+ private final Alphabet alphabet;
+
+ @Nullable
+ private final Character paddingChar;
+
+ StandardBaseEncoding(String name, String alphabetChars, @Nullable Character paddingChar) {
+ this(new Alphabet(name, alphabetChars.toCharArray()), paddingChar);
+ }
+
+ StandardBaseEncoding(Alphabet alphabet, Character paddingChar) {
+ this.alphabet = checkNotNull(alphabet);
+ checkArgument(paddingChar == null || !alphabet.matches(paddingChar),
+ "Padding character %s was already in alphabet", paddingChar);
+ this.paddingChar = paddingChar;
+ }
+
+ @Override
+ CharMatcher padding() {
+ return (paddingChar == null) ? CharMatcher.NONE : CharMatcher.is(paddingChar.charValue());
+ }
+
+ @Override
+ int maxEncodedSize(int bytes) {
+ return alphabet.charsPerChunk * divide(bytes, alphabet.bytesPerChunk, CEILING);
+ }
+
+ @Override
+ ByteOutput encodingStream(final CharOutput out) {
+ checkNotNull(out);
+ return new ByteOutput() {
+ int bitBuffer = 0;
+ int bitBufferLength = 0;
+ int writtenChars = 0;
+
+ @Override
+ public void write(byte b) throws IOException {
+ bitBuffer <<= 8;
+ bitBuffer |= b & 0xFF;
+ bitBufferLength += 8;
+ while (bitBufferLength >= alphabet.bitsPerChar) {
+ int charIndex = (bitBuffer >> (bitBufferLength - alphabet.bitsPerChar))
+ & alphabet.mask;
+ out.write(alphabet.encode(charIndex));
+ writtenChars++;
+ bitBufferLength -= alphabet.bitsPerChar;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (bitBufferLength > 0) {
+ int charIndex = (bitBuffer << (alphabet.bitsPerChar - bitBufferLength))
+ & alphabet.mask;
+ out.write(alphabet.encode(charIndex));
+ writtenChars++;
+ if (paddingChar != null) {
+ while (writtenChars % alphabet.charsPerChunk != 0) {
+ out.write(paddingChar.charValue());
+ writtenChars++;
+ }
+ }
+ }
+ out.close();
+ }
+ };
+ }
+
+ @Override
+ int maxDecodedSize(int chars) {
+ return (int) ((alphabet.bitsPerChar * (long) chars + 7L) / 8L);
+ }
+
+ @Override
+ ByteInput decodingStream(final CharInput reader) {
+ checkNotNull(reader);
+ return new ByteInput() {
+ int bitBuffer = 0;
+ int bitBufferLength = 0;
+ int readChars = 0;
+ boolean hitPadding = false;
+ final CharMatcher paddingMatcher = padding();
+
+ @Override
+ public int read() throws IOException {
+ while (true) {
+ int readChar = reader.read();
+ if (readChar == -1) {
+ if (!hitPadding && !alphabet.isValidPaddingStartPosition(readChars)) {
+ throw new IOException("Invalid input length " + readChars);
+ }
+ return -1;
+ }
+ readChars++;
+ char ch = (char) readChar;
+ if (paddingMatcher.matches(ch)) {
+ if (!hitPadding
+ && (readChars == 1 || !alphabet.isValidPaddingStartPosition(readChars - 1))) {
+ throw new IOException("Padding cannot start at index " + readChars);
+ }
+ hitPadding = true;
+ } else if (hitPadding) {
+ throw new IOException(
+ "Expected padding character but found '" + ch + "' at index " + readChars);
+ } else {
+ bitBuffer <<= alphabet.bitsPerChar;
+ bitBuffer |= alphabet.decode(ch);
+ bitBufferLength += alphabet.bitsPerChar;
+
+ if (bitBufferLength >= 8) {
+ bitBufferLength -= 8;
+ return (bitBuffer >> bitBufferLength) & 0xFF;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ reader.close();
+ }
+ };
+ }
+
+ @Override
+ public BaseEncoding omitPadding() {
+ return (paddingChar == null) ? this : new StandardBaseEncoding(alphabet, null);
+ }
+
+ @Override
+ public BaseEncoding withPadChar(char padChar) {
+ if (8 % alphabet.bitsPerChar == 0 ||
+ (paddingChar != null && paddingChar.charValue() == padChar)) {
+ return this;
+ } else {
+ return new StandardBaseEncoding(alphabet, padChar);
+ }
+ }
+
+ @Override
+ public BaseEncoding withSeparator(String separator, int afterEveryChars) {
+ checkNotNull(separator);
+ checkArgument(padding().or(alphabet).matchesNoneOf(separator),
+ "Separator cannot contain alphabet or padding characters");
+ return new SeparatedBaseEncoding(this, separator, afterEveryChars);
+ }
+
+ private transient BaseEncoding upperCase;
+ private transient BaseEncoding lowerCase;
+
+ @Override
+ public BaseEncoding upperCase() {
+ BaseEncoding result = upperCase;
+ if (result == null) {
+ Alphabet upper = alphabet.upperCase();
+ result = upperCase =
+ (upper == alphabet) ? this : new StandardBaseEncoding(upper, paddingChar);
+ }
+ return result;
+ }
+
+ @Override
+ public BaseEncoding lowerCase() {
+ BaseEncoding result = lowerCase;
+ if (result == null) {
+ Alphabet lower = alphabet.lowerCase();
+ result = lowerCase =
+ (lower == alphabet) ? this : new StandardBaseEncoding(lower, paddingChar);
+ }
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("BaseEncoding.");
+ builder.append(alphabet.toString());
+ if (8 % alphabet.bitsPerChar != 0) {
+ if (paddingChar == null) {
+ builder.append(".omitPadding()");
+ } else {
+ builder.append(".withPadChar(").append(paddingChar).append(')');
+ }
+ }
+ return builder.toString();
+ }
+ }
+
+ static CharInput ignoringInput(final CharInput delegate, final CharMatcher toIgnore) {
+ checkNotNull(delegate);
+ checkNotNull(toIgnore);
+ return new CharInput() {
+ @Override
+ public int read() throws IOException {
+ int readChar;
+ do {
+ readChar = delegate.read();
+ } while (readChar != -1 && toIgnore.matches((char) readChar));
+ return readChar;
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+ };
+ }
+
+ static CharOutput separatingOutput(
+ final CharOutput delegate, final String separator, final int afterEveryChars) {
+ checkNotNull(delegate);
+ checkNotNull(separator);
+ checkArgument(afterEveryChars > 0);
+ return new CharOutput() {
+ int charsUntilSeparator = afterEveryChars;
+
+ @Override
+ public void write(char c) throws IOException {
+ if (charsUntilSeparator == 0) {
+ for (int i = 0; i < separator.length(); i++) {
+ delegate.write(separator.charAt(i));
+ }
+ charsUntilSeparator = afterEveryChars;
+ }
+ delegate.write(c);
+ charsUntilSeparator--;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ delegate.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ delegate.close();
+ }
+ };
+ }
+
+ static final class SeparatedBaseEncoding extends BaseEncoding {
+ private final BaseEncoding delegate;
+ private final String separator;
+ private final int afterEveryChars;
+ private final CharMatcher separatorChars;
+
+ SeparatedBaseEncoding(BaseEncoding delegate, String separator, int afterEveryChars) {
+ this.delegate = checkNotNull(delegate);
+ this.separator = checkNotNull(separator);
+ this.afterEveryChars = afterEveryChars;
+ checkArgument(
+ afterEveryChars > 0, "Cannot add a separator after every %s chars", afterEveryChars);
+ this.separatorChars = CharMatcher.anyOf(separator).precomputed();
+ }
+
+ @Override
+ CharMatcher padding() {
+ return delegate.padding();
+ }
+
+ @Override
+ int maxEncodedSize(int bytes) {
+ int unseparatedSize = delegate.maxEncodedSize(bytes);
+ return unseparatedSize + separator.length()
+ * divide(Math.max(0, unseparatedSize - 1), afterEveryChars, FLOOR);
+ }
+
+ @Override
+ ByteOutput encodingStream(final CharOutput output) {
+ return delegate.encodingStream(separatingOutput(output, separator, afterEveryChars));
+ }
+
+ @Override
+ int maxDecodedSize(int chars) {
+ return delegate.maxDecodedSize(chars);
+ }
+
+ @Override
+ ByteInput decodingStream(final CharInput input) {
+ return delegate.decodingStream(ignoringInput(input, separatorChars));
+ }
+
+ @Override
+ public BaseEncoding omitPadding() {
+ return delegate.omitPadding().withSeparator(separator, afterEveryChars);
+ }
+
+ @Override
+ public BaseEncoding withPadChar(char padChar) {
+ return delegate.withPadChar(padChar).withSeparator(separator, afterEveryChars);
+ }
+
+ @Override
+ public BaseEncoding withSeparator(String separator, int afterEveryChars) {
+ throw new UnsupportedOperationException("Already have a separator");
+ }
+
+ @Override
+ public BaseEncoding upperCase() {
+ return delegate.upperCase().withSeparator(separator, afterEveryChars);
+ }
+
+ @Override
+ public BaseEncoding lowerCase() {
+ return delegate.lowerCase().withSeparator(separator, afterEveryChars);
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString() +
+ ".withSeparator(\"" + separator + "\", " + afterEveryChars + ")";
+ }
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/GwtWorkarounds.java b/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/GwtWorkarounds.java
new file mode 100644
index 000000000..f3f955eda
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/GwtWorkarounds.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.io;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.io.IOException;
+
+/**
+ * Provides simple GWT-compatible substitutes for {@code InputStream}, {@code OutputStream},
+ * {@code Reader}, and {@code Writer} so that {@code BaseEncoding} can use streaming implementations
+ * while remaining GWT-compatible.
+ *
+ * @author Louis Wasserman
+ */
+@GwtCompatible(emulated = true)
+final class GwtWorkarounds {
+ private GwtWorkarounds() {}
+
+ /**
+ * A GWT-compatible substitute for a {@code Reader}.
+ */
+ interface CharInput {
+ int read() throws IOException;
+ void close() throws IOException;
+ }
+
+ /**
+ * Views a {@code CharSequence} as a {@code CharInput}.
+ */
+ static CharInput asCharInput(final CharSequence chars) {
+ checkNotNull(chars);
+ return new CharInput() {
+ int index = 0;
+
+ @Override
+ public int read() {
+ if (index < chars.length()) {
+ return chars.charAt(index++);
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public void close() {
+ index = chars.length();
+ }
+ };
+ }
+
+ /**
+ * A GWT-compatible substitute for an {@code InputStream}.
+ */
+ interface ByteInput {
+ int read() throws IOException;
+ void close() throws IOException;
+ }
+
+ /**
+ * A GWT-compatible substitute for an {@code OutputStream}.
+ */
+ interface ByteOutput {
+ void write(byte b) throws IOException;
+ void flush() throws IOException;
+ void close() throws IOException;
+ }
+
+ /**
+ * A GWT-compatible substitute for a {@code Writer}.
+ */
+ interface CharOutput {
+ void write(char c) throws IOException;
+ void flush() throws IOException;
+ void close() throws IOException;
+ }
+
+ /**
+ * Returns a {@code CharOutput} whose {@code toString()} method can be used
+ * to get the combined output.
+ */
+ static CharOutput stringBuilderOutput(int initialSize) {
+ final StringBuilder builder = new StringBuilder(initialSize);
+ return new CharOutput() {
+
+ @Override
+ public void write(char c) {
+ builder.append(c);
+ }
+
+ @Override
+ public void flush() {}
+
+ @Override
+ public void close() {}
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+ };
+ }
+}
+
diff --git a/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/BigIntegerMath.java b/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/BigIntegerMath.java
new file mode 100644
index 000000000..75f19be9e
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/BigIntegerMath.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.math;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.math.MathPreconditions.checkNonNegative;
+import static com.google.common.math.MathPreconditions.checkPositive;
+import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
+import static java.math.RoundingMode.CEILING;
+import static java.math.RoundingMode.FLOOR;
+import static java.math.RoundingMode.HALF_EVEN;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.annotations.VisibleForTesting;
+
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class for arithmetic on values of type {@code BigInteger}.
+ *
+ * <p>The implementations of many methods in this class are based on material from Henry S. Warren,
+ * Jr.'s <i>Hacker's Delight</i>, (Addison Wesley, 2002).
+ *
+ * <p>Similar functionality for {@code int} and for {@code long} can be found in
+ * {@link IntMath} and {@link LongMath} respectively.
+ *
+ * @author Louis Wasserman
+ * @since 11.0
+ */
+@GwtCompatible(emulated = true)
+public final class BigIntegerMath {
+ /**
+ * Returns {@code true} if {@code x} represents a power of two.
+ */
+ public static boolean isPowerOfTwo(BigInteger x) {
+ checkNotNull(x);
+ return x.signum() > 0 && x.getLowestSetBit() == x.bitLength() - 1;
+ }
+
+ /**
+ * Returns the base-2 logarithm of {@code x}, rounded according to the specified rounding mode.
+ *
+ * @throws IllegalArgumentException if {@code x <= 0}
+ * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
+ * is not a power of two
+ */
+ @SuppressWarnings("fallthrough")
+ // TODO(kevinb): remove after this warning is disabled globally
+ public static int log2(BigInteger x, RoundingMode mode) {
+ checkPositive("x", checkNotNull(x));
+ int logFloor = x.bitLength() - 1;
+ switch (mode) {
+ case UNNECESSARY:
+ checkRoundingUnnecessary(isPowerOfTwo(x)); // fall through
+ case DOWN:
+ case FLOOR:
+ return logFloor;
+
+ case UP:
+ case CEILING:
+ return isPowerOfTwo(x) ? logFloor : logFloor + 1;
+
+ case HALF_DOWN:
+ case HALF_UP:
+ case HALF_EVEN:
+ if (logFloor < SQRT2_PRECOMPUTE_THRESHOLD) {
+ BigInteger halfPower = SQRT2_PRECOMPUTED_BITS.shiftRight(
+ SQRT2_PRECOMPUTE_THRESHOLD - logFloor);
+ if (x.compareTo(halfPower) <= 0) {
+ return logFloor;
+ } else {
+ return logFloor + 1;
+ }
+ }
+ /*
+ * Since sqrt(2) is irrational, log2(x) - logFloor cannot be exactly 0.5
+ *
+ * To determine which side of logFloor.5 the logarithm is, we compare x^2 to 2^(2 *
+ * logFloor + 1).
+ */
+ BigInteger x2 = x.pow(2);
+ int logX2Floor = x2.bitLength() - 1;
+ return (logX2Floor < 2 * logFloor + 1) ? logFloor : logFloor + 1;
+
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ /*
+ * The maximum number of bits in a square root for which we'll precompute an explicit half power
+ * of two. This can be any value, but higher values incur more class load time and linearly
+ * increasing memory consumption.
+ */
+ @VisibleForTesting static final int SQRT2_PRECOMPUTE_THRESHOLD = 256;
+
+ @VisibleForTesting static final BigInteger SQRT2_PRECOMPUTED_BITS =
+ new BigInteger("16a09e667f3bcc908b2fb1366ea957d3e3adec17512775099da2f590b0667322a", 16);
+
+ private static final double LN_10 = Math.log(10);
+ private static final double LN_2 = Math.log(2);
+
+ /**
+ * Returns {@code n!}, that is, the product of the first {@code n} positive
+ * integers, or {@code 1} if {@code n == 0}.
+ *
+ * <p><b>Warning</b>: the result takes <i>O(n log n)</i> space, so use cautiously.
+ *
+ * <p>This uses an efficient binary recursive algorithm to compute the factorial
+ * with balanced multiplies. It also removes all the 2s from the intermediate
+ * products (shifting them back in at the end).
+ *
+ * @throws IllegalArgumentException if {@code n < 0}
+ */
+ public static BigInteger factorial(int n) {
+ checkNonNegative("n", n);
+
+ // If the factorial is small enough, just use LongMath to do it.
+ if (n < LongMath.factorials.length) {
+ return BigInteger.valueOf(LongMath.factorials[n]);
+ }
+
+ // Pre-allocate space for our list of intermediate BigIntegers.
+ int approxSize = IntMath.divide(n * IntMath.log2(n, CEILING), Long.SIZE, CEILING);
+ ArrayList<BigInteger> bignums = new ArrayList<BigInteger>(approxSize);
+
+ // Start from the pre-computed maximum long factorial.
+ int startingNumber = LongMath.factorials.length;
+ long product = LongMath.factorials[startingNumber - 1];
+ // Strip off 2s from this value.
+ int shift = Long.numberOfTrailingZeros(product);
+ product >>= shift;
+
+ // Use floor(log2(num)) + 1 to prevent overflow of multiplication.
+ int productBits = LongMath.log2(product, FLOOR) + 1;
+ int bits = LongMath.log2(startingNumber, FLOOR) + 1;
+ // Check for the next power of two boundary, to save us a CLZ operation.
+ int nextPowerOfTwo = 1 << (bits - 1);
+
+ // Iteratively multiply the longs as big as they can go.
+ for (long num = startingNumber; num <= n; num++) {
+ // Check to see if the floor(log2(num)) + 1 has changed.
+ if ((num & nextPowerOfTwo) != 0) {
+ nextPowerOfTwo <<= 1;
+ bits++;
+ }
+ // Get rid of the 2s in num.
+ int tz = Long.numberOfTrailingZeros(num);
+ long normalizedNum = num >> tz;
+ shift += tz;
+ // Adjust floor(log2(num)) + 1.
+ int normalizedBits = bits - tz;
+ // If it won't fit in a long, then we store off the intermediate product.
+ if (normalizedBits + productBits >= Long.SIZE) {
+ bignums.add(BigInteger.valueOf(product));
+ product = 1;
+ productBits = 0;
+ }
+ product *= normalizedNum;
+ productBits = LongMath.log2(product, FLOOR) + 1;
+ }
+ // Check for leftovers.
+ if (product > 1) {
+ bignums.add(BigInteger.valueOf(product));
+ }
+ // Efficiently multiply all the intermediate products together.
+ return listProduct(bignums).shiftLeft(shift);
+ }
+
+ static BigInteger listProduct(List<BigInteger> nums) {
+ return listProduct(nums, 0, nums.size());
+ }
+
+ static BigInteger listProduct(List<BigInteger> nums, int start, int end) {
+ switch (end - start) {
+ case 0:
+ return BigInteger.ONE;
+ case 1:
+ return nums.get(start);
+ case 2:
+ return nums.get(start).multiply(nums.get(start + 1));
+ case 3:
+ return nums.get(start).multiply(nums.get(start + 1)).multiply(nums.get(start + 2));
+ default:
+ // Otherwise, split the list in half and recursively do this.
+ int m = (end + start) >>> 1;
+ return listProduct(nums, start, m).multiply(listProduct(nums, m, end));
+ }
+ }
+
+ /**
+ * Returns {@code n} choose {@code k}, also known as the binomial coefficient of {@code n} and
+ * {@code k}, that is, {@code n! / (k! (n - k)!)}.
+ *
+ * <p><b>Warning</b>: the result can take as much as <i>O(k log n)</i> space.
+ *
+ * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0}, or {@code k > n}
+ */
+ public static BigInteger binomial(int n, int k) {
+ checkNonNegative("n", n);
+ checkNonNegative("k", k);
+ checkArgument(k <= n, "k (%s) > n (%s)", k, n);
+ if (k > (n >> 1)) {
+ k = n - k;
+ }
+ if (k < LongMath.biggestBinomials.length && n <= LongMath.biggestBinomials[k]) {
+ return BigInteger.valueOf(LongMath.binomial(n, k));
+ }
+
+ BigInteger accum = BigInteger.ONE;
+
+ long numeratorAccum = n;
+ long denominatorAccum = 1;
+
+ int bits = LongMath.log2(n, RoundingMode.CEILING);
+
+ int numeratorBits = bits;
+
+ for (int i = 1; i < k; i++) {
+ int p = n - i;
+ int q = i + 1;
+
+ // log2(p) >= bits - 1, because p >= n/2
+
+ if (numeratorBits + bits >= Long.SIZE - 1) {
+ // The numerator is as big as it can get without risking overflow.
+ // Multiply numeratorAccum / denominatorAccum into accum.
+ accum = accum
+ .multiply(BigInteger.valueOf(numeratorAccum))
+ .divide(BigInteger.valueOf(denominatorAccum));
+ numeratorAccum = p;
+ denominatorAccum = q;
+ numeratorBits = bits;
+ } else {
+ // We can definitely multiply into the long accumulators without overflowing them.
+ numeratorAccum *= p;
+ denominatorAccum *= q;
+ numeratorBits += bits;
+ }
+ }
+ return accum
+ .multiply(BigInteger.valueOf(numeratorAccum))
+ .divide(BigInteger.valueOf(denominatorAccum));
+ }
+
+ // Returns true if BigInteger.valueOf(x.longValue()).equals(x).
+
+ private BigIntegerMath() {}
+}
+
diff --git a/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/IntMath.java b/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/IntMath.java
index 352351c26..31703ed8d 100644
--- a/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/IntMath.java
+++ b/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/IntMath.java
@@ -16,35 +16,42 @@
package com.google.common.math;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.math.MathPreconditions.checkNoOverflow;
import static com.google.common.math.MathPreconditions.checkNonNegative;
+import static com.google.common.math.MathPreconditions.checkPositive;
+import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
+import static java.lang.Math.abs;
+import static java.lang.Math.min;
+import static java.math.RoundingMode.HALF_EVEN;
+import static java.math.RoundingMode.HALF_UP;
-import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.VisibleForTesting;
+import java.math.RoundingMode;
+
/**
* A class for arithmetic on values of type {@code int}. Where possible, methods are defined and
* named analogously to their {@code BigInteger} counterparts.
- *
+ *
* <p>The implementations of many methods in this class are based on material from Henry S. Warren,
* Jr.'s <i>Hacker's Delight</i>, (Addison Wesley, 2002).
- *
+ *
* <p>Similar functionality for {@code long} and for {@link BigInteger} can be found in
* {@link LongMath} and {@link BigIntegerMath} respectively. For other common operations on
* {@code int} values, see {@link com.google.common.primitives.Ints}.
- *
+ *
* @author Louis Wasserman
* @since 11.0
*/
-@Beta
@GwtCompatible(emulated = true)
public final class IntMath {
// NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, ||
/**
* Returns {@code true} if {@code x} represents a power of two.
- *
+ *
* <p>This differs from {@code Integer.bitCount(x) == 1}, because
* {@code Integer.bitCount(Integer.MIN_VALUE) == 1}, but {@link Integer#MIN_VALUE} is not a power
* of two.
@@ -53,23 +60,75 @@ public final class IntMath {
return x > 0 & (x & (x - 1)) == 0;
}
+ /**
+ * Returns the base-2 logarithm of {@code x}, rounded according to the specified rounding mode.
+ *
+ * @throws IllegalArgumentException if {@code x <= 0}
+ * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
+ * is not a power of two
+ */
+ @SuppressWarnings("fallthrough")
+ // TODO(kevinb): remove after this warning is disabled globally
+ public static int log2(int x, RoundingMode mode) {
+ checkPositive("x", x);
+ switch (mode) {
+ case UNNECESSARY:
+ checkRoundingUnnecessary(isPowerOfTwo(x));
+ // fall through
+ case DOWN:
+ case FLOOR:
+ return (Integer.SIZE - 1) - Integer.numberOfLeadingZeros(x);
+
+ case UP:
+ case CEILING:
+ return Integer.SIZE - Integer.numberOfLeadingZeros(x - 1);
+
+ case HALF_DOWN:
+ case HALF_UP:
+ case HALF_EVEN:
+ // Since sqrt(2) is irrational, log2(x) - logFloor cannot be exactly 0.5
+ int leadingZeros = Integer.numberOfLeadingZeros(x);
+ int cmp = MAX_POWER_OF_SQRT2_UNSIGNED >>> leadingZeros;
+ // floor(2^(logFloor + 0.5))
+ int logFloor = (Integer.SIZE - 1) - leadingZeros;
+ return (x <= cmp) ? logFloor : logFloor + 1;
+
+ default:
+ throw new AssertionError();
+ }
+ }
+
/** The biggest half power of two that can fit in an unsigned int. */
@VisibleForTesting static final int MAX_POWER_OF_SQRT2_UNSIGNED = 0xB504F333;
-
+
private static int log10Floor(int x) {
- for (int i = 1; i < POWERS_OF_10.length; i++) {
- if (x < POWERS_OF_10[i]) {
- return i - 1;
- }
- }
- return POWERS_OF_10.length - 1;
+ /*
+ * Based on Hacker's Delight Fig. 11-5, the two-table-lookup, branch-free implementation.
+ *
+ * The key idea is that based on the number of leading zeros (equivalently, floor(log2(x))),
+ * we can narrow the possible floor(log10(x)) values to two. For example, if floor(log2(x))
+ * is 6, then 64 <= x < 128, so floor(log10(x)) is either 1 or 2.
+ */
+ int y = maxLog10ForLeadingZeros[Integer.numberOfLeadingZeros(x)];
+ // y is the higher of the two possible values of floor(log10(x))
+
+ int sgn = (x - powersOf10[y]) >>> (Integer.SIZE - 1);
+ /*
+ * sgn is the sign bit of x - 10^y; it is 1 if x < 10^y, and 0 otherwise. If x < 10^y, then we
+ * want the lower of the two possible values, or y - 1, otherwise, we want y.
+ */
+ return y - sgn;
}
- @VisibleForTesting static final int[] POWERS_OF_10 =
- {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
+ // maxLog10ForLeadingZeros[i] == floor(log10(2^(Long.SIZE - i)))
+ @VisibleForTesting static final byte[] maxLog10ForLeadingZeros = {9, 9, 9, 8, 8, 8,
+ 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0};
+
+ @VisibleForTesting static final int[] powersOf10 = {1, 10, 100, 1000, 10000,
+ 100000, 1000000, 10000000, 100000000, 1000000000};
- // HALF_POWERS_OF_10[i] = largest int less than 10^(i + 0.5)
- @VisibleForTesting static final int[] HALF_POWERS_OF_10 =
+ // halfPowersOf10[i] = largest int less than 10^(i + 0.5)
+ @VisibleForTesting static final int[] halfPowersOf10 =
{3, 31, 316, 3162, 31622, 316227, 3162277, 31622776, 316227766, Integer.MAX_VALUE};
private static int sqrtFloor(int x) {
@@ -79,17 +138,81 @@ public final class IntMath {
}
/**
+ * Returns the result of dividing {@code p} by {@code q}, rounding using the specified
+ * {@code RoundingMode}.
+ *
+ * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a}
+ * is not an integer multiple of {@code b}
+ */
+ @SuppressWarnings("fallthrough")
+ public static int divide(int p, int q, RoundingMode mode) {
+ checkNotNull(mode);
+ if (q == 0) {
+ throw new ArithmeticException("/ by zero"); // for GWT
+ }
+ int div = p / q;
+ int rem = p - q * div; // equal to p % q
+
+ if (rem == 0) {
+ return div;
+ }
+
+ /*
+ * Normal Java division rounds towards 0, consistently with RoundingMode.DOWN. We just have to
+ * deal with the cases where rounding towards 0 is wrong, which typically depends on the sign of
+ * p / q.
+ *
+ * signum is 1 if p and q are both nonnegative or both negative, and -1 otherwise.
+ */
+ int signum = 1 | ((p ^ q) >> (Integer.SIZE - 1));
+ boolean increment;
+ switch (mode) {
+ case UNNECESSARY:
+ checkRoundingUnnecessary(rem == 0);
+ // fall through
+ case DOWN:
+ increment = false;
+ break;
+ case UP:
+ increment = true;
+ break;
+ case CEILING:
+ increment = signum > 0;
+ break;
+ case FLOOR:
+ increment = signum < 0;
+ break;
+ case HALF_EVEN:
+ case HALF_DOWN:
+ case HALF_UP:
+ int absRem = abs(rem);
+ int cmpRemToHalfDivisor = absRem - (abs(q) - absRem);
+ // subtracting two nonnegative ints can't overflow
+ // cmpRemToHalfDivisor has the same sign as compare(abs(rem), abs(q) / 2).
+ if (cmpRemToHalfDivisor == 0) { // exactly on the half mark
+ increment = (mode == HALF_UP || (mode == HALF_EVEN & (div & 1) != 0));
+ } else {
+ increment = cmpRemToHalfDivisor > 0; // closer to the UP value
+ }
+ break;
+ default:
+ throw new AssertionError();
+ }
+ return increment ? div + signum : div;
+ }
+
+ /**
* Returns {@code x mod m}. This differs from {@code x % m} in that it always returns a
* non-negative result.
- *
+ *
* <p>For example:<pre> {@code
- *
+ *
* mod(7, 4) == 3
* mod(-7, 4) == 1
* mod(-1, 4) == 3
* mod(-8, 4) == 0
* mod(8, 4) == 0}</pre>
- *
+ *
* @throws ArithmeticException if {@code m <= 0}
*/
public static int mod(int x, int m) {
@@ -103,7 +226,7 @@ public final class IntMath {
/**
* Returns the greatest common divisor of {@code a, b}. Returns {@code 0} if
* {@code a == 0 && b == 0}.
- *
+ *
* @throws IllegalArgumentException if {@code a < 0} or {@code b < 0}
*/
public static int gcd(int a, int b) {
@@ -114,18 +237,46 @@ public final class IntMath {
*/
checkNonNegative("a", a);
checkNonNegative("b", b);
- // The simple Euclidean algorithm is the fastest for ints, and is easily the most readable.
- while (b != 0) {
- int t = b;
- b = a % b;
- a = t;
+ if (a == 0) {
+ // 0 % b == 0, so b divides a, but the converse doesn't hold.
+ // BigInteger.gcd is consistent with this decision.
+ return b;
+ } else if (b == 0) {
+ return a; // similar logic
+ }
+ /*
+ * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm.
+ * This is >40% faster than the Euclidean algorithm in benchmarks.
+ */
+ int aTwos = Integer.numberOfTrailingZeros(a);
+ a >>= aTwos; // divide out all 2s
+ int bTwos = Integer.numberOfTrailingZeros(b);
+ b >>= bTwos; // divide out all 2s
+ while (a != b) { // both a, b are odd
+ // The key to the binary GCD algorithm is as follows:
+ // Both a and b are odd. Assume a > b; then gcd(a - b, b) = gcd(a, b).
+ // But in gcd(a - b, b), a - b is even and b is odd, so we can divide out powers of two.
+
+ // We bend over backwards to avoid branching, adapting a technique from
+ // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
+
+ int delta = a - b; // can't overflow, since a and b are nonnegative
+
+ int minDeltaOrZero = delta & (delta >> (Integer.SIZE - 1));
+ // equivalent to Math.min(delta, 0)
+
+ a = delta - minDeltaOrZero - minDeltaOrZero; // sets a to Math.abs(a - b)
+ // a is now nonnegative and even
+
+ b += minDeltaOrZero; // sets b to min(old a, b)
+ a >>= Integer.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b
}
- return a;
+ return a << min(aTwos, bTwos);
}
/**
* Returns the sum of {@code a} and {@code b}, provided it does not overflow.
- *
+ *
* @throws ArithmeticException if {@code a + b} overflows in signed {@code int} arithmetic
*/
public static int checkedAdd(int a, int b) {
@@ -136,7 +287,7 @@ public final class IntMath {
/**
* Returns the difference of {@code a} and {@code b}, provided it does not overflow.
- *
+ *
* @throws ArithmeticException if {@code a - b} overflows in signed {@code int} arithmetic
*/
public static int checkedSubtract(int a, int b) {
@@ -147,7 +298,7 @@ public final class IntMath {
/**
* Returns the product of {@code a} and {@code b}, provided it does not overflow.
- *
+ *
* @throws ArithmeticException if {@code a * b} overflows in signed {@code int} arithmetic
*/
public static int checkedMultiply(int a, int b) {
@@ -156,9 +307,67 @@ public final class IntMath {
return (int) result;
}
+ /**
+ * Returns the {@code b} to the {@code k}th power, provided it does not overflow.
+ *
+ * <p>{@link #pow} may be faster, but does not check for overflow.
+ *
+ * @throws ArithmeticException if {@code b} to the {@code k}th power overflows in signed
+ * {@code int} arithmetic
+ */
+ public static int checkedPow(int b, int k) {
+ checkNonNegative("exponent", k);
+ switch (b) {
+ case 0:
+ return (k == 0) ? 1 : 0;
+ case 1:
+ return 1;
+ case (-1):
+ return ((k & 1) == 0) ? 1 : -1;
+ case 2:
+ checkNoOverflow(k < Integer.SIZE - 1);
+ return 1 << k;
+ case (-2):
+ checkNoOverflow(k < Integer.SIZE);
+ return ((k & 1) == 0) ? 1 << k : -1 << k;
+ default:
+ // continue below to handle the general case
+ }
+ int accum = 1;
+ while (true) {
+ switch (k) {
+ case 0:
+ return accum;
+ case 1:
+ return checkedMultiply(accum, b);
+ default:
+ if ((k & 1) != 0) {
+ accum = checkedMultiply(accum, b);
+ }
+ k >>= 1;
+ if (k > 0) {
+ checkNoOverflow(-FLOOR_SQRT_MAX_INT <= b & b <= FLOOR_SQRT_MAX_INT);
+ b *= b;
+ }
+ }
+ }
+ }
+
@VisibleForTesting static final int FLOOR_SQRT_MAX_INT = 46340;
-
- static final int[] FACTORIALS = {
+
+ /**
+ * Returns {@code n!}, that is, the product of the first {@code n} positive
+ * integers, {@code 1} if {@code n == 0}, or {@link Integer#MAX_VALUE} if the
+ * result does not fit in a {@code int}.
+ *
+ * @throws IllegalArgumentException if {@code n < 0}
+ */
+ public static int factorial(int n) {
+ checkNonNegative("n", n);
+ return (n < factorials.length) ? factorials[n] : Integer.MAX_VALUE;
+ }
+
+ private static final int[] factorials = {
1,
1,
1 * 2,
@@ -173,8 +382,8 @@ public final class IntMath {
1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11,
1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12};
- // binomial(BIGGEST_BINOMIALS[k], k) fits in an int, but not binomial(BIGGEST_BINOMIALS[k]+1,k).
- @VisibleForTesting static int[] BIGGEST_BINOMIALS = {
+ // binomial(biggestBinomials[k], k) fits in an int, but not binomial(biggestBinomials[k]+1,k).
+ @VisibleForTesting static int[] biggestBinomials = {
Integer.MAX_VALUE,
Integer.MAX_VALUE,
65536,
@@ -193,7 +402,20 @@ public final class IntMath {
34,
33
};
-
+
+ /**
+ * Returns the arithmetic mean of {@code x} and {@code y}, rounded towards
+ * negative infinity. This method is overflow resilient.
+ *
+ * @since 14.0
+ */
+ public static int mean(int x, int y) {
+ // Efficient method for computing the arithmetic mean.
+ // The alternative (x + y) / 2 fails for large values.
+ // The alternative (x + y) >>> 1 fails for negative values.
+ return (x & y) + ((x ^ y) >> 1);
+ }
+
private IntMath() {}
}
diff --git a/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/LongMath.java b/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/LongMath.java
new file mode 100644
index 000000000..2f153603d
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/LongMath.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.math;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.math.MathPreconditions.checkNonNegative;
+import static com.google.common.math.MathPreconditions.checkPositive;
+import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
+import static java.lang.Math.min;
+import static java.math.RoundingMode.HALF_EVEN;
+import static java.math.RoundingMode.HALF_UP;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.annotations.VisibleForTesting;
+
+import java.math.RoundingMode;
+
+/**
+ * A class for arithmetic on values of type {@code long}. Where possible, methods are defined and
+ * named analogously to their {@code BigInteger} counterparts.
+ *
+ * <p>The implementations of many methods in this class are based on material from Henry S. Warren,
+ * Jr.'s <i>Hacker's Delight</i>, (Addison Wesley, 2002).
+ *
+ * <p>Similar functionality for {@code int} and for {@link BigInteger} can be found in
+ * {@link IntMath} and {@link BigIntegerMath} respectively. For other common operations on
+ * {@code long} values, see {@link com.google.common.primitives.Longs}.
+ *
+ * @author Louis Wasserman
+ * @since 11.0
+ */
+@GwtCompatible(emulated = true)
+public final class LongMath {
+ // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, ||
+
+ /**
+ * Returns {@code true} if {@code x} represents a power of two.
+ *
+ * <p>This differs from {@code Long.bitCount(x) == 1}, because
+ * {@code Long.bitCount(Long.MIN_VALUE) == 1}, but {@link Long#MIN_VALUE} is not a power of two.
+ */
+ public static boolean isPowerOfTwo(long x) {
+ return x > 0 & (x & (x - 1)) == 0;
+ }
+
+ /**
+ * Returns the base-2 logarithm of {@code x}, rounded according to the specified rounding mode.
+ *
+ * @throws IllegalArgumentException if {@code x <= 0}
+ * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
+ * is not a power of two
+ */
+ @SuppressWarnings("fallthrough")
+ // TODO(kevinb): remove after this warning is disabled globally
+ public static int log2(long x, RoundingMode mode) {
+ checkPositive("x", x);
+ switch (mode) {
+ case UNNECESSARY:
+ checkRoundingUnnecessary(isPowerOfTwo(x));
+ // fall through
+ case DOWN:
+ case FLOOR:
+ return (Long.SIZE - 1) - Long.numberOfLeadingZeros(x);
+
+ case UP:
+ case CEILING:
+ return Long.SIZE - Long.numberOfLeadingZeros(x - 1);
+
+ case HALF_DOWN:
+ case HALF_UP:
+ case HALF_EVEN:
+ // Since sqrt(2) is irrational, log2(x) - logFloor cannot be exactly 0.5
+ int leadingZeros = Long.numberOfLeadingZeros(x);
+ long cmp = MAX_POWER_OF_SQRT2_UNSIGNED >>> leadingZeros;
+ // floor(2^(logFloor + 0.5))
+ int logFloor = (Long.SIZE - 1) - leadingZeros;
+ return (x <= cmp) ? logFloor : logFloor + 1;
+
+ default:
+ throw new AssertionError("impossible");
+ }
+ }
+
+ /** The biggest half power of two that fits into an unsigned long */
+ @VisibleForTesting static final long MAX_POWER_OF_SQRT2_UNSIGNED = 0xB504F333F9DE6484L;
+
+ // maxLog10ForLeadingZeros[i] == floor(log10(2^(Long.SIZE - i)))
+ @VisibleForTesting static final byte[] maxLog10ForLeadingZeros = {
+ 19, 18, 18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12,
+ 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4,
+ 3, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0 };
+
+ // halfPowersOf10[i] = largest long less than 10^(i + 0.5)
+
+ /**
+ * Returns the greatest common divisor of {@code a, b}. Returns {@code 0} if
+ * {@code a == 0 && b == 0}.
+ *
+ * @throws IllegalArgumentException if {@code a < 0} or {@code b < 0}
+ */
+ public static long gcd(long a, long b) {
+ /*
+ * The reason we require both arguments to be >= 0 is because otherwise, what do you return on
+ * gcd(0, Long.MIN_VALUE)? BigInteger.gcd would return positive 2^63, but positive 2^63 isn't
+ * an int.
+ */
+ checkNonNegative("a", a);
+ checkNonNegative("b", b);
+ if (a == 0) {
+ // 0 % b == 0, so b divides a, but the converse doesn't hold.
+ // BigInteger.gcd is consistent with this decision.
+ return b;
+ } else if (b == 0) {
+ return a; // similar logic
+ }
+ /*
+ * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm.
+ * This is >60% faster than the Euclidean algorithm in benchmarks.
+ */
+ int aTwos = Long.numberOfTrailingZeros(a);
+ a >>= aTwos; // divide out all 2s
+ int bTwos = Long.numberOfTrailingZeros(b);
+ b >>= bTwos; // divide out all 2s
+ while (a != b) { // both a, b are odd
+ // The key to the binary GCD algorithm is as follows:
+ // Both a and b are odd. Assume a > b; then gcd(a - b, b) = gcd(a, b).
+ // But in gcd(a - b, b), a - b is even and b is odd, so we can divide out powers of two.
+
+ // We bend over backwards to avoid branching, adapting a technique from
+ // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
+
+ long delta = a - b; // can't overflow, since a and b are nonnegative
+
+ long minDeltaOrZero = delta & (delta >> (Long.SIZE - 1));
+ // equivalent to Math.min(delta, 0)
+
+ a = delta - minDeltaOrZero - minDeltaOrZero; // sets a to Math.abs(a - b)
+ // a is now nonnegative and even
+
+ b += minDeltaOrZero; // sets b to min(old a, b)
+ a >>= Long.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b
+ }
+ return a << min(aTwos, bTwos);
+ }
+
+ static final long[] factorials = {
+ 1L,
+ 1L,
+ 1L * 2,
+ 1L * 2 * 3,
+ 1L * 2 * 3 * 4,
+ 1L * 2 * 3 * 4 * 5,
+ 1L * 2 * 3 * 4 * 5 * 6,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19,
+ 1L * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 * 17 * 18 * 19 * 20
+ };
+
+ /**
+ * Returns {@code n} choose {@code k}, also known as the binomial coefficient of {@code n} and
+ * {@code k}, or {@link Long#MAX_VALUE} if the result does not fit in a {@code long}.
+ *
+ * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0}, or {@code k > n}
+ */
+ public static long binomial(int n, int k) {
+ checkNonNegative("n", n);
+ checkNonNegative("k", k);
+ checkArgument(k <= n, "k (%s) > n (%s)", k, n);
+ if (k > (n >> 1)) {
+ k = n - k;
+ }
+ switch (k) {
+ case 0:
+ return 1;
+ case 1:
+ return n;
+ default:
+ if (n < factorials.length) {
+ return factorials[n] / (factorials[k] * factorials[n - k]);
+ } else if (k >= biggestBinomials.length || n > biggestBinomials[k]) {
+ return Long.MAX_VALUE;
+ } else if (k < biggestSimpleBinomials.length && n <= biggestSimpleBinomials[k]) {
+ // guaranteed not to overflow
+ long result = n--;
+ for (int i = 2; i <= k; n--, i++) {
+ result *= n;
+ result /= i;
+ }
+ return result;
+ } else {
+ int nBits = LongMath.log2(n, RoundingMode.CEILING);
+
+ long result = 1;
+ long numerator = n--;
+ long denominator = 1;
+
+ int numeratorBits = nBits;
+ // This is an upper bound on log2(numerator, ceiling).
+
+ /*
+ * We want to do this in long math for speed, but want to avoid overflow. We adapt the
+ * technique previously used by BigIntegerMath: maintain separate numerator and
+ * denominator accumulators, multiplying the fraction into result when near overflow.
+ */
+ for (int i = 2; i <= k; i++, n--) {
+ if (numeratorBits + nBits < Long.SIZE - 1) {
+ // It's definitely safe to multiply into numerator and denominator.
+ numerator *= n;
+ denominator *= i;
+ numeratorBits += nBits;
+ } else {
+ // It might not be safe to multiply into numerator and denominator,
+ // so multiply (numerator / denominator) into result.
+ result = multiplyFraction(result, numerator, denominator);
+ numerator = n;
+ denominator = i;
+ numeratorBits = nBits;
+ }
+ }
+ return multiplyFraction(result, numerator, denominator);
+ }
+ }
+ }
+
+ /**
+ * Returns (x * numerator / denominator), which is assumed to come out to an integral value.
+ */
+ static long multiplyFraction(long x, long numerator, long denominator) {
+ if (x == 1) {
+ return numerator / denominator;
+ }
+ long commonDivisor = gcd(x, denominator);
+ x /= commonDivisor;
+ denominator /= commonDivisor;
+ // We know gcd(x, denominator) = 1, and x * numerator / denominator is exact,
+ // so denominator must be a divisor of numerator.
+ return x * (numerator / denominator);
+ }
+
+ /*
+ * binomial(biggestBinomials[k], k) fits in a long, but not
+ * binomial(biggestBinomials[k] + 1, k).
+ */
+ static final int[] biggestBinomials =
+ {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 3810779, 121977, 16175, 4337, 1733,
+ 887, 534, 361, 265, 206, 169, 143, 125, 111, 101, 94, 88, 83, 79, 76, 74, 72, 70, 69, 68,
+ 67, 67, 66, 66, 66, 66};
+
+ /*
+ * binomial(biggestSimpleBinomials[k], k) doesn't need to use the slower GCD-based impl,
+ * but binomial(biggestSimpleBinomials[k] + 1, k) does.
+ */
+ @VisibleForTesting static final int[] biggestSimpleBinomials =
+ {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 2642246, 86251, 11724, 3218, 1313,
+ 684, 419, 287, 214, 169, 139, 119, 105, 95, 87, 81, 76, 73, 70, 68, 66, 64, 63, 62, 62,
+ 61, 61, 61};
+ // These values were generated by using checkedMultiply to see when the simple multiply/divide
+ // algorithm would lead to an overflow.
+
+ /**
+ * Returns the arithmetic mean of {@code x} and {@code y}, rounded toward
+ * negative infinity. This method is resilient to overflow.
+ *
+ * @since 14.0
+ */
+ public static long mean(long x, long y) {
+ // Efficient method for computing the arithmetic mean.
+ // The alternative (x + y) / 2 fails for large values.
+ // The alternative (x + y) >>> 1 fails for negative values.
+ return (x & y) + ((x ^ y) >> 1);
+ }
+
+ private LongMath() {}
+}
+
diff --git a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Chars.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Chars.java
index 5f7886518..62c716a02 100644
--- a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Chars.java
+++ b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Chars.java
@@ -38,6 +38,10 @@ import java.util.RandomAccess;
* <p>All the operations in this class treat {@code char} values strictly
* numerically; they are neither Unicode-aware nor locale-dependent.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained">
+ * primitive utilities</a>.
+ *
* @author Kevin Bourrillion
* @since 1.0
*/
@@ -468,7 +472,8 @@ public final class Chars {
@Override public Character set(int index, Character element) {
checkElementIndex(index, size());
char oldValue = array[start + index];
- array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize)
+ // checkNotNull for GWT (do not optimize)
+ array[start + index] = checkNotNull(element);
return oldValue;
}
@@ -519,7 +524,7 @@ public final class Chars {
}
char[] toCharArray() {
- // Arrays.copyOfRange() requires Java 6
+ // Arrays.copyOfRange() is not available under GWT
int size = size();
char[] result = new char[size];
System.arraycopy(array, start, result, 0, size);
diff --git a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Longs.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Doubles.java
index 000e05585..3634fe708 100644
--- a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Longs.java
+++ b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Doubles.java
@@ -20,6 +20,8 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes;
+import static java.lang.Double.NEGATIVE_INFINITY;
+import static java.lang.Double.POSITIVE_INFINITY;
import com.google.common.annotations.GwtCompatible;
@@ -32,69 +34,80 @@ import java.util.List;
import java.util.RandomAccess;
/**
- * Static utility methods pertaining to {@code long} primitives, that are not
- * already found in either {@link Long} or {@link Arrays}.
+ * Static utility methods pertaining to {@code double} primitives, that are not
+ * already found in either {@link Double} or {@link Arrays}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained">
+ * primitive utilities</a>.
*
* @author Kevin Bourrillion
* @since 1.0
*/
@GwtCompatible(emulated = true)
-public final class Longs {
- private Longs() {}
+public final class Doubles {
+ private Doubles() {}
/**
- * The number of bytes required to represent a primitive {@code long}
+ * The number of bytes required to represent a primitive {@code double}
* value.
- */
- public static final int BYTES = Long.SIZE / Byte.SIZE;
-
- /**
- * The largest power of two that can be represented as a {@code long}.
*
* @since 10.0
*/
- public static final long MAX_POWER_OF_TWO = 1L << (Long.SIZE - 2);
+ public static final int BYTES = Double.SIZE / Byte.SIZE;
/**
* Returns a hash code for {@code value}; equal to the result of invoking
- * {@code ((Long) value).hashCode()}.
- *
- * <p>This method always return the value specified by {@link
- * Long#hashCode()} in java, which might be different from
- * {@code ((Long) value).hashCode()} in GWT because {@link Long#hashCode()}
- * in GWT does not obey the JRE contract.
+ * {@code ((Double) value).hashCode()}.
*
- * @param value a primitive {@code long} value
+ * @param value a primitive {@code double} value
* @return a hash code for the value
*/
- public static int hashCode(long value) {
- return (int) (value ^ (value >>> 32));
+ public static int hashCode(double value) {
+ return ((Double) value).hashCode();
+ // TODO(kevinb): do it this way when we can (GWT problem):
+ // long bits = Double.doubleToLongBits(value);
+ // return (int)(bits ^ (bits >>> 32));
}
/**
- * Compares the two specified {@code long} values. The sign of the value
- * returned is the same as that of {@code ((Long) a).compareTo(b)}.
+ * Compares the two specified {@code double} values. The sign of the value
+ * returned is the same as that of <code>((Double) a).{@linkplain
+ * Double#compareTo compareTo}(b)</code>. As with that method, {@code NaN} is
+ * treated as greater than all other values, and {@code 0.0 > -0.0}.
*
- * @param a the first {@code long} to compare
- * @param b the second {@code long} to compare
+ * @param a the first {@code double} to compare
+ * @param b the second {@code double} to compare
* @return a negative value if {@code a} is less than {@code b}; a positive
* value if {@code a} is greater than {@code b}; or zero if they are equal
*/
- public static int compare(long a, long b) {
- return (a < b) ? -1 : ((a > b) ? 1 : 0);
+ public static int compare(double a, double b) {
+ return Double.compare(a, b);
+ }
+
+ /**
+ * Returns {@code true} if {@code value} represents a real number. This is
+ * equivalent to, but not necessarily implemented as,
+ * {@code !(Double.isInfinite(value) || Double.isNaN(value))}.
+ *
+ * @since 10.0
+ */
+ public static boolean isFinite(double value) {
+ return NEGATIVE_INFINITY < value & value < POSITIVE_INFINITY;
}
/**
* Returns {@code true} if {@code target} is present as an element anywhere in
- * {@code array}.
+ * {@code array}. Note that this always returns {@code false} when {@code
+ * target} is {@code NaN}.
*
- * @param array an array of {@code long} values, possibly empty
- * @param target a primitive {@code long} value
+ * @param array an array of {@code double} values, possibly empty
+ * @param target a primitive {@code double} value
* @return {@code true} if {@code array[i] == target} for some value of {@code
* i}
*/
- public static boolean contains(long[] array, long target) {
- for (long value : array) {
+ public static boolean contains(double[] array, double target) {
+ for (double value : array) {
if (value == target) {
return true;
}
@@ -104,20 +117,21 @@ public final class Longs {
/**
* Returns the index of the first appearance of the value {@code target} in
- * {@code array}.
+ * {@code array}. Note that this always returns {@code -1} when {@code target}
+ * is {@code NaN}.
*
- * @param array an array of {@code long} values, possibly empty
- * @param target a primitive {@code long} value
+ * @param array an array of {@code double} values, possibly empty
+ * @param target a primitive {@code double} value
* @return the least index {@code i} for which {@code array[i] == target}, or
* {@code -1} if no such index exists.
*/
- public static int indexOf(long[] array, long target) {
+ public static int indexOf(double[] array, double target) {
return indexOf(array, target, 0, array.length);
}
// TODO(kevinb): consider making this public
private static int indexOf(
- long[] array, long target, int start, int end) {
+ double[] array, double target, int start, int end) {
for (int i = start; i < end; i++) {
if (array[i] == target) {
return i;
@@ -134,10 +148,13 @@ public final class Longs {
* java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly
* the same elements as {@code target}.
*
+ * <p>Note that this always returns {@code -1} when {@code target} contains
+ * {@code NaN}.
+ *
* @param array the array to search for the sequence {@code target}
* @param target the array to search for as a sub-sequence of {@code array}
*/
- public static int indexOf(long[] array, long[] target) {
+ public static int indexOf(double[] array, double[] target) {
checkNotNull(array, "array");
checkNotNull(target, "target");
if (target.length == 0) {
@@ -158,20 +175,21 @@ public final class Longs {
/**
* Returns the index of the last appearance of the value {@code target} in
- * {@code array}.
+ * {@code array}. Note that this always returns {@code -1} when {@code target}
+ * is {@code NaN}.
*
- * @param array an array of {@code long} values, possibly empty
- * @param target a primitive {@code long} value
+ * @param array an array of {@code double} values, possibly empty
+ * @param target a primitive {@code double} value
* @return the greatest index {@code i} for which {@code array[i] == target},
* or {@code -1} if no such index exists.
*/
- public static int lastIndexOf(long[] array, long target) {
+ public static int lastIndexOf(double[] array, double target) {
return lastIndexOf(array, target, 0, array.length);
}
// TODO(kevinb): consider making this public
private static int lastIndexOf(
- long[] array, long target, int start, int end) {
+ double[] array, double target, int start, int end) {
for (int i = end - 1; i >= start; i--) {
if (array[i] == target) {
return i;
@@ -181,60 +199,58 @@ public final class Longs {
}
/**
- * Returns the least value present in {@code array}.
+ * Returns the least value present in {@code array}, using the same rules of
+ * comparison as {@link Math#min(double, double)}.
*
- * @param array a <i>nonempty</i> array of {@code long} values
+ * @param array a <i>nonempty</i> array of {@code double} values
* @return the value present in {@code array} that is less than or equal to
* every other value in the array
* @throws IllegalArgumentException if {@code array} is empty
*/
- public static long min(long... array) {
+ public static double min(double... array) {
checkArgument(array.length > 0);
- long min = array[0];
+ double min = array[0];
for (int i = 1; i < array.length; i++) {
- if (array[i] < min) {
- min = array[i];
- }
+ min = Math.min(min, array[i]);
}
return min;
}
/**
- * Returns the greatest value present in {@code array}.
+ * Returns the greatest value present in {@code array}, using the same rules
+ * of comparison as {@link Math#max(double, double)}.
*
- * @param array a <i>nonempty</i> array of {@code long} values
+ * @param array a <i>nonempty</i> array of {@code double} values
* @return the value present in {@code array} that is greater than or equal to
* every other value in the array
* @throws IllegalArgumentException if {@code array} is empty
*/
- public static long max(long... array) {
+ public static double max(double... array) {
checkArgument(array.length > 0);
- long max = array[0];
+ double max = array[0];
for (int i = 1; i < array.length; i++) {
- if (array[i] > max) {
- max = array[i];
- }
+ max = Math.max(max, array[i]);
}
return max;
}
/**
* Returns the values from each provided array combined into a single array.
- * For example, {@code concat(new long[] {a, b}, new long[] {}, new
- * long[] {c}} returns the array {@code {a, b, c}}.
+ * For example, {@code concat(new double[] {a, b}, new double[] {}, new
+ * double[] {c}} returns the array {@code {a, b, c}}.
*
- * @param arrays zero or more {@code long} arrays
+ * @param arrays zero or more {@code double} arrays
* @return a single array containing all the values from the source arrays, in
* order
*/
- public static long[] concat(long[]... arrays) {
+ public static double[] concat(double[]... arrays) {
int length = 0;
- for (long[] array : arrays) {
+ for (double[] array : arrays) {
length += array.length;
}
- long[] result = new long[length];
+ double[] result = new double[length];
int pos = 0;
- for (long[] array : arrays) {
+ for (double[] array : arrays) {
System.arraycopy(array, 0, result, pos, array.length);
pos += array.length;
}
@@ -257,8 +273,8 @@ public final class Longs {
* @return an array containing the values of {@code array}, with guaranteed
* minimum length {@code minLength}
*/
- public static long[] ensureCapacity(
- long[] array, int minLength, int padding) {
+ public static double[] ensureCapacity(
+ double[] array, int minLength, int padding) {
checkArgument(minLength >= 0, "Invalid minLength: %s", minLength);
checkArgument(padding >= 0, "Invalid padding: %s", padding);
return (array.length < minLength)
@@ -267,29 +283,34 @@ public final class Longs {
}
// Arrays.copyOf() requires Java 6
- private static long[] copyOf(long[] original, int length) {
- long[] copy = new long[length];
+ private static double[] copyOf(double[] original, int length) {
+ double[] copy = new double[length];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, length));
return copy;
}
/**
- * Returns a string containing the supplied {@code long} values separated
- * by {@code separator}. For example, {@code join("-", 1L, 2L, 3L)} returns
- * the string {@code "1-2-3"}.
+ * Returns a string containing the supplied {@code double} values, converted
+ * to strings as specified by {@link Double#toString(double)}, and separated
+ * by {@code separator}. For example, {@code join("-", 1.0, 2.0, 3.0)} returns
+ * the string {@code "1.0-2.0-3.0"}.
+ *
+ * <p>Note that {@link Double#toString(double)} formats {@code double}
+ * differently in GWT sometimes. In the previous example, it returns the
+ * string {@code "1-2-3"}.
*
* @param separator the text that should appear between consecutive values in
* the resulting string (but not at the start or end)
- * @param array an array of {@code long} values, possibly empty
+ * @param array an array of {@code double} values, possibly empty
*/
- public static String join(String separator, long... array) {
+ public static String join(String separator, double... array) {
checkNotNull(separator);
if (array.length == 0) {
return "";
}
// For pre-sizing a builder, just get the right order of magnitude
- StringBuilder builder = new StringBuilder(array.length * 10);
+ StringBuilder builder = new StringBuilder(array.length * 12);
builder.append(array[0]);
for (int i = 1; i < array.length; i++) {
builder.append(separator).append(array[i]);
@@ -298,33 +319,33 @@ public final class Longs {
}
/**
- * Returns a comparator that compares two {@code long} arrays
+ * Returns a comparator that compares two {@code double} arrays
* lexicographically. That is, it compares, using {@link
- * #compare(long, long)}), the first pair of values that follow any
+ * #compare(double, double)}), the first pair of values that follow any
* common prefix, or when one array is a prefix of the other, treats the
* shorter array as the lesser. For example,
- * {@code [] < [1L] < [1L, 2L] < [2L]}.
+ * {@code [] < [1.0] < [1.0, 2.0] < [2.0]}.
*
* <p>The returned comparator is inconsistent with {@link
* Object#equals(Object)} (since arrays support only identity equality), but
- * it is consistent with {@link Arrays#equals(long[], long[])}.
+ * it is consistent with {@link Arrays#equals(double[], double[])}.
*
* @see <a href="http://en.wikipedia.org/wiki/Lexicographical_order">
* Lexicographical order article at Wikipedia</a>
* @since 2.0
*/
- public static Comparator<long[]> lexicographicalComparator() {
+ public static Comparator<double[]> lexicographicalComparator() {
return LexicographicalComparator.INSTANCE;
}
- private enum LexicographicalComparator implements Comparator<long[]> {
+ private enum LexicographicalComparator implements Comparator<double[]> {
INSTANCE;
@Override
- public int compare(long[] left, long[] right) {
+ public int compare(double[] left, double[] right) {
int minLength = Math.min(left.length, right.length);
for (int i = 0; i < minLength; i++) {
- int result = Longs.compare(left[i], right[i]);
+ int result = Doubles.compare(left[i], right[i]);
if (result != 0) {
return result;
}
@@ -334,30 +355,31 @@ public final class Longs {
}
/**
- * Copies a collection of {@code Long} instances into a new array of
- * primitive {@code long} values.
+ * Returns an array containing each value of {@code collection}, converted to
+ * a {@code double} value in the manner of {@link Number#doubleValue}.
*
* <p>Elements are copied from the argument collection as if by {@code
* collection.toArray()}. Calling this method is as thread-safe as calling
* that method.
*
- * @param collection a collection of {@code Long} objects
+ * @param collection a collection of {@code Number} instances
* @return an array containing the same values as {@code collection}, in the
* same order, converted to primitives
* @throws NullPointerException if {@code collection} or any of its elements
* is null
+ * @since 1.0 (parameter was {@code Collection<Double>} before 12.0)
*/
- public static long[] toArray(Collection<Long> collection) {
- if (collection instanceof LongArrayAsList) {
- return ((LongArrayAsList) collection).toLongArray();
+ public static double[] toArray(Collection<? extends Number> collection) {
+ if (collection instanceof DoubleArrayAsList) {
+ return ((DoubleArrayAsList) collection).toDoubleArray();
}
Object[] boxedArray = collection.toArray();
int len = boxedArray.length;
- long[] array = new long[len];
+ double[] array = new double[len];
for (int i = 0; i < len; i++) {
// checkNotNull for GWT (do not optimize)
- array[i] = (Long) checkNotNull(boxedArray[i]);
+ array[i] = ((Number) checkNotNull(boxedArray[i])).doubleValue();
}
return array;
}
@@ -369,32 +391,35 @@ public final class Longs {
* NullPointerException}.
*
* <p>The returned list maintains the values, but not the identities, of
- * {@code Long} objects written to or read from it. For example, whether
+ * {@code Double} objects written to or read from it. For example, whether
* {@code list.get(0) == list.get(0)} is true for the returned list is
* unspecified.
*
+ * <p>The returned list may have unexpected behavior if it contains {@code
+ * NaN}, or if {@code NaN} is used as a parameter to any of its methods.
+ *
* @param backingArray the array to back the list
* @return a list view of the array
*/
- public static List<Long> asList(long... backingArray) {
+ public static List<Double> asList(double... backingArray) {
if (backingArray.length == 0) {
return Collections.emptyList();
}
- return new LongArrayAsList(backingArray);
+ return new DoubleArrayAsList(backingArray);
}
@GwtCompatible
- private static class LongArrayAsList extends AbstractList<Long>
+ private static class DoubleArrayAsList extends AbstractList<Double>
implements RandomAccess, Serializable {
- final long[] array;
+ final double[] array;
final int start;
final int end;
- LongArrayAsList(long[] array) {
+ DoubleArrayAsList(double[] array) {
this(array, 0, array.length);
}
- LongArrayAsList(long[] array, int start, int end) {
+ DoubleArrayAsList(double[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
@@ -408,21 +433,21 @@ public final class Longs {
return false;
}
- @Override public Long get(int index) {
+ @Override public Double get(int index) {
checkElementIndex(index, size());
return array[start + index];
}
@Override public boolean contains(Object target) {
// Overridden to prevent a ton of boxing
- return (target instanceof Long)
- && Longs.indexOf(array, (Long) target, start, end) != -1;
+ return (target instanceof Double)
+ && Doubles.indexOf(array, (Double) target, start, end) != -1;
}
@Override public int indexOf(Object target) {
// Overridden to prevent a ton of boxing
- if (target instanceof Long) {
- int i = Longs.indexOf(array, (Long) target, start, end);
+ if (target instanceof Double) {
+ int i = Doubles.indexOf(array, (Double) target, start, end);
if (i >= 0) {
return i - start;
}
@@ -432,8 +457,8 @@ public final class Longs {
@Override public int lastIndexOf(Object target) {
// Overridden to prevent a ton of boxing
- if (target instanceof Long) {
- int i = Longs.lastIndexOf(array, (Long) target, start, end);
+ if (target instanceof Double) {
+ int i = Doubles.lastIndexOf(array, (Double) target, start, end);
if (i >= 0) {
return i - start;
}
@@ -441,28 +466,29 @@ public final class Longs {
return -1;
}
- @Override public Long set(int index, Long element) {
+ @Override public Double set(int index, Double element) {
checkElementIndex(index, size());
- long oldValue = array[start + index];
- array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize)
+ double oldValue = array[start + index];
+ // checkNotNull for GWT (do not optimize)
+ array[start + index] = checkNotNull(element);
return oldValue;
}
- @Override public List<Long> subList(int fromIndex, int toIndex) {
+ @Override public List<Double> subList(int fromIndex, int toIndex) {
int size = size();
checkPositionIndexes(fromIndex, toIndex, size);
if (fromIndex == toIndex) {
return Collections.emptyList();
}
- return new LongArrayAsList(array, start + fromIndex, start + toIndex);
+ return new DoubleArrayAsList(array, start + fromIndex, start + toIndex);
}
@Override public boolean equals(Object object) {
if (object == this) {
return true;
}
- if (object instanceof LongArrayAsList) {
- LongArrayAsList that = (LongArrayAsList) object;
+ if (object instanceof DoubleArrayAsList) {
+ DoubleArrayAsList that = (DoubleArrayAsList) object;
int size = size();
if (that.size() != size) {
return false;
@@ -480,13 +506,13 @@ public final class Longs {
@Override public int hashCode() {
int result = 1;
for (int i = start; i < end; i++) {
- result = 31 * result + Longs.hashCode(array[i]);
+ result = 31 * result + Doubles.hashCode(array[i]);
}
return result;
}
@Override public String toString() {
- StringBuilder builder = new StringBuilder(size() * 10);
+ StringBuilder builder = new StringBuilder(size() * 12);
builder.append('[').append(array[start]);
for (int i = start + 1; i < end; i++) {
builder.append(", ").append(array[i]);
@@ -494,10 +520,10 @@ public final class Longs {
return builder.append(']').toString();
}
- long[] toLongArray() {
- // Arrays.copyOfRange() requires Java 6
+ double[] toDoubleArray() {
+ // Arrays.copyOfRange() is not available under GWT
int size = size();
- long[] result = new long[size];
+ double[] result = new double[size];
System.arraycopy(array, start, result, 0, size);
return result;
}
diff --git a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Floats.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Floats.java
new file mode 100644
index 000000000..8e4eaf7a9
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Floats.java
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2008 The Guava Authors
+ *
+ * Licensed 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 com.google.common.primitives;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkElementIndex;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkPositionIndexes;
+import static java.lang.Float.NEGATIVE_INFINITY;
+import static java.lang.Float.POSITIVE_INFINITY;
+
+import com.google.common.annotations.GwtCompatible;
+
+import java.io.Serializable;
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * Static utility methods pertaining to {@code float} primitives, that are not
+ * already found in either {@link Float} or {@link Arrays}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained">
+ * primitive utilities</a>.
+ *
+ * @author Kevin Bourrillion
+ * @since 1.0
+ */
+@GwtCompatible(emulated = true)
+public final class Floats {
+ private Floats() {}
+
+ /**
+ * The number of bytes required to represent a primitive {@code float}
+ * value.
+ *
+ * @since 10.0
+ */
+ public static final int BYTES = Float.SIZE / Byte.SIZE;
+
+ /**
+ * Returns a hash code for {@code value}; equal to the result of invoking
+ * {@code ((Float) value).hashCode()}.
+ *
+ * @param value a primitive {@code float} value
+ * @return a hash code for the value
+ */
+ public static int hashCode(float value) {
+ // TODO(kevinb): is there a better way, that's still gwt-safe?
+ return ((Float) value).hashCode();
+ }
+
+ /**
+ * Compares the two specified {@code float} values using {@link
+ * Float#compare(float, float)}. You may prefer to invoke that method
+ * directly; this method exists only for consistency with the other utilities
+ * in this package.
+ *
+ * @param a the first {@code float} to compare
+ * @param b the second {@code float} to compare
+ * @return the result of invoking {@link Float#compare(float, float)}
+ */
+ public static int compare(float a, float b) {
+ return Float.compare(a, b);
+ }
+
+ /**
+ * Returns {@code true} if {@code value} represents a real number. This is
+ * equivalent to, but not necessarily implemented as,
+ * {@code !(Float.isInfinite(value) || Float.isNaN(value))}.
+ *
+ * @since 10.0
+ */
+ public static boolean isFinite(float value) {
+ return NEGATIVE_INFINITY < value & value < POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns {@code true} if {@code target} is present as an element anywhere in
+ * {@code array}. Note that this always returns {@code false} when {@code
+ * target} is {@code NaN}.
+ *
+ * @param array an array of {@code float} values, possibly empty
+ * @param target a primitive {@code float} value
+ * @return {@code true} if {@code array[i] == target} for some value of {@code
+ * i}
+ */
+ public static boolean contains(float[] array, float target) {
+ for (float value : array) {
+ if (value == target) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the index of the first appearance of the value {@code target} in
+ * {@code array}. Note that this always returns {@code -1} when {@code target}
+ * is {@code NaN}.
+ *
+ * @param array an array of {@code float} values, possibly empty
+ * @param target a primitive {@code float} value
+ * @return the least index {@code i} for which {@code array[i] == target}, or
+ * {@code -1} if no such index exists.
+ */
+ public static int indexOf(float[] array, float target) {
+ return indexOf(array, target, 0, array.length);
+ }
+
+ // TODO(kevinb): consider making this public
+ private static int indexOf(
+ float[] array, float target, int start, int end) {
+ for (int i = start; i < end; i++) {
+ if (array[i] == target) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the start position of the first occurrence of the specified {@code
+ * target} within {@code array}, or {@code -1} if there is no such occurrence.
+ *
+ * <p>More formally, returns the lowest index {@code i} such that {@code
+ * java.util.Arrays.copyOfRange(array, i, i + target.length)} contains exactly
+ * the same elements as {@code target}.
+ *
+ * <p>Note that this always returns {@code -1} when {@code target} contains
+ * {@code NaN}.
+ *
+ * @param array the array to search for the sequence {@code target}
+ * @param target the array to search for as a sub-sequence of {@code array}
+ */
+ public static int indexOf(float[] array, float[] target) {
+ checkNotNull(array, "array");
+ checkNotNull(target, "target");
+ if (target.length == 0) {
+ return 0;
+ }
+
+ outer:
+ for (int i = 0; i < array.length - target.length + 1; i++) {
+ for (int j = 0; j < target.length; j++) {
+ if (array[i + j] != target[j]) {
+ continue outer;
+ }
+ }
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index of the last appearance of the value {@code target} in
+ * {@code array}. Note that this always returns {@code -1} when {@code target}
+ * is {@code NaN}.
+ *
+ * @param array an array of {@code float} values, possibly empty
+ * @param target a primitive {@code float} value
+ * @return the greatest index {@code i} for which {@code array[i] == target},
+ * or {@code -1} if no such index exists.
+ */
+ public static int lastIndexOf(float[] array, float target) {
+ return lastIndexOf(array, target, 0, array.length);
+ }
+
+ // TODO(kevinb): consider making this public
+ private static int lastIndexOf(
+ float[] array, float target, int start, int end) {
+ for (int i = end - 1; i >= start; i--) {
+ if (array[i] == target) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the least value present in {@code array}, using the same rules of
+ * comparison as {@link Math#min(float, float)}.
+ *
+ * @param array a <i>nonempty</i> array of {@code float} values
+ * @return the value present in {@code array} that is less than or equal to
+ * every other value in the array
+ * @throws IllegalArgumentException if {@code array} is empty
+ */
+ public static float min(float... array) {
+ checkArgument(array.length > 0);
+ float min = array[0];
+ for (int i = 1; i < array.length; i++) {
+ min = Math.min(min, array[i]);
+ }
+ return min;
+ }
+
+ /**
+ * Returns the greatest value present in {@code array}, using the same rules
+ * of comparison as {@link Math#min(float, float)}.
+ *
+ * @param array a <i>nonempty</i> array of {@code float} values
+ * @return the value present in {@code array} that is greater than or equal to
+ * every other value in the array
+ * @throws IllegalArgumentException if {@code array} is empty
+ */
+ public static float max(float... array) {
+ checkArgument(array.length > 0);
+ float max = array[0];
+ for (int i = 1; i < array.length; i++) {
+ max = Math.max(max, array[i]);
+ }
+ return max;
+ }
+
+ /**
+ * Returns the values from each provided array combined into a single array.
+ * For example, {@code concat(new float[] {a, b}, new float[] {}, new
+ * float[] {c}} returns the array {@code {a, b, c}}.
+ *
+ * @param arrays zero or more {@code float} arrays
+ * @return a single array containing all the values from the source arrays, in
+ * order
+ */
+ public static float[] concat(float[]... arrays) {
+ int length = 0;
+ for (float[] array : arrays) {
+ length += array.length;
+ }
+ float[] result = new float[length];
+ int pos = 0;
+ for (float[] array : arrays) {
+ System.arraycopy(array, 0, result, pos, array.length);
+ pos += array.length;
+ }
+ return result;
+ }
+
+ /**
+ * Returns an array containing the same values as {@code array}, but
+ * guaranteed to be of a specified minimum length. If {@code array} already
+ * has a length of at least {@code minLength}, it is returned directly.
+ * Otherwise, a new array of size {@code minLength + padding} is returned,
+ * containing the values of {@code array}, and zeroes in the remaining places.
+ *
+ * @param array the source array
+ * @param minLength the minimum length the returned array must guarantee
+ * @param padding an extra amount to "grow" the array by if growth is
+ * necessary
+ * @throws IllegalArgumentException if {@code minLength} or {@code padding} is
+ * negative
+ * @return an array containing the values of {@code array}, with guaranteed
+ * minimum length {@code minLength}
+ */
+ public static float[] ensureCapacity(
+ float[] array, int minLength, int padding) {
+ checkArgument(minLength >= 0, "Invalid minLength: %s", minLength);
+ checkArgument(padding >= 0, "Invalid padding: %s", padding);
+ return (array.length < minLength)
+ ? copyOf(array, minLength + padding)
+ : array;
+ }
+
+ // Arrays.copyOf() requires Java 6
+ private static float[] copyOf(float[] original, int length) {
+ float[] copy = new float[length];
+ System.arraycopy(original, 0, copy, 0, Math.min(original.length, length));
+ return copy;
+ }
+
+ /**
+ * Returns a string containing the supplied {@code float} values, converted
+ * to strings as specified by {@link Float#toString(float)}, and separated by
+ * {@code separator}. For example, {@code join("-", 1.0f, 2.0f, 3.0f)}
+ * returns the string {@code "1.0-2.0-3.0"}.
+ *
+ * <p>Note that {@link Float#toString(float)} formats {@code float}
+ * differently in GWT. In the previous example, it returns the string {@code
+ * "1-2-3"}.
+ *
+ * @param separator the text that should appear between consecutive values in
+ * the resulting string (but not at the start or end)
+ * @param array an array of {@code float} values, possibly empty
+ */
+ public static String join(String separator, float... array) {
+ checkNotNull(separator);
+ if (array.length == 0) {
+ return "";
+ }
+
+ // For pre-sizing a builder, just get the right order of magnitude
+ StringBuilder builder = new StringBuilder(array.length * 12);
+ builder.append(array[0]);
+ for (int i = 1; i < array.length; i++) {
+ builder.append(separator).append(array[i]);
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Returns a comparator that compares two {@code float} arrays
+ * lexicographically. That is, it compares, using {@link
+ * #compare(float, float)}), the first pair of values that follow any
+ * common prefix, or when one array is a prefix of the other, treats the
+ * shorter array as the lesser. For example, {@code [] < [1.0f] < [1.0f, 2.0f]
+ * < [2.0f]}.
+ *
+ * <p>The returned comparator is inconsistent with {@link
+ * Object#equals(Object)} (since arrays support only identity equality), but
+ * it is consistent with {@link Arrays#equals(float[], float[])}.
+ *
+ * @see <a href="http://en.wikipedia.org/wiki/Lexicographical_order">
+ * Lexicographical order article at Wikipedia</a>
+ * @since 2.0
+ */
+ public static Comparator<float[]> lexicographicalComparator() {
+ return LexicographicalComparator.INSTANCE;
+ }
+
+ private enum LexicographicalComparator implements Comparator<float[]> {
+ INSTANCE;
+
+ @Override
+ public int compare(float[] left, float[] right) {
+ int minLength = Math.min(left.length, right.length);
+ for (int i = 0; i < minLength; i++) {
+ int result = Floats.compare(left[i], right[i]);
+ if (result != 0) {
+ return result;
+ }
+ }
+ return left.length - right.length;
+ }
+ }
+
+ /**
+ * Returns an array containing each value of {@code collection}, converted to
+ * a {@code float} value in the manner of {@link Number#floatValue}.
+ *
+ * <p>Elements are copied from the argument collection as if by {@code
+ * collection.toArray()}. Calling this method is as thread-safe as calling
+ * that method.
+ *
+ * @param collection a collection of {@code Number} instances
+ * @return an array containing the same values as {@code collection}, in the
+ * same order, converted to primitives
+ * @throws NullPointerException if {@code collection} or any of its elements
+ * is null
+ * @since 1.0 (parameter was {@code Collection<Float>} before 12.0)
+ */
+ public static float[] toArray(Collection<? extends Number> collection) {
+ if (collection instanceof FloatArrayAsList) {
+ return ((FloatArrayAsList) collection).toFloatArray();
+ }
+
+ Object[] boxedArray = collection.toArray();
+ int len = boxedArray.length;
+ float[] array = new float[len];
+ for (int i = 0; i < len; i++) {
+ // checkNotNull for GWT (do not optimize)
+ array[i] = ((Number) checkNotNull(boxedArray[i])).floatValue();
+ }
+ return array;
+ }
+
+ /**
+ * Returns a fixed-size list backed by the specified array, similar to {@link
+ * Arrays#asList(Object[])}. The list supports {@link List#set(int, Object)},
+ * but any attempt to set a value to {@code null} will result in a {@link
+ * NullPointerException}.
+ *
+ * <p>The returned list maintains the values, but not the identities, of
+ * {@code Float} objects written to or read from it. For example, whether
+ * {@code list.get(0) == list.get(0)} is true for the returned list is
+ * unspecified.
+ *
+ * <p>The returned list may have unexpected behavior if it contains {@code
+ * NaN}, or if {@code NaN} is used as a parameter to any of its methods.
+ *
+ * @param backingArray the array to back the list
+ * @return a list view of the array
+ */
+ public static List<Float> asList(float... backingArray) {
+ if (backingArray.length == 0) {
+ return Collections.emptyList();
+ }
+ return new FloatArrayAsList(backingArray);
+ }
+
+ @GwtCompatible
+ private static class FloatArrayAsList extends AbstractList<Float>
+ implements RandomAccess, Serializable {
+ final float[] array;
+ final int start;
+ final int end;
+
+ FloatArrayAsList(float[] array) {
+ this(array, 0, array.length);
+ }
+
+ FloatArrayAsList(float[] array, int start, int end) {
+ this.array = array;
+ this.start = start;
+ this.end = end;
+ }
+
+ @Override public int size() {
+ return end - start;
+ }
+
+ @Override public boolean isEmpty() {
+ return false;
+ }
+
+ @Override public Float get(int index) {
+ checkElementIndex(index, size());
+ return array[start + index];
+ }
+
+ @Override public boolean contains(Object target) {
+ // Overridden to prevent a ton of boxing
+ return (target instanceof Float)
+ && Floats.indexOf(array, (Float) target, start, end) != -1;
+ }
+
+ @Override public int indexOf(Object target) {
+ // Overridden to prevent a ton of boxing
+ if (target instanceof Float) {
+ int i = Floats.indexOf(array, (Float) target, start, end);
+ if (i >= 0) {
+ return i - start;
+ }
+ }
+ return -1;
+ }
+
+ @Override public int lastIndexOf(Object target) {
+ // Overridden to prevent a ton of boxing
+ if (target instanceof Float) {
+ int i = Floats.lastIndexOf(array, (Float) target, start, end);
+ if (i >= 0) {
+ return i - start;
+ }
+ }
+ return -1;
+ }
+
+ @Override public Float set(int index, Float element) {
+ checkElementIndex(index, size());
+ float oldValue = array[start + index];
+ // checkNotNull for GWT (do not optimize)
+ array[start + index] = checkNotNull(element);
+ return oldValue;
+ }
+
+ @Override public List<Float> subList(int fromIndex, int toIndex) {
+ int size = size();
+ checkPositionIndexes(fromIndex, toIndex, size);
+ if (fromIndex == toIndex) {
+ return Collections.emptyList();
+ }
+ return new FloatArrayAsList(array, start + fromIndex, start + toIndex);
+ }
+
+ @Override public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof FloatArrayAsList) {
+ FloatArrayAsList that = (FloatArrayAsList) object;
+ int size = size();
+ if (that.size() != size) {
+ return false;
+ }
+ for (int i = 0; i < size; i++) {
+ if (array[start + i] != that.array[that.start + i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return super.equals(object);
+ }
+
+ @Override public int hashCode() {
+ int result = 1;
+ for (int i = start; i < end; i++) {
+ result = 31 * result + Floats.hashCode(array[i]);
+ }
+ return result;
+ }
+
+ @Override public String toString() {
+ StringBuilder builder = new StringBuilder(size() * 12);
+ builder.append('[').append(array[start]);
+ for (int i = start + 1; i < end; i++) {
+ builder.append(", ").append(array[i]);
+ }
+ return builder.append(']').toString();
+ }
+
+ float[] toFloatArray() {
+ // Arrays.copyOfRange() is not available under GWT
+ int size = size();
+ float[] result = new float[size];
+ System.arraycopy(array, start, result, 0, size);
+ return result;
+ }
+
+ private static final long serialVersionUID = 0;
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Ints.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Ints.java
index 0cee6c4a5..8f9315d2e 100644
--- a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Ints.java
+++ b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Ints.java
@@ -35,6 +35,10 @@ import java.util.RandomAccess;
* Static utility methods pertaining to {@code int} primitives, that are not
* already found in either {@link Integer} or {@link Arrays}.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained">
+ * primitive utilities</a>.
+ *
* @author Kevin Bourrillion
* @since 1.0
*/
@@ -360,20 +364,21 @@ public final class Ints {
}
/**
- * Copies a collection of {@code Integer} instances into a new array of
- * primitive {@code int} values.
+ * Returns an array containing each value of {@code collection}, converted to
+ * a {@code int} value in the manner of {@link Number#intValue}.
*
* <p>Elements are copied from the argument collection as if by {@code
* collection.toArray()}. Calling this method is as thread-safe as calling
* that method.
*
- * @param collection a collection of {@code Integer} objects
+ * @param collection a collection of {@code Number} instances
* @return an array containing the same values as {@code collection}, in the
* same order, converted to primitives
* @throws NullPointerException if {@code collection} or any of its elements
* is null
+ * @since 1.0 (parameter was {@code Collection<Integer>} before 12.0)
*/
- public static int[] toArray(Collection<Integer> collection) {
+ public static int[] toArray(Collection<? extends Number> collection) {
if (collection instanceof IntArrayAsList) {
return ((IntArrayAsList) collection).toIntArray();
}
@@ -383,7 +388,7 @@ public final class Ints {
int[] array = new int[len];
for (int i = 0; i < len; i++) {
// checkNotNull for GWT (do not optimize)
- array[i] = (Integer) checkNotNull(boxedArray[i]);
+ array[i] = ((Number) checkNotNull(boxedArray[i])).intValue();
}
return array;
}
@@ -470,7 +475,8 @@ public final class Ints {
@Override public Integer set(int index, Integer element) {
checkElementIndex(index, size());
int oldValue = array[start + index];
- array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize)
+ // checkNotNull for GWT (do not optimize)
+ array[start + index] = checkNotNull(element);
return oldValue;
}
@@ -521,7 +527,7 @@ public final class Ints {
}
int[] toIntArray() {
- // Arrays.copyOfRange() requires Java 6
+ // Arrays.copyOfRange() is not available under GWT
int size = size();
int[] result = new int[size];
System.arraycopy(array, start, result, 0, size);
diff --git a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Shorts.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Shorts.java
index d1cee1317..92899c7d0 100644
--- a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Shorts.java
+++ b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Shorts.java
@@ -35,6 +35,10 @@ import java.util.RandomAccess;
* Static utility methods pertaining to {@code short} primitives, that are not
* already found in either {@link Short} or {@link Arrays}.
*
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained">
+ * primitive utilities</a>.
+ *
* @author Kevin Bourrillion
* @since 1.0
*/
@@ -362,20 +366,21 @@ public final class Shorts {
}
/**
- * Copies a collection of {@code Short} instances into a new array of
- * primitive {@code short} values.
+ * Returns an array containing each value of {@code collection}, converted to
+ * a {@code short} value in the manner of {@link Number#shortValue}.
*
* <p>Elements are copied from the argument collection as if by {@code
* collection.toArray()}. Calling this method is as thread-safe as calling
* that method.
*
- * @param collection a collection of {@code Short} objects
+ * @param collection a collection of {@code Number} instances
* @return an array containing the same values as {@code collection}, in the
* same order, converted to primitives
* @throws NullPointerException if {@code collection} or any of its elements
* is null
+ * @since 1.0 (parameter was {@code Collection<Short>} before 12.0)
*/
- public static short[] toArray(Collection<Short> collection) {
+ public static short[] toArray(Collection<? extends Number> collection) {
if (collection instanceof ShortArrayAsList) {
return ((ShortArrayAsList) collection).toShortArray();
}
@@ -385,7 +390,7 @@ public final class Shorts {
short[] array = new short[len];
for (int i = 0; i < len; i++) {
// checkNotNull for GWT (do not optimize)
- array[i] = (Short) checkNotNull(boxedArray[i]);
+ array[i] = ((Number) checkNotNull(boxedArray[i])).shortValue();
}
return array;
}
@@ -472,7 +477,8 @@ public final class Shorts {
@Override public Short set(int index, Short element) {
checkElementIndex(index, size());
short oldValue = array[start + index];
- array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize)
+ // checkNotNull for GWT (do not optimize)
+ array[start + index] = checkNotNull(element);
return oldValue;
}
@@ -523,7 +529,7 @@ public final class Shorts {
}
short[] toShortArray() {
- // Arrays.copyOfRange() requires Java 6
+ // Arrays.copyOfRange() is not available under GWT
int size = size();
short[] result = new short[size];
System.arraycopy(array, start, result, 0, size);
diff --git a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/UnsignedInteger.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/UnsignedInteger.java
new file mode 100644
index 000000000..20e6dce17
--- /dev/null
+++ b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/UnsignedInteger.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.primitives;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.primitives.UnsignedInts.INT_MASK;
+import static com.google.common.primitives.UnsignedInts.compare;
+import static com.google.common.primitives.UnsignedInts.toLong;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+
+import java.math.BigInteger;
+
+import javax.annotation.CheckReturnValue;
+import javax.annotation.Nullable;
+
+/**
+ * A wrapper class for unsigned {@code int} values, supporting arithmetic operations.
+ *
+ * <p>In some cases, when speed is more important than code readability, it may be faster simply to
+ * treat primitive {@code int} values as unsigned, using the methods from {@link UnsignedInts}.
+ *
+ * <p>See the Guava User Guide article on <a href=
+ * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained#Unsigned_support">
+ * unsigned primitive utilities</a>.
+ *
+ * @author Louis Wasserman
+ * @since 11.0
+ */
+@GwtCompatible(emulated = true)
+public final class UnsignedInteger extends Number implements Comparable<UnsignedInteger> {
+ public static final UnsignedInteger ZERO = asUnsigned(0);
+ public static final UnsignedInteger ONE = asUnsigned(1);
+ public static final UnsignedInteger MAX_VALUE = asUnsigned(-1);
+
+ private final int value;
+
+ private UnsignedInteger(int value) {
+ // GWT doesn't consistently overflow values to make them 32-bit, so we need to force it.
+ this.value = value & 0xffffffff;
+ }
+
+ /**
+ * Returns an {@code UnsignedInteger} that, when treated as signed, is
+ * equal to {@code value}.
+ *
+ * @deprecated Use {@link #fromIntBits(int)}. This method is scheduled to be removed in Guava
+ * release 15.0.
+ */
+ @Deprecated
+ @Beta
+ public static UnsignedInteger asUnsigned(int value) {
+ return fromIntBits(value);
+ }
+
+ /**
+ * Returns an {@code UnsignedInteger} corresponding to a given bit representation.
+ * The argument is interpreted as an unsigned 32-bit value. Specifically, the sign bit
+ * of {@code bits} is interpreted as a normal bit, and all other bits are treated as usual.
+ *
+ * <p>If the argument is nonnegative, the returned result will be equal to {@code bits},
+ * otherwise, the result will be equal to {@code 2^32 + bits}.
+ *
+ * <p>To represent unsigned decimal constants, consider {@link #valueOf(long)} instead.
+ *
+ * @since 14.0
+ */
+ public static UnsignedInteger fromIntBits(int bits) {
+ return new UnsignedInteger(bits);
+ }
+
+ /**
+ * Returns an {@code UnsignedInteger} that is equal to {@code value},
+ * if possible. The inverse operation of {@link #longValue()}.
+ */
+ public static UnsignedInteger valueOf(long value) {
+ checkArgument((value & INT_MASK) == value,
+ "value (%s) is outside the range for an unsigned integer value", value);
+ return fromIntBits((int) value);
+ }
+
+ /**
+ * Returns a {@code UnsignedInteger} representing the same value as the specified
+ * {@link BigInteger}. This is the inverse operation of {@link #bigIntegerValue()}.
+ *
+ * @throws IllegalArgumentException if {@code value} is negative or {@code value >= 2^32}
+ */
+ public static UnsignedInteger valueOf(BigInteger value) {
+ checkNotNull(value);
+ checkArgument(value.signum() >= 0 && value.bitLength() <= Integer.SIZE,
+ "value (%s) is outside the range for an unsigned integer value", value);
+ return fromIntBits(value.intValue());
+ }
+
+ /**
+ * Returns an {@code UnsignedInteger} holding the value of the specified {@code String}, parsed
+ * as an unsigned {@code int} value.
+ *
+ * @throws NumberFormatException if the string does not contain a parsable unsigned {@code int}
+ * value
+ */
+ public static UnsignedInteger valueOf(String string) {
+ return valueOf(string, 10);
+ }
+
+ /**
+ * Returns an {@code UnsignedInteger} holding the value of the specified {@code String}, parsed
+ * as an unsigned {@code int} value in the specified radix.
+ *
+ * @throws NumberFormatException if the string does not contain a parsable unsigned {@code int}
+ * value
+ */
+ public static UnsignedInteger valueOf(String string, int radix) {
+ return fromIntBits(UnsignedInts.parseUnsignedInt(string, radix));
+ }
+
+ /**
+ * Returns the result of adding this and {@code val}. If the result would have more than 32 bits,
+ * returns the low 32 bits of the result.
+ *
+ * @deprecated Use {@link #plus(UnsignedInteger)}. This method is scheduled to be removed in Guava
+ * release 15.0.
+ */
+ @Deprecated
+ @Beta
+ public UnsignedInteger add(UnsignedInteger val) {
+ return plus(val);
+ }
+
+ /**
+ * Returns the result of adding this and {@code val}. If the result would have more than 32 bits,
+ * returns the low 32 bits of the result.
+ *
+ * @since 14.0
+ */
+ @CheckReturnValue
+ public UnsignedInteger plus(UnsignedInteger val) {
+ return fromIntBits(this.value + checkNotNull(val).value);
+ }
+
+ /**
+ * Returns the result of subtracting this and {@code val}. If the result would be negative,
+ * returns the low 32 bits of the result.
+ *
+ * @deprecated Use {@link #minus(UnsignedInteger)}. This method is scheduled to be removed in
+ * Guava release 15.0.
+ */
+ @Deprecated
+ @Beta
+ public UnsignedInteger subtract(UnsignedInteger val) {
+ return minus(val);
+ }
+
+ /**
+ * Returns the result of subtracting this and {@code val}. If the result would be negative,
+ * returns the low 32 bits of the result.
+ *
+ * @since 14.0
+ */
+ @CheckReturnValue
+ public UnsignedInteger minus(UnsignedInteger val) {
+ return fromIntBits(value - checkNotNull(val).value);
+ }
+
+ /**
+ * Returns the result of dividing this by {@code val}.
+ *
+ * @deprecated Use {@link #dividedBy(UnsignedInteger)}. This method is scheduled to be removed in
+ * Guava release 15.0.
+ */
+ @Deprecated
+ @Beta
+ public UnsignedInteger divide(UnsignedInteger val) {
+ return dividedBy(val);
+ }
+
+ /**
+ * Returns the result of dividing this by {@code val}.
+ *
+ * @throws ArithmeticException if {@code val} is zero
+ * @since 14.0
+ */
+ @CheckReturnValue
+ public UnsignedInteger dividedBy(UnsignedInteger val) {
+ return fromIntBits(UnsignedInts.divide(value, checkNotNull(val).value));
+ }
+
+ /**
+ * Returns the remainder of dividing this by {@code val}.
+ *
+ * @deprecated Use {@link #mod(UnsignedInteger)}. This method is scheduled to be removed in Guava
+ * release 15.0.
+ */
+ @Deprecated
+ @Beta
+ public UnsignedInteger remainder(UnsignedInteger val) {
+ return mod(val);
+ }
+
+ /**
+ * Returns this mod {@code val}.
+ *
+ * @throws ArithmeticException if {@code val} is zero
+ * @since 14.0
+ */
+ @CheckReturnValue
+ public UnsignedInteger mod(UnsignedInteger val) {
+ return fromIntBits(UnsignedInts.remainder(value, checkNotNull(val).value));
+ }
+
+ /**
+ * Returns the value of this {@code UnsignedInteger} as an {@code int}. This is an inverse
+ * operation to {@link #fromIntBits}.
+ *
+ * <p>Note that if this {@code UnsignedInteger} holds a value {@code >= 2^31}, the returned value
+ * will be equal to {@code this - 2^32}.
+ */
+ @Override
+ public int intValue() {
+ return value;
+ }
+
+ /**
+ * Returns the value of this {@code UnsignedInteger} as a {@code long}.
+ */
+ @Override
+ public long longValue() {
+ return toLong(value);
+ }
+
+ /**
+ * Returns the value of this {@code UnsignedInteger} as a {@code float}, analogous to a widening
+ * primitive conversion from {@code int} to {@code float}, and correctly rounded.
+ */
+ @Override
+ public float floatValue() {
+ return longValue();
+ }
+
+ /**
+ * Returns the value of this {@code UnsignedInteger} as a {@code float}, analogous to a widening
+ * primitive conversion from {@code int} to {@code double}, and correctly rounded.
+ */
+ @Override
+ public double doubleValue() {
+ return longValue();
+ }
+
+ /**
+ * Returns the value of this {@code UnsignedInteger} as a {@link BigInteger}.
+ */
+ public BigInteger bigIntegerValue() {
+ return BigInteger.valueOf(longValue());
+ }
+
+ /**
+ * Compares this unsigned integer to another unsigned integer.
+ * Returns {@code 0} if they are equal, a negative number if {@code this < other},
+ * and a positive number if {@code this > other}.
+ */
+ @Override
+ public int compareTo(UnsignedInteger other) {
+ checkNotNull(other);
+ return compare(value, other.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (obj instanceof UnsignedInteger) {
+ UnsignedInteger other = (UnsignedInteger) obj;
+ return value == other.value;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a string representation of the {@code UnsignedInteger} value, in base 10.
+ */
+ @Override
+ public String toString() {
+ return toString(10);
+ }
+
+ /**
+ * Returns a string representation of the {@code UnsignedInteger} value, in base {@code radix}.
+ * If {@code radix < Character.MIN_RADIX} or {@code radix > Character.MAX_RADIX}, the radix
+ * {@code 10} is used.
+ */
+ public String toString(int radix) {
+ return UnsignedInts.toString(value, radix);
+ }
+}
+
diff --git a/guava-gwt/src-super/java/nio/charset/Charset.gwt.xml b/guava-gwt/src-super/java/nio/charset/Charset.gwt.xml
new file mode 100644
index 000000000..d23fb8cc8
--- /dev/null
+++ b/guava-gwt/src-super/java/nio/charset/Charset.gwt.xml
@@ -0,0 +1,4 @@
+<module>
+ <source path=""/>
+ <inherits name="java.nio.charset.Charset"/>
+</module> \ No newline at end of file
diff --git a/guava-gwt/src-super/java/nio/charset/Charset.java b/guava-gwt/src-super/java/nio/charset/Charset.java
new file mode 100644
index 000000000..146333390
--- /dev/null
+++ b/guava-gwt/src-super/java/nio/charset/Charset.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 java.nio.charset;
+
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * A minimal GWT emulation of {@link Charset}.
+ *
+ * @author Gregory Kick
+ */
+public abstract class Charset implements Comparable<Charset> {
+ private static final Charset UTF_8 = new Charset("UTF-8") {};
+
+ private static final SortedMap<String, Charset> AVAILABLE_CHARSETS =
+ new TreeMap<String, Charset>();
+ static {
+ AVAILABLE_CHARSETS.put(UTF_8.name(), UTF_8);
+ }
+
+ public static SortedMap<String, Charset> availableCharsets() {
+ return Collections.unmodifiableSortedMap(AVAILABLE_CHARSETS);
+ }
+
+ public static Charset forName(String charsetName) {
+ if (charsetName == null) {
+ throw new IllegalArgumentException("Null charset name");
+ }
+ int length = charsetName.length();
+ if (length == 0) {
+ throw new IllegalCharsetNameException(charsetName);
+ }
+ for (int i = 0; i < length; i++) {
+ char c = charsetName.charAt(i);
+ if ((c >= 'A' && c <= 'Z')
+ || (c >= 'a' && c <= 'z')
+ || (c >= '0' && c <= '9')
+ || (c == '-' && i != 0)
+ || (c == ':' && i != 0)
+ || (c == '_' && i != 0)
+ || (c == '.' && i != 0)) {
+ continue;
+ }
+ throw new IllegalCharsetNameException(charsetName);
+ }
+ Charset charset = AVAILABLE_CHARSETS.get(charsetName.toUpperCase());
+ if (charset != null) {
+ return charset;
+ }
+ throw new UnsupportedCharsetException(charsetName);
+ }
+
+ private final String name;
+
+ private Charset(String name) {
+ this.name = name;
+ }
+
+ public final String name() {
+ return name;
+ }
+
+ public final int compareTo(Charset that) {
+ return this.name.compareToIgnoreCase(that.name);
+ }
+
+ public final int hashCode() {
+ return name.hashCode();
+ }
+
+ public final boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ } else if (o instanceof Charset) {
+ Charset that = (Charset) o;
+ return this.name.equals(that.name);
+ } else {
+ return false;
+ }
+ }
+
+ public final String toString() {
+ return name;
+ }
+}
diff --git a/guava-gwt/src-super/java/nio/charset/IllegalCharsetNameException.java b/guava-gwt/src-super/java/nio/charset/IllegalCharsetNameException.java
new file mode 100644
index 000000000..19755e235
--- /dev/null
+++ b/guava-gwt/src-super/java/nio/charset/IllegalCharsetNameException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 java.nio.charset;
+
+/**
+ * GWT emulation of {@link IllegalCharsetNameException}.
+ *
+ * @author Gregory Kick
+ */
+public class IllegalCharsetNameException extends IllegalArgumentException {
+ private final String charsetName;
+
+ public IllegalCharsetNameException(String charsetName) {
+ super(String.valueOf(charsetName));
+ this.charsetName = charsetName;
+ }
+
+ public String getCharsetName() {
+ return charsetName;
+ }
+}
diff --git a/guava-gwt/src-super/java/nio/charset/UnsupportedCharsetException.java b/guava-gwt/src-super/java/nio/charset/UnsupportedCharsetException.java
new file mode 100644
index 000000000..f489a1d39
--- /dev/null
+++ b/guava-gwt/src-super/java/nio/charset/UnsupportedCharsetException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 java.nio.charset;
+
+/**
+ * GWT emulation of {@link UnsupportedCharsetException}.
+ *
+ * @author Gregory Kick
+ */
+public class UnsupportedCharsetException extends IllegalArgumentException {
+ private final String charsetName;
+
+ public UnsupportedCharsetException(String charsetName) {
+ super(String.valueOf(charsetName));
+ this.charsetName = charsetName;
+ }
+
+ public String getCharsetName() {
+ return charsetName;
+ }
+}
diff --git a/guava-gwt/src-super/java/util/super/java/util/concurrent/Callable.java b/guava-gwt/src-super/java/util/super/java/util/concurrent/Callable.java
index 548f3a686..607d3f76d 100644
--- a/guava-gwt/src-super/java/util/super/java/util/concurrent/Callable.java
+++ b/guava-gwt/src-super/java/util/super/java/util/concurrent/Callable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2011 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentHashMap.java b/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentHashMap.java
index 77544b537..886d8f78d 100644
--- a/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentHashMap.java
+++ b/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentHashMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2009 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentMap.java b/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentMap.java
index 61a8a04de..49c05ce22 100644
--- a/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentMap.java
+++ b/guava-gwt/src-super/java/util/super/java/util/concurrent/ConcurrentMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2009 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/guava-gwt/src-super/java/util/super/java/util/concurrent/ExecutionException.java b/guava-gwt/src-super/java/util/super/java/util/concurrent/ExecutionException.java
index b25f476cc..c1fc0f4fc 100644
--- a/guava-gwt/src-super/java/util/super/java/util/concurrent/ExecutionException.java
+++ b/guava-gwt/src-super/java/util/super/java/util/concurrent/ExecutionException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2011 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ package java.util.concurrent;
/**
* Emulation of ExecutionException.
*
- * @author fry@google.com (Charles Fry)
+ * @author Charles Fry
*/
public class ExecutionException extends Exception {
protected ExecutionException() { }
diff --git a/guava-gwt/src-super/java/util/super/java/util/concurrent/TimeUnit.java b/guava-gwt/src-super/java/util/super/java/util/concurrent/TimeUnit.java
index 2ce1de38b..52f3fb4b7 100644
--- a/guava-gwt/src-super/java/util/super/java/util/concurrent/TimeUnit.java
+++ b/guava-gwt/src-super/java/util/super/java/util/concurrent/TimeUnit.java
@@ -1,4 +1,8 @@
/*
+ * This file is a modified version of
+ * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/TimeUnit.java
+ * which contained the following notice:
+ *
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
diff --git a/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicInteger.java b/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicInteger.java
index ac98dcf5c..d9c16a667 100644
--- a/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicInteger.java
+++ b/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicInteger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2009 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicLong.java b/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicLong.java
index 86964227e..fdd1b34f9 100644
--- a/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicLong.java
+++ b/guava-gwt/src-super/java/util/super/java/util/concurrent/atomic/AtomicLong.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2011 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/guava-gwt/src/com/google/common/ForceGuavaCompilation.gwt.xml b/guava-gwt/src/com/google/common/ForceGuavaCompilation.gwt.xml
new file mode 100644
index 000000000..9c4d5078e
--- /dev/null
+++ b/guava-gwt/src/com/google/common/ForceGuavaCompilation.gwt.xml
@@ -0,0 +1,15 @@
+<module>
+ <inherits name="com.google.common.annotations.Annotations" />
+ <inherits name="com.google.common.base.Base" />
+ <inherits name="com.google.common.cache.Cache" />
+ <inherits name="com.google.common.collect.Collect" />
+ <inherits name="com.google.common.io.Io" />
+ <inherits name="com.google.common.math.Math" />
+ <inherits name="com.google.common.net.Net" />
+ <inherits name="com.google.common.primitives.Primitives" />
+ <inherits name="com.google.common.util.concurrent.Concurrent" />
+ <inherits name="com.google.gwt.user.User" />
+
+ <source path="" />
+ <entry-point class="com.google.common.ForceGuavaCompilationEntryPoint" />
+</module>
diff --git a/guava-gwt/src-super/com/google/common/testing/super/com/google/common/testing/Platform.java b/guava-gwt/src/com/google/common/ForceGuavaCompilationEntryPoint.java
index 9fa092e2f..f7260a231 100644
--- a/guava-gwt/src-super/com/google/common/testing/super/com/google/common/testing/Platform.java
+++ b/guava-gwt/src/com/google/common/ForceGuavaCompilationEntryPoint.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Guava Authors
+ * Copyright (C) 2010 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,23 +14,16 @@
* limitations under the License.
*/
-package com.google.common.testing;
+package com.google.common;
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.gwt.core.client.EntryPoint;
/**
- * Methods factored out so that they can be emulated differently in GWT.
+ * A dummy entry point to convince Maven to compile our classes.
*
* @author Chris Povirk
*/
-final class Platform {
- /**
- * Serializes and deserializes the specified object (a no-op under GWT).
- */
- @SuppressWarnings("unchecked")
- static <T> T reserialize(T object) {
- return checkNotNull(object);
+public class ForceGuavaCompilationEntryPoint implements EntryPoint {
+ @Override public void onModuleLoad() {
}
-
- private Platform() {}
}
diff --git a/guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml b/guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml
index 0b388fa6d..8903a62ce 100644
--- a/guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml
+++ b/guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml
@@ -1,3 +1,4 @@
+<!-- semi-autogenerated module descriptor -->
<module>
- <source path=""/>
+<source path=""/>
</module>
diff --git a/guava-gwt/src/com/google/common/base/Absent_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/base/Absent_CustomFieldSerializer.java
new file mode 100644
index 000000000..ecdac0f1c
--- /dev/null
+++ b/guava-gwt/src/com/google/common/base/Absent_CustomFieldSerializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.base;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * Custom GWT serializer for {@link Absent}.
+ *
+ * <p>GWT can serialize an absent {@code Optional} on its own, but the resulting object is a
+ * different instance than the singleton {@code Absent.INSTANCE}, which breaks equality. We
+ * implement a custom serializer to maintain the singleton property.
+ *
+ * @author Chris Povirk
+ */
+@GwtCompatible
+public class Absent_CustomFieldSerializer {
+ public static void deserialize(SerializationStreamReader reader, Absent instance) {}
+
+ public static Absent instantiate(SerializationStreamReader reader) {
+ return Absent.INSTANCE;
+ }
+
+ public static void serialize(SerializationStreamWriter writer, Absent instance) {}
+}
diff --git a/guava-gwt/src/com/google/common/base/Base.gwt.xml b/guava-gwt/src/com/google/common/base/Base.gwt.xml
index f1ca1877c..d0c84c886 100644
--- a/guava-gwt/src/com/google/common/base/Base.gwt.xml
+++ b/guava-gwt/src/com/google/common/base/Base.gwt.xml
@@ -1,5 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
+
<source path=""/>
+
<super-source path="super"/>
+
+ <inherits name="java.nio.charset.Charset"/>
+
<inherits name="java.util.Util"/>
+
+ <inherits name="com.google.common.annotations.Annotations"/>
+
</module>
diff --git a/guava-gwt/src/com/google/common/base/GwtSerializationDependencies.java b/guava-gwt/src/com/google/common/base/GwtSerializationDependencies.java
index 77f93f2c9..7f0643f7c 100644
--- a/guava-gwt/src/com/google/common/base/GwtSerializationDependencies.java
+++ b/guava-gwt/src/com/google/common/base/GwtSerializationDependencies.java
@@ -18,6 +18,10 @@ package com.google.common.base;
import com.google.common.annotations.GwtCompatible;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
/**
* Contains dummy collection implementations to convince GWT that part of
* serializing a collection is serializing its elements.
@@ -32,4 +36,57 @@ import com.google.common.annotations.GwtCompatible;
@SuppressWarnings("serial")
final class GwtSerializationDependencies {
private GwtSerializationDependencies() {}
+
+ static final class OptionalDependencies<T> extends Optional<T> {
+ T value;
+
+ OptionalDependencies() {
+ super();
+ }
+
+ @Override public boolean isPresent() {
+ throw new AssertionError();
+ }
+
+ @Override public T get() {
+ throw new AssertionError();
+ }
+
+ @Override public T or(T defaultValue) {
+ throw new AssertionError();
+ }
+
+ @Override public Optional<T> or(Optional<? extends T> secondChoice) {
+ throw new AssertionError();
+ }
+
+ @Override public T or(Supplier<? extends T> supplier) {
+ throw new AssertionError();
+ }
+
+ @Override public T orNull() {
+ throw new AssertionError();
+ }
+
+ @Override public Set<T> asSet() {
+ throw new AssertionError();
+ }
+
+ @Override public <V> Optional<V> transform(
+ Function<? super T, V> function) {
+ throw new AssertionError();
+ }
+
+ @Override public boolean equals(@Nullable Object object) {
+ throw new AssertionError();
+ }
+
+ @Override public int hashCode() {
+ throw new AssertionError();
+ }
+
+ @Override public String toString() {
+ throw new AssertionError();
+ }
+ }
}
diff --git a/guava-gwt/src/com/google/common/base/PairwiseEquivalence_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/base/PairwiseEquivalence_CustomFieldSerializer.java
index 6f125f945..531550c16 100644
--- a/guava-gwt/src/com/google/common/base/PairwiseEquivalence_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/base/PairwiseEquivalence_CustomFieldSerializer.java
@@ -23,7 +23,7 @@ import com.google.gwt.user.client.rpc.SerializationStreamWriter;
/**
* GWT serialization logic for {@link PairwiseEquivalence}.
*
- * @author kkanitkar@google.com (Kedar Kanitkar)
+ * @author Kedar Kanitkar
*/
public class PairwiseEquivalence_CustomFieldSerializer {
diff --git a/guava-gwt/src/com/google/common/base/Present_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/base/Present_CustomFieldSerializer.java
new file mode 100644
index 000000000..710086dbf
--- /dev/null
+++ b/guava-gwt/src/com/google/common/base/Present_CustomFieldSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.base;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * Custom GWT serializer for {@link Present}.
+ *
+ * @author Chris Povirk
+ */
+@GwtCompatible
+public class Present_CustomFieldSerializer {
+ public static void deserialize(SerializationStreamReader reader, Present<?> instance) {}
+
+ public static Present<Object> instantiate(SerializationStreamReader reader)
+ throws SerializationException {
+ return (Present<Object>) Optional.of(reader.readObject());
+ }
+
+ public static void serialize(SerializationStreamWriter writer, Present<?> instance)
+ throws SerializationException {
+ writer.writeObject(instance.get());
+ }
+}
diff --git a/guava-gwt/src/com/google/common/cache/Cache.gwt.xml b/guava-gwt/src/com/google/common/cache/Cache.gwt.xml
index 9c5b75858..62c7187b8 100644
--- a/guava-gwt/src/com/google/common/cache/Cache.gwt.xml
+++ b/guava-gwt/src/com/google/common/cache/Cache.gwt.xml
@@ -1,9 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
<source path=""/>
<super-source path="super"/>
+
+ <inherits name="java.util.Util"/>
<inherits name="com.google.common.annotations.Annotations"/>
@@ -13,6 +16,4 @@
<inherits name="com.google.common.util.concurrent.Concurrent"/>
- <inherits name="java.util.Util"/>
-
</module>
diff --git a/guava-gwt/src/com/google/common/collect/AllEqualOrdering_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/AllEqualOrdering_CustomFieldSerializer.java
new file mode 100644
index 000000000..4b9d37047
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/AllEqualOrdering_CustomFieldSerializer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * This class implements the GWT serialization of {@link AllEqualOrdering}.
+ *
+ * @author Chris Povirk
+ */
+public class AllEqualOrdering_CustomFieldSerializer {
+ public static void deserialize(SerializationStreamReader reader, AllEqualOrdering instance) {
+ }
+
+ public static AllEqualOrdering instantiate(SerializationStreamReader reader) {
+ return AllEqualOrdering.INSTANCE;
+ }
+
+ public static void serialize(SerializationStreamWriter writer, AllEqualOrdering instance) {
+ }
+}
diff --git a/guava-gwt/src/com/google/common/collect/Collect.gwt.xml b/guava-gwt/src/com/google/common/collect/Collect.gwt.xml
index 31441af78..e042bcbb6 100644
--- a/guava-gwt/src/com/google/common/collect/Collect.gwt.xml
+++ b/guava-gwt/src/com/google/common/collect/Collect.gwt.xml
@@ -1,9 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
<source path=""/>
<super-source path="super"/>
+
+ <inherits name="java.util.Util"/>
<inherits name="com.google.common.annotations.Annotations"/>
@@ -12,7 +15,5 @@
<inherits name="com.google.common.math.Math"/>
<inherits name="com.google.common.primitives.Primitives"/>
-
- <inherits name="java.util.Util"/>
-
+
</module>
diff --git a/guava-gwt/src/com/google/common/collect/EmptyImmutableMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/EmptyImmutableBiMap_CustomFieldSerializer.java
index 67d63b2df..c2d7a0552 100644
--- a/guava-gwt/src/com/google/common/collect/EmptyImmutableMap_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/EmptyImmutableBiMap_CustomFieldSerializer.java
@@ -21,22 +21,18 @@ import com.google.gwt.user.client.rpc.SerializationStreamWriter;
/**
* This class implements the GWT serialization of
- * {@link EmptyImmutableMap}.
- *
+ * {@link EmptyImmutableBiMap}.
+ *
* @author Chris Povirk
*/
-public class EmptyImmutableMap_CustomFieldSerializer {
-
- public static void deserialize(SerializationStreamReader reader,
- EmptyImmutableMap instance) {
+public class EmptyImmutableBiMap_CustomFieldSerializer {
+ public static void deserialize(SerializationStreamReader reader, EmptyImmutableBiMap instance) {
}
- public static EmptyImmutableMap instantiate(
- SerializationStreamReader reader) {
- return EmptyImmutableMap.INSTANCE;
+ public static EmptyImmutableBiMap instantiate(SerializationStreamReader reader) {
+ return EmptyImmutableBiMap.INSTANCE;
}
- public static void serialize(SerializationStreamWriter writer,
- EmptyImmutableMap instance) {
+ public static void serialize(SerializationStreamWriter writer, EmptyImmutableBiMap instance) {
}
}
diff --git a/guava-gwt/src/com/google/common/collect/EmptyImmutableSortedMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/EmptyImmutableSortedMap_CustomFieldSerializer.java
new file mode 100644
index 000000000..a47f6a077
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/EmptyImmutableSortedMap_CustomFieldSerializer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * This class implements the GWT serialization of {@link EmptyImmutableSortedMap}.
+ *
+ * @author Chris Povirk
+ */
+public class EmptyImmutableSortedMap_CustomFieldSerializer {
+ public static void deserialize(
+ SerializationStreamReader reader, EmptyImmutableSortedMap<?, ?> instance) {
+ }
+
+ public static EmptyImmutableSortedMap<?, ?> instantiate(
+ SerializationStreamReader reader) throws SerializationException {
+ return (EmptyImmutableSortedMap<?, ?>)
+ ImmutableSortedMap_CustomFieldSerializerBase.instantiate(reader);
+ }
+
+ public static void serialize(
+ SerializationStreamWriter writer, EmptyImmutableSortedMap<?, ?> instance)
+ throws SerializationException {
+ ImmutableSortedMap_CustomFieldSerializerBase.serialize(writer, instance);
+ }
+}
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableMap.java b/guava-gwt/src/com/google/common/collect/ForwardingImmutableList_CustomFieldSerializer.java
index dc3fecfef..b6767a225 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableMap.java
+++ b/guava-gwt/src/com/google/common/collect/ForwardingImmutableList_CustomFieldSerializer.java
@@ -17,12 +17,9 @@
package com.google.common.collect;
/**
- * GWT emulation of {@link EmptyImmutableMap}. In GWT, it is a thin wrapper
- * around {@link java.util.Collections#emptyMap()}.
+ * Even though {@link ForwardingImmutableList} cannot be instantiated, we still
+ * need a custom field serializer. TODO(cpovirk): why?
*
* @author Hayward Chan
*/
-final class EmptyImmutableMap extends ImmutableMap<Object, Object> {
-
- static final EmptyImmutableMap INSTANCE = new EmptyImmutableMap();
-}
+public final class ForwardingImmutableList_CustomFieldSerializer {}
diff --git a/guava-gwt/src/com/google/common/collect/ForwardingImmutableSet_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ForwardingImmutableSet_CustomFieldSerializer.java
new file mode 100644
index 000000000..b49de225b
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/ForwardingImmutableSet_CustomFieldSerializer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+/**
+ * Even though {@link ForwardingImmutableSet} cannot be instantiated, we still
+ * need a custom field serializer. TODO(cpovirk): why?
+ *
+ * @author Hayward Chan
+ */
+public final class ForwardingImmutableSet_CustomFieldSerializer {}
diff --git a/guava-gwt/src/com/google/common/collect/GwtPlatform.java b/guava-gwt/src/com/google/common/collect/GwtPlatform.java
new file mode 100644
index 000000000..e4d62e1b4
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/GwtPlatform.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.common.annotations.GwtCompatible;
+import com.google.gwt.core.client.GwtScriptOnly;
+
+import java.lang.reflect.Array;
+
+// TODO(kevinb): guava javadoc path seems set wrong, doesn't find that last
+// import
+/**
+ * Version of {@link GwtPlatform} used in hosted-mode. It includes methods in
+ * {@link Platform} that requires different implementions in web mode and
+ * hosted mode. It is factored out from {@link Platform} because {@code
+ * GwtScriptOnly} only supports public classes and methods.
+ *
+ * @author Hayward Chan
+ */
+// TODO(hhchan): Once we start using server-side source in hosted mode, we won't
+// need this.
+@GwtCompatible(emulated = true)
+@GwtScriptOnly
+public final class GwtPlatform {
+
+ private GwtPlatform() {}
+
+ /** See {@link Platform#clone(Object[])} */
+ public static <T> T[] clone(T[] array) {
+ return array.clone();
+ }
+
+ /** See {@link Platform#newArray(Object[], int)} */
+ public static <T> T[] newArray(T[] reference, int length) {
+ Class<?> type = reference.getClass().getComponentType();
+
+ // the cast is safe because
+ // result.getClass() == reference.getClass().getComponentType()
+ @SuppressWarnings("unchecked")
+ T[] result = (T[]) Array.newInstance(type, length);
+ return result;
+ }
+}
diff --git a/guava-gwt/src/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java
index 4cb55ee5f..7466c22bd 100644
--- a/guava-gwt/src/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Guava Authors
+ * Copyright (C) 2009 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,40 +16,10 @@
package com.google.common.collect;
-import com.google.common.annotations.GwtCompatible;
-import com.google.gwt.user.client.rpc.SerializationException;
-import com.google.gwt.user.client.rpc.SerializationStreamReader;
-import com.google.gwt.user.client.rpc.SerializationStreamWriter;
-import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
-
-import java.util.ArrayList;
-import java.util.List;
-
/**
- * This class implements the server-side GWT serialization of
- * {@link ImmutableAsList}.
+ * Even though {@link ImmutableAsList} cannot be instantiated, we still need
+ * a custom field serializer. TODO(cpovirk): why?
*
* @author Hayward Chan
*/
-@GwtCompatible(emulated = true)
-public class ImmutableAsList_CustomFieldSerializer {
-
- public static void deserialize(SerializationStreamReader reader,
- ImmutableAsList<?> instance) {
- }
-
- public static ImmutableAsList<Object> instantiate(
- SerializationStreamReader reader) throws SerializationException {
- List<Object> elements = new ArrayList<Object>();
- Collection_CustomFieldSerializerBase.deserialize(reader, elements);
- ImmutableList<Object> asImmutableList = ImmutableList.copyOf(elements);
- return new ImmutableAsList<Object>(
- asImmutableList.toArray(new Object[asImmutableList.size()]),
- asImmutableList);
- }
-
- public static void serialize(SerializationStreamWriter writer,
- ImmutableAsList<?> instance) throws SerializationException {
- Collection_CustomFieldSerializerBase.serialize(writer, instance);
- }
-}
+public final class ImmutableAsList_CustomFieldSerializer {}
diff --git a/guava-gwt/src/com/google/common/collect/ImmutableBiMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ImmutableBiMap_CustomFieldSerializer.java
new file mode 100644
index 000000000..5f290dcf8
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/ImmutableBiMap_CustomFieldSerializer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+/**
+ * Even though {@link ImmutableBiMap} cannot be instantiated, we still need
+ * a custom field serializer to unify the type signature of
+ * {@code ImmutableBiMap[]} on server and client side.
+ *
+ * @author Hayward Chan
+ */
+public final class ImmutableBiMap_CustomFieldSerializer {}
diff --git a/guava-gwt/src/com/google/common/collect/ImmutableEntry_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ImmutableEntry_CustomFieldSerializer.java
index ab1512a2c..821c33a8a 100644
--- a/guava-gwt/src/com/google/common/collect/ImmutableEntry_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/ImmutableEntry_CustomFieldSerializer.java
@@ -24,7 +24,7 @@ import com.google.gwt.user.client.rpc.SerializationStreamWriter;
* This class implements the GWT serialization of
* {@link ImmutableEntry}.
*
- * @author iteratee@google.com (Kyle Butt)
+ * @author Kyle Butt
*/
public class ImmutableEntry_CustomFieldSerializer {
diff --git a/guava-gwt/src/com/google/common/collect/ImmutableEnumMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ImmutableEnumMap_CustomFieldSerializer.java
new file mode 100644
index 000000000..868cb749c
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/ImmutableEnumMap_CustomFieldSerializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Map_CustomFieldSerializerBase;
+
+import java.util.Map;
+
+/**
+ * This class implements the GWT serialization of {@link ImmutableEnumMap}.
+ *
+ * @author Louis Wasserman
+ */
+public class ImmutableEnumMap_CustomFieldSerializer {
+
+ public static void deserialize(SerializationStreamReader reader,
+ ImmutableEnumMap<?, ?> instance) {
+ }
+
+ public static <K extends Enum<K>, V> ImmutableEnumMap<?, ?> instantiate(
+ SerializationStreamReader reader) throws SerializationException {
+ Map<K, V> deserialized = Maps.newHashMap();
+ Map_CustomFieldSerializerBase.deserialize(reader, deserialized);
+ /*
+ * It is safe to cast to ImmutableEnumSet because in order for it to be
+ * serialized as an ImmutableEnumSet, it must be non-empty to start
+ * with.
+ */
+ return (ImmutableEnumMap<?, ?>) Maps.immutableEnumMap(deserialized);
+ }
+
+ public static void serialize(SerializationStreamWriter writer,
+ ImmutableEnumMap<?, ?> instance) throws SerializationException {
+ Map_CustomFieldSerializerBase.serialize(writer, instance);
+ }
+
+}
diff --git a/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializer.java
index 62936863d..70cf908fa 100644
--- a/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializer.java
@@ -16,44 +16,11 @@
package com.google.common.collect;
-import com.google.gwt.user.client.rpc.SerializationException;
-import com.google.gwt.user.client.rpc.SerializationStreamReader;
-import com.google.gwt.user.client.rpc.SerializationStreamWriter;
-import com.google.gwt.user.client.rpc.core.java.util.Map_CustomFieldSerializerBase;
-
-import java.util.Comparator;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
/**
- * This class implements the GWT serialization of {@link ImmutableSortedMap}.
+ * Even though {@link ImmutableSortedMap} cannot be instantiated, we still need a custom field
+ * serializer. TODO(cpovirk): why? Does it help if ensure that the GWT and non-GWT classes have the
+ * same fields? Is that worth the trouble?
*
* @author Chris Povirk
*/
-public class ImmutableSortedMap_CustomFieldSerializer {
- public static void deserialize(SerializationStreamReader reader,
- ImmutableSortedMap<?, ?> instance) {
- }
-
- public static ImmutableSortedMap<?, ?> instantiate(
- SerializationStreamReader reader) throws SerializationException {
- /*
- * Nothing we can do, but we're already assuming the serialized form is
- * correctly typed, anyway.
- */
- @SuppressWarnings("unchecked")
- Comparator<Object> comparator = (Comparator<Object>) reader.readObject();
-
- SortedMap<Object, Object> entries = new TreeMap<Object, Object>(comparator);
- Map_CustomFieldSerializerBase.deserialize(reader, entries);
-
- return ImmutableSortedMap.orderedBy(comparator).putAll(entries).build();
- }
-
- public static void serialize(SerializationStreamWriter writer,
- ImmutableSortedMap<?, ?> instance) throws SerializationException {
- writer.writeObject(instance.comparator());
-
- Map_CustomFieldSerializerBase.serialize(writer, instance);
- }
-}
+public final class ImmutableSortedMap_CustomFieldSerializer {}
diff --git a/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializerBase.java b/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializerBase.java
new file mode 100644
index 000000000..4ccaaa1c5
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializerBase.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import com.google.gwt.user.client.rpc.core.java.util.Map_CustomFieldSerializerBase;
+
+import java.util.Comparator;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * This class contains static utility methods for writing {@code ImmutableSortedMap} GWT field
+ * serializers.
+ *
+ * @author Chris Povirk
+ */
+final class ImmutableSortedMap_CustomFieldSerializerBase {
+ static ImmutableSortedMap<Object, Object> instantiate(SerializationStreamReader reader)
+ throws SerializationException {
+ /*
+ * Nothing we can do, but we're already assuming the serialized form is
+ * correctly typed, anyway.
+ */
+ @SuppressWarnings("unchecked")
+ Comparator<Object> comparator = (Comparator<Object>) reader.readObject();
+
+ SortedMap<Object, Object> entries = new TreeMap<Object, Object>(comparator);
+ Map_CustomFieldSerializerBase.deserialize(reader, entries);
+
+ return ImmutableSortedMap.orderedBy(comparator).putAll(entries).build();
+ }
+
+ static void serialize(SerializationStreamWriter writer, ImmutableSortedMap<?, ?> instance)
+ throws SerializationException {
+ writer.writeObject(instance.comparator());
+
+ Map_CustomFieldSerializerBase.serialize(writer, instance);
+ }
+
+ private ImmutableSortedMap_CustomFieldSerializerBase() {}
+}
diff --git a/guava-gwt/src/com/google/common/collect/LexicographicalOrdering_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/LexicographicalOrdering_CustomFieldSerializer.java
index fdc195633..597207df1 100644
--- a/guava-gwt/src/com/google/common/collect/LexicographicalOrdering_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/LexicographicalOrdering_CustomFieldSerializer.java
@@ -21,7 +21,8 @@ import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
/**
- * This class implements the GWT serialization of {@link LexicographicalOrdering}.
+ * This class implements the GWT serialization of {@link
+ * LexicographicalOrdering}.
*
* @author Chris Povirk
*/
diff --git a/guava-gwt/src/com/google/common/collect/LinkedHashMultimap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/LinkedHashMultimap_CustomFieldSerializer.java
index 30a280e8e..a0a9163ea 100644
--- a/guava-gwt/src/com/google/common/collect/LinkedHashMultimap_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/LinkedHashMultimap_CustomFieldSerializer.java
@@ -20,11 +20,13 @@ import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.Map;
/**
* This class implements the GWT serialization of {@link LinkedHashMultimap}.
- *
+ *
* @author Chris Povirk
*/
public class LinkedHashMultimap_CustomFieldSerializer {
@@ -34,28 +36,39 @@ public class LinkedHashMultimap_CustomFieldSerializer {
}
public static LinkedHashMultimap<Object, Object> instantiate(
- SerializationStreamReader in) throws SerializationException {
- LinkedHashMultimap<Object, Object> multimap =
- (LinkedHashMultimap<Object, Object>)
- Multimap_CustomFieldSerializerBase.populate(
- in, LinkedHashMultimap.create());
-
- multimap.linkedEntries.clear(); // will clear and repopulate entries
- for (int i = 0; i < multimap.size(); i++) {
- Object key = in.readObject();
- Object value = in.readObject();
- multimap.linkedEntries.add(Maps.immutableEntry(key, value));
+ SerializationStreamReader stream) throws SerializationException {
+ LinkedHashMultimap<Object, Object> multimap = LinkedHashMultimap.create();
+
+ multimap.valueSetCapacity = stream.readInt();
+ int distinctKeys = stream.readInt();
+ Map<Object, Collection<Object>> map =
+ new LinkedHashMap<Object, Collection<Object>>(Maps.capacity(distinctKeys));
+ for (int i = 0; i < distinctKeys; i++) {
+ Object key = stream.readObject();
+ map.put(key, multimap.createCollection(key));
}
+ int entries = stream.readInt();
+ for (int i = 0; i < entries; i++) {
+ Object key = stream.readObject();
+ Object value = stream.readObject();
+ map.get(key).add(value);
+ }
+ multimap.setMap(map);
return multimap;
}
- public static void serialize(SerializationStreamWriter out,
+ public static void serialize(SerializationStreamWriter stream,
LinkedHashMultimap<?, ?> multimap) throws SerializationException {
- Multimap_CustomFieldSerializerBase.serialize(out, multimap);
+ stream.writeInt(multimap.valueSetCapacity);
+ stream.writeInt(multimap.keySet().size());
+ for (Object key : multimap.keySet()) {
+ stream.writeObject(key);
+ }
+ stream.writeInt(multimap.size());
for (Map.Entry<?, ?> entry : multimap.entries()) {
- out.writeObject(entry.getKey());
- out.writeObject(entry.getValue());
+ stream.writeObject(entry.getKey());
+ stream.writeObject(entry.getValue());
}
}
}
diff --git a/guava-gwt/src/com/google/common/collect/Multiset_CustomFieldSerializerBase.java b/guava-gwt/src/com/google/common/collect/Multiset_CustomFieldSerializerBase.java
index 0cad1991f..7c44bba2d 100644
--- a/guava-gwt/src/com/google/common/collect/Multiset_CustomFieldSerializerBase.java
+++ b/guava-gwt/src/com/google/common/collect/Multiset_CustomFieldSerializerBase.java
@@ -23,25 +23,12 @@ import com.google.gwt.user.client.rpc.SerializationStreamWriter;
/**
* This class contains static utility methods for writing {@code Multiset} GWT
* field serializers. Serializers should delegate to
- * {@link #serialize(SerializationStreamWriter, Multiset)} and to either
- * {@link #instantiate(SerializationStreamReader, ImmutableMultiset.Builder)} or
+ * {@link #serialize(SerializationStreamWriter, Multiset)} and
* {@link #populate(SerializationStreamReader, Multiset)}.
*
* @author Chris Povirk
*/
final class Multiset_CustomFieldSerializerBase {
- static ImmutableMultiset<Object> instantiate(
- SerializationStreamReader reader,
- ImmutableMultiset.Builder<Object> builder)
- throws SerializationException {
- int distinctElements = reader.readInt();
- for (int i = 0; i < distinctElements; i++) {
- Object element = reader.readObject();
- int count = reader.readInt();
- builder.addCopies(element, count);
- }
- return builder.build();
- }
static Multiset<Object> populate(
SerializationStreamReader reader, Multiset<Object> multiset)
diff --git a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/RegularImmutableAsList_CustomFieldSerializer.java
index 1712f5a95..f41b8cfc6 100644
--- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/RegularImmutableAsList_CustomFieldSerializer.java
@@ -16,35 +16,35 @@
package com.google.common.collect;
+import com.google.common.annotations.GwtCompatible;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
-import com.google.gwt.user.client.rpc.core.java.util.Collection_CustomFieldSerializerBase;
-
-import java.util.ArrayList;
-import java.util.List;
/**
- * This class implements the client-side GWT serialization of
- * {@link ImmutableAsList}.
+ * This class implements the server-side GWT serialization of
+ * {@link RegularImmutableAsList}.
*
* @author Hayward Chan
*/
-public class ImmutableAsList_CustomFieldSerializer {
+@GwtCompatible(emulated = true)
+public class RegularImmutableAsList_CustomFieldSerializer {
public static void deserialize(SerializationStreamReader reader,
- RegularImmutableList<?> instance) {
+ RegularImmutableAsList<?> instance) {
}
- public static ImmutableAsList<Object> instantiate(
+ public static RegularImmutableAsList<Object> instantiate(
SerializationStreamReader reader) throws SerializationException {
- List<Object> elements = new ArrayList<Object>();
- Collection_CustomFieldSerializerBase.deserialize(reader, elements);
- return new ImmutableAsList<Object>(elements);
+ @SuppressWarnings("unchecked") // serialization is necessarily type unsafe
+ ImmutableCollection<Object> delegateCollection = (ImmutableCollection) reader.readObject();
+ ImmutableList<?> delegateList = (ImmutableList<?>) reader.readObject();
+ return new RegularImmutableAsList<Object>(delegateCollection, delegateList);
}
public static void serialize(SerializationStreamWriter writer,
- ImmutableAsList<?> instance) throws SerializationException {
- Collection_CustomFieldSerializerBase.serialize(writer, instance);
+ RegularImmutableAsList<?> instance) throws SerializationException {
+ writer.writeObject(instance.delegateCollection());
+ writer.writeObject(instance.delegateList());
}
}
diff --git a/guava-gwt/src/com/google/common/collect/RegularImmutableSortedMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/RegularImmutableSortedMap_CustomFieldSerializer.java
new file mode 100644
index 000000000..9f64e375f
--- /dev/null
+++ b/guava-gwt/src/com/google/common/collect/RegularImmutableSortedMap_CustomFieldSerializer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Guava Authors
+ *
+ * Licensed 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 com.google.common.collect;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * This class implements the GWT serialization of {@link RegularImmutableSortedMap}.
+ *
+ * @author Chris Povirk
+ */
+public class RegularImmutableSortedMap_CustomFieldSerializer {
+ public static void deserialize(
+ SerializationStreamReader reader, RegularImmutableSortedMap<?, ?> instance) {
+ }
+
+ public static RegularImmutableSortedMap<?, ?> instantiate(
+ SerializationStreamReader reader) throws SerializationException {
+ return (RegularImmutableSortedMap<?, ?>)
+ ImmutableSortedMap_CustomFieldSerializerBase.instantiate(reader);
+ }
+
+ public static void serialize(
+ SerializationStreamWriter writer, RegularImmutableSortedMap<?, ?> instance)
+ throws SerializationException {
+ ImmutableSortedMap_CustomFieldSerializerBase.serialize(writer, instance);
+ }
+}
diff --git a/guava-gwt/src/com/google/common/collect/SingletonImmutableMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/SingletonImmutableBiMap_CustomFieldSerializer.java
index 6a3ef380f..7b87633a5 100644
--- a/guava-gwt/src/com/google/common/collect/SingletonImmutableMap_CustomFieldSerializer.java
+++ b/guava-gwt/src/com/google/common/collect/SingletonImmutableBiMap_CustomFieldSerializer.java
@@ -23,25 +23,25 @@ import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
/**
- * This class implements the GWT serialization of {@link SingletonImmutableMap}.
+ * This class implements the GWT serialization of {@link SingletonImmutableBiMap}.
*
* @author Chris Povirk
*/
-public class SingletonImmutableMap_CustomFieldSerializer {
+public class SingletonImmutableBiMap_CustomFieldSerializer {
public static void deserialize(SerializationStreamReader reader,
- SingletonImmutableMap<?, ?> instance) {
+ SingletonImmutableBiMap<?, ?> instance) {
}
- public static SingletonImmutableMap<Object, Object> instantiate(
+ public static SingletonImmutableBiMap<Object, Object> instantiate(
SerializationStreamReader reader) throws SerializationException {
Object key = checkNotNull(reader.readObject());
Object value = checkNotNull(reader.readObject());
- return new SingletonImmutableMap<Object, Object>(key, value);
+ return new SingletonImmutableBiMap<Object, Object>(key, value);
}
public static void serialize(SerializationStreamWriter writer,
- SingletonImmutableMap<?, ?> instance) throws SerializationException {
+ SingletonImmutableBiMap<?, ?> instance) throws SerializationException {
writer.writeObject(instance.singleKey);
writer.writeObject(instance.singleValue);
}
diff --git a/guava-gwt/test/com/google/common/testing/Testing.gwt.xml b/guava-gwt/src/com/google/common/io/Io.gwt.xml
index e1f85eb7e..f272c7c43 100644
--- a/guava-gwt/test/com/google/common/testing/Testing.gwt.xml
+++ b/guava-gwt/src/com/google/common/io/Io.gwt.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
<source path=""/>
@@ -9,10 +10,8 @@
<inherits name="com.google.common.base.Base"/>
- <inherits name="com.google.common.collect.Collect"/>
+ <inherits name="com.google.common.math.Math"/>
- <inherits name="com.google.gwt.junit.JUnit"/>
+ <inherits name="com.google.common.primitives.Primitives"/>
- <inherits name="java.util.Util"/>
-
</module>
diff --git a/guava-gwt/src/com/google/common/math/Math.gwt.xml b/guava-gwt/src/com/google/common/math/Math.gwt.xml
index 1ccb925bc..a87feb117 100644
--- a/guava-gwt/src/com/google/common/math/Math.gwt.xml
+++ b/guava-gwt/src/com/google/common/math/Math.gwt.xml
@@ -1,6 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
+
<source path=""/>
+
<super-source path="super"/>
+
<inherits name="com.google.common.annotations.Annotations"/>
+
<inherits name="com.google.common.base.Base"/>
+
</module>
diff --git a/guava-gwt/src/com/google/common/net/Net.gwt.xml b/guava-gwt/src/com/google/common/net/Net.gwt.xml
index 6df61fba6..b6f5049b3 100644
--- a/guava-gwt/src/com/google/common/net/Net.gwt.xml
+++ b/guava-gwt/src/com/google/common/net/Net.gwt.xml
@@ -1,20 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
<source path=""/>
<super-source path="super"/>
+
+ <inherits name="java.nio.charset.Charset"/>
+
+ <inherits name="java.util.Util"/>
<inherits name="com.google.common.annotations.Annotations"/>
<inherits name="com.google.common.base.Base"/>
<inherits name="com.google.common.collect.Collect"/>
-
- <inherits name="com.google.common.escape.Escape"/>
-
- <inherits name="com.google.common.primitives.Primitives"/>
-
- <inherits name="java.util.Util"/>
-
+
</module>
diff --git a/guava-gwt/src/com/google/common/primitives/Primitives.gwt.xml b/guava-gwt/src/com/google/common/primitives/Primitives.gwt.xml
index fa86e6bd5..3966a5135 100644
--- a/guava-gwt/src/com/google/common/primitives/Primitives.gwt.xml
+++ b/guava-gwt/src/com/google/common/primitives/Primitives.gwt.xml
@@ -1,6 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
- <source path=""/>
- <super-source path="super"/>
- <inherits name="com.google.common.annotations.Annotations"/>
- <inherits name="com.google.common.base.Base"/>
+
+ <source path=""/>
+
+ <super-source path="super"/>
+
+ <inherits name="com.google.common.annotations.Annotations"/>
+
+ <inherits name="com.google.common.base.Base"/>
+
</module>
diff --git a/guava-gwt/src/com/google/common/primitives/UnsignedLong_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/primitives/UnsignedLong_CustomFieldSerializer.java
new file mode 100644
index 000000000..732730ef0
--- /dev/null
+++ b/guava-gwt/src/com/google/common/primitives/UnsignedLong_CustomFieldSerializer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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 com.google.common.primitives;
+
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * This class implements the GWT serialization of {@code UnsignedLong}.
+ *
+ * @author Louis Wasserman
+ */
+public class UnsignedLong_CustomFieldSerializer {
+ public static void deserialize(SerializationStreamReader reader,
+ UnsignedLong instance) {}
+
+ public static UnsignedLong instantiate(SerializationStreamReader reader)
+ throws SerializationException {
+ return UnsignedLong.fromLongBits(reader.readLong());
+ }
+
+ public static void serialize(SerializationStreamWriter writer,
+ UnsignedLong instance) throws SerializationException {
+ writer.writeLong(instance.longValue());
+ }
+}
diff --git a/guava-gwt/src/com/google/common/util/concurrent/Concurrent.gwt.xml b/guava-gwt/src/com/google/common/util/concurrent/Concurrent.gwt.xml
index cc8ffd613..1bb0da018 100644
--- a/guava-gwt/src/com/google/common/util/concurrent/Concurrent.gwt.xml
+++ b/guava-gwt/src/com/google/common/util/concurrent/Concurrent.gwt.xml
@@ -1,4 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- semi-autogenerated module descriptor -->
<module>
+
<source path=""/>
+
<inherits name="com.google.common.annotations.Annotations"/>
+
+ <inherits name="com.google.common.base.Base"/>
+
+ <inherits name="com.google.common.collect.Collect"/>
+
</module>
diff --git a/guava-gwt/test/com/google/common/testing/TestModuleEntryPoint.java b/guava-gwt/test/com/google/common/cache/TestModuleEntryPoint.java
index ed6e199c6..baf8478c0 100644
--- a/guava-gwt/test/com/google/common/testing/TestModuleEntryPoint.java
+++ b/guava-gwt/test/com/google/common/cache/TestModuleEntryPoint.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.common.testing;
+package com.google.common.cache;
import com.google.gwt.core.client.EntryPoint;
diff --git a/guava-gwt/test/com/google/common/cache/testModule.gwt.xml b/guava-gwt/test/com/google/common/cache/testModule.gwt.xml
new file mode 100644
index 000000000..7de886bf2
--- /dev/null
+++ b/guava-gwt/test/com/google/common/cache/testModule.gwt.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.junit.JUnit"/>
+ <inherits name="com.google.common.annotations.Annotations"/>
+ <inherits name="com.google.common.base.Base"/>
+ <inherits name="com.google.common.cache.Cache"/>
+ <inherits name="com.google.common.collect.Collect"/>
+ <inherits name="com.google.common.testing.Testing"/>
+ <inherits name="com.google.common.util.concurrent.Concurrent"/>
+ <inherits name="org.junit.contrib.truth.Truth"/>
+ <entry-point class="com.google.common.cache.TestModuleEntryPoint"/>
+
+ <source path=""/>
+
+ <add-linker name="std"/>
+
+ <super-source path="super"/>
+
+</module>
diff --git a/guava-gwt/test/com/google/common/collect/testModule.gwt.xml b/guava-gwt/test/com/google/common/collect/testModule.gwt.xml
index 47b2f235b..72ee8bf78 100644
--- a/guava-gwt/test/com/google/common/collect/testModule.gwt.xml
+++ b/guava-gwt/test/com/google/common/collect/testModule.gwt.xml
@@ -4,9 +4,10 @@
<inherits name="com.google.gwt.junit.JUnit"/>
<inherits name="com.google.common.base.Base"/>
<inherits name="com.google.common.collect.Collect"/>
+ <inherits name="com.google.common.collect.testing.Testing"/>
+ <inherits name="com.google.common.collect.testing.google.Google"/>
<inherits name="com.google.common.testing.Testing"/>
<inherits name="org.junit.contrib.truth.Truth"/>
- <inherits name="com.google.common.annotations.Annotations"/>
<entry-point class="com.google.common.collect.TestModuleEntryPoint"/>
<source path=""/>
diff --git a/guava-gwt/test/com/google/common/io/TestModuleEntryPoint.java b/guava-gwt/test/com/google/common/io/TestModuleEntryPoint.java
new file mode 100644
index 000000000..e06885af6
--- /dev/null
+++ b/guava-gwt/test/com/google/common/io/TestModuleEntryPoint.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Guava Authors
+ *
+ * Licensed 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 com.google.common.io;
+
+import com.google.gwt.core.client.EntryPoint;
+
+/**
+ * A dummy entry point of the test module.
+ *
+ * @author Hayward Chan
+ */
+public class TestModuleEntryPoint implements EntryPoint {
+
+ @Override public void onModuleLoad() {
+ }
+}
diff --git a/guava-gwt/test/com/google/common/io/testModule.gwt.xml b/guava-gwt/test/com/google/common/io/testModule.gwt.xml
new file mode 100644
index 000000000..3611690f9
--- /dev/null
+++ b/guava-gwt/test/com/google/common/io/testModule.gwt.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.junit.JUnit"/>
+ <inherits name="com.google.common.annotations.Annotations"/>
+ <inherits name="com.google.common.base.Base"/>
+ <inherits name="com.google.common.collect.Collect"/>
+ <inherits name="com.google.common.io.Io"/>
+ <inherits name="com.google.common.math.Math"/>
+ <entry-point class="com.google.common.io.TestModuleEntryPoint"/>
+
+ <source path=""/>
+
+ <add-linker name="std"/>
+
+ <super-source path="super"/>
+
+</module>
diff --git a/guava-gwt/test/com/google/common/math/testModule.gwt.xml b/guava-gwt/test/com/google/common/math/testModule.gwt.xml
index 17f579d38..58552f16d 100644
--- a/guava-gwt/test/com/google/common/math/testModule.gwt.xml
+++ b/guava-gwt/test/com/google/common/math/testModule.gwt.xml
@@ -6,8 +6,8 @@
<inherits name="com.google.common.collect.Collect"/>
<inherits name="com.google.common.math.Math"/>
<inherits name="com.google.common.testing.Testing"/>
- <inherits name="com.google.common.annotations.Annotations"/>
<inherits name="org.junit.contrib.truth.Truth"/>
+ <inherits name="com.google.common.annotations.Annotations"/>
<entry-point class="com.google.common.math.TestModuleEntryPoint"/>
<source path=""/>
diff --git a/guava-gwt/test/com/google/common/net/testModule.gwt.xml b/guava-gwt/test/com/google/common/net/testModule.gwt.xml
index 0e6338b44..1b61e1bc6 100644
--- a/guava-gwt/test/com/google/common/net/testModule.gwt.xml
+++ b/guava-gwt/test/com/google/common/net/testModule.gwt.xml
@@ -6,7 +6,6 @@
<inherits name="com.google.common.net.Net"/>
<inherits name="com.google.common.testing.Testing"/>
<inherits name="org.junit.contrib.truth.Truth"/>
- <inherits name="com.google.common.annotations.Annotations"/>
<entry-point class="com.google.common.net.TestModuleEntryPoint"/>
<source path=""/>
diff --git a/guava-gwt/test/com/google/common/primitives/testModule.gwt.xml b/guava-gwt/test/com/google/common/primitives/testModule.gwt.xml
index 7bb40136c..72da4d2c2 100644
--- a/guava-gwt/test/com/google/common/primitives/testModule.gwt.xml
+++ b/guava-gwt/test/com/google/common/primitives/testModule.gwt.xml
@@ -2,10 +2,10 @@
<module>
<inherits name="com.google.gwt.user.User"/>
<inherits name="com.google.gwt.junit.JUnit"/>
+ <inherits name="com.google.common.collect.testing.Testing"/>
<inherits name="com.google.common.primitives.Primitives"/>
<inherits name="com.google.common.testing.Testing"/>
<inherits name="org.junit.contrib.truth.Truth"/>
- <inherits name="com.google.common.annotations.Annotations"/>
<entry-point class="com.google.common.primitives.TestModuleEntryPoint"/>
<source path=""/>
diff --git a/guava-gwt/test/com/google/common/util/concurrent/TestModuleEntryPoint.java b/guava-gwt/test/com/google/common/util/concurrent/TestModuleEntryPoint.java
new file mode 100644
index 000000000..7ed972168
--- /dev/null
+++ b/guava-gwt/test/com/google/common/util/concurrent/TestModuleEntryPoint.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 The Guava Authors
+ *
+ * Licensed 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 com.google.common.util.concurrent;
+
+import com.google.gwt.core.client.EntryPoint;
+
+/**
+ * A dummy entry point of the test module.
+ *
+ * @author Hayward Chan
+ */
+public class TestModuleEntryPoint implements EntryPoint {
+
+ @Override public void onModuleLoad() {
+ }
+}
diff --git a/guava-gwt/test/com/google/common/testing/testModule.gwt.xml b/guava-gwt/test/com/google/common/util/concurrent/testModule.gwt.xml
index 2015440ea..ffae2990c 100644
--- a/guava-gwt/test/com/google/common/testing/testModule.gwt.xml
+++ b/guava-gwt/test/com/google/common/util/concurrent/testModule.gwt.xml
@@ -2,9 +2,11 @@
<module>
<inherits name="com.google.gwt.user.User"/>
<inherits name="com.google.gwt.junit.JUnit"/>
+ <inherits name="com.google.common.annotations.Annotations"/>
+ <inherits name="com.google.common.base.Base"/>
<inherits name="com.google.common.collect.Collect"/>
- <inherits name="com.google.common.testing.Testing"/>
- <entry-point class="com.google.common.testing.TestModuleEntryPoint"/>
+ <inherits name="com.google.common.util.concurrent.Concurrent"/>
+ <entry-point class="com.google.common.util.concurrent.TestModuleEntryPoint"/>
<source path=""/>