diff options
Diffstat (limited to 'guava-gwt')
146 files changed, 3459 insertions, 15211 deletions
diff --git a/guava-gwt/pom.xml b/guava-gwt/pom.xml index 6b44fac..f087b3d 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>14.0.1</version> + <version>11.0.2</version> </parent> <artifactId>guava-gwt</artifactId> <name>Guava GWT compatible libs</name> @@ -16,89 +16,53 @@ 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.code.findbugs</groupId> - <artifactId>jsr305</artifactId> - <version>1.3.9</version> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>${project.version}</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>${gwt.version}</version> + <version>2.2.0</version> + <type>jar</type> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.gwt</groupId> <artifactId>gwt-user</artifactId> - <version>${gwt.version}</version> + <version>2.2.0</version> + <type>jar</type> <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.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> + <source>1.5</source> + <target>1.5</target> </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> @@ -111,6 +75,7 @@ <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> @@ -118,16 +83,9 @@ </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> @@ -151,27 +109,9 @@ </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>test</testSourceDirectory> + <testSourceDirectory>disabled</testSourceDirectory> <resources> <resource> <directory>src</directory> @@ -183,10 +123,5 @@ <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 deleted file mode 100644 index ed30766..0000000 --- a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/CharMatcher.java +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * 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 deleted file mode 100644 index 4c080be..0000000 --- a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Charsets.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 deleted file mode 100644 index 61dd6e1..0000000 --- a/guava-gwt/src-super/com/google/common/base/super/com/google/common/base/Enums.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 04f088e..366ca0d 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,10 +34,6 @@ 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) */ @@ -230,7 +226,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; @@ -245,7 +241,7 @@ public final class Predicates { this.predicate = checkNotNull(predicate); } @Override - public boolean apply(@Nullable T t) { + public boolean apply(T t) { return !predicate.apply(t); } @Override public int hashCode() { @@ -274,8 +270,7 @@ public final class Predicates { this.components = components; } @Override - public boolean apply(@Nullable T t) { - // Avoid using the Iterator to avoid generating garbage (issue 820). + public boolean apply(T t) { for (int i = 0; i < components.size(); i++) { if (!components.get(i).apply(t)) { return false; @@ -284,7 +279,7 @@ public final class Predicates { return true; } @Override public int hashCode() { - // add a random number to avoid collisions with OrPredicate + // 0x12472c2c is a random number to help avoid collisions with OrPredicate return components.hashCode() + 0x12472c2c; } @Override public boolean equals(@Nullable Object obj) { @@ -308,8 +303,7 @@ public final class Predicates { this.components = components; } @Override - public boolean apply(@Nullable T t) { - // Avoid using the Iterator to avoid generating garbage (issue 820). + public boolean apply(T t) { for (int i = 0; i < components.size(); i++) { if (components.get(i).apply(t)) { return true; @@ -318,7 +312,7 @@ public final class Predicates { return false; } @Override public int hashCode() { - // add a random number to avoid collisions with AndPredicate + // 0x053c91cf is a random number to help avoid collisions with AndPredicate return components.hashCode() + 0x053c91cf; } @Override public boolean equals(@Nullable Object obj) { @@ -371,7 +365,7 @@ public final class Predicates { } @Override - public boolean apply(@Nullable T t) { + public boolean apply(T t) { try { return target.contains(t); } catch (NullPointerException e) { @@ -411,7 +405,7 @@ public final class Predicates { } @Override - public boolean apply(@Nullable A a) { + public boolean apply(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 458b5ad..142fbec 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,63 +30,60 @@ import java.util.Map; import javax.annotation.CheckReturnValue; /** - * 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}. + * 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}. * - * <p>For example, this expression: <pre> {@code + * <p>Here is the most basic example of {@code Splitter} usage: <pre> {@code * - * Splitter.on(',').split("foo,bar,qux")}</pre> + * Splitter.on(',').split("foo,bar")}</pre> * - * ... produces an {@code Iterable} containing {@code "foo"}, {@code "bar"} and - * {@code "qux"}, in that order. + * This invocation returns an {@code Iterable<String>} containing {@code "foo"} + * and {@code "bar"}, in that order. * - * <p>By default, {@code Splitter}'s behavior is simplistic and unassuming. The - * following expression: <pre> {@code + * <p>By default {@code Splitter}'s behavior is very simplistic: <pre> {@code * - * Splitter.on(',').split(" foo,,, bar ,")}</pre> + * Splitter.on(',').split("foo,,bar, quux")}</pre> * - * ... 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 + * 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 * * private static final Splitter MY_SPLITTER = Splitter.on(',') * .trimResults() * .omitEmptyStrings();}</pre> * - * 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. + * 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. * - * <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 + * <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 * - * // Do NOT do this + * // Bad! Do not do this! * Splitter splitter = Splitter.on('/'); * splitter.trimResults(); // does nothing! * return splitter.split("wrong / wrong / wrong");}</pre> * - * <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). + * 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>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>. + * <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}. * * @author Julien Silland * @author Jesse Wilson @@ -156,8 +153,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")} returns an - * iterable containing {@code ["foo", "bar,baz"]}. + * example, {@code Splitter.on(", ").split("foo, bar, baz,qux")} returns an + * iterable containing {@code ["foo", "bar", "baz,qux"]}. * * @param separator the literal, nonempty string to recognize as a separator * @return a splitter, with default settings, that recognizes that separator @@ -200,18 +197,9 @@ public final class Splitter { * iterable containing {@code ["ab", "cd", "e"]}. The last piece can be * smaller than {@code length} but will never be empty. * - * <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 + * @param length the desired length of pieces after splitting * @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"); @@ -328,12 +316,6 @@ 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(); - } }; } @@ -355,18 +337,6 @@ 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. * @@ -435,7 +405,8 @@ 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; @@ -464,15 +435,8 @@ 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 = nextStart; + int start = offset; int end; int separatorPosition = separatorStart(offset); @@ -483,20 +447,6 @@ 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++; @@ -506,8 +456,6 @@ 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 d591f62..841362a 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.elapsed(MILLISECONDS); + * long millis = stopwatch.{@link #elapsedMillis elapsedMillis}(); * * 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, "ticker"); + this.ticker = checkNotNull(ticker); } /** @@ -107,8 +107,7 @@ public final class Stopwatch { * @throws IllegalStateException if the stopwatch is already running. */ public Stopwatch start() { - checkState(!isRunning, - "This stopwatch is already running; it cannot be started more than once."); + checkState(!isRunning); isRunning = true; startTick = ticker.read(); return this; @@ -123,8 +122,7 @@ public final class Stopwatch { */ public Stopwatch stop() { long tick = ticker.read(); - checkState(isRunning, - "This stopwatch is already stopped; it cannot be stopped more than once."); + checkState(isRunning); isRunning = false; elapsedNanos += tick - startTick; return this; @@ -153,40 +151,18 @@ 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 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); + return desiredUnit.convert(elapsedNanos(), NANOSECONDS); } /** * Returns the current elapsed time shown on this stopwatch, expressed * in milliseconds, with any fraction rounded down. This is identical to - * {@code elapsed(TimeUnit.MILLISECONDS)}. - * - * @deprecated Use {@code stopwatch.elapsed(MILLISECONDS)} instead. This - * method is scheduled to be removed in Guava release 16.0. + * {@code elapsedTime(TimeUnit.MILLISECONDS}. */ - @Deprecated public long elapsedMillis() { - return elapsed(MILLISECONDS); + return elapsedTime(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 8b01a0e..4c30815 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) 2009 The Guava Authors + * 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. @@ -16,251 +16,48 @@ 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.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 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 java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; /** - * <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. + * CacheBuilder emulation. * - * <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 */ -@GwtCompatible(emulated = true) -public final class CacheBuilder<K, V> { +// TODO(fry): eventually we should emmulate LocalCache instead of CacheBuilder +public class CacheBuilder<K, V> { + private static final int UNSET_INT = -1; 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) {} - @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); + private int initialCapacity = -1; + private int concurrencyLevel = -1; + private long expirationMillis = -1; + private int maximumSize = -1; - 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); @@ -269,354 +66,258 @@ public final class CacheBuilder<K, V> { return this; } - int getInitialCapacity() { + private 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; } - int getConcurrencyLevel() { - return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel; + 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; } - /** - * 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; + private long getExpirationMillis() { + return (expirationMillis == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expirationMillis; } - long getMaximumWeight() { - if (expireAfterWriteNanos == 0 || expireAfterAccessNanos == 0) { - return 0; + 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); } - return (weigher == null) ? maximumSize : maximumWeight; + this.maximumSize = maximumSize; + return 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> Cache<K1, V1> build() { + return new LocalManualCache<K1, V1>(this); } - CacheBuilder<K, V> setKeyStrength(Strength strength) { - checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); - keyStrength = checkNotNull(strength); - return this; + public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build( + CacheLoader<? super K1, V1> loader) { + return new LocalLoadingCache<K1, V1>(this, loader); } - Strength getKeyStrength() { - return firstNonNull(keyStrength, Strength.STRONG); - } + private static class LocalManualCache<K, V> + extends AbstractCache<K, V> implements Function<K, V> { + final LocalCache<K, V> localCache; - CacheBuilder<K, V> setValueStrength(Strength strength) { - checkState(valueStrength == null, "Value strength was already set to %s", valueStrength); - valueStrength = checkNotNull(strength); - return this; - } + LocalManualCache(CacheBuilder<? super K, ? super V> builder) { + this(builder, null); + } - Strength getValueStrength() { - return firstNonNull(valueStrength, Strength.STRONG); - } + protected LocalManualCache(CacheBuilder<? super K, ? super V> builder, + CacheLoader<? super K, V> loader) { + this.localCache = new LocalCache<K, V>(builder, loader); + } - /** - * 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; - } + // Cache methods - long getExpireAfterWriteNanos() { - return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos; - } + @Override + public V get(K key) throws ExecutionException { + return localCache.getOrLoad(key); + } - /** - * 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 V getUnchecked(K key) { + try { + return get(key); + } catch (ExecutionException e) { + throw new UncheckedExecutionException(e.getCause()); + } + } - long getExpireAfterAccessNanos() { - return (expireAfterAccessNanos == UNSET_INT) - ? DEFAULT_EXPIRATION_NANOS : expireAfterAccessNanos; - } + @Override + public final V apply(K key) { + return getUnchecked(key); + } - long getRefreshNanos() { - return (refreshNanos == UNSET_INT) ? DEFAULT_REFRESH_NANOS : refreshNanos; - } + @Override + @Nullable + public V getIfPresent(K key) { + return localCache.get(key); + } - /** - * 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 put(K key, V value) { + localCache.put(key, value); + } - Ticker getTicker(boolean recordsTime) { - if (ticker != null) { - return ticker; + @Override + public void invalidate(Object key) { + checkNotNull(key); + localCache.remove(key); } - return recordsTime ? Ticker.systemTicker() : NULL_TICKER; - } - /** - * 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 void invalidateAll() { + localCache.clear(); + } - // 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 long size() { + return localCache.size(); + } - /** - * 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; + @Override + public ConcurrentMap<K, V> asMap() { + return localCache; + } } - Supplier<? extends StatsCounter> getStatsCounterSupplier() { - return statsCounterSupplier; - } + private static class LocalLoadingCache<K, V> + extends LocalManualCache<K, V> implements LoadingCache<K, V> { - /** - * 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); - } + LocalLoadingCache(CacheBuilder<? super K, ? super V> builder, + CacheLoader<? super K, V> loader) { + super(builder, checkNotNull(loader)); + } - /** - * 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); - } + // Cache methods - private void checkNonLoadingCache() { - checkState(refreshNanos == UNSET_INT, "refreshAfterWrite requires a LoadingCache"); - } + @Override + public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException { + 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"); - } - } + @Override + public void refresh(K key) { + throw new UnsupportedOperationException(); } } - /** - * 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); + // 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; } - if (concurrencyLevel != UNSET_INT) { - s.add("concurrencyLevel", concurrencyLevel); - } - if (maximumSize != UNSET_INT) { - s.add("maximumSize", maximumSize); + + @Override + public V put(K key, V value) { + V result = super.put(key, value); + if (expirationMillis > 0) { + scheduleRemoval(key, value); + } + return result; } - if (maximumWeight != UNSET_INT) { - s.add("maximumWeight", maximumWeight); + + @Override + protected boolean removeEldestEntry(Map.Entry<K, V> ignored) { + return (maximumSize == -1) ? false : size() > maximumSize; } - if (expireAfterWriteNanos != UNSET_INT) { - s.add("expireAfterWrite", expireAfterWriteNanos + "ns"); + + @Override + public V putIfAbsent(K key, V value) { + if (!containsKey(key)) { + return put(key, value); + } else { + return get(key); + } } - if (expireAfterAccessNanos != UNSET_INT) { - s.add("expireAfterAccess", expireAfterAccessNanos + "ns"); + + @Override + public boolean remove(Object key, Object value) { + if (containsKey(key) && get(key).equals(value)) { + remove(key); + return true; + } + return false; } - if (keyStrength != null) { - s.add("keyStrength", Ascii.toLowerCase(keyStrength.toString())); + + @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 (valueStrength != null) { - s.add("valueStrength", Ascii.toLowerCase(valueStrength.toString())); + + @Override + public V replace(K key, V value) { + return containsKey(key) ? put(key, value) : null; } - if (keyEquivalence != null) { - s.addValue("keyEquivalence"); + + 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 (valueEquivalence != null) { - s.addValue("valueEquivalence"); + + 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 (removalListener != null) { - s.addValue("removalListener"); + + 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; } - 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 c345553..c0815dd 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,7 +18,6 @@ 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; @@ -27,20 +26,11 @@ import java.io.Serializable; import java.util.Map; /** - * Computes or retrieves values, based on a key, for use in populating a {@link LoadingCache}. + * Computes or retrieves values, based on a key, for use in populating a {@code Cache}. * * <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 */ @@ -56,16 +46,12 @@ 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 LoadingCache#getAll}. + * {@link Cache#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 @@ -73,33 +59,22 @@ 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 LoadingCache#getAll} will defer to individual calls - * to {@link LoadingCache#get} if this method is not overriden. + * many individual lookups. Note that {@link Cache#getAll} will defer to individual calls to + * {@link Cache#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 - // LoadingCache.get + // This will be caught by getAll(), causing it to fall back to multiple calls to Cache.get throw new UnsupportedLoadingOperationException(); } /** - * 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} + * Returns a {@code CacheLoader} which creates values by applying a {@code Function} to the key. */ - @Beta public static <K, V> CacheLoader<K, V> from(Function<K, V> function) { return new FunctionToCacheLoader<K, V>(function); } @@ -114,22 +89,16 @@ public abstract class CacheLoader<K, V> { @Override public V load(K key) { - return computingFunction.apply(checkNotNull(key)); + return computingFunction.apply(key); } private static final long serialVersionUID = 0; } /** - * 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 + * Returns a {@code CacheLoader} which obtains values from a {@code Supplier} (independent of the + * key). */ - @Beta public static <V> CacheLoader<Object, V> from(Supplier<V> supplier) { return new SupplierToCacheLoader<V>(supplier); } @@ -144,7 +113,6 @@ 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 deleted file mode 100644 index 44528bf..0000000 --- a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LocalCache.java +++ /dev/null @@ -1,829 +0,0 @@ -/* - * 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/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 deleted file mode 100644 index 34b6f37..0000000 --- a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAdder.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 7c882fe..a6cec16 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; - transient AbstractBiMap<V, K> inverse; + private transient AbstractBiMap<V, K> inverse; /** Package-private constructor for creating a map-backed bimap. */ AbstractBiMap(Map<K, V> forward, Map<V, K> backward) { @@ -63,20 +63,6 @@ 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. */ @@ -96,24 +82,22 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> // Query Operations (optimizations) - @Override public boolean containsValue(@Nullable Object value) { + @Override public boolean containsValue(Object value) { return inverse.containsKey(value); } // Modification Operations - @Override public V put(@Nullable K key, @Nullable V value) { + @Override public V put(K key, V value) { return putInBothMaps(key, value, false); } @Override - public V forcePut(@Nullable K key, @Nullable V value) { + public V forcePut(K key, 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; @@ -136,7 +120,7 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> inverse.delegate.put(newValue, key); } - @Override public V remove(@Nullable Object key) { + @Override public V remove(Object key) { return containsKey(key) ? removeFromBothMaps(key) : null; } @@ -203,7 +187,27 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> } @Override public Iterator<K> iterator() { - return Maps.keyIterator(entrySet().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); + } + }; } } @@ -226,7 +230,23 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> } @Override public Iterator<V> iterator() { - return Maps.valueIterator(entrySet().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); + } + }; } @Override public Object[] toArray() { @@ -358,16 +378,6 @@ 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 deleted file mode 100644 index 2cfb3ef..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractMapBasedMultimap.java +++ /dev/null @@ -1,1259 +0,0 @@ -/* - * 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 3c0f05d..6bccc6d 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,6 +25,7 @@ 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; @@ -34,7 +35,7 @@ import javax.annotation.Nullable; /** * Basic implementation of {@code Multiset<E>} backed by an instance of {@code - * Map<E, Count>}. + * Map<E, AtomicInteger>}. * * <p>For serialization to work, the subclass must specify explicit {@code * readObject} and {@code writeObject} methods. @@ -60,6 +61,10 @@ 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; @@ -116,7 +121,8 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> @Override public void remove() { - Iterators.checkRemove(toRemove != null); + checkState(toRemove != null, + "no calls to next() since the last call to remove()"); size -= toRemove.getValue().getAndSet(0); backingEntries.remove(); toRemove = null; @@ -150,7 +156,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> /* * Not subclassing AbstractMultiset$MultisetIterator because next() needs to - * retrieve the Map.Entry<E, Count> entry, which can then be used for + * retrieve the Map.Entry<E, AtomicInteger> entry, which can then be used for * a more efficient remove() call. */ private class MapBasedMultisetIterator implements Iterator<E> { @@ -196,8 +202,14 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> } @Override public int count(@Nullable Object element) { - Count frequency = Maps.safeGet(backingMap, element); - return (frequency == null) ? 0 : frequency.get(); + try { + Count frequency = backingMap.get(element); + return (frequency == null) ? 0 : frequency.get(); + } catch (NullPointerException e) { + return 0; + } catch (ClassCastException e) { + return 0; + } } // Optional Operations - Modification Operations @@ -258,7 +270,7 @@ abstract class AbstractMapBasedMultiset<E> extends AbstractMultiset<E> } // Roughly a 33% performance improvement over AbstractMultiset.setCount(). - @Override public int setCount(@Nullable E element, int count) { + @Override public int setCount(E element, int count) { checkNonnegative(count, "count"); Count existingCounter; @@ -287,6 +299,97 @@ 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 deleted file mode 100644 index 3eea734..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/AbstractSortedMultiset.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 59d4e77..da5478c 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,10 +50,6 @@ 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) @@ -61,7 +57,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 = 3; + private static final int DEFAULT_VALUES_PER_KEY = 10; @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 deleted file mode 100644 index bde92c5..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ArrayTable.java +++ /dev/null @@ -1,817 +0,0 @@ -/* - * 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 deleted file mode 100644 index 5cebc98..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ContiguousSet.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 deleted file mode 100644 index a708d9c..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/DescendingMultiset.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 368e856..f8fbe59 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,6 +69,11 @@ 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/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 24b1151..435fc26 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,25 +13,14 @@ * 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 ForwardingImmutableList<Object> { - static final EmptyImmutableList INSTANCE = new EmptyImmutableList(); - - private EmptyImmutableList() { - } +final class EmptyImmutableList extends ImmutableList<Object> { - @Override List<Object> delegateList() { - return emptyList(); - } + static final EmptyImmutableList INSTANCE = new EmptyImmutableList(); } diff --git a/guava-gwt/src/com/google/common/collect/ForwardingImmutableSet_CustomFieldSerializer.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableMap.java index b49de22..dc3fecf 100644 --- a/guava-gwt/src/com/google/common/collect/ForwardingImmutableSet_CustomFieldSerializer.java +++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableMap.java @@ -17,9 +17,12 @@ package com.google.common.collect; /** - * Even though {@link ForwardingImmutableSet} cannot be instantiated, we still - * need a custom field serializer. TODO(cpovirk): why? + * GWT emulation of {@link EmptyImmutableMap}. In GWT, it is a thin wrapper + * around {@link java.util.Collections#emptyMap()}. * * @author Hayward Chan */ -public final class ForwardingImmutableSet_CustomFieldSerializer {} +final class EmptyImmutableMap extends ImmutableMap<Object, Object> { + + static final EmptyImmutableMap INSTANCE = new EmptyImmutableMap(); +} 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 ae3b3f3..6166c36 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,17 +16,12 @@ package com.google.common.collect; -import java.util.Collections; - /** * GWT emulation of {@link EmptyImmutableSet}. * * @author Hayward Chan */ -final class EmptyImmutableSet extends ForwardingImmutableSet<Object> { - private EmptyImmutableSet() { - super(Collections.emptySet()); - } +final class EmptyImmutableSet extends ImmutableSet<Object> { 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 deleted file mode 100644 index 4159cd4..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableSortedMap.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 ff45a39..b21896b 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(Sets.newTreeSet(comparator)); + super(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 46fa641..f6d7e90 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,7 +17,6 @@ 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; @@ -28,10 +27,6 @@ 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) @@ -106,15 +101,5 @@ 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 3d9503f..dce8d66 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,8 +16,6 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.GwtCompatible; import java.util.EnumMap; @@ -30,10 +28,6 @@ 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) @@ -78,12 +72,7 @@ public final class EnumHashBiMap<K extends Enum<K>, V> this.keyType = keyType; } - // Overriding these 3 methods to show that values may be null (but not keys) - - @Override - K checkKey(K key) { - return checkNotNull(key); - } + // Overriding these two methods to show that values may be null (but not keys) @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 4e769fb..589706b 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,10 +23,6 @@ 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) @@ -54,19 +50,6 @@ 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 deleted file mode 100644 index 7c08944..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/FluentIterable.java +++ /dev/null @@ -1,508 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1327ba0..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableList.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 deleted file mode 100644 index 9ef0d96..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableMap.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6db3339..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ForwardingImmutableSet.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 b3b4319..b75f4bf 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,11 +52,28 @@ 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 10e4e35..15c3f9e 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,16 +16,22 @@ package com.google.common.collect; +import com.google.common.annotations.GwtCompatible; + import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; /** - * GWT emulation of {@code HashBiMap} that just delegates to two HashMaps. + * 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. * * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) */ +@GwtCompatible(emulated = true) public final class HashBiMap<K, V> extends AbstractBiMap<K, V> { /** @@ -79,3 +85,4 @@ 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 582c2f7..55ecd46 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 = 2; + private static final int DEFAULT_VALUES_PER_KEY = 8; @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 1279bee..a790487 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) 2009 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. @@ -16,39 +16,15 @@ package com.google.common.collect; -import com.google.common.annotations.GwtCompatible; +import java.util.List; /** - * List returned by {@link ImmutableCollection#asList} that delegates {@code contains} checks - * to the backing collection. + * GWT emulation of {@link ImmutableAsList}. * - * @author Jared Levy - * @author Louis Wasserman + * @author Hayward Chan */ -@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(); +final class ImmutableAsList<E> extends RegularImmutableList<E> { + ImmutableAsList(List<E> delegate) { + super(delegate); } } - diff --git a/guava-gwt/src/com/google/common/collect/RegularImmutableAsList_CustomFieldSerializer.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList_CustomFieldSerializer.java index f41b8cf..1712f5a 100644 --- a/guava-gwt/src/com/google/common/collect/RegularImmutableAsList_CustomFieldSerializer.java +++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableAsList_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 server-side GWT serialization of - * {@link RegularImmutableAsList}. + * This class implements the client-side GWT serialization of + * {@link ImmutableAsList}. * * @author Hayward Chan */ -@GwtCompatible(emulated = true) -public class RegularImmutableAsList_CustomFieldSerializer { +public class ImmutableAsList_CustomFieldSerializer { public static void deserialize(SerializationStreamReader reader, - RegularImmutableAsList<?> instance) { + RegularImmutableList<?> instance) { } - public static RegularImmutableAsList<Object> instantiate( + public static ImmutableAsList<Object> instantiate( SerializationStreamReader reader) throws SerializationException { - @SuppressWarnings("unchecked") // serialization is necessarily type unsafe - ImmutableCollection<Object> delegateCollection = (ImmutableCollection) reader.readObject(); - ImmutableList<?> delegateList = (ImmutableList<?>) reader.readObject(); - return new RegularImmutableAsList<Object>(delegateCollection, delegateList); + List<Object> elements = new ArrayList<Object>(); + Collection_CustomFieldSerializerBase.deserialize(reader, elements); + return new ImmutableAsList<Object>(elements); } public static void serialize(SerializationStreamWriter writer, - RegularImmutableAsList<?> instance) throws SerializationException { - writer.writeObject(instance.delegateCollection()); - writer.writeObject(instance.delegateList()); + ImmutableAsList<?> instance) throws SerializationException { + Collection_CustomFieldSerializerBase.serialize(writer, instance); } } 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 c8cdebb..59cb788 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,8 +16,7 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - +import java.util.Collections; import java.util.Map; /** @@ -25,18 +24,20 @@ import java.util.Map; * * @author Hayward Chan */ -public abstract class ImmutableBiMap<K, V> extends ForwardingImmutableMap<K, V> +public abstract class ImmutableBiMap<K, V> extends ImmutableMap<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>) EmptyImmutableBiMap.INSTANCE; + return (ImmutableBiMap<K, V>) EMPTY_IMMUTABLE_BIMAP; } public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1) { - return new SingletonImmutableBiMap<K, V>( - checkNotNull(k1), checkNotNull(v1)); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of(k1, v1)); } public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1, K k2, V v2) { @@ -117,4 +118,14 @@ public abstract class ImmutableBiMap<K, V> extends ForwardingImmutableMap<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 936f900..04bd060 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,15 +16,16 @@ 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}. * @@ -88,10 +89,6 @@ 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(); } @@ -130,13 +127,15 @@ public abstract class ImmutableCollection<E> case 1: return ImmutableList.of(iterator().next()); default: - return new RegularImmutableAsList<E>(this, toArray()); + @SuppressWarnings("unchecked") + E[] castedArray = (E[]) toArray(); + return new ImmutableAsList<E>(Arrays.asList(castedArray)); } } 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 deleted file mode 100644 index 86f0c26..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableEnumMap.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 a9c21c8..14506c2 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,18 +24,7 @@ import java.util.Set; * * @author Hayward Chan */ -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); - } - } - +final class ImmutableEnumSet<E> extends ImmutableSet<E> { 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 ca5a95a..db67daa 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,20 +26,30 @@ 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 ImmutableCollection<E> +public abstract class ImmutableList<E> extends ForwardingImmutableCollection<E> implements List<E>, RandomAccess { - ImmutableList() {} + + private transient final List<E> delegate; + + ImmutableList(List<E> delegate) { + super(delegate); + this.delegate = Collections.unmodifiableList(delegate); + } + + ImmutableList() { + this(Collections.<E>emptyList()); + } // Casting to any type is safe because the list will never hold any elements. @SuppressWarnings("unchecked") @@ -116,6 +126,19 @@ public abstract class ImmutableList<E> extends ImmutableCollection<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); } @@ -165,7 +188,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } // Factory method that skips the null checks. Used only when the elements - // are guaranteed to be non-null. + // are guaranteed to be null. static <E> ImmutableList<E> unsafeDelegateList(List<? extends E> list) { switch (list.size()) { case 0: @@ -179,14 +202,8 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } } - /** - * 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)); + static <E> ImmutableList<E> backedBy(E[] elements) { + return unsafeDelegateList(Arrays.asList(elements)); } private static <E> List<E> nullCheckedList(Object... array) { @@ -200,14 +217,12 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> return Arrays.asList(castedArray); } - @Override public int indexOf(@Nullable Object object) { - return (object == null) ? -1 : Lists.indexOfImpl(this, object); + return delegate.indexOf(object); } - @Override public int lastIndexOf(@Nullable Object object) { - return (object == null) ? -1 : Lists.lastIndexOfImpl(this, object); + return delegate.lastIndexOf(object); } public final boolean addAll(int index, Collection<? extends E> newElements) { @@ -226,45 +241,44 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> throw new UnsupportedOperationException(); } - @Override public UnmodifiableIterator<E> iterator() { - return listIterator(); + public E get(int index) { + return delegate.get(index); } - @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { - return unsafeDelegateList(Lists.subListImpl(this, fromIndex, toIndex)); + public ImmutableList<E> subList(int fromIndex, int toIndex) { + return unsafeDelegateList(delegate.subList(fromIndex, toIndex)); } - @Override public UnmodifiableListIterator<E> listIterator() { - return listIterator(0); + public ListIterator<E> listIterator() { + return delegate.listIterator(); } - @Override public UnmodifiableListIterator<E> listIterator(int index) { - return new AbstractIndexedListIterator<E>(size(), index) { - @Override - protected E get(int index) { - return ImmutableList.this.get(index); - } - }; + public ListIterator<E> listIterator(int index) { + return delegate.listIterator(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 boolean equals(@Nullable Object obj) { - return Lists.equalsImpl(this, obj); + @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 int hashCode() { - return Lists.hashCodeImpl(this); + @Override public boolean equals(Object obj) { + return delegate.equals(obj); } - public ImmutableList<E> reverse() { - List<E> list = Lists.newArrayList(this); - Collections.reverse(list); - return unsafeDelegateList(list); + @Override public int hashCode() { + return delegate.hashCode(); } public static <E> Builder<E> builder() { @@ -272,15 +286,9 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } public static final class Builder<E> extends ImmutableCollection.Builder<E> { - private final ArrayList<E> contents; + private final ArrayList<E> contents = Lists.newArrayList(); - public Builder() { - contents = Lists.newArrayList(); - } - - Builder(int capacity) { - contents = Lists.newArrayListWithCapacity(capacity); - } + public Builder() {} @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 5803848..fe445d9 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,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.Collection; @@ -40,10 +41,6 @@ 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) */ @@ -195,7 +192,7 @@ public class ImmutableListMultimap<K, V> * * @since 8.0 */ - @Override + @Beta @Override public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { super.orderKeysBy(keyComparator); return this; @@ -206,7 +203,7 @@ public class ImmutableListMultimap<K, V> * * @since 8.0 */ - @Override + @Beta @Override public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { super.orderValuesBy(valueComparator); return this; @@ -286,14 +283,13 @@ 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.0 + * @since 11 */ - @Override + @Beta public ImmutableListMultimap<V, K> inverse() { ImmutableListMultimap<V, K> result = inverse; return (result == null) ? (inverse = invert()) : result; @@ -313,9 +309,8 @@ public class ImmutableListMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableList<V> removeAll(Object key) { + @Override public ImmutableList<V> removeAll(Object key) { throw new UnsupportedOperationException(); } @@ -323,9 +318,8 @@ public class ImmutableListMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableList<V> replaceValues( + @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 410538c..84896b5 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,10 +21,9 @@ 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; @@ -40,15 +39,39 @@ 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() {} + 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); + } + // 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 ImmutableBiMap.of(); + return (ImmutableMap<K, V>) EmptyImmutableMap.INSTANCE; } public static <K, V> ImmutableMap<K, V> of(K k1, V v1) { - return ImmutableBiMap.of(k1, v1); + return new SingletonImmutableMap<K, V>( + checkNotNull(k1), checkNotNull(v1)); } public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) { @@ -94,7 +117,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 @@ -125,7 +148,8 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { return of(); case 1: Entry<K, V> entry = getOnlyElement(entries); - return of(entry.getKey(), entry.getValue()); + return new SingletonImmutableMap<K, V>( + entry.getKey(), entry.getValue()); default: @SuppressWarnings("unchecked") Entry<K, V>[] entryArray @@ -141,16 +165,6 @@ 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(); @@ -170,8 +184,10 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { return new RegularImmutableMap<K, V>(orderPreservingCopy); } } - - abstract boolean isPartialView(); + + boolean isPartialView(){ + return false; + } public final V put(K k, V v) { throw new UnsupportedOperationException(); @@ -189,19 +205,20 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { throw new UnsupportedOperationException(); } - @Override - public boolean isEmpty() { - return size() == 0; + public final boolean isEmpty() { + return delegate.isEmpty(); } - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; + public final boolean containsKey(@Nullable Object key) { + return Maps.safeContainsKey(delegate, key); } - @Override - public boolean containsValue(@Nullable Object value) { - return value != null && Maps.containsValueImpl(this, value); + 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); } private transient ImmutableSet<Entry<K, V>> cachedEntrySet = null; @@ -210,22 +227,41 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { if (cachedEntrySet != null) { return cachedEntrySet; } - return cachedEntrySet = createEntrySet(); + 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; + } + }); } - abstract ImmutableSet<Entry<K, V>> createEntrySet(); - private transient ImmutableSet<K> cachedKeySet = null; public ImmutableSet<K> keySet() { if (cachedKeySet != null) { return cachedKeySet; } - return cachedKeySet = createKeySet(); - } - - ImmutableSet<K> createKeySet() { - return new ImmutableMapKeySet<K, V>(this); + return cachedKeySet = ImmutableSet.unsafeDelegate(delegate.keySet()); } private transient ImmutableCollection<V> cachedValues = null; @@ -234,102 +270,22 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { if (cachedValues != null) { return cachedValues; } - 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; - } - }; + return cachedValues = ImmutableCollection.unsafeDelegate(delegate.values()); } - ImmutableCollection<V> createValues() { - return new ImmutableMapValues<K, V>(this); + public int size() { + return delegate.size(); } @Override public boolean equals(@Nullable Object object) { - return Maps.equalsImpl(this, object); + return delegate.equals(object); } @Override public int 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(); + return delegate.hashCode(); } @Override public String toString() { - return Maps.toStringImpl(this); + return delegate.toString(); } } 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 deleted file mode 100644 index 7a5e580..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapEntrySet.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 deleted file mode 100644 index 78508f4..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapKeySet.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 deleted file mode 100644 index ccf4b7d..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableMapValues.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 f02ea43..a971129 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,9 +29,8 @@ 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.Set; +import java.util.TreeMap; import javax.annotation.Nullable; @@ -53,16 +52,13 @@ 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) -public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> - implements Serializable { +// TODO(user): If BiMultimap graduates from labs, this class should implement it. +public abstract class ImmutableMultimap<K, V> + implements Multimap<K, V>, Serializable { /** Returns an empty multimap. */ public static <K, V> ImmutableMultimap<K, V> of() { @@ -122,7 +118,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * value orderings, allows duplicate values, and performs better than * {@link LinkedListMultimap}. */ - private static class BuilderMultimap<K, V> extends AbstractMapBasedMultimap<K, V> { + private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> { BuilderMultimap() { super(new LinkedHashMap<K, Collection<V>>()); } @@ -133,6 +129,23 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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 @@ -152,7 +165,6 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ public static class Builder<K, V> { Multimap<K, V> builderMultimap = new BuilderMultimap<K, V>(); - Comparator<? super K> keyComparator; Comparator<? super V> valueComparator; /** @@ -227,8 +239,10 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * * @since 8.0 */ + @Beta public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { - this.keyComparator = checkNotNull(keyComparator); + builderMultimap = new SortedKeyBuilderMultimap<K, V>( + checkNotNull(keyComparator), builderMultimap); return this; } @@ -237,6 +251,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * * @since 8.0 */ + @Beta public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { this.valueComparator = checkNotNull(valueComparator); return this; @@ -252,23 +267,6 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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); } } @@ -317,9 +315,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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(); @@ -329,9 +325,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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) { @@ -342,9 +336,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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(); @@ -364,17 +356,16 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> * key-value mapping in the original, the result will have a mapping with * key and value reversed. * - * @since 11.0 + * @since 11 */ + @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(); @@ -384,9 +375,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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(); @@ -396,9 +385,7 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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(); @@ -408,30 +395,65 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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 /** @@ -453,11 +475,8 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> public ImmutableMap<K, Collection<V>> asMap() { return (ImmutableMap) map; } - - @Override - Map<K, Collection<V>> createAsMap() { - throw new AssertionError("should never be called"); - } + + private transient ImmutableCollection<Entry<K, V>> entries; /** * Returns an immutable collection of all key-value pairs in the multimap. Its @@ -466,12 +485,9 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ @Override public ImmutableCollection<Entry<K, V>> entries() { - return (ImmutableCollection<Entry<K, V>>) super.entries(); - } - - @Override - ImmutableCollection<Entry<K, V>> createEntries() { - return new EntryCollection<K, V>(this); + ImmutableCollection<Entry<K, V>> result = entries; + return (result == null) + ? (entries = new EntryCollection<K, V>(this)) : result; } private static class EntryCollection<K, V> @@ -483,7 +499,30 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> } @Override public UnmodifiableIterator<Entry<K, V>> iterator() { - return multimap.entryIterator(); + 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()); + } + }; } @Override boolean isPartialView() { @@ -505,34 +544,8 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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(); - } - @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()); - } - }; - } + private transient ImmutableMultiset<K> keys; /** * Returns a collection, which may contain duplicates, of all keys. The number @@ -542,78 +555,21 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ @Override public ImmutableMultiset<K> keys() { - return (ImmutableMultiset<K>) super.keys(); + ImmutableMultiset<K> result = keys; + return (result == null) ? (keys = createKeys()) : result; } - @Override - ImmutableMultiset<K> createKeys() { - return new Keys(); - } - - @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; + 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(); } + private transient ImmutableCollection<V> values; + /** * Returns an immutable collection of the values in this multimap. Its * iterator traverses the values for the first key, the values for the second @@ -621,12 +577,8 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> */ @Override public ImmutableCollection<V> values() { - return (ImmutableCollection<V>) super.values(); - } - - @Override - ImmutableCollection<V> createValues() { - return new Values<V>(this); + ImmutableCollection<V> result = values; + return (result == null) ? (values = new Values<V>(this)) : result; } private static class Values<V> extends ImmutableCollection<V> { @@ -637,7 +589,18 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<K, V> } @Override public UnmodifiableIterator<V> iterator() { - return Maps.valueIterator(multimap.entries().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(); + } + }; } @Override @@ -654,4 +617,3 @@ public abstract class ImmutableMultimap<K, V> extends AbstractMultimap<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 97d9dd1..26bc57f 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,8 +37,16 @@ import java.util.Set; * @author Hayward Chan */ @SuppressWarnings("serial") // Serialization only done in GWT. -public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements Set<E> { - ImmutableSet() {} +public abstract class ImmutableSet<E> extends ForwardingImmutableCollection<E> + implements Set<E> { + + ImmutableSet(Set<E> delegate) { + super(Collections.unmodifiableSet(delegate)); + } + + ImmutableSet() { + this(Collections.<E>emptySet()); + } // Casting to any type is safe because the set will never hold any elements. @SuppressWarnings({"unchecked"}) @@ -71,7 +79,8 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements } @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); @@ -102,7 +111,8 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements } 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; @@ -165,7 +175,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> implements } @Override public int hashCode() { - return Sets.hashCodeImpl(this); + return delegate.hashCode(); } 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 43e701e..b5eaabf 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,17 +18,15 @@ 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; @@ -48,10 +46,6 @@ 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) */ @@ -146,7 +140,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 AbstractMapBasedMultimap<K, V> { + private static class BuilderMultimap<K, V> extends AbstractMultimap<K, V> { BuilderMultimap() { super(new LinkedHashMap<K, Collection<V>>()); } @@ -157,6 +151,23 @@ 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 @@ -181,7 +192,7 @@ public class ImmutableSetMultimap<K, V> * generated by {@link ImmutableSetMultimap#builder}. */ public Builder() { - builderMultimap = new BuilderMultimap<K, V>(); + builderMultimap = new BuilderMultimap<K, V>(); } /** @@ -230,25 +241,26 @@ public class ImmutableSetMultimap<K, V> * * @since 8.0 */ - @Override + @Beta @Override public Builder<K, V> orderKeysBy(Comparator<? super K> keyComparator) { - this.keyComparator = checkNotNull(keyComparator); + builderMultimap = new SortedKeyBuilderMultimap<K, V>( + checkNotNull(keyComparator), builderMultimap); 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. - @Override + @Beta @Override public Builder<K, V> orderValuesBy(Comparator<? super V> valueComparator) { super.orderValuesBy(valueComparator); return this; @@ -258,23 +270,6 @@ 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); } } @@ -297,7 +292,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) { @@ -323,7 +318,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); @@ -338,10 +333,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); } @@ -370,13 +365,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.0 + * @since 11 */ + @Beta public ImmutableSetMultimap<V, K> inverse() { ImmutableSetMultimap<V, K> result = inverse; return (result == null) ? (inverse = invert()) : result; @@ -396,9 +391,8 @@ public class ImmutableSetMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableSet<V> removeAll(Object key) { + @Override public ImmutableSet<V> removeAll(Object key) { throw new UnsupportedOperationException(); } @@ -406,9 +400,8 @@ public class ImmutableSetMultimap<K, V> * Guaranteed to throw an exception and leave the multimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public ImmutableSet<V> replaceValues( + @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 deleted file mode 100644 index c94e88e..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/ImmutableSortedAsList.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 19139d1..89624d8 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,11 +18,10 @@ 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; @@ -34,49 +33,60 @@ import java.util.SortedMap; * * @author Hayward Chan */ -public abstract class ImmutableSortedMap<K, V> - extends ForwardingImmutableMap<K, V> implements SortedMap<K, V> { +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(); @SuppressWarnings("unchecked") - static final Comparator NATURAL_ORDER = Ordering.natural(); + private static final ImmutableSortedMap<Object, Object> NATURAL_EMPTY_MAP + = create(NATURAL_ORDER); // This reference is only used by GWT compiler to infer the keys and values // of the map that needs to be serialized. - private Comparator<? super K> unusedComparatorForSerialization; + private Comparator<K> unusedComparatorForSerialization; private K unusedKeyForSerialization; private V unusedValueForSerialization; - private final transient SortedMap<K, V> sortedDelegate; + private transient final 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(). - // (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; + private transient final Comparator<K> comparator; - ImmutableSortedMap(SortedMap<K, V> delegate, 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) { super(delegate); - this.comparator = comparator; - this.sortedDelegate = delegate; + this.comparator = (delegate.comparator() == null) + ? NATURAL_ORDER : delegate.comparator(); + this.sortedDelegate = Collections.unmodifiableSortedMap(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 = newModifiableDelegate(comparator); + SortedMap<K, V> delegate = Maps.newTreeMap(comparator); for (Entry<? extends K, ? extends V> entry : entries) { delegate.put(entry.getKey(), entry.getValue()); } - return newView(unmodifiableSortedMap(delegate), comparator); + return new ImmutableSortedMap<K, V>(delegate); } // 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 EmptyImmutableSortedMap.forComparator(NATURAL_ORDER); + return (ImmutableSortedMap) NATURAL_EMPTY_MAP; } public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> @@ -145,11 +155,11 @@ public abstract class ImmutableSortedMap<K, V> } } - SortedMap<K, V> delegate = newModifiableDelegate(comparator); + SortedMap<K, V> delegate = Maps.newTreeMap(comparator); for (Entry<? extends K, ? extends V> entry : map.entrySet()) { putEntryWithChecks(delegate, entry); } - return newView(unmodifiableSortedMap(delegate), comparator); + return new ImmutableSortedMap<K, V>(delegate); } private static <K, V> void putEntryWithChecks( @@ -169,7 +179,7 @@ public abstract class ImmutableSortedMap<K, V> map.put(key, value); } - public static <K extends Comparable<?>, V> Builder<K, V> naturalOrder() { + public static <K extends Comparable<K>, V> Builder<K, V> naturalOrder() { return new Builder<K, V>(Ordering.natural()); } @@ -177,7 +187,7 @@ public abstract class ImmutableSortedMap<K, V> return new Builder<K, V>(comparator); } - public static <K extends Comparable<?>, V> Builder<K, V> reverseOrder() { + public static <K extends Comparable<K>, V> Builder<K, V> reverseOrder() { return new Builder<K, V>(Ordering.natural().reverse()); } @@ -206,11 +216,11 @@ public abstract class ImmutableSortedMap<K, V> } @Override public ImmutableSortedMap<K, V> build() { - SortedMap<K, V> delegate = newModifiableDelegate(comparator); + SortedMap<K, V> delegate = Maps.newTreeMap(comparator); for (Entry<? extends K, ? extends V> entry : entries) { putEntryWithChecks(delegate, entry); } - return newView(unmodifiableSortedMap(delegate), comparator); + return new ImmutableSortedMap<K, V>(delegate); } } @@ -221,7 +231,7 @@ public abstract class ImmutableSortedMap<K, V> return (ks == null) ? (keySet = createKeySet()) : ks; } - @Override ImmutableSortedSet<K> createKeySet() { + private 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. @@ -256,7 +266,7 @@ public abstract class ImmutableSortedMap<K, V> public ImmutableSortedMap<K, V> headMap(K toKey) { checkNotNull(toKey); - return newView(sortedDelegate.headMap(toKey)); + return new ImmutableSortedMap<K, V>(sortedDelegate.headMap(toKey)); } ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive) { @@ -275,7 +285,7 @@ public abstract class ImmutableSortedMap<K, V> checkNotNull(fromKey); checkNotNull(toKey); checkArgument(comparator.compare(fromKey, toKey) <= 0); - return newView(sortedDelegate.subMap(fromKey, toKey)); + return new ImmutableSortedMap<K, V>(sortedDelegate.subMap(fromKey, toKey)); } ImmutableSortedMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive){ @@ -284,10 +294,10 @@ public abstract 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 newView(sortedDelegate.tailMap(fromKey)); + return new ImmutableSortedMap<K, V>(sortedDelegate.tailMap(fromKey)); } public ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) { @@ -295,42 +305,16 @@ public abstract class ImmutableSortedMap<K, V> if (!inclusive) { fromKey = higher(fromKey); if (fromKey == null) { - return EmptyImmutableSortedMap.forComparator(comparator()); + return emptyMap(comparator()); } } return tailMap(fromKey); } - 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); + static <K, V> ImmutableSortedMap<K, V> emptyMap(Comparator<? super K> comparator) { + if (comparator == NATURAL_ORDER) { + return (ImmutableSortedMap) NATURAL_EMPTY_MAP; } - 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(); + return create(comparator); } } 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 c3cb7aa..cfb1da2 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.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkArgument; import java.util.ArrayList; import java.util.Collection; @@ -36,8 +36,7 @@ import javax.annotation.Nullable; * @author Hayward Chan */ public abstract class ImmutableSortedSet<E> - extends ForwardingImmutableSet<E> implements SortedSet<E>, SortedIterable<E> { - // TODO(cpovirk): split into ImmutableSortedSet/ForwardingImmutableSortedSet? + extends ImmutableSet<E> implements SortedSet<E>, SortedIterable<E> { // In the non-emulated source, this is in ImmutableSortedSetFauxverideShim, // which overrides ImmutableSet & which ImmutableSortedSet extends. @@ -247,19 +246,6 @@ 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)); } @@ -385,11 +371,11 @@ public abstract class ImmutableSortedSet<E> return new Builder<E>(comparator); } - public static <E extends Comparable<?>> Builder<E> reverseOrder() { + public static <E extends Comparable<E>> Builder<E> reverseOrder() { return new Builder<E>(Ordering.natural().reverse()); } - public static <E extends Comparable<?>> Builder<E> naturalOrder() { + public static <E extends Comparable<E>> 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 330c5bf..1299a62 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,6 +22,7 @@ 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; @@ -48,10 +49,6 @@ 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) @@ -82,7 +79,7 @@ public final class Iterables { return checkNotNull(iterable); } - private static final class UnmodifiableIterable<T> extends FluentIterable<T> { + private static final class UnmodifiableIterable<T> implements Iterable<T> { private final Iterable<T> iterable; private UnmodifiableIterable(Iterable<T> iterable) { @@ -111,14 +108,20 @@ public final class Iterables { } /** - * Returns {@code true} if {@code iterable} contains any object for which {@code equals(element)} - * is true. + * Returns {@code true} if {@code iterable} contains {@code element}; that is, + * 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; - return Collections2.safeContains(collection, element); + try { + return collection.contains(element); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } } return Iterators.contains(iterable.iterator(), element); } @@ -240,13 +243,6 @@ 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()); } @@ -276,9 +272,8 @@ public final class Iterables { * @throws IllegalArgumentException if the iterator contains multiple * elements */ - @Nullable public static <T> T getOnlyElement( - Iterable<? extends T> iterable, @Nullable T defaultValue) { + Iterable<T> iterable, @Nullable T defaultValue) { return Iterators.getOnlyElement(iterable.iterator(), defaultValue); } @@ -355,7 +350,7 @@ public final class Iterables { */ public static <T> Iterable<T> cycle(final Iterable<T> iterable) { checkNotNull(iterable); - return new FluentIterable<T>() { + return new Iterable<T>() { @Override public Iterator<T> iterator() { return Iterators.cycle(iterable); @@ -470,7 +465,7 @@ public final class Iterables { public static <T> Iterable<T> concat( final Iterable<? extends Iterable<? extends T>> inputs) { checkNotNull(inputs); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.concat(iterators(inputs)); @@ -521,7 +516,7 @@ public final class Iterables { final Iterable<T> iterable, final int size) { checkNotNull(iterable); checkArgument(size > 0); - return new FluentIterable<List<T>>() { + return new IterableWithToString<List<T>>() { @Override public Iterator<List<T>> iterator() { return Iterators.partition(iterable.iterator(), size); @@ -550,7 +545,7 @@ public final class Iterables { final Iterable<T> iterable, final int size) { checkNotNull(iterable); checkArgument(size > 0); - return new FluentIterable<List<T>>() { + return new IterableWithToString<List<T>>() { @Override public Iterator<List<T>> iterator() { return Iterators.paddedPartition(iterable.iterator(), size); @@ -566,7 +561,7 @@ public final class Iterables { final Iterable<T> unfiltered, final Predicate<? super T> predicate) { checkNotNull(unfiltered); checkNotNull(predicate); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.filter(unfiltered.iterator(), predicate); @@ -575,7 +570,8 @@ public final class Iterables { } /** - * Returns {@code true} if any element in {@code iterable} satisfies the predicate. + * Returns {@code true} if one or more elements in {@code iterable} satisfy + * the predicate. */ public static <T> boolean any( Iterable<T> iterable, Predicate<? super T> predicate) { @@ -594,8 +590,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, Object)} instead. + * it is possible that <i>no</i> element will match, use {@link + * #tryFind)} or {@link #find(Iterable, Predicate, T)} instead. * * @throws NoSuchElementException if no element in {@code iterable} matches * the given predicate @@ -613,8 +609,7 @@ public final class Iterables { * * @since 7.0 */ - @Nullable - public static <T> T find(Iterable<? extends T> iterable, + public static <T> T find(Iterable<T> iterable, Predicate<? super T> predicate, @Nullable T defaultValue) { return Iterators.find(iterable.iterator(), predicate, defaultValue); } @@ -666,7 +661,7 @@ public final class Iterables { final Function<? super F, ? extends T> function) { checkNotNull(fromIterable); checkNotNull(function); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.transform(fromIterable.iterator(), function); @@ -719,8 +714,8 @@ public final class Iterables { * @throws IndexOutOfBoundsException if {@code position} is negative * @since 4.0 */ - @Nullable - public static <T> T get(Iterable<? extends T> iterable, int position, @Nullable T defaultValue) { + public static <T> T get(Iterable<T> iterable, int position, + @Nullable T defaultValue) { checkNotNull(iterable); checkNonnegativeIndex(position); @@ -736,16 +731,11 @@ 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 */ - @Nullable - public static <T> T getFirst(Iterable<? extends T> iterable, @Nullable T defaultValue) { + public static <T> T getFirst(Iterable<T> iterable, @Nullable T defaultValue) { return Iterators.getNext(iterable.iterator(), defaultValue); } @@ -786,17 +776,16 @@ public final class Iterables { * @return the last element of {@code iterable} or the default value * @since 3.0 */ - @Nullable - public static <T> T getLast(Iterable<? extends T> iterable, @Nullable T defaultValue) { + public static <T> T getLast(Iterable<T> iterable, @Nullable T defaultValue) { if (iterable instanceof Collection) { - Collection<? extends T> collection = Collections2.cast(iterable); + Collection<T> collection = (Collection<T>) iterable; if (collection.isEmpty()) { return defaultValue; } } if (iterable instanceof List) { - List<? extends T> list = Lists.cast(iterable); + List<T> list = (List<T>) iterable; return getLastInNonemptyList(list); } @@ -806,7 +795,7 @@ public final class Iterables { * call this method. */ if (iterable instanceof SortedSet) { - SortedSet<? extends T> sortedSet = Sets.cast(iterable); + SortedSet<T> sortedSet = (SortedSet<T>) iterable; return sortedSet.last(); } @@ -844,7 +833,7 @@ public final class Iterables { if (iterable instanceof List) { final List<T> list = (List<T>) iterable; - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { // TODO(kevinb): Support a concurrently modified collection? @@ -855,12 +844,12 @@ public final class Iterables { }; } - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { final Iterator<T> iterator = iterable.iterator(); - Iterators.advance(iterator, numberToSkip); + Iterators.skip(iterator, numberToSkip); /* * We can't just return the iterator because an immediate call to its @@ -916,7 +905,7 @@ public final class Iterables { final Iterable<T> iterable, final int limitSize) { checkNotNull(iterable); checkArgument(limitSize >= 0, "limit is negative"); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.limit(iterable.iterator(), limitSize); @@ -945,7 +934,7 @@ public final class Iterables { */ public static <T> Iterable<T> consumingIterable(final Iterable<T> iterable) { if (iterable instanceof Queue) { - return new FluentIterable<T>() { + return new Iterable<T>() { @Override public Iterator<T> iterator() { return new ConsumingQueueIterator<T>((Queue<T>) iterable); @@ -955,7 +944,7 @@ public final class Iterables { checkNotNull(iterable); - return new FluentIterable<T>() { + return new Iterable<T>() { @Override public Iterator<T> iterator() { return Iterators.consumingIterator(iterable.iterator()); @@ -982,6 +971,30 @@ 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 @@ -997,6 +1010,43 @@ 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. @@ -1015,7 +1065,7 @@ public final class Iterables { final Comparator<? super T> comparator) { checkNotNull(iterables, "iterables"); checkNotNull(comparator, "comparator"); - Iterable<T> iterable = new FluentIterable<T>() { + Iterable<T> iterable = new Iterable<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 ead102f..9da1da6 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,7 +23,6 @@ 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; @@ -36,7 +35,6 @@ 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; @@ -52,10 +50,6 @@ 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) @@ -64,8 +58,8 @@ import javax.annotation.Nullable; public final class Iterators { private Iterators() {} - static final UnmodifiableListIterator<Object> EMPTY_LIST_ITERATOR - = new UnmodifiableListIterator<Object>() { + static final UnmodifiableIterator<Object> EMPTY_ITERATOR + = new UnmodifiableIterator<Object>() { @Override public boolean hasNext() { return false; @@ -74,22 +68,6 @@ 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; - } }; /** @@ -98,20 +76,10 @@ 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") - static <T> UnmodifiableListIterator<T> emptyListIterator() { - return (UnmodifiableListIterator<T>) EMPTY_LIST_ITERATOR; + public static <T> UnmodifiableIterator<T> emptyIterator() { + return (UnmodifiableIterator<T>) EMPTY_ITERATOR; } private static final Iterator<Object> EMPTY_MODIFIABLE_ITERATOR = @@ -305,11 +273,15 @@ public final class Iterators { * {@code hasNext()} method will return {@code false}. */ public static String toString(Iterator<?> iterator) { - return Joiner.on(", ") - .useForNull("null") - .appendTo(new StringBuilder().append('['), iterator) - .append(']') - .toString(); + 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(); } /** @@ -345,8 +317,8 @@ public final class Iterators { * @throws IllegalArgumentException if the iterator contains multiple * elements. The state of the iterator is unspecified. */ - @Nullable - public static <T> T getOnlyElement(Iterator<? extends T> iterator, @Nullable T defaultValue) { + public static <T> T getOnlyElement( + Iterator<T> iterator, @Nullable T defaultValue) { return iterator.hasNext() ? getOnlyElement(iterator) : defaultValue; } @@ -441,8 +413,8 @@ public final class Iterators { /** * Returns an iterator that cycles indefinitely over the provided elements. * - * <p>The returned iterator supports {@code remove()}. After {@code remove()} - * is called, subsequent cycles omit the removed + * <p>The returned iterator supports {@code remove()} if the provided iterator + * does. 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. @@ -462,11 +434,6 @@ 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, @@ -484,11 +451,6 @@ 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, @@ -507,11 +469,6 @@ 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, @@ -532,11 +489,6 @@ 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) { @@ -551,11 +503,6 @@ 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) { @@ -728,8 +675,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, Object)} instead. + * <i>no</i> element will match, use {@link #tryFind)} or {@link + * #find(Iterator, Predicate, T)} instead. * * @throws NoSuchElementException if no element in {@code iterator} matches * the given predicate @@ -749,10 +696,9 @@ public final class Iterators { * * @since 7.0 */ - @Nullable - public static <T> T find(Iterator<? extends T> iterator, Predicate<? super T> predicate, + public static <T> T find(Iterator<T> iterator, Predicate<? super T> predicate, @Nullable T defaultValue) { - UnmodifiableIterator<? extends T> filteredIterator = filter(iterator, predicate); + UnmodifiableIterator<T> filteredIterator = filter(iterator, predicate); return filteredIterator.hasNext() ? filteredIterator.next() : defaultValue; } @@ -817,12 +763,22 @@ 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 TransformedIterator<F, T>(fromIterator) { + return new Iterator<T>() { + @Override + public boolean hasNext() { + return fromIterator.hasNext(); + } @Override - T transform(F from) { + public T next() { + F from = fromIterator.next(); return function.apply(from); } + @Override + public void remove() { + fromIterator.remove(); + } }; } @@ -874,8 +830,8 @@ public final class Iterators { * @throws IndexOutOfBoundsException if {@code position} is negative * @since 4.0 */ - @Nullable - public static <T> T get(Iterator<? extends T> iterator, int position, @Nullable T defaultValue) { + public static <T> T get(Iterator<T> iterator, int position, + @Nullable T defaultValue) { checkNonnegative(position); try { @@ -894,8 +850,7 @@ public final class Iterators { * @return the next element of {@code iterator} or the default value * @since 7.0 */ - @Nullable - public static <T> T getNext(Iterator<? extends T> iterator, @Nullable T defaultValue) { + public static <T> T getNext(Iterator<T> iterator, @Nullable T defaultValue) { return iterator.hasNext() ? iterator.next() : defaultValue; } @@ -922,24 +877,24 @@ public final class Iterators { * @return the last element of {@code iterator} * @since 3.0 */ - @Nullable - public static <T> T getLast(Iterator<? extends T> iterator, @Nullable T defaultValue) { + public static <T> T getLast(Iterator<T> iterator, @Nullable T defaultValue) { return iterator.hasNext() ? getLast(iterator) : defaultValue; } /** - * Calls {@code next()} on {@code iterator}, either {@code numberToAdvance} times + * Calls {@code next()} on {@code iterator}, either {@code numberToSkip} times * or until {@code hasNext()} returns {@code false}, whichever comes first. * - * @return the number of elements the iterator was advanced - * @since 13.0 (since 3.0 as {@code Iterators.skip}) + * @return the number of elements skipped + * @since 3.0 */ - public static int advance(Iterator<?> iterator, int numberToAdvance) { + @Beta + public static <T> int skip(Iterator<T> iterator, int numberToSkip) { checkNotNull(iterator); - checkArgument(numberToAdvance >= 0, "number to advance cannot be negative"); + checkArgument(numberToSkip >= 0, "number to skip cannot be negative"); int i; - for (i = 0; i < numberToAdvance && iterator.hasNext(); i++) { + for (i = 0; i < numberToSkip && iterator.hasNext(); i++) { iterator.next(); } return i; @@ -1015,21 +970,6 @@ 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 /** @@ -1067,14 +1007,21 @@ public final class Iterators { } /** - * Returns a list iterator containing the elements in the specified range of - * {@code array} in order, starting at the specified index. + * 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. * * <p>The {@code Iterable} equivalent of this method is {@code - * Arrays.asList(array).subList(offset, offset + length).listIterator(index)}. + * 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} */ - static <T> UnmodifiableListIterator<T> forArray( - final T[] array, final int offset, int length, int index) { + static <T> UnmodifiableIterator<T> forArray( + final T[] array, final int offset, int length) { checkArgument(length >= 0); int end = offset + length; @@ -1086,7 +1033,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, index) { + return new AbstractIndexedListIterator<T>(length) { @Override protected T get(int index) { return array[offset + index]; } @@ -1343,19 +1290,4 @@ 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 74e0ff1..d426893 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,20 +16,16 @@ 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.Objects; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; -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; @@ -65,23 +61,29 @@ 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>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY); + return new LinkedHashMultimap<K, V>(); } /** @@ -95,9 +97,7 @@ 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>( - Maps.capacity(expectedKeys), - Maps.capacity(expectedValuesPerKey)); + return new LinkedHashMultimap<K, V>(expectedKeys, expectedValuesPerKey); } /** @@ -111,158 +111,201 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { */ public static <K, V> LinkedHashMultimap<K, V> create( Multimap<? extends K, ? extends V> multimap) { - LinkedHashMultimap<K, V> result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY); - result.putAll(multimap); - return result; - } - - 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); + return new LinkedHashMultimap<K, V>(multimap); } - private static <K, V> void succeedsInValueSet(ValueSetLink<K, V> pred, ValueSetLink<K, V> succ) { - pred.setSuccessorInValueSet(succ); - succ.setPredecessorInValueSet(pred); + private LinkedHashMultimap() { + super(new LinkedHashMap<K, Collection<V>>()); + linkedEntries = Sets.newLinkedHashSet(); } - private static <K, V> void succeedsInMultimap( - ValueEntry<K, V> pred, ValueEntry<K, V> succ) { - pred.setSuccessorInMultimap(succ); - succ.setPredecessorInMultimap(pred); + 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 deleteFromValueSet(ValueSetLink<K, V> entry) { - succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet()); + 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 deleteFromMultimap(ValueEntry<K, V> entry) { - succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap()); + /** + * {@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)); } /** - * 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. + * {@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 */ - @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; + @Override Collection<V> createCollection(@Nullable K key) { + return new SetDecorator(key, createCollection()); + } - ValueEntry<K, V> predecessorInMultimap; - ValueEntry<K, V> successorInMultimap; + private class SetDecorator extends ForwardingSet<V> { + final Set<V> delegate; + final K key; - ValueEntry(@Nullable K key, @Nullable V value, int valueHash, - @Nullable ValueEntry<K, V> nextInValueSetHashRow) { + SetDecorator(@Nullable K key, Set<V> delegate) { + this.delegate = delegate; this.key = key; - this.value = value; - this.valueHash = valueHash; - this.nextInValueSetHashRow = nextInValueSetHashRow; } - @Override - public K getKey() { - return key; + @Override protected Set<V> delegate() { + return delegate; } - @Override - public V getValue() { - return value; + <E> Map.Entry<K, E> createEntry(@Nullable E value) { + return Maps.immutableEntry(key, value); } - @Override - public ValueSetLink<K, V> getPredecessorInValueSet() { - return predecessorInValueSet; + <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> getSuccessorInValueSet() { - return successorInValueSet; + @Override public boolean add(@Nullable V value) { + boolean changed = delegate.add(value); + if (changed) { + linkedEntries.add(createEntry(value)); + } + return changed; } - @Override - public void setPredecessorInValueSet(ValueSetLink<K, V> entry) { - predecessorInValueSet = entry; + @Override public boolean addAll(Collection<? extends V> values) { + boolean changed = delegate.addAll(values); + if (changed) { + linkedEntries.addAll(createEntries(delegate())); + } + return changed; } - @Override - public void setSuccessorInValueSet(ValueSetLink<K, V> entry) { - successorInValueSet = entry; + @Override public void clear() { + for (V value : delegate) { + linkedEntries.remove(createEntry(value)); + } + delegate.clear(); } - public ValueEntry<K, V> getPredecessorInMultimap() { - return predecessorInMultimap; - } + @Override public Iterator<V> iterator() { + final Iterator<V> delegateIterator = delegate.iterator(); + return new Iterator<V>() { + V value; - public ValueEntry<K, V> getSuccessorInMultimap() { - return successorInMultimap; + @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 void setSuccessorInMultimap(ValueEntry<K, V> multimapSuccessor) { - this.successorInMultimap = multimapSuccessor; + @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 void setPredecessorInMultimap(ValueEntry<K, V> multimapPredecessor) { - this.predecessorInMultimap = multimapPredecessor; + @Override public boolean removeAll(Collection<?> values) { + boolean changed = delegate.removeAll(values); + if (changed) { + linkedEntries.removeAll(createEntries(values)); + } + return changed; } - } - 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); + @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; + } } /** * {@inheritDoc} * - * <p>Creates an empty {@code LinkedHashSet} for a collection of values for - * one key. + * <p>Generates an iterator across map entries that follows the ordering in + * which the key-value pairs were added to the multimap. * - * @return a new {@code LinkedHashSet} containing a collection of values for - * one key + * @return a key-value iterator with the correct ordering */ - @Override - Set<V> createCollection() { - return new LinkedHashSet<V>(valueSetCapacity); - } + @Override Iterator<Map.Entry<K, V>> createEntryIterator() { + final Iterator<Map.Entry<K, V>> delegateIterator = linkedEntries.iterator(); - /** - * {@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); + 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 + public void remove() { + // Remove from iterator first to keep iterator valid. + delegateIterator.remove(); + LinkedHashMultimap.this.remove(entry.getKey(), entry.getValue()); + } + }; } /** @@ -273,8 +316,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); } @@ -305,249 +348,7 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { return super.values(); } - @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); - } + // Unfortunately, the entries() ordering does not determine the key ordering; + // see the example in the LinkedListMultimap class Javadoc. } 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 da93ead..7715784 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,10 +27,6 @@ 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 f51d292..2273535 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,7 +17,9 @@ 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; @@ -25,10 +27,11 @@ 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; @@ -88,10 +91,6 @@ 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) */ @@ -122,32 +121,12 @@ 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 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; + 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 /** * Creates a new, empty {@code LinkedListMultimap} with the default initial @@ -181,11 +160,15 @@ public class LinkedListMultimap<K, V> } LinkedListMultimap() { - keyToKeyList = Maps.newHashMap(); + keyCount = LinkedHashMultiset.create(); + keyToKeyHead = Maps.newHashMap(); + keyToKeyTail = Maps.newHashMap(); } private LinkedListMultimap(int expectedKeys) { - keyToKeyList = new HashMap<K, KeyList<K, V>>(expectedKeys); + keyCount = LinkedHashMultiset.create(expectedKeys); + keyToKeyHead = Maps.newHashMapWithExpectedSize(expectedKeys); + keyToKeyTail = Maps.newHashMapWithExpectedSize(expectedKeys); } private LinkedListMultimap(Multimap<? extends K, ? extends V> multimap) { @@ -204,32 +187,27 @@ public class LinkedListMultimap<K, V> Node<K, V> node = new Node<K, V>(key, value); if (head == null) { // empty list head = tail = node; - keyToKeyList.put(key, new KeyList<K, V>(node)); - modCount++; + keyToKeyHead.put(key, node); + keyToKeyTail.put(key, node); } else if (nextSibling == null) { // non-empty list, add to tail tail.next = node; node.previous = tail; - tail = node; - KeyList<K, V> keyList = keyToKeyList.get(key); - if (keyList == null) { - keyToKeyList.put(key, keyList = new KeyList<K, V>(node)); - modCount++; + Node<K, V> keyTail = keyToKeyTail.get(key); + if (keyTail == null) { // first for this key + keyToKeyHead.put(key, node); } 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 - keyToKeyList.get(key).head = node; + keyToKeyHead.put(key, node); } else { nextSibling.previousSibling.nextSibling = node; } @@ -241,7 +219,7 @@ public class LinkedListMultimap<K, V> nextSibling.previous = node; nextSibling.previousSibling = node; } - size++; + keyCount.add(key); return node; } @@ -261,27 +239,21 @@ public class LinkedListMultimap<K, V> } else { // node was tail tail = node.previous; } - if (node.previousSibling == null && node.nextSibling == null) { - KeyList<K, V> keyList = keyToKeyList.remove(node.key); - keyList.count = 0; - modCount++; + if (node.previousSibling != null) { + node.previousSibling.nextSibling = node.nextSibling; + } else if (node.nextSibling != null) { // node was key head + keyToKeyHead.put(node.key, node.nextSibling); } else { - 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; - } + keyToKeyHead.remove(node.key); // don't leak a key-null entry } - size--; + 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 + } + keyCount.remove(node.key); } /** Removes all nodes for the specified key. */ @@ -305,7 +277,6 @@ public class LinkedListMultimap<K, V> Node<K, V> next; Node<K, V> current; Node<K, V> previous; - int expectedModCount = modCount; NodeIterator() { next = head; @@ -327,19 +298,12 @@ 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; @@ -348,7 +312,6 @@ public class LinkedListMultimap<K, V> } @Override public void remove() { - checkForConcurrentModification(); checkState(current != null); if (current != next) { // after call to next() previous = current.previous; @@ -358,16 +321,13 @@ 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; @@ -401,21 +361,13 @@ 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); @@ -426,11 +378,9 @@ public class LinkedListMultimap<K, V> } @Override public void remove() { - checkForConcurrentModification(); checkState(current != null); removeAllNodes(current.key); current = null; - expectedModCount = modCount; } } @@ -445,8 +395,7 @@ public class LinkedListMultimap<K, V> /** Constructs a new iterator over all values for the specified key. */ ValueForKeyIterator(@Nullable Object key) { this.key = key; - KeyList<K, V> keyList = keyToKeyList.get(key); - next = (keyList == null) ? null : keyList.head; + next = keyToKeyHead.get(key); } /** @@ -459,17 +408,16 @@ public class LinkedListMultimap<K, V> * @throws IndexOutOfBoundsException if index is invalid */ public ValueForKeyIterator(@Nullable Object key, int index) { - KeyList<K, V> keyList = keyToKeyList.get(key); - int size = (keyList == null) ? 0 : keyList.count; + int size = keyCount.count(key); Preconditions.checkPositionIndex(index, size); if (index >= (size / 2)) { - previous = (keyList == null) ? null : keyList.tail; + previous = keyToKeyTail.get(key); nextIndex = size; while (index++ < size) { previous(); } } else { - next = (keyList == null) ? null : keyList.head; + next = keyToKeyHead.get(key); while (index-- > 0) { next(); } @@ -548,7 +496,7 @@ public class LinkedListMultimap<K, V> @Override public int size() { - return size; + return keyCount.size(); } @Override @@ -558,7 +506,7 @@ public class LinkedListMultimap<K, V> @Override public boolean containsKey(@Nullable Object key) { - return keyToKeyList.containsKey(key); + return keyToKeyHead.containsKey(key); } @Override @@ -685,9 +633,9 @@ public class LinkedListMultimap<K, V> public void clear() { head = null; tail = null; - keyToKeyList.clear(); - size = 0; - modCount++; + keyCount.clear(); + keyToKeyHead.clear(); + keyToKeyTail.clear(); } // Views @@ -705,8 +653,7 @@ public class LinkedListMultimap<K, V> public List<V> get(final @Nullable K key) { return new AbstractSequentialList<V>() { @Override public int size() { - KeyList<K, V> keyList = keyToKeyList.get(key); - return (keyList == null) ? 0 : keyList.count; + return keyCount.count(key); } @Override public ListIterator<V> listIterator(int index) { return new ValueForKeyIterator(key, index); @@ -726,19 +673,19 @@ public class LinkedListMultimap<K, V> public Set<K> keySet() { Set<K> result = keySet; if (result == null) { - keySet = result = new Sets.ImprovedAbstractSet<K>() { + keySet = result = new AbstractSet<K>() { @Override public int size() { - return keyToKeyList.size(); + return keyCount.elementSet().size(); } @Override public Iterator<K> iterator() { return new DistinctKeyIterator(); } @Override public boolean contains(Object key) { // for performance - return containsKey(key); + return keyCount.contains(key); } - @Override - public boolean remove(Object o) { // for performance - return !LinkedListMultimap.this.removeAll(o).isEmpty(); + @Override public boolean removeAll(Collection<?> c) { + checkNotNull(c); // eager for GWT + return super.removeAll(c); } }; } @@ -756,50 +703,39 @@ public class LinkedListMultimap<K, V> return result; } - private class MultisetView extends AbstractMultiset<K> { - @Override - public int size() { - return size; - } + private class MultisetView extends AbstractCollection<K> + implements Multiset<K> { - @Override - public int count(Object element) { - KeyList<K, V> keyList = keyToKeyList.get(element); - return (keyList == null) ? 0 : keyList.count; + @Override public int size() { + return keyCount.size(); } - @Override - Iterator<Entry<K>> entryIterator() { - return new TransformedIterator<K, Entry<K>>(new DistinctKeyIterator()) { + @Override public Iterator<K> iterator() { + final Iterator<Node<K, V>> nodes = new NodeIterator(); + return new Iterator<K>() { @Override - 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; - } - }; + public boolean hasNext() { + return nodes.hasNext(); + } + @Override + public K next() { + return nodes.next().key; + } + @Override + public void remove() { + nodes.remove(); } }; } @Override - int distinctElements() { - return elementSet().size(); + public int count(@Nullable Object key) { + return keyCount.count(key); } - @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 + public int add(@Nullable K key, int occurrences) { + throw new UnsupportedOperationException(); } @Override @@ -815,9 +751,77 @@ 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; @@ -837,20 +841,47 @@ public class LinkedListMultimap<K, V> if (result == null) { valuesList = result = new AbstractSequentialList<V>() { @Override public int size() { - return size; + return keyCount.size(); } @Override public ListIterator<V> listIterator(int index) { final NodeIterator nodes = new NodeIterator(index); - return new TransformedListIterator<Node<K, V>, V>(nodes) { + return new ListIterator<V>() { @Override - V transform(Node<K, V> node) { - return node.value; + public boolean hasNext() { + return nodes.hasNext(); + } + @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 void set(V value) { - nodes.setValue(value); + 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); + } + @Override + public void add(V e) { + throw new UnsupportedOperationException(); } }; } @@ -901,14 +932,55 @@ public class LinkedListMultimap<K, V> if (result == null) { entries = result = new AbstractSequentialList<Entry<K, V>>() { @Override public int size() { - return size; + return keyCount.size(); } @Override public ListIterator<Entry<K, V>> listIterator(int index) { - return new TransformedListIterator<Node<K, V>, Entry<K, V>>(new NodeIterator(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(); + } + @Override - Entry<K, V> transform(Node<K, V> node) { - return createEntry(node); + public void add(Map.Entry<K, V> e) { + throw new UnsupportedOperationException(); } }; } @@ -917,39 +989,75 @@ 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 Multimaps.AsMap<K, V>() { - @Override - public int size() { - return keyToKeyList.size(); + 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; } - @Override - Multimap<K, V> multimap() { - return LinkedListMultimap.this; + // The following methods are included for performance. + + @Override public boolean containsKey(@Nullable Object key) { + return LinkedListMultimap.this.containsKey(key); } - @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; - } + @SuppressWarnings("unchecked") + @Override public Collection<V> get(@Nullable Object key) { + Collection<V> collection = LinkedListMultimap.this.get((K) key); + return collection.isEmpty() ? null : collection; + } - @Override public Collection<V> getValue() { - return LinkedListMultimap.this.get(key); - } - }; - } - }; + @Override public Collection<V> remove(@Nullable Object key) { + Collection<V> collection = removeAll(key); + return collection.isEmpty() ? null : collection; } }; } 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 deleted file mode 100644 index 0bdb38a..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Lists.java +++ /dev/null @@ -1,1122 +0,0 @@ -/* - * 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 6f88f89..52c1a98 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,6 +184,12 @@ 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( @@ -221,16 +227,26 @@ 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 fd6909d..78165de 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,6 +22,7 @@ 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; @@ -34,6 +35,7 @@ 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; @@ -46,20 +48,14 @@ 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 (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>. + * Static utility methods pertaining to {@link Map} instances. Also see this + * class's counterparts {@link Lists} and {@link Sets}. * * @author Kevin Bourrillion * @author Mike Bostock @@ -71,60 +67,6 @@ 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. * @@ -324,6 +266,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); + } + + /** * 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. @@ -347,7 +321,7 @@ public final class Maps { SortedMapDifference<K, V> result = difference(sortedLeft, right); return result; } - return difference(left, right, Equivalence.equals()); + return difference(left, right, Equivalences.equals()); } /** @@ -519,7 +493,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()) @@ -556,6 +530,7 @@ 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); @@ -640,313 +615,6 @@ 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 @@ -967,6 +635,24 @@ 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. @@ -1128,38 +814,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); - } - - /** * 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 @@ -1182,7 +836,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; - BiMap<V, K> inverse; + transient BiMap<V, K> inverse; transient Set<V> values; UnmodifiableBiMap(BiMap<? extends K, ? extends V> delegate, @@ -1256,8 +910,16 @@ 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, Function<? super V1, V2> function) { - return transformEntries(fromMap, asEntryTransformer(function)); + 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); } /** @@ -1299,20 +961,18 @@ public final class Maps { * * @since 11.0 */ + @Beta public static <K, V1, V2> SortedMap<K, V2> transformValues( - 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) { + SortedMap<K, V1> fromMap, final Function<? super V1, V2> function) { checkNotNull(function); - return new EntryTransformer<K, V1, V2>() { - @Override - public V2 transformEntry(K key, V1 value) { - return function.apply(value); - } - }; + 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); } /** @@ -1427,14 +1087,9 @@ public final class Maps { * * @since 11.0 */ + @Beta public static <K, V1, V2> SortedMap<K, V2> transformEntries( - 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, + final SortedMap<K, V1> fromMap, EntryTransformer<? super K, ? super V1, V2> transformer) { return new TransformedEntriesSortedMap<K, V1, V2>(fromMap, transformer); } @@ -1526,23 +1181,17 @@ public final class Maps { } @Override public Iterator<Entry<K, V2>> iterator() { - 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(); + 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())); } - - @Override - public V2 getValue() { - return transformer.transformEntry(entry.getKey(), entry.getValue()); - } - }; - } - }; + }); } }; } @@ -1600,32 +1249,7 @@ 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()); - } } /** @@ -1660,11 +1284,15 @@ 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 KeyPredicate<K, V>(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 (unfiltered instanceof AbstractFilteredMap) ? filterFiltered((AbstractFilteredMap<K, V>) unfiltered, entryPredicate) : new FilteredKeyMap<K, V>( @@ -1701,42 +1329,19 @@ 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(user): Return a subclass of Maps.FilteredKeyMap for slightly better + // TODO: 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); - return filterEntries(unfiltered, new KeyPredicate<K, V>(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); } /** @@ -1772,10 +1377,16 @@ 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); } - return filterEntries(unfiltered, new ValuePredicate<K, 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); } /** @@ -1809,42 +1420,18 @@ 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) { - 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)); + 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); } /** @@ -1880,8 +1467,6 @@ 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) @@ -1920,15 +1505,10 @@ 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) @@ -1936,42 +1516,6 @@ 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. */ @@ -2116,7 +1660,6 @@ public final class Maps { } } } - /** * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when * filtering a filtered sorted map. @@ -2177,66 +1720,6 @@ 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; @@ -2328,14 +1811,10 @@ public final class Maps { @Override public Set<K> keySet() { Set<K> result = keySet; - return (result == null) ? keySet = createKeySet() : result; - } - - Set<K> createKeySet() { - return new KeySet(); + return (result == null) ? keySet = new KeySet() : result; } - private class KeySet extends Sets.ImprovedAbstractSet<K> { + private class KeySet extends AbstractSet<K> { @Override public Iterator<K> iterator() { final Iterator<Entry<K, V>> iterator = filteredEntrySet.iterator(); return new UnmodifiableIterator<K>() { @@ -2371,13 +1850,22 @@ 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 (predicate.apply(entry) && !collection.contains(entry.getKey())) { + if (!collection.contains(entry.getKey()) && predicate.apply(entry)) { iterator.remove(); changed = true; } @@ -2396,10 +1884,6 @@ 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 @@ -2408,7 +1892,7 @@ public final class Maps { * implementation. */ @GwtCompatible - abstract static class ImprovedAbstractMap<K, V> extends AbstractMap<K, V> { + static abstract 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} @@ -2445,7 +1929,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; } @@ -2453,6 +1937,17 @@ 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 = @@ -2460,46 +1955,25 @@ public final class Maps { /** * Delegates to {@link Map#get}. Returns {@code null} on {@code - * ClassCastException} and {@code NullPointerException}. + * ClassCastException}. */ 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} and {@code NullPointerException}. + * ClassCastException} */ 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; } } @@ -2558,6 +2032,13 @@ 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) { @@ -2581,30 +2062,36 @@ public final class Maps { * An admittedly inefficient implementation of {@link Map#containsKey}. */ static boolean containsKeyImpl(Map<?, ?> map, @Nullable Object key) { - return Iterators.contains(keyIterator(map.entrySet().iterator()), key); + for (Entry<?, ?> entry : map.entrySet()) { + if (Objects.equal(entry.getKey(), key)) { + return true; + } + } + return false; } /** * An implementation of {@link Map#containsValue}. */ static boolean containsValueImpl(Map<?, ?> map, @Nullable Object value) { - 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(); + for (Entry<?, ?> entry : map.entrySet()) { + if (Objects.equal(entry.getValue(), value)) { + return true; } - }; + } + return false; } - abstract static class KeySet<K, V> extends Sets.ImprovedAbstractSet<K> { + abstract static class KeySet<K, V> extends AbstractSet<K> { abstract Map<K, V> map(); @Override public Iterator<K> iterator() { - return keyIterator(map().entrySet().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(); + } + }); } @Override public int size() { @@ -2627,50 +2114,27 @@ 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 valueIterator(map().entrySet().iterator()); + return Iterators.transform(map().entrySet().iterator(), + new Function<Entry<K, V>, V>() { + @Override public V apply(Entry<K, V> entry) { + return entry.getValue(); + } + }); } @Override public boolean remove(Object o) { @@ -2732,8 +2196,7 @@ public final class Maps { } } - abstract static class EntrySet<K, V> - extends Sets.ImprovedAbstractSet<Entry<K, V>> { + abstract static class EntrySet<K, V> extends AbstractSet<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 c2d630f..7e8e2f7 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,17 +20,21 @@ 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; @@ -48,10 +52,6 @@ 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,14 +63,9 @@ public final class Multimaps { private Multimaps() {} /** - * 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}. + * 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. * * <p>The {@code factory}-generated and {@code map} classes determine the * multimap iteration order. They also specify the behavior of the @@ -110,7 +105,7 @@ public final class Multimaps { return new CustomMultimap<K, V>(map, factory); } - private static class CustomMultimap<K, V> extends AbstractMapBasedMultimap<K, V> { + private static class CustomMultimap<K, V> extends AbstractMultimap<K, V> { transient Supplier<? extends Collection<V>> factory; CustomMultimap(Map<K, Collection<V>> map, @@ -334,13 +329,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> multimap = Multimaps.synchronizedMultimap( + * Multimap<K, V> m = Multimaps.synchronizedMultimap( * HashMultimap.<K, V>create()); * ... - * Collection<V> values = multimap.get(key); // Needn't be in synchronized block + * Set<K> s = m.keySet(); // Needn't be in synchronized block * ... - * synchronized (multimap) { // Synchronizing on multimap, not values! - * Iterator<V> i = values.iterator(); // Must be in synchronized block + * synchronized (m) { // Synchronizing on m, not s! + * Iterator<K> i = s.iterator(); // Must be in synchronized block * while (i.hasNext()) { * foo(i.next()); * } @@ -541,7 +536,7 @@ public final class Multimaps { } @Override public Iterator<Collection<V>> iterator() { final Iterator<Collection<V>> iterator = delegate.iterator(); - return new UnmodifiableIterator<Collection<V>>() { + return new Iterator<Collection<V>>() { @Override public boolean hasNext() { return iterator.hasNext(); @@ -550,6 +545,10 @@ public final class Multimaps { public Collection<V> next() { return unmodifiableValueCollection(iterator.next()); } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } }; } @Override public Object[] toArray() { @@ -972,7 +971,7 @@ public final class Multimaps { @Override public Set<V> get(final K key) { - return new Sets.ImprovedAbstractSet<V>() { + return new AbstractSet<V>() { @Override public Iterator<V> iterator() { return new Iterator<V>() { int i; @@ -1053,7 +1052,7 @@ public final class Multimaps { @Override public Multiset<K> keys() { - return new Multimaps.Keys<K, V>(this); + return Multisets.forSet(map.keySet()); } @Override @@ -1104,27 +1103,35 @@ public final class Multimaps { } /** @see MapMultimap#asMap */ - class AsMapEntries extends Sets.ImprovedAbstractSet<Entry<K, Collection<V>>> { + class AsMapEntries extends AbstractSet<Entry<K, Collection<V>>> { @Override public int size() { return map.size(); } @Override public Iterator<Entry<K, Collection<V>>> iterator() { - return new TransformedIterator<K, Entry<K, Collection<V>>>(map.keySet().iterator()) { + return new Iterator<Entry<K, Collection<V>>>() { + final Iterator<K> keys = map.keySet().iterator(); + @Override - Entry<K, Collection<V>> transform(final K key) { + public boolean hasNext() { + return keys.hasNext(); + } + @Override + public Entry<K, Collection<V>> next() { + final K key = keys.next(); 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(); + } }; } @@ -1226,6 +1233,7 @@ 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); @@ -1294,29 +1302,15 @@ 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> - extends AbstractMultimap<K, V2> { + implements Multimap<K, V2> { final Multimap<K, V1> fromMultimap; final EntryTransformer<? super K, ? super V1, V2> transformer; @@ -1326,25 +1320,30 @@ public final class Multimaps { this.transformer = checkNotNull(transformer); } - 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); - } + 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); + } + }); } - @Override - Map<K, Collection<V2>> createAsMap() { - return Maps.transformEntries(fromMultimap.asMap(), - new EntryTransformer<K, Collection<V1>, Collection<V2>>() { + private transient Map<K, Collection<V2>> asMap; - @Override public Collection<V2> transformEntry( - K key, Collection<V1> value) { - return transform(key, value); - } - }); + @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 public Collection<V2> transformEntry( + K key, Collection<V1> value) { + return transform(key, value); + } + }); + asMap = aM; + return aM; + } + return asMap; } @Override public void clear() { @@ -1365,25 +1364,58 @@ public final class Multimaps { return values().contains(value); } - @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(); - } + 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 - public V2 getValue() { - return transformer.transformEntry(entry.getKey(), entry.getValue()); - } - }; - } - }); } @Override public Collection<V2> get(final K key) { @@ -1433,16 +1465,39 @@ public final class Multimaps { @Override public int size() { return fromMultimap.size(); } - - @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()); - } - }); + + 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(); } } @@ -1487,6 +1542,7 @@ 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) { @@ -1553,6 +1609,7 @@ 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) { @@ -1639,6 +1696,24 @@ 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, @@ -1694,36 +1769,39 @@ public final class Multimaps { return builder.build(); } - static class Keys<K, V> extends AbstractMultiset<K> { - final Multimap<K, V> multimap; - - Keys(Multimap<K, V> multimap) { - this.multimap = multimap; - } + static abstract class Keys<K, V> extends AbstractMultiset<K> { + abstract Multimap<K, V> multimap(); @Override Iterator<Multiset.Entry<K>> entryIterator() { - 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) { + 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 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() { @@ -1744,22 +1822,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; @@ -1770,16 +1848,30 @@ 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 Maps.keyIterator(multimap.entries().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(); + } + }); } @Override public int count(@Nullable Object element) { - Collection<V> values = Maps.safeGet(multimap.asMap(), element); - return (values == null) ? 0 : values.size(); + 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; + } } @Override public int remove(@Nullable Object element, int occurrences) { @@ -1788,7 +1880,14 @@ public final class Multimaps { return count(element); } - Collection<V> values = Maps.safeGet(multimap.asMap(), element); + Collection<V> values; + try { + values = multimap().asMap().get(element); + } catch (ClassCastException e) { + return 0; + } catch (NullPointerException e) { + return 0; + } if (values == null) { return 0; @@ -1808,35 +1907,45 @@ public final class Multimaps { } @Override public void clear() { - multimap.clear(); + multimap().clear(); } @Override public Set<K> elementSet() { - return multimap.keySet(); + return multimap().keySet(); } } - static class Values<K, V> extends AbstractCollection<V> { - final Multimap<K, V> multimap; - - Values(Multimap<K, V> multimap) { - this.multimap = multimap; - } + static abstract class Values<K, V> extends AbstractCollection<V> { + abstract Multimap<K, V> multimap(); @Override public Iterator<V> iterator() { - return Maps.valueIterator(multimap.entries().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(); + } + }; } @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(); } } @@ -1852,7 +1961,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()); } @@ -1860,7 +1969,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()); } @@ -1953,15 +2062,476 @@ 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 FilteredEntryMultimap constructor would lead to a multimap whose removal + * the FilteredMultimap 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> multimap, + private static <K, V> Multimap<K, V> filterFiltered(FilteredMultimap<K, V> map, Predicate<? super Entry<K, V>> entryPredicate) { Predicate<Entry<K, V>> predicate - = Predicates.and(multimap.entryPredicate(), entryPredicate); - return new FilteredEntryMultimap<K, V>(multimap.unfiltered, 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); + } + } + } } // 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 6465ee7..087fd17 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,8 +30,6 @@ import javax.annotation.Nullable; */ @GwtCompatible(emulated = true) public final class ObjectArrays { - static final Object[] EMPTY_ARRAY = new Object[0]; - private ObjectArrays() {} /** @@ -57,7 +55,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; - System.arraycopy(array, 0, result, 1, array.length); + Platform.unsafeArrayCopy(array, 0, result, 1, array.length); return result; } @@ -79,7 +77,7 @@ public final class ObjectArrays { /** GWT safe version of Arrays.copyOf. */ static <T> T[] arraysCopyOf(T[] original, int newLength) { T[] copy = newArray(original, newLength); - System.arraycopy( + Platform.unsafeArrayCopy( original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } @@ -154,14 +152,5 @@ 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 3fe8150..4fbdde0 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,14 +16,6 @@ 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}. * @@ -37,32 +29,24 @@ class Platform { return GwtPlatform.clone(array); } - static <T> T[] newArray(T[] reference, int length) { - return GwtPlatform.newArray(reference, length); + // 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 MapMaker tryWeakKeys(MapMaker mapMaker) { - return mapMaker; + static <T> T[] newArray(Class<T> type, int length) { + throw new UnsupportedOperationException( + "Platform.newArray is not supported in GWT yet."); } - 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 <T> T[] newArray(T[] reference, int length) { + return GwtPlatform.newArray(reference, length); } - 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); + static MapMaker tryWeakKeys(MapMaker mapMaker) { + return mapMaker; } } 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 999f6ee..a63b391 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,33 +39,32 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> this.range = range; } - 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 */ ContiguousSet<C> headSetImpl(C toElement, boolean inclusive) { + return range.intersection(Ranges.upTo(toElement, BoundType.forBoolean(inclusive))) + .asSet(domain); } - @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 */ int indexOf(Object target) { + return contains(target) ? (int) domain.distance(first(), (C) target) : -1; } - @Override ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive, C toElement, + // Abstract method doesn't exist in GWT emulation + /* @Override */ ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { - 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))); + return range.intersection(Ranges.range(fromElement, BoundType.forBoolean(fromInclusive), + toElement, BoundType.forBoolean(toInclusive))).asSet(domain); } - @Override ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive) { - return intersectionInCurrentDomain(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); + // 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 public UnmodifiableIterator<C> iterator() { - return new AbstractSequentialIterator<C>(first()) { + return new AbstractLinkedIterator<C>(first()) { final C last = last(); @Override @@ -96,10 +95,7 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> return (distance >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) distance + 1; } - @Override public boolean contains(@Nullable Object object) { - if (object == null) { - return false; - } + @Override public boolean contains(Object object) { try { return range.contains((C) object); } catch (ClassCastException e) { @@ -108,7 +104,11 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> } @Override public boolean containsAll(Collection<?> targets) { - return Collections2.containsAllImpl(this, targets); + try { + return range.containsAll((Iterable<? extends C>) targets); + } catch (ClassCastException e) { + return false; + } } @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) - ? ContiguousSet.create(Range.closed(lowerEndpoint, upperEndpoint), domain) + ? Ranges.closed(lowerEndpoint, upperEndpoint).asSet(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 Range.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain), + return Ranges.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain), range.upperBound.withUpperBoundType(upperBoundType, domain)); } - @Override public boolean equals(@Nullable Object object) { + @Override public boolean equals(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 f82a6b6..5cdddff 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,8 +16,6 @@ package com.google.common.collect; -import static java.util.Collections.unmodifiableList; - import java.util.List; /** @@ -25,15 +23,8 @@ import java.util.List; * * @author Hayward Chan */ -class RegularImmutableList<E> extends ForwardingImmutableList<E> { - private final List<E> delegate; - +class RegularImmutableList<E> extends ImmutableList<E> { RegularImmutableList(List<E> delegate) { - // TODO(cpovirk): avoid redundant unmodifiableList wrapping - this.delegate = unmodifiableList(delegate); - } - - @Override List<E> delegateList() { - return delegate; + super(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 b79ce80..bdf536b 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 ForwardingImmutableMap<K, V> { +final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { RegularImmutableMap(Map<? extends K, ? extends V> delegate) { super(delegate); @@ -31,5 +31,5 @@ final class RegularImmutableMap<K, V> extends ForwardingImmutableMap<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 72da3e4..f462ade 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 ForwardingImmutableSet<E> { +final class RegularImmutableSet<E> extends ImmutableSet<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 deleted file mode 100644 index 2e36f39..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/RegularImmutableSortedMap.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 3279449..8256e98 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,8 +32,4 @@ 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 bb06a9c..be31363 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,10 +19,15 @@ 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; @@ -45,11 +50,7 @@ import javax.annotation.Nullable; /** * Static utility methods pertaining to {@link Set} instances. Also see this - * 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>. + * class's counterparts {@link Lists} and {@link Maps}. * * @author Kevin Bourrillion * @author Jared Levy @@ -61,22 +62,6 @@ 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}. * @@ -91,7 +76,7 @@ public final class Sets { @GwtCompatible(serializable = true) public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet( E anElement, E... otherElements) { - return ImmutableEnumSet.asImmutable(EnumSet.of(anElement, otherElements)); + return new ImmutableEnumSet<E>(EnumSet.of(anElement, otherElements)); } /** @@ -109,25 +94,20 @@ public final class Sets { @GwtCompatible(serializable = true) public static <E extends Enum<E>> ImmutableSet<E> immutableEnumSet( Iterable<E> elements) { - 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(); - } + 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()); + } + return new ImmutableEnumSet<E>(set); } /** @@ -138,6 +118,20 @@ 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; @@ -572,11 +566,6 @@ 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) { @@ -807,13 +796,10 @@ 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. @@ -828,13 +814,21 @@ public final class Sets { checkNotNull(unfiltered), checkNotNull(predicate)); } - private static class FilteredSortedSet<E> extends FilteredSet<E> + private static class FilteredSortedSet<E> extends FilteredCollection<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(); @@ -895,22 +889,12 @@ public final class Sets { * <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 : 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). + * 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). * * <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 @@ -936,7 +920,8 @@ public final class Sets { return ImmutableSet.of(); } } - return CartesianSet.create(sets); + CartesianSet<B> cartesianSet = new CartesianSet<B>(sets); + return cartesianSet; } /** @@ -960,22 +945,12 @@ public final class Sets { * <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 : 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). + * 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). * * <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 @@ -999,51 +974,73 @@ public final class Sets { return cartesianProduct(Arrays.asList(sets)); } - 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(); + 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()); } - axesBuilder.add(copy); + } catch (ArithmeticException overflow) { + throw new IllegalArgumentException("cartesian product too big"); } - final ImmutableList<ImmutableSet<E>> axes = axesBuilder.build(); - ImmutableList<List<E>> listAxes = new ImmutableList<List<E>>() { + this.axes = builder.build(); + size = dividend; + } - @Override - public int size() { - return axes.size(); - } + @Override public int size() { + return size; + } + + @Override public UnmodifiableIterator<List<B>> iterator() { + return new UnmodifiableIterator<List<B>>() { + int index; @Override - public List<E> get(int index) { - return axes.get(index).asList(); + public boolean hasNext() { + return index < size; } @Override - boolean isPartialView() { - return true; + 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; } }; - return new CartesianSet<E>(axes, new CartesianList<E>(listAxes)); } - 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 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; } @Override public boolean equals(@Nullable Object object) { @@ -1056,26 +1053,56 @@ 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 } - int hash = 1; - for (Set<E> axis : axes) { - hash = 31 * hash + (size() / axis.size() * axis.hashCode()); + return axes.hashCode() + adjust; + } - hash = ~~hash; + 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 += adjust; - return ~~hash; } } @@ -1213,9 +1240,6 @@ 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; } @@ -1242,47 +1266,117 @@ public final class Sets { } /** - * Remove each element in an iterable from a set. + * 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 boolean removeAllImpl(Set<?> set, Iterator<?> iterator) { - boolean changed = false; - while (iterator.hasNext()) { - changed |= set.remove(iterator.next()); + 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. + */ + 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; + } + }; } - return changed; } - static boolean removeAllImpl(Set<?> set, Collection<?> collection) { - checkNotNull(collection); // for GWT - if (collection instanceof Multiset) { - collection = ((Multiset<?>) collection).elementSet(); + 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; } - /* - * 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()); + + @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)); + } + + @Override public void clear() { + delegate.clear(); } } /** - * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 + * Remove each element in an iterable from a set. */ - static <T> SortedSet<T> cast(Iterable<T> iterable) { - return (SortedSet<T>) iterable; + 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; } } 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 e570f00..6172191 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,29 +17,23 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.Collections.singletonList; -import java.util.List; +import java.util.Collections; /** * GWT emulated version of {@link SingletonImmutableList}. * * @author Hayward Chan */ -final class SingletonImmutableList<E> extends ForwardingImmutableList<E> { +final class SingletonImmutableList<E> extends ImmutableList<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) { - this.delegate = singletonList(checkNotNull(element)); + super(Collections.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/SingletonImmutableBiMap.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableMap.java index f086bf3..236d76e 100644 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableBiMap.java +++ b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableMap.java @@ -21,11 +21,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Collections; /** - * GWT emulation of {@link SingletonImmutableBiMap}. + * GWT emulation of {@link SingletonImmutableMap}. * * @author Hayward Chan */ -final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> { +final class SingletonImmutableMap<K, V> extends ImmutableMap<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,34 +36,9 @@ final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> { K singleKey; V singleValue; - transient SingletonImmutableBiMap<V, K> inverse; - - SingletonImmutableBiMap(K key, V value) { + SingletonImmutableMap(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/SingletonImmutableSet.java b/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SingletonImmutableSet.java index d1aa6b2..b51d4aa 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,8 +18,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.collect.Iterators; -import com.google.common.collect.UnmodifiableIterator; +import java.util.Collections; /** * GWT emulation of {@link SingletonImmutableSet}. @@ -27,7 +26,7 @@ import com.google.common.collect.UnmodifiableIterator; * @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. @@ -36,21 +35,7 @@ final class SingletonImmutableSet<E> extends ImmutableSet<E> { E element; SingletonImmutableSet(E 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); + super(Collections.singleton(checkNotNull(element))); + this.element = element; } } 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 deleted file mode 100644 index d9248dd..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultiset.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 deleted file mode 100644 index e3955b5..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/SortedMultisets.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 c809da8..fd09c97 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,8 +28,6 @@ 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; @@ -196,7 +194,7 @@ final class Synchronized { static class SynchronizedSet<E> extends SynchronizedCollection<E> implements Set<E> { - + SynchronizedSet(Set<E> delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -836,7 +834,7 @@ final class Synchronized { } @Override public Map.Entry<K, Collection<V>> next() { - final Map.Entry<K, Collection<V>> entry = super.next(); + final Map.Entry<K, Collection<V>> entry = iterator.next(); return new ForwardingMapEntry<K, Collection<V>>() { @Override protected Map.Entry<K, Collection<V>> delegate() { return entry; @@ -1028,12 +1026,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> { @@ -1197,67 +1195,12 @@ final class Synchronized { return iterator; } @Override public Collection<V> next() { - return typePreservingCollection(super.next(), mutex); + return typePreservingCollection(iterator.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 5023a0c..228dc2d 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,12 +22,11 @@ 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 @@ -61,16 +60,11 @@ import javax.annotation.Nullable; * 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 AbstractSortedKeySortedSetMultimap<K, V> { +public class TreeMultimap<K, V> extends AbstractSortedSetMultimap<K, V> { private transient Comparator<? super K> keyComparator; private transient Comparator<? super V> valueComparator; @@ -136,14 +130,6 @@ public class TreeMultimap<K, V> extends AbstractSortedKeySortedSetMultimap<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. */ @@ -156,10 +142,26 @@ public class TreeMultimap<K, V> extends AbstractSortedKeySortedSetMultimap<K, V> return valueComparator; } - /* - * The following @GwtIncompatible methods override the methods in - * AbstractSortedKeySortedSetMultimap, so GWT will fall back to the ASKSSM implementations, - * which return SortedSets and SortedMaps. + /** + * {@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. */ + @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 b45d127..622454d 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,217 +17,168 @@ 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 com.google.common.annotations.GwtCompatible; -import com.google.common.base.Objects; -import com.google.common.primitives.Ints; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; 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>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. + * <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}. * * @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; } - private final transient Reference<AvlNode<E>> rootReference; - private final transient GeneralRange<E> range; - private final transient AvlNode<E> header; - - TreeMultiset(Reference<AvlNode<E>> rootReference, GeneralRange<E> range, AvlNode<E> endLink) { - super(range.comparator()); - this.rootReference = rootReference; - this.range = range; - this.header = endLink; + /** + * Returns an iterator over the elements contained in this collection. + */ + @Override + public Iterator<E> iterator() { + // Needed to avoid Javadoc bug. + return super.iterator(); } - TreeMultiset(Comparator<? super E> comparator) { + private 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>>(); + this.rootReference = new Reference<Node<E>>(); } - /** - * A function which can be summed across a subtree. - */ - private enum Aggregate { - SIZE { - @Override - int nodeAggregate(AvlNode<?> node) { - return node.elemCount; - } + private TreeMultiset(GeneralRange<E> range, Reference<Node<E>> root) { + super(range.comparator()); + this.range = range; + this.rootReference = root; + } - @Override - long treeAggregate(@Nullable AvlNode<?> root) { - return (root == null) ? 0 : root.totalCount; - } - }, - DISTINCT { - @Override - int nodeAggregate(AvlNode<?> node) { - return 1; - } + @SuppressWarnings("unchecked") + E checkElement(Object o) { + return (E) o; + } - @Override - long treeAggregate(@Nullable AvlNode<?> root) { - return (root == null) ? 0 : root.distinctElements; - } - }; - abstract int nodeAggregate(AvlNode<?> node); + private transient final GeneralRange<E> range; - abstract long treeAggregate(@Nullable AvlNode<?> root); - } + private transient final Reference<Node<E>> rootReference; - 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; - } + static final class Reference<T> { + T 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 Reference() {} - private long aggregateAboveRange(Aggregate aggr, @Nullable AvlNode<E> node) { - if (node == null) { - return 0; + public T get() { + return value; } - 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(); + + public boolean compareAndSet(T expected, T newValue) { + if (value == expected) { + value = newValue; + return true; } - } else { - return aggr.treeAggregate(node.right) + aggr.nodeAggregate(node) - + aggregateAboveRange(aggr, node.left); + return false; } } @Override - public int size() { - return Ints.saturatedCast(aggregateForEntries(Aggregate.SIZE)); + int distinctElements() { + Node<E> root = rootReference.get(); + return Ints.checkedCast(BstRangeOps.totalInRange(distinctAggregate(), range, root)); } @Override - int distinctElements() { - return Ints.saturatedCast(aggregateForEntries(Aggregate.DISTINCT)); + public int size() { + Node<E> root = rootReference.get(); + return Ints.saturatedCast(BstRangeOps.totalInRange(sizeAggregate(), range, root)); } @Override public int count(@Nullable Object element) { try { - @SuppressWarnings("unchecked") - E e = (E) element; - AvlNode<E> root = rootReference.get(); - if (!range.contains(e) || root == null) { - return 0; + E e = checkElement(element); + if (range.contains(e)) { + Node<E> node = BstOperations.seek(comparator(), rootReference.get(), e); + return countOrZero(node); } - return root.count(comparator(), e); + return 0; } catch (ClassCastException e) { return 0; } catch (NullPointerException e) { @@ -235,713 +186,354 @@ public final class TreeMultiset<E> extends AbstractSortedMultiset<E> implements } } + 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(@Nullable E element, int occurrences) { - checkArgument(occurrences >= 0, "occurrences must be >= 0 but was %s", occurrences); + public int add(E element, int occurrences) { + checkElement(element); if (occurrences == 0) { return count(element); } checkArgument(range.contains(element)); - 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]; + return mutate(element, new AddModifier(occurrences)); } @Override public int remove(@Nullable Object element, int occurrences) { - checkArgument(occurrences >= 0, "occurrences must be >= 0 but was %s", occurrences); - if (occurrences == 0) { + if (element == null) { + return 0; + } else 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 { - @SuppressWarnings("unchecked") - E e = (E) element; - if (!range.contains(e) || root == null) { - return 0; - } - newRoot = root.remove(comparator(), e, occurrences, result); + E e = checkElement(element); + return range.contains(e) ? mutate(e, new RemoveModifier(occurrences)) : 0; } catch (ClassCastException e) { return 0; - } catch (NullPointerException e) { - return 0; } - rootReference.checkAndSet(root, newRoot); - return result[0]; } @Override - 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]; + public boolean setCount(E element, int oldCount, int newCount) { + checkElement(element); + checkArgument(range.contains(element)); + return mutate(element, new ConditionalSetCountModifier(oldCount, newCount)) + == oldCount; } @Override - public boolean setCount(@Nullable E element, int oldCount, int newCount) { - checkArgument(newCount >= 0); - checkArgument(oldCount >= 0); + public int setCount(E element, int count) { + checkElement(element); checkArgument(range.contains(element)); - - 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; + return mutate(element, new SetCountModifier(count)); } - 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; - } - } - }; + private BstPathFactory<Node<E>, BstInOrderPath<Node<E>>> pathFactory() { + return BstInOrderPath.inOrderFactory(); } - /** - * 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>> entryIterator() { + Node<E> root = rootReference.get(); + final BstInOrderPath<Node<E>> startingPath = + BstRangeOps.furthestPath(range, LEFT, pathFactory(), root); + return iteratorInDirection(startingPath, RIGHT); } - @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; + @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); } - @Override - Iterator<Entry<E>> entryIterator() { + 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; + } + }; return new Iterator<Entry<E>>() { - AvlNode<E> current = firstNode(); - Entry<E> prevEntry; + E toRemove = null; @Override public boolean hasNext() { - if (current == null) { - return false; - } else if (range.tooHigh(current.getElement())) { - current = null; - return false; - } else { - return true; - } + return pathIterator.hasNext(); } @Override public Entry<E> next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - Entry<E> result = wrapEntry(current); - prevEntry = result; - if (current.succ == header) { - current = null; - } else { - current = current.succ; - } - return result; + BstInOrderPath<Node<E>> path = pathIterator.next(); + return new LiveEntry( + toRemove = path.getTip().getKey(), path.getTip().elemCount()); } @Override public void remove() { - checkState(prevEntry != null); - setCount(prevEntry.getElement(), 0); - prevEntry = null; + checkState(toRemove != null); + setCount(toRemove, 0); + toRemove = null; } }; } - @Override - Iterator<Entry<E>> descendingEntryIterator() { - return new Iterator<Entry<E>>() { - AvlNode<E> current = lastNode(); - Entry<E> prevEntry = null; + class LiveEntry extends Multisets.AbstractEntry<E> { + private Node<E> expectedRoot; + private final E element; + private int count; - @Override - public boolean hasNext() { - if (current == null) { - return false; - } else if (range.tooLow(current.getElement())) { - current = null; - return false; - } else { - return true; - } - } + private LiveEntry(E element, int count) { + this.expectedRoot = rootReference.get(); + this.element = element; + this.count = count; + } - @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 E getElement() { + return element; + } - @Override - public void remove() { - checkState(prevEntry != null); - setCount(prevEntry.getElement(), 0); - prevEntry = null; + @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 SortedMultiset<E> headMultiset(@Nullable E upperBound, BoundType boundType) { - return new TreeMultiset<E>(rootReference, range.intersect(GeneralRange.upTo( - comparator(), - upperBound, - boundType)), header); + 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(); + } } @Override - public SortedMultiset<E> tailMultiset(@Nullable E lowerBound, BoundType boundType) { - return new TreeMultiset<E>(rootReference, range.intersect(GeneralRange.downTo( - comparator(), - lowerBound, - boundType)), header); + public SortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { + checkNotNull(upperBound); + return new TreeMultiset<E>( + range.intersect(GeneralRange.upTo(comparator, upperBound, boundType)), rootReference); } - static int distinctElements(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.distinctElements; + @Override + public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { + checkNotNull(lowerBound); + return new TreeMultiset<E>( + range.intersect(GeneralRange.downTo(comparator, lowerBound, boundType)), rootReference); } - 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; - } + /** + * {@inheritDoc} + * + * @since 11.0 + */ + @Override + public Comparator<? super E> comparator() { + return super.comparator(); } - private static final class AvlNode<E> extends Multisets.AbstractEntry<E> { - @Nullable private final E elem; + private static final class Node<E> extends BstNode<E, Node<E>> implements Serializable { + private final long size; + private final int distinct; - // 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) { + private Node(E key, int elemCount, @Nullable Node<E> left, + @Nullable Node<E> right) { + super(key, left, right); checkArgument(elemCount > 0); - this.elem = elem; - this.elemCount = elemCount; - this.totalCount = elemCount; - this.distinctElements = 1; - this.height = 1; - this.left = null; - this.right = null; - } - - 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 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; + this.size = (long) elemCount + sizeOrZero(left) + sizeOrZero(right); + this.distinct = 1 + distinctOrZero(left) + distinctOrZero(right); } - 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; + int elemCount() { + long result = size - sizeOrZero(childOrNull(LEFT)) + - sizeOrZero(childOrNull(RIGHT)); + return Ints.checkedCast(result); } - 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; - - 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; - - right = initRight.add(comparator, e, count, result); - if (result[0] == 0) { - distinctElements++; - } - this.totalCount += count; - return (right.height == initHeight) ? this : rebalance(); - } - - // 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; + private Node(E key, int elemCount) { + this(key, elemCount, null, null); } - 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; - } + private static final long serialVersionUID = 0; + } - left = initLeft.remove(comparator, e, count, result); + private static long sizeOrZero(@Nullable Node<?> node) { + return (node == null) ? 0 : node.size; + } - 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; - } + private static int distinctOrZero(@Nullable Node<?> node) { + return (node == null) ? 0 : node.distinct; + } - right = initRight.remove(comparator, e, count, result); + private static int countOrZero(@Nullable Node<?> entry) { + return (entry == null) ? 0 : entry.elemCount(); + } - if (result[0] > 0) { - if (count >= result[0]) { - this.distinctElements--; - this.totalCount -= result[0]; - } else { - this.totalCount -= count; - } - } - return rebalance(); - } + @SuppressWarnings("unchecked") + private BstAggregate<Node<E>> distinctAggregate() { + return (BstAggregate) DISTINCT_AGGREGATE; + } - // removing count from me! - result[0] = elemCount; - if (count >= elemCount) { - return deleteMe(); - } else { - this.elemCount -= count; - this.totalCount -= count; - return this; - } + private static final BstAggregate<Node<Object>> DISTINCT_AGGREGATE = + new BstAggregate<Node<Object>>() { + @Override + public int entryValue(Node<Object> entry) { + return 1; } - 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; - } - - left = initLeft.setCount(comparator, e, count, result); - - if (count == 0 && result[0] != 0) { - this.distinctElements--; - } else if (count > 0 && result[0] == 0) { - this.distinctElements++; - } - - 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; - } - - right = initRight.setCount(comparator, e, count, result); - - if (count == 0 && result[0] != 0) { - this.distinctElements--; - } else if (count > 0 && result[0] == 0) { - this.distinctElements++; - } - - this.totalCount += count - result[0]; - return rebalance(); - } - - // setting my count - result[0] = elemCount; - if (count == 0) { - return deleteMe(); - } - this.totalCount += count - elemCount; - this.elemCount = count; - return this; + @Override + public long treeValue(@Nullable Node<Object> tree) { + return distinctOrZero(tree); } + }; - 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); + @SuppressWarnings("unchecked") + private BstAggregate<Node<E>> sizeAggregate() { + return (BstAggregate) SIZE_AGGREGATE; + } - 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; + private static final BstAggregate<Node<Object>> SIZE_AGGREGATE = + new BstAggregate<Node<Object>>() { + @Override + public int entryValue(Node<Object> entry) { + return entry.elemCount(); } - 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]; + @Override + public long treeValue(@Nullable Node<Object> tree) { + return sizeOrZero(tree); } - 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; - } + @SuppressWarnings("unchecked") + private BstNodeFactory<Node<E>> nodeFactory() { + return (BstNodeFactory) NODE_FACTORY; + } - 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 { - AvlNode<E> newTop = succ; - newTop.right = right.removeMin(newTop); - newTop.left = left; - newTop.distinctElements = distinctElements - 1; - newTop.totalCount = totalCount - oldElemCount; - return newTop.rebalance(); - } - } + 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); + } + }; - // 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 abstract class MultisetModifier implements BstModifier<E, Node<E>> { + abstract int newCount(int oldCount); - // Removes the maximum node from this subtree to be reused elsewhere - private AvlNode<E> removeMax(AvlNode<E> node) { - if (right == null) { - return left; + @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)); } else { - right = right.removeMax(node); - distinctElements--; - totalCount -= node.elemCount; - return rebalance(); + return BstModificationResult.rebuildingChange(originalEntry, + new Node<E>(originalEntry.getKey(), newCount)); } } + } - private void recomputeMultiset() { - this.distinctElements = 1 + TreeMultiset.distinctElements(left) - + TreeMultiset.distinctElements(right); - this.totalCount = elemCount + totalCount(left) + totalCount(right); - } - - private void recomputeHeight() { - this.height = 1 + Math.max(height(left), height(right)); - } - - private void recompute() { - recomputeMultiset(); - recomputeHeight(); - } + private final class AddModifier extends MultisetModifier { + private final int countToAdd; - 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 AddModifier(int countToAdd) { + checkArgument(countToAdd > 0); + this.countToAdd = countToAdd; } - private int balanceFactor() { - return height(left) - height(right); + @Override + int newCount(int oldCount) { + checkArgument(countToAdd <= Integer.MAX_VALUE - oldCount, "Cannot add this many elements"); + return oldCount + countToAdd; } + } - 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; - } + private final class RemoveModifier extends MultisetModifier { + private final int countToRemove; - 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 RemoveModifier(int countToRemove) { + checkArgument(countToRemove > 0); + this.countToRemove = countToRemove; } - private static long totalCount(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.totalCount; + @Override + int newCount(int oldCount) { + return Math.max(0, oldCount - countToRemove); } + } - private static int height(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.height; - } + private final class SetCountModifier extends MultisetModifier { + private final int countToSet; - @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); - } + private SetCountModifier(int countToSet) { + checkArgument(countToSet >= 0); + this.countToSet = countToSet; } @Override - public E getElement() { - return elem; + int newCount(int oldCount) { + return countToSet; } + } - @Override - public int getCount() { - return elemCount; + private final class ConditionalSetCountModifier extends MultisetModifier { + private final int expectedCount; + private final int setCount; + + private ConditionalSetCountModifier(int expectedCount, int setCount) { + checkArgument(setCount >= 0 & expectedCount >= 0); + this.expectedCount = expectedCount; + this.setCount = setCount; } @Override - public String toString() { - return Multisets.immutableEntry(getElement(), getCount()).toString(); + int newCount(int oldCount) { + return (oldCount == expectedCount) ? setCount : oldCount; } } - 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 deleted file mode 100644 index 87937fe..0000000 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/UnmodifiableSortedMultiset.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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/collect/super/com/google/common/collect/EmptyImmutableBiMap.java b/guava-gwt/src-super/com/google/common/escape/super/com/google/common/escape/Platform.java index 1520d58..36f1b2e 100644 --- a/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/EmptyImmutableBiMap.java +++ b/guava-gwt/src-super/com/google/common/escape/super/com/google/common/escape/Platform.java @@ -14,23 +14,20 @@ * limitations under the License. */ -package com.google.common.collect; - -import java.util.Collections; +package com.google.common.escape; /** - * GWT emulation of {@link EmptyImmutableBiMap}. - * - * @author Hayward Chan + * @author Jesse Wilson */ -@SuppressWarnings("serial") -final class EmptyImmutableBiMap extends ImmutableBiMap<Object, Object> { - static final EmptyImmutableBiMap INSTANCE = new EmptyImmutableBiMap(); +class Platform { - private EmptyImmutableBiMap() { - super(Collections.emptyMap()); - } - @Override public ImmutableBiMap<Object, Object> inverse() { - return this; + 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; } } 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 deleted file mode 100644 index dee5b89..0000000 --- a/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/BaseEncoding.java +++ /dev/null @@ -1,794 +0,0 @@ -/* - * 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 deleted file mode 100644 index f3f955e..0000000 --- a/guava-gwt/src-super/com/google/common/io/super/com/google/common/io/GwtWorkarounds.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 deleted file mode 100644 index 75f19be..0000000 --- a/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/BigIntegerMath.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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 31703ed..352351c 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,42 +16,35 @@ 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. @@ -60,75 +53,23 @@ 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) { - /* - * 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; + 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; } - // 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}; + @VisibleForTesting static final int[] POWERS_OF_10 = + {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; - // halfPowersOf10[i] = largest int less than 10^(i + 0.5) - @VisibleForTesting static final int[] halfPowersOf10 = + // HALF_POWERS_OF_10[i] = largest int less than 10^(i + 0.5) + @VisibleForTesting static final int[] HALF_POWERS_OF_10 = {3, 31, 316, 3162, 31622, 316227, 3162277, 31622776, 316227766, Integer.MAX_VALUE}; private static int sqrtFloor(int x) { @@ -138,81 +79,17 @@ 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) { @@ -226,7 +103,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) { @@ -237,46 +114,18 @@ public final class IntMath { */ 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 >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 + // 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; } - return a << min(aTwos, bTwos); + return a; } /** * 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) { @@ -287,7 +136,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) { @@ -298,7 +147,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) { @@ -307,67 +156,9 @@ 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; - - /** - * 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 = { + + static final int[] FACTORIALS = { 1, 1, 1 * 2, @@ -382,8 +173,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(biggestBinomials[k], k) fits in an int, but not binomial(biggestBinomials[k]+1,k). - @VisibleForTesting static int[] biggestBinomials = { + // binomial(BIGGEST_BINOMIALS[k], k) fits in an int, but not binomial(BIGGEST_BINOMIALS[k]+1,k). + @VisibleForTesting static int[] BIGGEST_BINOMIALS = { Integer.MAX_VALUE, Integer.MAX_VALUE, 65536, @@ -402,20 +193,7 @@ 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 deleted file mode 100644 index 2f15360..0000000 --- a/guava-gwt/src-super/com/google/common/math/super/com/google/common/math/LongMath.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * 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 62c716a..5f78865 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,10 +38,6 @@ 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 */ @@ -472,8 +468,7 @@ public final class Chars { @Override public Character set(int index, Character element) { checkElementIndex(index, size()); char oldValue = array[start + index]; - // checkNotNull for GWT (do not optimize) - array[start + index] = checkNotNull(element); + array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize) return oldValue; } @@ -524,7 +519,7 @@ public final class Chars { } char[] toCharArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 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/Floats.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Floats.java deleted file mode 100644 index 8e4eaf7..0000000 --- a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Floats.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * 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 8f9315d..0cee6c4 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,10 +35,6 @@ 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 */ @@ -364,21 +360,20 @@ public final class Ints { } /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code int} value in the manner of {@link Number#intValue}. + * Copies a collection of {@code Integer} instances into a new array of + * primitive {@code int} values. * * <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 + * @param collection a collection of {@code Integer} objects * @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<? extends Number> collection) { + public static int[] toArray(Collection<Integer> collection) { if (collection instanceof IntArrayAsList) { return ((IntArrayAsList) collection).toIntArray(); } @@ -388,7 +383,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] = ((Number) checkNotNull(boxedArray[i])).intValue(); + array[i] = (Integer) checkNotNull(boxedArray[i]); } return array; } @@ -475,8 +470,7 @@ public final class Ints { @Override public Integer set(int index, Integer element) { checkElementIndex(index, size()); int oldValue = array[start + index]; - // checkNotNull for GWT (do not optimize) - array[start + index] = checkNotNull(element); + array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize) return oldValue; } @@ -527,7 +521,7 @@ public final class Ints { } int[] toIntArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 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/Doubles.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Longs.java index 3634fe7..000e055 100644 --- a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Doubles.java +++ b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Longs.java @@ -20,8 +20,6 @@ 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; @@ -34,80 +32,69 @@ import java.util.List; import java.util.RandomAccess; /** - * 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>. + * Static utility methods pertaining to {@code long} primitives, that are not + * already found in either {@link Long} or {@link Arrays}. * * @author Kevin Bourrillion * @since 1.0 */ @GwtCompatible(emulated = true) -public final class Doubles { - private Doubles() {} +public final class Longs { + private Longs() {} /** - * The number of bytes required to represent a primitive {@code double} + * The number of bytes required to represent a primitive {@code long} * 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 int BYTES = Double.SIZE / Byte.SIZE; + public static final long MAX_POWER_OF_TWO = 1L << (Long.SIZE - 2); /** * Returns a hash code for {@code value}; equal to the result of invoking - * {@code ((Double) value).hashCode()}. + * {@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. * - * @param value a primitive {@code double} value + * @param value a primitive {@code long} value * @return a hash code for the value */ - 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)); + public static int hashCode(long value) { + return (int) (value ^ (value >>> 32)); } /** - * 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}. + * Compares the two specified {@code long} values. The sign of the value + * returned is the same as that of {@code ((Long) a).compareTo(b)}. * - * @param a the first {@code double} to compare - * @param b the second {@code double} to compare + * @param a the first {@code long} to compare + * @param b the second {@code long} 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(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; + public static int compare(long a, long b) { + return (a < b) ? -1 : ((a > b) ? 1 : 0); } /** * 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}. + * {@code array}. * - * @param array an array of {@code double} values, possibly empty - * @param target a primitive {@code double} value + * @param array an array of {@code long} values, possibly empty + * @param target a primitive {@code long} value * @return {@code true} if {@code array[i] == target} for some value of {@code * i} */ - public static boolean contains(double[] array, double target) { - for (double value : array) { + public static boolean contains(long[] array, long target) { + for (long value : array) { if (value == target) { return true; } @@ -117,21 +104,20 @@ public final class Doubles { /** * 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}. + * {@code array}. * - * @param array an array of {@code double} values, possibly empty - * @param target a primitive {@code double} value + * @param array an array of {@code long} values, possibly empty + * @param target a primitive {@code long} 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(double[] array, double target) { + public static int indexOf(long[] array, long target) { return indexOf(array, target, 0, array.length); } // TODO(kevinb): consider making this public private static int indexOf( - double[] array, double target, int start, int end) { + long[] array, long target, int start, int end) { for (int i = start; i < end; i++) { if (array[i] == target) { return i; @@ -148,13 +134,10 @@ public final class Doubles { * 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(double[] array, double[] target) { + public static int indexOf(long[] array, long[] target) { checkNotNull(array, "array"); checkNotNull(target, "target"); if (target.length == 0) { @@ -175,21 +158,20 @@ public final class Doubles { /** * 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}. + * {@code array}. * - * @param array an array of {@code double} values, possibly empty - * @param target a primitive {@code double} value + * @param array an array of {@code long} values, possibly empty + * @param target a primitive {@code long} 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(double[] array, double target) { + public static int lastIndexOf(long[] array, long target) { return lastIndexOf(array, target, 0, array.length); } // TODO(kevinb): consider making this public private static int lastIndexOf( - double[] array, double target, int start, int end) { + long[] array, long target, int start, int end) { for (int i = end - 1; i >= start; i--) { if (array[i] == target) { return i; @@ -199,58 +181,60 @@ public final class Doubles { } /** - * Returns the least value present in {@code array}, using the same rules of - * comparison as {@link Math#min(double, double)}. + * Returns the least value present in {@code array}. * - * @param array a <i>nonempty</i> array of {@code double} values + * @param array a <i>nonempty</i> array of {@code long} 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 double min(double... array) { + public static long min(long... array) { checkArgument(array.length > 0); - double min = array[0]; + long min = array[0]; for (int i = 1; i < array.length; i++) { - min = Math.min(min, array[i]); + if (array[i] < min) { + min = array[i]; + } } return min; } /** - * Returns the greatest value present in {@code array}, using the same rules - * of comparison as {@link Math#max(double, double)}. + * Returns the greatest value present in {@code array}. * - * @param array a <i>nonempty</i> array of {@code double} values + * @param array a <i>nonempty</i> array of {@code long} 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 double max(double... array) { + public static long max(long... array) { checkArgument(array.length > 0); - double max = array[0]; + long max = array[0]; for (int i = 1; i < array.length; i++) { - max = Math.max(max, array[i]); + if (array[i] > max) { + max = array[i]; + } } return max; } /** * Returns the values from each provided array combined into a single array. - * For example, {@code concat(new double[] {a, b}, new double[] {}, new - * double[] {c}} returns the array {@code {a, b, c}}. + * For example, {@code concat(new long[] {a, b}, new long[] {}, new + * long[] {c}} returns the array {@code {a, b, c}}. * - * @param arrays zero or more {@code double} arrays + * @param arrays zero or more {@code long} arrays * @return a single array containing all the values from the source arrays, in * order */ - public static double[] concat(double[]... arrays) { + public static long[] concat(long[]... arrays) { int length = 0; - for (double[] array : arrays) { + for (long[] array : arrays) { length += array.length; } - double[] result = new double[length]; + long[] result = new long[length]; int pos = 0; - for (double[] array : arrays) { + for (long[] array : arrays) { System.arraycopy(array, 0, result, pos, array.length); pos += array.length; } @@ -273,8 +257,8 @@ public final class Doubles { * @return an array containing the values of {@code array}, with guaranteed * minimum length {@code minLength} */ - public static double[] ensureCapacity( - double[] array, int minLength, int padding) { + public static long[] ensureCapacity( + long[] array, int minLength, int padding) { checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); checkArgument(padding >= 0, "Invalid padding: %s", padding); return (array.length < minLength) @@ -283,34 +267,29 @@ public final class Doubles { } // Arrays.copyOf() requires Java 6 - private static double[] copyOf(double[] original, int length) { - double[] copy = new double[length]; + private static long[] copyOf(long[] original, int length) { + long[] copy = new long[length]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); return copy; } /** - * 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"}. + * 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"}. * * @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 double} values, possibly empty + * @param array an array of {@code long} values, possibly empty */ - public static String join(String separator, double... array) { + public static String join(String separator, long... 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); + StringBuilder builder = new StringBuilder(array.length * 10); builder.append(array[0]); for (int i = 1; i < array.length; i++) { builder.append(separator).append(array[i]); @@ -319,33 +298,33 @@ public final class Doubles { } /** - * Returns a comparator that compares two {@code double} arrays + * Returns a comparator that compares two {@code long} arrays * lexicographically. That is, it compares, using {@link - * #compare(double, double)}), the first pair of values that follow any + * #compare(long, long)}), 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.0] < [1.0, 2.0] < [2.0]}. + * {@code [] < [1L] < [1L, 2L] < [2L]}. * * <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(double[], double[])}. + * it is consistent with {@link Arrays#equals(long[], long[])}. * * @see <a href="http://en.wikipedia.org/wiki/Lexicographical_order"> * Lexicographical order article at Wikipedia</a> * @since 2.0 */ - public static Comparator<double[]> lexicographicalComparator() { + public static Comparator<long[]> lexicographicalComparator() { return LexicographicalComparator.INSTANCE; } - private enum LexicographicalComparator implements Comparator<double[]> { + private enum LexicographicalComparator implements Comparator<long[]> { INSTANCE; @Override - public int compare(double[] left, double[] right) { + public int compare(long[] left, long[] right) { int minLength = Math.min(left.length, right.length); for (int i = 0; i < minLength; i++) { - int result = Doubles.compare(left[i], right[i]); + int result = Longs.compare(left[i], right[i]); if (result != 0) { return result; } @@ -355,31 +334,30 @@ public final class Doubles { } /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code double} value in the manner of {@link Number#doubleValue}. + * Copies a collection of {@code Long} instances into a new array of + * primitive {@code long} values. * * <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 + * @param collection a collection of {@code Long} objects * @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 double[] toArray(Collection<? extends Number> collection) { - if (collection instanceof DoubleArrayAsList) { - return ((DoubleArrayAsList) collection).toDoubleArray(); + public static long[] toArray(Collection<Long> collection) { + if (collection instanceof LongArrayAsList) { + return ((LongArrayAsList) collection).toLongArray(); } Object[] boxedArray = collection.toArray(); int len = boxedArray.length; - double[] array = new double[len]; + long[] array = new long[len]; for (int i = 0; i < len; i++) { // checkNotNull for GWT (do not optimize) - array[i] = ((Number) checkNotNull(boxedArray[i])).doubleValue(); + array[i] = (Long) checkNotNull(boxedArray[i]); } return array; } @@ -391,35 +369,32 @@ public final class Doubles { * NullPointerException}. * * <p>The returned list maintains the values, but not the identities, of - * {@code Double} objects written to or read from it. For example, whether + * {@code Long} 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<Double> asList(double... backingArray) { + public static List<Long> asList(long... backingArray) { if (backingArray.length == 0) { return Collections.emptyList(); } - return new DoubleArrayAsList(backingArray); + return new LongArrayAsList(backingArray); } @GwtCompatible - private static class DoubleArrayAsList extends AbstractList<Double> + private static class LongArrayAsList extends AbstractList<Long> implements RandomAccess, Serializable { - final double[] array; + final long[] array; final int start; final int end; - DoubleArrayAsList(double[] array) { + LongArrayAsList(long[] array) { this(array, 0, array.length); } - DoubleArrayAsList(double[] array, int start, int end) { + LongArrayAsList(long[] array, int start, int end) { this.array = array; this.start = start; this.end = end; @@ -433,21 +408,21 @@ public final class Doubles { return false; } - @Override public Double get(int index) { + @Override public Long 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 Double) - && Doubles.indexOf(array, (Double) target, start, end) != -1; + return (target instanceof Long) + && Longs.indexOf(array, (Long) target, start, end) != -1; } @Override public int indexOf(Object target) { // Overridden to prevent a ton of boxing - if (target instanceof Double) { - int i = Doubles.indexOf(array, (Double) target, start, end); + if (target instanceof Long) { + int i = Longs.indexOf(array, (Long) target, start, end); if (i >= 0) { return i - start; } @@ -457,8 +432,8 @@ public final class Doubles { @Override public int lastIndexOf(Object target) { // Overridden to prevent a ton of boxing - if (target instanceof Double) { - int i = Doubles.lastIndexOf(array, (Double) target, start, end); + if (target instanceof Long) { + int i = Longs.lastIndexOf(array, (Long) target, start, end); if (i >= 0) { return i - start; } @@ -466,29 +441,28 @@ public final class Doubles { return -1; } - @Override public Double set(int index, Double element) { + @Override public Long set(int index, Long element) { checkElementIndex(index, size()); - double oldValue = array[start + index]; - // checkNotNull for GWT (do not optimize) - array[start + index] = checkNotNull(element); + long oldValue = array[start + index]; + array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize) return oldValue; } - @Override public List<Double> subList(int fromIndex, int toIndex) { + @Override public List<Long> subList(int fromIndex, int toIndex) { int size = size(); checkPositionIndexes(fromIndex, toIndex, size); if (fromIndex == toIndex) { return Collections.emptyList(); } - return new DoubleArrayAsList(array, start + fromIndex, start + toIndex); + return new LongArrayAsList(array, start + fromIndex, start + toIndex); } @Override public boolean equals(Object object) { if (object == this) { return true; } - if (object instanceof DoubleArrayAsList) { - DoubleArrayAsList that = (DoubleArrayAsList) object; + if (object instanceof LongArrayAsList) { + LongArrayAsList that = (LongArrayAsList) object; int size = size(); if (that.size() != size) { return false; @@ -506,13 +480,13 @@ public final class Doubles { @Override public int hashCode() { int result = 1; for (int i = start; i < end; i++) { - result = 31 * result + Doubles.hashCode(array[i]); + result = 31 * result + Longs.hashCode(array[i]); } return result; } @Override public String toString() { - StringBuilder builder = new StringBuilder(size() * 12); + StringBuilder builder = new StringBuilder(size() * 10); builder.append('[').append(array[start]); for (int i = start + 1; i < end; i++) { builder.append(", ").append(array[i]); @@ -520,10 +494,10 @@ public final class Doubles { return builder.append(']').toString(); } - double[] toDoubleArray() { - // Arrays.copyOfRange() is not available under GWT + long[] toLongArray() { + // Arrays.copyOfRange() requires Java 6 int size = size(); - double[] result = new double[size]; + long[] result = new long[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/Shorts.java b/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/Shorts.java index 92899c7..d1cee13 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,10 +35,6 @@ 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 */ @@ -366,21 +362,20 @@ public final class Shorts { } /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code short} value in the manner of {@link Number#shortValue}. + * Copies a collection of {@code Short} instances into a new array of + * primitive {@code short} values. * * <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 + * @param collection a collection of {@code Short} objects * @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<? extends Number> collection) { + public static short[] toArray(Collection<Short> collection) { if (collection instanceof ShortArrayAsList) { return ((ShortArrayAsList) collection).toShortArray(); } @@ -390,7 +385,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] = ((Number) checkNotNull(boxedArray[i])).shortValue(); + array[i] = (Short) checkNotNull(boxedArray[i]); } return array; } @@ -477,8 +472,7 @@ public final class Shorts { @Override public Short set(int index, Short element) { checkElementIndex(index, size()); short oldValue = array[start + index]; - // checkNotNull for GWT (do not optimize) - array[start + index] = checkNotNull(element); + array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize) return oldValue; } @@ -529,7 +523,7 @@ public final class Shorts { } short[] toShortArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 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 deleted file mode 100644 index 20e6dce..0000000 --- a/guava-gwt/src-super/com/google/common/primitives/super/com/google/common/primitives/UnsignedInteger.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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/com/google/common/ForceGuavaCompilationEntryPoint.java b/guava-gwt/src-super/com/google/common/testing/super/com/google/common/testing/Platform.java index f7260a2..9fa092e 100644 --- a/guava-gwt/src/com/google/common/ForceGuavaCompilationEntryPoint.java +++ b/guava-gwt/src-super/com/google/common/testing/super/com/google/common/testing/Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Guava Authors + * 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. @@ -14,16 +14,23 @@ * limitations under the License. */ -package com.google.common; +package com.google.common.testing; -import com.google.gwt.core.client.EntryPoint; +import static com.google.common.base.Preconditions.checkNotNull; /** - * A dummy entry point to convince Maven to compile our classes. + * Methods factored out so that they can be emulated differently in GWT. * * @author Chris Povirk */ -public class ForceGuavaCompilationEntryPoint implements EntryPoint { - @Override public void onModuleLoad() { +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); } + + private Platform() {} } diff --git a/guava-gwt/src-super/java/nio/charset/Charset.gwt.xml b/guava-gwt/src-super/java/nio/charset/Charset.gwt.xml deleted file mode 100644 index d23fb8c..0000000 --- a/guava-gwt/src-super/java/nio/charset/Charset.gwt.xml +++ /dev/null @@ -1,4 +0,0 @@ -<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 deleted file mode 100644 index 1463333..0000000 --- a/guava-gwt/src-super/java/nio/charset/Charset.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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 deleted file mode 100644 index 19755e2..0000000 --- a/guava-gwt/src-super/java/nio/charset/IllegalCharsetNameException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 deleted file mode 100644 index f489a1d..0000000 --- a/guava-gwt/src-super/java/nio/charset/UnsupportedCharsetException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 607d3f7..548f3a6 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 The Guava Authors + * Copyright (C) 2011 Google Inc. * * 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 886d8f7..77544b5 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 The Guava Authors + * Copyright (C) 2009 Google Inc. * * 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 49c05ce..61a8a04 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 The Guava Authors + * Copyright (C) 2009 Google Inc. * * 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 c1fc0f4..b25f476 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 The Guava Authors + * Copyright (C) 2011 Google Inc. * * 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 Charles Fry + * @author fry@google.com (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 52f3fb4..2ce1de3 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,8 +1,4 @@ /* - * 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 d9c16a6..ac98dcf 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 The Guava Authors + * Copyright (C) 2009 Google Inc. * * 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 fdd1b34..8696422 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 The Guava Authors + * Copyright (C) 2011 Google Inc. * * 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 deleted file mode 100644 index 9c4d507..0000000 --- a/guava-gwt/src/com/google/common/ForceGuavaCompilation.gwt.xml +++ /dev/null @@ -1,15 +0,0 @@ -<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/com/google/common/annotations/Annotations.gwt.xml b/guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml index 8903a62..0b388fa 100644 --- a/guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml +++ b/guava-gwt/src/com/google/common/annotations/Annotations.gwt.xml @@ -1,4 +1,3 @@ -<!-- 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 deleted file mode 100644 index ecdac0f..0000000 --- a/guava-gwt/src/com/google/common/base/Absent_CustomFieldSerializer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 d0c84c8..f1ca187 100644 --- a/guava-gwt/src/com/google/common/base/Base.gwt.xml +++ b/guava-gwt/src/com/google/common/base/Base.gwt.xml @@ -1,15 +1,5 @@ -<?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 7f0643f..77f93f2 100644 --- a/guava-gwt/src/com/google/common/base/GwtSerializationDependencies.java +++ b/guava-gwt/src/com/google/common/base/GwtSerializationDependencies.java @@ -18,10 +18,6 @@ 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. @@ -36,57 +32,4 @@ import javax.annotation.Nullable; @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 531550c..6f125f9 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 Kedar Kanitkar + * @author kkanitkar@google.com (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 deleted file mode 100644 index 710086d..0000000 --- a/guava-gwt/src/com/google/common/base/Present_CustomFieldSerializer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 62c7187..9c5b758 100644 --- a/guava-gwt/src/com/google/common/cache/Cache.gwt.xml +++ b/guava-gwt/src/com/google/common/cache/Cache.gwt.xml @@ -1,12 +1,9 @@ <?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"/> @@ -16,4 +13,6 @@ <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 deleted file mode 100644 index 4b9d370..0000000 --- a/guava-gwt/src/com/google/common/collect/AllEqualOrdering_CustomFieldSerializer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 e042bcb..31441af 100644 --- a/guava-gwt/src/com/google/common/collect/Collect.gwt.xml +++ b/guava-gwt/src/com/google/common/collect/Collect.gwt.xml @@ -1,12 +1,9 @@ <?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"/> @@ -15,5 +12,7 @@ <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/EmptyImmutableBiMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/EmptyImmutableMap_CustomFieldSerializer.java index c2d7a05..67d63b2 100644 --- a/guava-gwt/src/com/google/common/collect/EmptyImmutableBiMap_CustomFieldSerializer.java +++ b/guava-gwt/src/com/google/common/collect/EmptyImmutableMap_CustomFieldSerializer.java @@ -21,18 +21,22 @@ import com.google.gwt.user.client.rpc.SerializationStreamWriter; /** * This class implements the GWT serialization of - * {@link EmptyImmutableBiMap}. - * + * {@link EmptyImmutableMap}. + * * @author Chris Povirk */ -public class EmptyImmutableBiMap_CustomFieldSerializer { - public static void deserialize(SerializationStreamReader reader, EmptyImmutableBiMap instance) { +public class EmptyImmutableMap_CustomFieldSerializer { + + public static void deserialize(SerializationStreamReader reader, + EmptyImmutableMap instance) { } - public static EmptyImmutableBiMap instantiate(SerializationStreamReader reader) { - return EmptyImmutableBiMap.INSTANCE; + public static EmptyImmutableMap instantiate( + SerializationStreamReader reader) { + return EmptyImmutableMap.INSTANCE; } - public static void serialize(SerializationStreamWriter writer, EmptyImmutableBiMap instance) { + public static void serialize(SerializationStreamWriter writer, + EmptyImmutableMap 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 deleted file mode 100644 index a47f6a0..0000000 --- a/guava-gwt/src/com/google/common/collect/EmptyImmutableSortedMap_CustomFieldSerializer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/com/google/common/collect/ForwardingImmutableList_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ForwardingImmutableList_CustomFieldSerializer.java deleted file mode 100644 index b6767a2..0000000 --- a/guava-gwt/src/com/google/common/collect/ForwardingImmutableList_CustomFieldSerializer.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 ForwardingImmutableList} cannot be instantiated, we still - * need a custom field serializer. TODO(cpovirk): why? - * - * @author Hayward Chan - */ -public final class ForwardingImmutableList_CustomFieldSerializer {} diff --git a/guava-gwt/src/com/google/common/collect/GwtPlatform.java b/guava-gwt/src/com/google/common/collect/GwtPlatform.java deleted file mode 100644 index e4d62e1..0000000 --- a/guava-gwt/src/com/google/common/collect/GwtPlatform.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 7466c22..4cb55ee 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) 2009 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. @@ -16,10 +16,40 @@ 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; + /** - * Even though {@link ImmutableAsList} cannot be instantiated, we still need - * a custom field serializer. TODO(cpovirk): why? + * This class implements the server-side GWT serialization of + * {@link ImmutableAsList}. * * @author Hayward Chan */ -public final class ImmutableAsList_CustomFieldSerializer {} +@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); + } +} diff --git a/guava-gwt/src/com/google/common/collect/ImmutableBiMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/ImmutableBiMap_CustomFieldSerializer.java deleted file mode 100644 index 5f290dc..0000000 --- a/guava-gwt/src/com/google/common/collect/ImmutableBiMap_CustomFieldSerializer.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 821c33a..ab1512a 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 Kyle Butt + * @author iteratee@google.com (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 deleted file mode 100644 index 868cb74..0000000 --- a/guava-gwt/src/com/google/common/collect/ImmutableEnumMap_CustomFieldSerializer.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 70cf908..6293686 100644 --- a/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializer.java +++ b/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializer.java @@ -16,11 +16,44 @@ 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; + /** - * 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? + * This class implements the GWT serialization of {@link ImmutableSortedMap}. * * @author Chris Povirk */ -public final class ImmutableSortedMap_CustomFieldSerializer {} +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); + } +} diff --git a/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializerBase.java b/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializerBase.java deleted file mode 100644 index 4ccaaa1..0000000 --- a/guava-gwt/src/com/google/common/collect/ImmutableSortedMap_CustomFieldSerializerBase.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 597207d..fdc1956 100644 --- a/guava-gwt/src/com/google/common/collect/LexicographicalOrdering_CustomFieldSerializer.java +++ b/guava-gwt/src/com/google/common/collect/LexicographicalOrdering_CustomFieldSerializer.java @@ -21,8 +21,7 @@ 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 a0a9163..30a280e 100644 --- a/guava-gwt/src/com/google/common/collect/LinkedHashMultimap_CustomFieldSerializer.java +++ b/guava-gwt/src/com/google/common/collect/LinkedHashMultimap_CustomFieldSerializer.java @@ -20,13 +20,11 @@ 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 { @@ -36,39 +34,28 @@ public class LinkedHashMultimap_CustomFieldSerializer { } public static LinkedHashMultimap<Object, Object> instantiate( - 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)); + 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)); } - 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 stream, + public static void serialize(SerializationStreamWriter out, LinkedHashMultimap<?, ?> multimap) throws SerializationException { - stream.writeInt(multimap.valueSetCapacity); - stream.writeInt(multimap.keySet().size()); - for (Object key : multimap.keySet()) { - stream.writeObject(key); - } - stream.writeInt(multimap.size()); + Multimap_CustomFieldSerializerBase.serialize(out, multimap); for (Map.Entry<?, ?> entry : multimap.entries()) { - stream.writeObject(entry.getKey()); - stream.writeObject(entry.getValue()); + out.writeObject(entry.getKey()); + out.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 7c44bba..0cad199 100644 --- a/guava-gwt/src/com/google/common/collect/Multiset_CustomFieldSerializerBase.java +++ b/guava-gwt/src/com/google/common/collect/Multiset_CustomFieldSerializerBase.java @@ -23,12 +23,25 @@ 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 + * {@link #serialize(SerializationStreamWriter, Multiset)} and to either + * {@link #instantiate(SerializationStreamReader, ImmutableMultiset.Builder)} or * {@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/com/google/common/collect/RegularImmutableSortedMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/RegularImmutableSortedMap_CustomFieldSerializer.java deleted file mode 100644 index 9f64e37..0000000 --- a/guava-gwt/src/com/google/common/collect/RegularImmutableSortedMap_CustomFieldSerializer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/SingletonImmutableBiMap_CustomFieldSerializer.java b/guava-gwt/src/com/google/common/collect/SingletonImmutableMap_CustomFieldSerializer.java index 7b87633..6a3ef38 100644 --- a/guava-gwt/src/com/google/common/collect/SingletonImmutableBiMap_CustomFieldSerializer.java +++ b/guava-gwt/src/com/google/common/collect/SingletonImmutableMap_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 SingletonImmutableBiMap}. + * This class implements the GWT serialization of {@link SingletonImmutableMap}. * * @author Chris Povirk */ -public class SingletonImmutableBiMap_CustomFieldSerializer { +public class SingletonImmutableMap_CustomFieldSerializer { public static void deserialize(SerializationStreamReader reader, - SingletonImmutableBiMap<?, ?> instance) { + SingletonImmutableMap<?, ?> instance) { } - public static SingletonImmutableBiMap<Object, Object> instantiate( + public static SingletonImmutableMap<Object, Object> instantiate( SerializationStreamReader reader) throws SerializationException { Object key = checkNotNull(reader.readObject()); Object value = checkNotNull(reader.readObject()); - return new SingletonImmutableBiMap<Object, Object>(key, value); + return new SingletonImmutableMap<Object, Object>(key, value); } public static void serialize(SerializationStreamWriter writer, - SingletonImmutableBiMap<?, ?> instance) throws SerializationException { + SingletonImmutableMap<?, ?> instance) throws SerializationException { writer.writeObject(instance.singleKey); writer.writeObject(instance.singleValue); } 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 a87feb1..1ccb925 100644 --- a/guava-gwt/src/com/google/common/math/Math.gwt.xml +++ b/guava-gwt/src/com/google/common/math/Math.gwt.xml @@ -1,13 +1,6 @@ -<?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 b6f5049..6df61fb 100644 --- a/guava-gwt/src/com/google/common/net/Net.gwt.xml +++ b/guava-gwt/src/com/google/common/net/Net.gwt.xml @@ -1,19 +1,20 @@ <?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 3966a51..fa86e6b 100644 --- a/guava-gwt/src/com/google/common/primitives/Primitives.gwt.xml +++ b/guava-gwt/src/com/google/common/primitives/Primitives.gwt.xml @@ -1,13 +1,6 @@ -<?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 deleted file mode 100644 index 732730e..0000000 --- a/guava-gwt/src/com/google/common/primitives/UnsignedLong_CustomFieldSerializer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 1bb0da0..cc8ffd6 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,13 +1,4 @@ -<?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/cache/testModule.gwt.xml b/guava-gwt/test/com/google/common/cache/testModule.gwt.xml deleted file mode 100644 index 7de886b..0000000 --- a/guava-gwt/test/com/google/common/cache/testModule.gwt.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?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 72ee8bf..47b2f23 100644 --- a/guava-gwt/test/com/google/common/collect/testModule.gwt.xml +++ b/guava-gwt/test/com/google/common/collect/testModule.gwt.xml @@ -4,10 +4,9 @@ <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 deleted file mode 100644 index e06885a..0000000 --- a/guava-gwt/test/com/google/common/io/TestModuleEntryPoint.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3611690..0000000 --- a/guava-gwt/test/com/google/common/io/testModule.gwt.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?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 58552f1..17f579d 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="org.junit.contrib.truth.Truth"/> <inherits name="com.google.common.annotations.Annotations"/> + <inherits name="org.junit.contrib.truth.Truth"/> <entry-point class="com.google.common.math.TestModuleEntryPoint"/> <source path=""/> diff --git a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAddables.java b/guava-gwt/test/com/google/common/net/TestPlatform.java index a911ef3..39301d5 100644 --- a/guava-gwt/src-super/com/google/common/cache/super/com/google/common/cache/LongAddables.java +++ b/guava-gwt/test/com/google/common/net/TestPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Guava Authors + * 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. @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.google.common.cache; +package com.google.common.net; + +import com.google.common.annotations.GwtCompatible; /** - * GWT emulation for LongAddables. - * - * @author Louis Wasserman + * @author Hayward Chan */ -final class LongAddables { - public static LongAddable create() { - return new LongAdder(); - } +@GwtCompatible(emulated = true) +class TestPlatform { } 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 1b61e1b..0e6338b 100644 --- a/guava-gwt/test/com/google/common/net/testModule.gwt.xml +++ b/guava-gwt/test/com/google/common/net/testModule.gwt.xml @@ -6,6 +6,7 @@ <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 72da4d2..7bb4013 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/cache/TestModuleEntryPoint.java b/guava-gwt/test/com/google/common/testing/TestModuleEntryPoint.java index baf8478..ed6e199 100644 --- a/guava-gwt/test/com/google/common/cache/TestModuleEntryPoint.java +++ b/guava-gwt/test/com/google/common/testing/TestModuleEntryPoint.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.common.cache; +package com.google.common.testing; import com.google.gwt.core.client.EntryPoint; diff --git a/guava-gwt/src/com/google/common/io/Io.gwt.xml b/guava-gwt/test/com/google/common/testing/Testing.gwt.xml index f272c7c..e1f85eb 100644 --- a/guava-gwt/src/com/google/common/io/Io.gwt.xml +++ b/guava-gwt/test/com/google/common/testing/Testing.gwt.xml @@ -1,5 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- semi-autogenerated module descriptor --> <module> <source path=""/> @@ -10,8 +9,10 @@ <inherits name="com.google.common.base.Base"/> - <inherits name="com.google.common.math.Math"/> + <inherits name="com.google.common.collect.Collect"/> - <inherits name="com.google.common.primitives.Primitives"/> + <inherits name="com.google.gwt.junit.JUnit"/> + <inherits name="java.util.Util"/> + </module> diff --git a/guava-gwt/test/com/google/common/util/concurrent/testModule.gwt.xml b/guava-gwt/test/com/google/common/testing/testModule.gwt.xml index ffae299..2015440 100644 --- a/guava-gwt/test/com/google/common/util/concurrent/testModule.gwt.xml +++ b/guava-gwt/test/com/google/common/testing/testModule.gwt.xml @@ -2,11 +2,9 @@ <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.util.concurrent.Concurrent"/> - <entry-point class="com.google.common.util.concurrent.TestModuleEntryPoint"/> + <inherits name="com.google.common.testing.Testing"/> + <entry-point class="com.google.common.testing.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 deleted file mode 100644 index 7ed9721..0000000 --- a/guava-gwt/test/com/google/common/util/concurrent/TestModuleEntryPoint.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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() { - } -} |