diff options
Diffstat (limited to 'guava/src')
364 files changed, 14494 insertions, 40345 deletions
diff --git a/guava/src/com/google/common/annotations/Beta.java b/guava/src/com/google/common/annotations/Beta.java index 5eefe9a..07009b0 100644 --- a/guava/src/com/google/common/annotations/Beta.java +++ b/guava/src/com/google/common/annotations/Beta.java @@ -26,15 +26,7 @@ import java.lang.annotation.Target; * Signifies that a public API (public class, method or field) is subject to * incompatible changes, or even removal, in a future release. An API bearing * this annotation is exempt from any compatibility guarantees made by its - * containing library. Note that the presence of this annotation implies nothing - * about the quality or performance of the API in question, only the fact that - * it is not "API-frozen." - * - * <p>It is generally safe for <i>applications</i> to depend on beta APIs, at - * the cost of some extra work during upgrades. However it is generally - * inadvisable for <i>libraries</i> (which get included on users' CLASSPATHs, - * outside the library developers' control) to do so. - * + * containing library. * * @author Kevin Bourrillion */ @@ -47,4 +39,5 @@ import java.lang.annotation.Target; ElementType.TYPE}) @Documented @GwtCompatible +@Beta public @interface Beta {} diff --git a/guava/src/com/google/common/annotations/GwtIncompatible.java b/guava/src/com/google/common/annotations/GwtIncompatible.java index a56d746..43ae705 100644 --- a/guava/src/com/google/common/annotations/GwtIncompatible.java +++ b/guava/src/com/google/common/annotations/GwtIncompatible.java @@ -36,12 +36,11 @@ import java.lang.annotation.Target; * @author Charles Fry */ @Retention(RetentionPolicy.CLASS) -@Target({ - ElementType.TYPE, ElementType.METHOD, - ElementType.CONSTRUCTOR, ElementType.FIELD }) +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD }) @Documented @GwtCompatible public @interface GwtIncompatible { + /** * Describes why the annotated element is incompatible with GWT. Since this is * generally due to a dependence on a type/method which GWT doesn't support, @@ -49,4 +48,5 @@ public @interface GwtIncompatible { * "Class.isInstance". */ String value(); + } diff --git a/guava/src/com/google/common/annotations/VisibleForTesting.java b/guava/src/com/google/common/annotations/VisibleForTesting.java index 6f867db..e591719 100644 --- a/guava/src/com/google/common/annotations/VisibleForTesting.java +++ b/guava/src/com/google/common/annotations/VisibleForTesting.java @@ -17,8 +17,8 @@ package com.google.common.annotations; /** - * Annotates a program element that exists, or is more widely visible than - * otherwise necessary, only for use in test code. + * An annotation that indicates that the visibility of a type or member has + * been relaxed to make the code testable. * * @author Johannes Henkel */ diff --git a/guava/src/com/google/common/base/Absent.java b/guava/src/com/google/common/base/Absent.java deleted file mode 100644 index a182236..0000000 --- a/guava/src/com/google/common/base/Absent.java +++ /dev/null @@ -1,89 +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.GwtCompatible; - -import java.util.Collections; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * Implementation of an {@link Optional} not containing a reference. - */ -@GwtCompatible -final class Absent extends Optional<Object> { - static final Absent INSTANCE = new Absent(); - - private Absent() {} - - @Override public boolean isPresent() { - return false; - } - - @Override public Object get() { - throw new IllegalStateException("Optional.get() cannot be called on an absent value"); - } - - @Override public Object or(Object defaultValue) { - return checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); - } - - @SuppressWarnings("unchecked") // safe covariant cast - @Override public Optional<Object> or(Optional<?> secondChoice) { - return (Optional) checkNotNull(secondChoice); - } - - @Override public Object or(Supplier<?> supplier) { - return checkNotNull(supplier.get(), - "use Optional.orNull() instead of a Supplier that returns null"); - } - - @Override @Nullable public Object orNull() { - return null; - } - - @Override public Set<Object> asSet() { - return Collections.emptySet(); - } - - @Override public <V> Optional<V> transform(Function<Object, V> function) { - checkNotNull(function); - return Optional.absent(); - } - - @Override public boolean equals(@Nullable Object object) { - return object == this; - } - - @Override public int hashCode() { - return 0x598df91c; - } - - @Override public String toString() { - return "Optional.absent()"; - } - - private Object readResolve() { - return INSTANCE; - } - - private static final long serialVersionUID = 0; -} diff --git a/guava/src/com/google/common/base/Ascii.java b/guava/src/com/google/common/base/Ascii.java index 3ccd0fe..a23a11f 100644 --- a/guava/src/com/google/common/base/Ascii.java +++ b/guava/src/com/google/common/base/Ascii.java @@ -16,6 +16,7 @@ package com.google.common.base; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** @@ -213,7 +214,7 @@ public final class Ascii { public static final byte DLE = 16; /** - * Device Control 1. Characters for the control + * Device Controls: Characters for the control * of ancillary devices associated with data processing or * telecommunication systems, more especially switching devices "on" or * "off." (If a single "stop" control is required to interrupt or turn @@ -224,7 +225,7 @@ public final class Ascii { public static final byte DC1 = 17; // aka XON /** - * Transmission On: Although originally defined as DC1, this ASCII + * Transmission on/off: Although originally defined as DC1, this ASCII * control character is now better known as the XON code used for software * flow control in serial communications. The main use is restarting * the transmission after the communication has been stopped by the XOFF @@ -235,40 +236,28 @@ public final class Ascii { public static final byte XON = 17; // aka DC1 /** - * Device Control 2. Characters for the control - * of ancillary devices associated with data processing or - * telecommunication systems, more especially switching devices "on" or - * "off." (If a single "stop" control is required to interrupt or turn - * off ancillary devices, DC4 is the preferred assignment.) + * @see #DC1 * * @since 8.0 */ public static final byte DC2 = 18; /** - * Device Control 3. Characters for the control - * of ancillary devices associated with data processing or - * telecommunication systems, more especially switching devices "on" or - * "off." (If a single "stop" control is required to interrupt or turn - * off ancillary devices, DC4 is the preferred assignment.) + * @see #DC1 * * @since 8.0 */ public static final byte DC3 = 19; // aka XOFF /** - * Transmission off. See {@link #XON} for explanation. + * Transmission off. @see #XON * * @since 8.0 */ public static final byte XOFF = 19; // aka DC3 /** - * Device Control 4. Characters for the control - * of ancillary devices associated with data processing or - * telecommunication systems, more especially switching devices "on" or - * "off." (If a single "stop" control is required to interrupt or turn - * off ancillary devices, DC4 is the preferred assignment.) + * @see #DC1 * * @since 8.0 */ @@ -341,7 +330,7 @@ public final class Ascii { public static final byte ESC = 27; /** - * File Separator: These four information separators may be + * File/Group/Record/Unit Separator: These information separators may be * used within data in optional fashion, except that their hierarchical * relationship shall be: FS is the most inclusive, then GS, then RS, * and US is least inclusive. (The content and length of a File, Group, @@ -352,33 +341,21 @@ public final class Ascii { public static final byte FS = 28; /** - * Group Separator: These four information separators may be - * used within data in optional fashion, except that their hierarchical - * relationship shall be: FS is the most inclusive, then GS, then RS, - * and US is least inclusive. (The content and length of a File, Group, - * Record, or Unit are not specified.) + * @see #FS * * @since 8.0 */ public static final byte GS = 29; /** - * Record Separator: These four information separators may be - * used within data in optional fashion, except that their hierarchical - * relationship shall be: FS is the most inclusive, then GS, then RS, - * and US is least inclusive. (The content and length of a File, Group, - * Record, or Unit are not specified.) + * @see #FS * * @since 8.0 */ public static final byte RS = 30; /** - * Unit Separator: These four information separators may be - * used within data in optional fashion, except that their hierarchical - * relationship shall be: FS is the most inclusive, then GS, then RS, - * and US is least inclusive. (The content and length of a File, Group, - * Record, or Unit are not specified.) + * @see #FS * * @since 8.0 */ @@ -412,16 +389,18 @@ public final class Ascii { /** * The minimum value of an ASCII character. * - * @since 9.0 (was type {@code int} before 12.0) + * @since 9.0 */ - public static final char MIN = 0; + @Beta + public static final int MIN = 0; /** * The maximum value of an ASCII character. * - * @since 9.0 (was type {@code int} before 12.0) + * @since 9.0 */ - public static final char MAX = 127; + @Beta + public static final int MAX = 127; /** * Returns a copy of the input string in which all {@linkplain #isUpperCase(char) uppercase ASCII @@ -429,21 +408,10 @@ public final class Ascii { * modification. */ public static String toLowerCase(String string) { - return toLowerCase((CharSequence) string); - } - - /** - * Returns a copy of the input character sequence in which all {@linkplain #isUpperCase(char) - * uppercase ASCII characters} have been converted to lowercase. All other characters are copied - * without modification. - * - * @since 14.0 - */ - public static String toLowerCase(CharSequence chars) { - int length = chars.length(); + int length = string.length(); StringBuilder builder = new StringBuilder(length); for (int i = 0; i < length; i++) { - builder.append(toLowerCase(chars.charAt(i))); + builder.append(toLowerCase(string.charAt(i))); } return builder.toString(); } @@ -455,28 +423,17 @@ public final class Ascii { public static char toLowerCase(char c) { return isUpperCase(c) ? (char) (c ^ 0x20) : c; } - + /** * Returns a copy of the input string in which all {@linkplain #isLowerCase(char) lowercase ASCII * characters} have been converted to uppercase. All other characters are copied without * modification. */ public static String toUpperCase(String string) { - return toUpperCase((CharSequence) string); - } - - /** - * Returns a copy of the input character sequence in which all {@linkplain #isLowerCase(char) - * lowercase ASCII characters} have been converted to uppercase. All other characters are copied - * without modification. - * - * @since 14.0 - */ - public static String toUpperCase(CharSequence chars) { - int length = chars.length(); + int length = string.length(); StringBuilder builder = new StringBuilder(length); for (int i = 0; i < length; i++) { - builder.append(toUpperCase(chars.charAt(i))); + builder.append(toUpperCase(string.charAt(i))); } return builder.toString(); } diff --git a/guava/src/com/google/common/base/CaseFormat.java b/guava/src/com/google/common/base/CaseFormat.java index b8b02c9..8ef7c5c 100644 --- a/guava/src/com/google/common/base/CaseFormat.java +++ b/guava/src/com/google/common/base/CaseFormat.java @@ -16,8 +16,6 @@ package com.google.common.base; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.GwtCompatible; /** @@ -31,74 +29,27 @@ public enum CaseFormat { /** * Hyphenated variable naming convention, e.g., "lower-hyphen". */ - LOWER_HYPHEN(CharMatcher.is('-'), "-") { - @Override String normalizeWord(String word) { - return Ascii.toLowerCase(word); - } - @Override String convert(CaseFormat format, String s) { - if (format == LOWER_UNDERSCORE) { - return s.replace('-', '_'); - } - if (format == UPPER_UNDERSCORE) { - return Ascii.toUpperCase(s.replace('-', '_')); - } - return super.convert(format, s); - } - }, + LOWER_HYPHEN(CharMatcher.is('-'), "-"), /** * C++ variable naming convention, e.g., "lower_underscore". */ - LOWER_UNDERSCORE(CharMatcher.is('_'), "_") { - @Override String normalizeWord(String word) { - return Ascii.toLowerCase(word); - } - @Override String convert(CaseFormat format, String s) { - if (format == LOWER_HYPHEN) { - return s.replace('_', '-'); - } - if (format == UPPER_UNDERSCORE) { - return Ascii.toUpperCase(s); - } - return super.convert(format, s); - } - }, + LOWER_UNDERSCORE(CharMatcher.is('_'), "_"), /** * Java variable naming convention, e.g., "lowerCamel". */ - LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { - @Override String normalizeWord(String word) { - return firstCharOnlyToUpper(word); - } - }, + LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), ""), /** * Java and C++ class naming convention, e.g., "UpperCamel". */ - UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { - @Override String normalizeWord(String word) { - return firstCharOnlyToUpper(word); - } - }, + UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), ""), /** * Java and C++ constant naming convention, e.g., "UPPER_UNDERSCORE". */ - UPPER_UNDERSCORE(CharMatcher.is('_'), "_") { - @Override String normalizeWord(String word) { - return Ascii.toUpperCase(word); - } - @Override String convert(CaseFormat format, String s) { - if (format == LOWER_HYPHEN) { - return Ascii.toLowerCase(s.replace('_', '-')); - } - if (format == LOWER_UNDERSCORE) { - return Ascii.toLowerCase(s); - } - return super.convert(format, s); - } - }; + UPPER_UNDERSCORE(CharMatcher.is('_'), "_"); private final CharMatcher wordBoundary; private final String wordSeparator; @@ -109,21 +60,51 @@ public enum CaseFormat { } /** - * Converts the specified {@code String str} from this format to the specified {@code format}. A - * "best effort" approach is taken; if {@code str} does not conform to the assumed format, then - * the behavior of this method is undefined but we make a reasonable effort at converting anyway. + * Converts the specified {@code String s} from this format to the specified {@code format}. A + * "best effort" approach is taken; if {@code s} does not conform to the assumed format, then the + * behavior of this method is undefined but we make a reasonable effort at converting anyway. */ - public final String to(CaseFormat format, String str) { - checkNotNull(format); - checkNotNull(str); - return (format == this) ? str : convert(format, str); - } + public String to(CaseFormat format, String s) { + if (format == null) { + throw new NullPointerException(); + } + if (s == null) { + throw new NullPointerException(); + } - /** - * Enum values can override for performance reasons. - */ - String convert(CaseFormat format, String s) { - // deal with camel conversion + if (format == this) { + return s; + } + + /* optimize cases where no camel conversion is required */ + switch (this) { + case LOWER_HYPHEN: + switch (format) { + case LOWER_UNDERSCORE: + return s.replace('-', '_'); + case UPPER_UNDERSCORE: + return Ascii.toUpperCase(s.replace('-', '_')); + } + break; + case LOWER_UNDERSCORE: + switch (format) { + case LOWER_HYPHEN: + return s.replace('_', '-'); + case UPPER_UNDERSCORE: + return Ascii.toUpperCase(s); + } + break; + case UPPER_UNDERSCORE: + switch (format) { + case LOWER_HYPHEN: + return Ascii.toLowerCase(s.replace('_', '-')); + case LOWER_UNDERSCORE: + return Ascii.toLowerCase(s); + } + break; + } + + // otherwise, deal with camel conversion StringBuilder out = null; int i = 0; int j = -1; @@ -138,23 +119,46 @@ public enum CaseFormat { out.append(format.wordSeparator); i = j + wordSeparator.length(); } - return (i == 0) - ? format.normalizeFirstWord(s) - : out.append(format.normalizeWord(s.substring(i))).toString(); + if (i == 0) { + return format.normalizeFirstWord(s); + } + out.append(format.normalizeWord(s.substring(i))); + return out.toString(); } - abstract String normalizeWord(String word); - private String normalizeFirstWord(String word) { - return (this == LOWER_CAMEL) ? Ascii.toLowerCase(word) : normalizeWord(word); + switch (this) { + case LOWER_CAMEL: + return Ascii.toLowerCase(word); + default: + return normalizeWord(word); + } + } + + private String normalizeWord(String word) { + switch (this) { + case LOWER_HYPHEN: + return Ascii.toLowerCase(word); + case LOWER_UNDERSCORE: + return Ascii.toLowerCase(word); + case LOWER_CAMEL: + return firstCharOnlyToUpper(word); + case UPPER_CAMEL: + return firstCharOnlyToUpper(word); + case UPPER_UNDERSCORE: + return Ascii.toUpperCase(word); + } + throw new RuntimeException("unknown case: " + this); } private static String firstCharOnlyToUpper(String word) { - return (word.isEmpty()) - ? word - : new StringBuilder(word.length()) - .append(Ascii.toUpperCase(word.charAt(0))) - .append(Ascii.toLowerCase(word.substring(1))) - .toString(); + int length = word.length(); + if (length == 0) { + return word; + } + return new StringBuilder(length) + .append(Ascii.toUpperCase(word.charAt(0))) + .append(Ascii.toLowerCase(word.substring(1))) + .toString(); } } diff --git a/guava/src/com/google/common/base/CharMatcher.java b/guava/src/com/google/common/base/CharMatcher.java index e79f7d4..1bea5c8 100644 --- a/guava/src/com/google/common/base/CharMatcher.java +++ b/guava/src/com/google/common/base/CharMatcher.java @@ -21,10 +21,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; +import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; +import java.util.List; import javax.annotation.CheckReturnValue; @@ -45,17 +45,38 @@ import javax.annotation.CheckReturnValue; * 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) +@GwtCompatible public abstract class CharMatcher implements Predicate<Character> { // Constants + + // Excludes 2000-2000a, which is handled as a range + private static final String BREAKING_WHITESPACE_CHARS = + "\t\n\013\f\r \u0085\u1680\u2028\u2029\u205f\u3000"; + + // Excludes 2007, which is handled as a gap in a pair of ranges + private static final String NON_BREAKING_WHITESPACE_CHARS = + "\u00a0\u180e\u202f"; + + /** + * 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 = + anyOf(BREAKING_WHITESPACE_CHARS + NON_BREAKING_WHITESPACE_CHARS) + .or(inRange('\u2000', '\u200a')) + .precomputed(); + /** * 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 @@ -63,96 +84,40 @@ public abstract class CharMatcher implements Predicate<Character> { * * @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"; - } - }; + public static final CharMatcher BREAKING_WHITESPACE = + anyOf(BREAKING_WHITESPACE_CHARS) + .or(inRange('\u2000', '\u2006')) + .or(inRange('\u2008', '\u200a')) + .precomputed(); /** * 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(); - } + public static final CharMatcher ASCII = inRange('\0', '\u007f'); /** * 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()); + public static final CharMatcher DIGIT; + + static { + CharMatcher digit = inRange('0', '9'); + String zeroes = + "\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"; + for (char base : zeroes.toCharArray()) { + digit = digit.or(inRange(base, (char) (base + 9))); + } + DIGIT = digit.precomputed(); + } /** * 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") { + public static final CharMatcher JAVA_DIGIT = new CharMatcher() { @Override public boolean matches(char c) { return Character.isDigit(c); } @@ -163,7 +128,7 @@ public abstract class CharMatcher implements Predicate<Character> { * 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") { + public static final CharMatcher JAVA_LETTER = new CharMatcher() { @Override public boolean matches(char c) { return Character.isLetter(c); } @@ -173,8 +138,7 @@ public abstract class CharMatcher implements Predicate<Character> { * 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") { + public static final CharMatcher JAVA_LETTER_OR_DIGIT = new CharMatcher() { @Override public boolean matches(char c) { return Character.isLetterOrDigit(c); } @@ -184,8 +148,7 @@ public abstract class CharMatcher implements Predicate<Character> { * 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") { + public static final CharMatcher JAVA_UPPER_CASE = new CharMatcher() { @Override public boolean matches(char c) { return Character.isUpperCase(c); } @@ -195,8 +158,7 @@ public abstract class CharMatcher implements Predicate<Character> { * 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") { + public static final CharMatcher JAVA_LOWER_CASE = new CharMatcher() { @Override public boolean matches(char c) { return Character.isLowerCase(c); } @@ -207,31 +169,26 @@ public abstract class CharMatcher implements Predicate<Character> { * Character#isISOControl(char)}. */ public static final CharMatcher JAVA_ISO_CONTROL = - inRange('\u0000', '\u001f') - .or(inRange('\u007f', '\u009f')) - .withToString("CharMatcher.JAVA_ISO_CONTROL"); + inRange('\u0000', '\u001f').or(inRange('\u007f', '\u009f')); /** * 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); - - } + public static final CharMatcher INVISIBLE = inRange('\u0000', '\u0020') + .or(inRange('\u007f', '\u00a0')) + .or(is('\u00ad')) + .or(inRange('\u0600', '\u0603')) + .or(anyOf("\u06dd\u070f\u1680\u17b4\u17b5\u180e")) + .or(inRange('\u2000', '\u200f')) + .or(inRange('\u2028', '\u202f')) + .or(inRange('\u205f', '\u2064')) + .or(inRange('\u206a', '\u206f')) + .or(is('\u3000')) + .or(inRange('\ud800', '\uf8ff')) + .or(anyOf("\ufeff\ufff9\ufffa\ufffb")) + .precomputed(); /** * Determines whether a character is single-width (not double-width). When in doubt, this matcher @@ -241,13 +198,24 @@ public abstract class CharMatcher implements Predicate<Character> { * <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()); + public static final CharMatcher SINGLE_WIDTH = inRange('\u0000', '\u04f9') + .or(is('\u05be')) + .or(inRange('\u05d0', '\u05ea')) + .or(is('\u05f3')) + .or(is('\u05f4')) + .or(inRange('\u0600', '\u06ff')) + .or(inRange('\u0750', '\u077f')) + .or(inRange('\u0e00', '\u0e7f')) + .or(inRange('\u1e00', '\u20af')) + .or(inRange('\u2100', '\u213a')) + .or(inRange('\ufb50', '\ufdff')) + .or(inRange('\ufe70', '\ufeff')) + .or(inRange('\uff61', '\uffdc')) + .precomputed(); /** Matches any character. */ public static final CharMatcher ANY = - new FastMatcher("CharMatcher.ANY") { + new CharMatcher() { @Override public boolean matches(char c) { return true; } @@ -319,11 +287,15 @@ public abstract class CharMatcher implements Predicate<Character> { @Override public CharMatcher negate() { return NONE; } + + @Override public CharMatcher precomputed() { + return this; + } }; /** Matches no characters. */ public static final CharMatcher NONE = - new FastMatcher("CharMatcher.NONE") { + new CharMatcher() { @Override public boolean matches(char c) { return false; } @@ -374,16 +346,6 @@ public abstract class CharMatcher implements Predicate<Character> { 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; @@ -401,6 +363,12 @@ public abstract class CharMatcher implements Predicate<Character> { @Override public CharMatcher negate() { return ANY; } + + @Override void setBits(LookupTable table) {} + + @Override public CharMatcher precomputed() { + return this; + } }; // Static factories @@ -409,8 +377,7 @@ public abstract class CharMatcher implements Predicate<Character> { * 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) { + return new CharMatcher() { @Override public boolean matches(char c) { return c == match; } @@ -431,11 +398,13 @@ public abstract class CharMatcher implements Predicate<Character> { return isNot(match); } - @GwtIncompatible("java.util.BitSet") - @Override - void setBits(BitSet table) { + @Override void setBits(LookupTable table) { table.set(match); } + + @Override public CharMatcher precomputed() { + return this; + } }; } @@ -445,8 +414,7 @@ public abstract class CharMatcher implements Predicate<Character> { * <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) { + return new CharMatcher() { @Override public boolean matches(char c) { return c != match; } @@ -459,13 +427,6 @@ public abstract class CharMatcher implements Predicate<Character> { return other.matches(match) ? ANY : this; } - @GwtIncompatible("java.util.BitSet") - @Override - void setBits(BitSet table) { - table.set(0, match); - table.set(match + 1, Character.MAX_VALUE + 1); - } - @Override public CharMatcher negate() { return is(match); } @@ -483,26 +444,33 @@ public abstract class CharMatcher implements Predicate<Character> { 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 + final char match1 = sequence.charAt(0); + final char match2 = sequence.charAt(1); + return new CharMatcher() { + @Override public boolean matches(char c) { + return c == match1 || c == match2; + } + + @Override void setBits(LookupTable table) { + table.set(match1); + table.set(match2); + } + + @Override public CharMatcher precomputed() { + return this; + } + }; } - // 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()) { + Arrays.sort(chars); // not worth collapsing duplicates + + return new CharMatcher() { @Override public boolean matches(char c) { return Arrays.binarySearch(chars, c) >= 0; } - @Override - @GwtIncompatible("java.util.BitSet") - void setBits(BitSet table) { + @Override void setBits(LookupTable table) { for (char c : chars) { table.set(c); } @@ -510,24 +478,6 @@ public abstract class CharMatcher implements Predicate<Character> { }; } - 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; - } - - @GwtIncompatible("java.util.BitSet") - @Override void setBits(BitSet table) { - table.set(match1); - table.set(match2); - } - }; - } - /** * Returns a {@code char} matcher that matches any character not present in the given character * sequence. @@ -545,22 +495,23 @@ public abstract class CharMatcher implements Predicate<Character> { */ 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) { + return new CharMatcher() { @Override public boolean matches(char c) { return startInclusive <= c && c <= endInclusive; } - @GwtIncompatible("java.util.BitSet") - @Override void setBits(BitSet table) { - table.set(startInclusive, endInclusive + 1); + @Override void setBits(LookupTable table) { + char c = startInclusive; + while (true) { + table.set(c); + if (c++ == endInclusive) { + break; + } + } + } + + @Override public CharMatcher precomputed() { + return this; } }; } @@ -574,8 +525,7 @@ public abstract class CharMatcher implements Predicate<Character> { if (predicate instanceof CharMatcher) { return (CharMatcher) predicate; } - String description = "CharMatcher.forPredicate(" + predicate + ")"; - return new CharMatcher(description) { + return new CharMatcher() { @Override public boolean matches(char c) { return predicate.apply(c); } @@ -586,25 +536,12 @@ public abstract class CharMatcher implements Predicate<Character> { }; } - // State - final String description; - // Constructors /** - * Sets the {@code toString()} from the given description. + * Constructor for use by subclasses. */ - 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(); - } + protected CharMatcher() {} // Abstract methods @@ -617,96 +554,57 @@ public abstract class CharMatcher implements Predicate<Character> { * 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); - } + final CharMatcher original = this; + return new CharMatcher() { + @Override public boolean matches(char c) { + return !original.matches(c); + } - @Override public int countIn(CharSequence sequence) { - return sequence.length() - original.countIn(sequence); - } + @Override public boolean matchesAllOf(CharSequence sequence) { + return original.matchesNoneOf(sequence); + } - @GwtIncompatible("java.util.BitSet") - @Override - void setBits(BitSet table) { - BitSet tmp = new BitSet(); - original.setBits(tmp); - tmp.flip(Character.MIN_VALUE, Character.MAX_VALUE + 1); - table.or(tmp); - } + @Override public boolean matchesNoneOf(CharSequence sequence) { + return original.matchesAllOf(sequence); + } - @Override public CharMatcher negate() { - return original; - } + @Override public int countIn(CharSequence sequence) { + return sequence.length() - original.countIn(sequence); + } - @Override - CharMatcher withToString(String description) { - return new NegatedMatcher(description, original); - } + @Override public CharMatcher negate() { + return 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)); + return new And(Arrays.asList(this, checkNotNull(other))); } private static class And extends CharMatcher { - final CharMatcher first; - final CharMatcher second; + List<CharMatcher> components; - And(CharMatcher a, CharMatcher b) { - this(a, b, "CharMatcher.and(" + a + ", " + b + ")"); + And(List<CharMatcher> components) { + this.components = components; // Skip defensive copy (private) } - 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); - } - - @GwtIncompatible("java.util.BitSet") - @Override - void setBits(BitSet table) { - BitSet tmp1 = new BitSet(); - first.setBits(tmp1); - BitSet tmp2 = new BitSet(); - second.setBits(tmp2); - tmp1.and(tmp2); - table.or(tmp1); + @Override public boolean matches(char c) { + for (CharMatcher matcher : components) { + if (!matcher.matches(c)) { + return false; + } + } + return true; } - @Override - CharMatcher withToString(String description) { - return new And(first, second, description); + @Override public CharMatcher and(CharMatcher other) { + List<CharMatcher> newComponents = new ArrayList<CharMatcher>(components); + newComponents.add(checkNotNull(other)); + return new And(newComponents); } } @@ -714,38 +612,35 @@ public abstract class CharMatcher implements Predicate<Character> { * 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)); + return new Or(Arrays.asList(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); - } + List<CharMatcher> components; - Or(CharMatcher a, CharMatcher b) { - this(a, b, "CharMatcher.or(" + a + ", " + b + ")"); + Or(List<CharMatcher> components) { + this.components = components; // Skip defensive copy (private) } - @GwtIncompatible("java.util.BitSet") - @Override - void setBits(BitSet table) { - first.setBits(table); - second.setBits(table); + @Override public boolean matches(char c) { + for (CharMatcher matcher : components) { + if (matcher.matches(c)) { + return true; + } + } + return false; } - @Override - public boolean matches(char c) { - return first.matches(c) || second.matches(c); + @Override public CharMatcher or(CharMatcher other) { + List<CharMatcher> newComponents = new ArrayList<CharMatcher>(components); + newComponents.add(checkNotNull(other)); + return new Or(newComponents); } - @Override - CharMatcher withToString(String description) { - return new Or(first, second, description); + @Override void setBits(LookupTable table) { + for (CharMatcher matcher : components) { + matcher.setBits(table); + } } } @@ -763,147 +658,67 @@ public abstract class CharMatcher implements Predicate<Character> { } /** - * Subclasses should provide a new CharMatcher with the same characteristics as {@code this}, - * but with their {@code toString} method overridden with the new description. + * This is the actual implementation of {@link #precomputed}, but we bounce calls through a method + * on {@link Platform} so that we can have different behavior in GWT. * - * <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; - - /** - * This is the actual implementation of {@link #precomputed}, but we bounce calls through a - * method on {@link Platform} so that we can have different behavior in GWT. + * <p>The default precomputation is to cache the configuration of the original matcher in an + * eight-kilobyte bit array. In some situations this produces a matcher which is faster to query + * than the original. * - * <p>This implementation tries to be smart in a number of ways. It recognizes cases where - * the negation is cheaper to precompute than the matcher itself; it tries to build small - * hash tables for matchers that only match a few characters, and so on. In the worst-case - * scenario, it constructs an eight-kilobyte bit array and queries that. - * In many situations this produces a matcher which is faster to query than the original. + * <p>The default implementation creates a new bit array and passes it to {@link + * #setBits(LookupTable)}. */ - @GwtIncompatible("java.util.BitSet") CharMatcher precomputedInternal() { - final BitSet table = new BitSet(); + final LookupTable table = new LookupTable(); setBits(table); - int totalCharacters = table.cardinality(); - if (totalCharacters * 2 <= DISTINCT_CHARS) { - return precomputedPositive(totalCharacters, table, description); - } else { - // TODO(user): is it worth it to worry about the last character of large matchers? - table.flip(Character.MIN_VALUE, Character.MAX_VALUE + 1); - int negatedCharacters = DISTINCT_CHARS - totalCharacters; - return new NegatedFastMatcher(toString(), - precomputedPositive(negatedCharacters, table, description + ".negate()")); - } - } - /** - * 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); - } + return new CharMatcher() { + @Override public boolean matches(char c) { + return table.get(c); + } - @Override - public final CharMatcher precomputed() { - return this; - } + // TODO(kevinb): make methods like negate() smart? - @Override - CharMatcher withToString(String description) { - return new NegatedFastMatcher(description, original); - } + @Override public CharMatcher precomputed() { + return this; + } + }; } /** - * Helper method for {@link #precomputedInternal} that doesn't test if the negation is cheaper. + * For use by implementors; sets the bit corresponding to each character ('\0' to '{@literal + * \}uFFFF') that matches this matcher in the given bit array, leaving all other bits untouched. + * + * <p>The default implementation loops over every possible character value, invoking {@link + * #matches} for each one. */ - @GwtIncompatible("java.util.BitSet") - private static CharMatcher precomputedPositive( - int totalCharacters, - BitSet table, - String description) { - switch (totalCharacters) { - case 0: - return NONE; - case 1: - return is((char) table.nextSetBit(0)); - case 2: - char c1 = (char) table.nextSetBit(0); - char c2 = (char) table.nextSetBit(c1 + 1); - return isEither(c1, c2); - default: - return isSmall(totalCharacters, table.length()) - ? SmallCharMatcher.from(table, description) - : new BitSetMatcher(table, description); - } - } - - private static boolean isSmall(int totalCharacters, int tableLength) { - return totalCharacters <= SmallCharMatcher.MAX_SIZE - && tableLength > (totalCharacters * Character.SIZE); - } - - @GwtIncompatible("java.util.BitSet") - private static class BitSetMatcher extends FastMatcher { - private final BitSet table; - - private BitSetMatcher(BitSet table, String description) { - super(description); - if (table.length() + Long.SIZE < table.size()) { - table = (BitSet) table.clone(); - // If only we could actually call BitSet.trimToSize() ourselves... + void setBits(LookupTable table) { + char c = Character.MIN_VALUE; + while (true) { + if (matches(c)) { + table.set(c); + } + if (c++ == Character.MAX_VALUE) { + break; } - this.table = table; - } - - @Override public boolean matches(char c) { - return table.get(c); - } - - @Override - void setBits(BitSet bitSet) { - bitSet.or(table); } } /** - * Sets bits in {@code table} matched by this matcher. + * A bit array with one bit per {@code char} value, used by {@link CharMatcher#precomputed}. + * + * <p>TODO(kevinb): possibly share a common BitArray class with BloomFilter and others... a + * simpler java.util.BitSet. */ - @GwtIncompatible("java.util.BitSet") - void setBits(BitSet table) { - for (int c = Character.MAX_VALUE; c >= Character.MIN_VALUE; c--) { - if (matches((char) c)) { - table.set(c); - } + private static final class LookupTable { + int[] data = new int[2048]; + + void set(char index) { + data[index >> 5] |= (1 << index); + } + + boolean get(char index) { + return (data[index >> 5] & (1 << index)) != 0; } } @@ -958,6 +773,8 @@ public abstract class CharMatcher implements Predicate<Character> { return indexIn(sequence) == -1; } + // TODO(kevinb): add matchesAnyOf() + /** * Returns the index of the first matching character in a character sequence, or {@code -1} if no * matching character is present. @@ -1212,12 +1029,15 @@ public abstract class CharMatcher implements Predicate<Character> { @CheckReturnValue public String trimLeadingFrom(CharSequence sequence) { int len = sequence.length(); - for (int first = 0; first < len; first++) { + int first; + + for (first = 0; first < len; first++) { if (!matches(sequence.charAt(first))) { - return sequence.subSequence(first, len).toString(); + break; } } - return ""; + + return sequence.subSequence(first, len).toString(); } /** @@ -1231,12 +1051,15 @@ public abstract class CharMatcher implements Predicate<Character> { @CheckReturnValue public String trimTrailingFrom(CharSequence sequence) { int len = sequence.length(); - for (int last = len - 1; last >= 0; last--) { + int last; + + for (last = len - 1; last >= 0; last--) { if (!matches(sequence.charAt(last))) { - return sequence.subSequence(0, last + 1).toString(); + break; } } - return ""; + + return sequence.subSequence(0, last + 1).toString(); } /** @@ -1259,25 +1082,29 @@ public abstract class CharMatcher implements Predicate<Character> { */ @CheckReturnValue public String collapseFrom(CharSequence sequence, char replacement) { - // This implementation avoids unnecessary allocation. - int len = sequence.length(); - for (int i = 0; i < len; i++) { + int first = indexIn(sequence); + if (first == -1) { + return sequence.toString(); + } + + // TODO(kevinb): see if this implementation can be made faster + StringBuilder builder = new StringBuilder(sequence.length()) + .append(sequence.subSequence(0, first)) + .append(replacement); + boolean in = true; + for (int i = first + 1; i < sequence.length(); 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); + if (apply(c)) { + if (!in) { + builder.append(replacement); + in = true; } + } else { + builder.append(c); + in = false; } } - // no replacement needed - return sequence.toString(); + return builder.toString(); } /** @@ -1287,35 +1114,22 @@ public abstract class CharMatcher implements Predicate<Character> { */ @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++) { + int first = negate().indexIn(sequence); + if (first == -1) { + return ""; // everything matches. nothing's left. + } + StringBuilder builder = new StringBuilder(sequence.length()); + boolean inMatchingGroup = false; + for (int i = first; i < sequence.length(); i++) { char c = sequence.charAt(i); - if (matches(c)) { - if (!inMatchingGroup) { + if (apply(c)) { + inMatchingGroup = true; + } else { + if (inMatchingGroup) { builder.append(replacement); - inMatchingGroup = true; + inMatchingGroup = false; } - } else { builder.append(c); - inMatchingGroup = false; } } return builder.toString(); @@ -1324,67 +1138,11 @@ public abstract class CharMatcher implements Predicate<Character> { // 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. + * Returns {@code true} if this matcher matches the given character. + * + * @throws NullPointerException if {@code character} is null */ @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/src/com/google/common/base/Charsets.java b/guava/src/com/google/common/base/Charsets.java index 79c9128..407e798 100644 --- a/guava/src/com/google/common/base/Charsets.java +++ b/guava/src/com/google/common/base/Charsets.java @@ -16,38 +16,26 @@ package com.google.common.base; -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; - 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() {} /** * US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US). */ - @GwtIncompatible("Non-UTF-8 Charset") public static final Charset US_ASCII = Charset.forName("US-ASCII"); /** * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). */ - @GwtIncompatible("Non-UTF-8 Charset") public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); /** @@ -58,20 +46,17 @@ public final class Charsets { /** * UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order. */ - @GwtIncompatible("Non-UTF-8 Charset") public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); /** * UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order. */ - @GwtIncompatible("Non-UTF-8 Charset") public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); /** * UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order * mark. */ - @GwtIncompatible("Non-UTF-8 Charset") public static final Charset UTF_16 = Charset.forName("UTF-16"); /* diff --git a/guava/src/com/google/common/base/Defaults.java b/guava/src/com/google/common/base/Defaults.java index 50717fc..f98cbbf 100644 --- a/guava/src/com/google/common/base/Defaults.java +++ b/guava/src/com/google/common/base/Defaults.java @@ -16,8 +16,6 @@ package com.google.common.base; -import static com.google.common.base.Preconditions.checkNotNull; - import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -26,7 +24,6 @@ import java.util.Map; * This class provides default values for all Java types, as defined by the JLS. * * @author Ben Yu - * @since 1.0 */ public final class Defaults { private Defaults() {} @@ -57,6 +54,6 @@ public final class Defaults { */ @SuppressWarnings("unchecked") public static <T> T defaultValue(Class<T> type) { - return (T) DEFAULTS.get(checkNotNull(type)); + return (T) DEFAULTS.get(type); } } diff --git a/guava/src/com/google/common/base/Enums.java b/guava/src/com/google/common/base/Enums.java index 6105410..f98e164 100644 --- a/guava/src/com/google/common/base/Enums.java +++ b/guava/src/com/google/common/base/Enums.java @@ -20,10 +20,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.io.Serializable; -import java.lang.reflect.Field; import javax.annotation.Nullable; @@ -34,31 +32,13 @@ import javax.annotation.Nullable; * * @since 9.0 */ -@GwtCompatible(emulated = true) +@GwtCompatible @Beta public final class Enums { private Enums() {} /** - * Returns the {@link Field} in which {@code enumValue} is defined. - * For example, to get the {@code Description} annotation on the {@code GOLF} - * constant of enum {@code Sport}, use - * {@code Enums.getField(Sport.GOLF).getAnnotation(Description.class)}. - * - * @since 12.0 - */ - @GwtIncompatible("reflection") - public static Field getField(Enum<?> enumValue) { - Class<?> clazz = enumValue.getDeclaringClass(); - try { - return clazz.getDeclaredField(enumValue.name()); - } catch (NoSuchFieldException impossible) { - throw new AssertionError(impossible); - } - } - - /** * 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. @@ -71,11 +51,11 @@ public final class Enums { } /** - * A {@link Function} that maps an {@link Enum} name to the associated + * {@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 static final class ValueOfFunction<T extends Enum<T>> implements + Function<String, T>, Serializable { private final Class<T> enumClass; @@ -107,22 +87,4 @@ public final class Enums { 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/src/com/google/common/base/Equivalence.java b/guava/src/com/google/common/base/Equivalence.java index 339bec0..f6e89bd 100644 --- a/guava/src/com/google/common/base/Equivalence.java +++ b/guava/src/com/google/common/base/Equivalence.java @@ -27,8 +27,8 @@ import javax.annotation.Nullable; /** * A strategy for determining whether two instances are considered equivalent. Examples of - * equivalences are the {@linkplain #identity() identity equivalence} and {@linkplain #equals equals - * equivalence}. + * equivalences are the {@link Equivalences#identity() identity equivalence} and {@link + * Equivalences#equals equals equivalence}. * * @author Bob Lee * @author Ben Yu @@ -36,6 +36,7 @@ import javax.annotation.Nullable; * @since 10.0 (<a href="http://code.google.com/p/guava-libraries/wiki/Compatibility" * >mostly source-compatible</a> since 4.0) */ +@Beta @GwtCompatible public abstract class Equivalence<T> { /** @@ -122,7 +123,7 @@ public abstract class Equivalence<T> { * * <p>For example: <pre> {@code * - * Equivalence<Person> SAME_AGE = Equivalence.equals().onResultOf(GET_PERSON_AGE); + * Equivalence<Person> SAME_AGE = Equivalences.equals().onResultOf(GET_PERSON_AGE); * }</pre> * * <p>{@code function} will never be invoked with a null value. @@ -130,7 +131,7 @@ public abstract class Equivalence<T> { * <p>Note that {@code function} must be consistent according to {@code this} equivalence * relation. That is, invoking {@link Function#apply} multiple times for a given value must return * equivalent results. - * For example, {@code Equivalence.identity().onResultOf(Functions.toStringFunction())} is broken + * For example, {@code Equivalences.identity().onResultOf(Functions.toStringFunction())} is broken * because it's not guaranteed that {@link Object#toString}) always returns the same string * instance. * @@ -171,6 +172,7 @@ public abstract class Equivalence<T> { * * @since 10.0 */ + @Beta public static final class Wrapper<T> implements Serializable { private final Equivalence<? super T> equivalence; @Nullable private final T reference; @@ -251,7 +253,6 @@ public abstract class Equivalence<T> { * * @since 10.0 */ - @Beta public final Predicate<T> equivalentTo(@Nullable T target) { return new EquivalentToPredicate<T>(this, target); } @@ -292,67 +293,4 @@ public abstract class Equivalence<T> { private static final long serialVersionUID = 0; } - - /** - * Returns an equivalence that delegates to {@link Object#equals} and {@link Object#hashCode}. - * {@link Equivalence#equivalent} returns {@code true} if both values are null, or if neither - * value is null and {@link Object#equals} returns {@code true}. {@link Equivalence#hash} returns - * {@code 0} if passed a null value. - * - * @since 13.0 - * @since 8.0 (in Equivalences with null-friendly behavior) - * @since 4.0 (in Equivalences) - */ - public static Equivalence<Object> equals() { - return Equals.INSTANCE; - } - - /** - * Returns an equivalence that uses {@code ==} to compare values and {@link - * System#identityHashCode(Object)} to compute the hash code. {@link Equivalence#equivalent} - * returns {@code true} if {@code a == b}, including in the case that a and b are both null. - * - * @since 13.0 - * @since 4.0 (in Equivalences) - */ - public static Equivalence<Object> identity() { - return Identity.INSTANCE; - } - - static final class Equals extends Equivalence<Object> - implements Serializable { - - static final Equals INSTANCE = new Equals(); - - @Override protected boolean doEquivalent(Object a, Object b) { - return a.equals(b); - } - @Override public int doHash(Object o) { - return o.hashCode(); - } - - private Object readResolve() { - return INSTANCE; - } - private static final long serialVersionUID = 1; - } - - static final class Identity extends Equivalence<Object> - implements Serializable { - - static final Identity INSTANCE = new Identity(); - - @Override protected boolean doEquivalent(Object a, Object b) { - return false; - } - - @Override protected int doHash(Object o) { - return System.identityHashCode(o); - } - - private Object readResolve() { - return INSTANCE; - } - private static final long serialVersionUID = 1; - } } diff --git a/guava/src/com/google/common/base/Equivalences.java b/guava/src/com/google/common/base/Equivalences.java new file mode 100644 index 0000000..6c88dcf --- /dev/null +++ b/guava/src/com/google/common/base/Equivalences.java @@ -0,0 +1,97 @@ +/* + * 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.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +import java.io.Serializable; + +/** + * Contains static factory methods for creating {@code Equivalence} instances. + * + * <p>All methods return serializable instances. + * + * @author Bob Lee + * @author Kurt Alfred Kluever + * @author Gregory Kick + * @since 4.0 + */ +@Beta +@GwtCompatible +public final class Equivalences { + private Equivalences() {} + + /** + * Returns an equivalence that delegates to {@link Object#equals} and {@link Object#hashCode}. + * {@link Equivalence#equivalent} returns {@code true} if both values are null, or if neither + * value is null and {@link Object#equals} returns {@code true}. {@link Equivalence#hash} returns + * {@code 0} if passed a null value. + * + * @since 8.0 (present null-friendly behavior) + * @since 4.0 (otherwise) + */ + public static Equivalence<Object> equals() { + return Equals.INSTANCE; + } + + /** + * Returns an equivalence that uses {@code ==} to compare values and {@link + * System#identityHashCode(Object)} to compute the hash code. {@link Equivalence#equivalent} + * returns {@code true} if {@code a == b}, including in the case that a and b are both null. + */ + public static Equivalence<Object> identity() { + return Identity.INSTANCE; + } + + private static final class Equals extends Equivalence<Object> + implements Serializable { + + static final Equals INSTANCE = new Equals(); + + @Override protected boolean doEquivalent(Object a, Object b) { + return a.equals(b); + } + @Override public int doHash(Object o) { + return o.hashCode(); + } + + private Object readResolve() { + return INSTANCE; + } + private static final long serialVersionUID = 1; + } + + private static final class Identity extends Equivalence<Object> + implements Serializable { + + static final Identity INSTANCE = new Identity(); + + @Override protected boolean doEquivalent(Object a, Object b) { + return false; + } + + @Override protected int doHash(Object o) { + return System.identityHashCode(o); + } + + private Object readResolve() { + return INSTANCE; + } + private static final long serialVersionUID = 1; + } +} diff --git a/guava/src/com/google/common/base/FinalizableReferenceQueue.java b/guava/src/com/google/common/base/FinalizableReferenceQueue.java index 2ca3681..f300b33 100644 --- a/guava/src/com/google/common/base/FinalizableReferenceQueue.java +++ b/guava/src/com/google/common/base/FinalizableReferenceQueue.java @@ -16,12 +16,8 @@ package com.google.common.base; -import com.google.common.annotations.VisibleForTesting; -import java.io.Closeable; - import java.io.FileNotFoundException; import java.io.IOException; -import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.reflect.Method; @@ -41,7 +37,7 @@ import java.util.logging.Logger; * @author Bob Lee * @since 2.0 (imported from Google Collections Library) */ -public class FinalizableReferenceQueue implements Closeable { +public class FinalizableReferenceQueue { /* * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a * map built by MapMaker) no longer has a strong reference to this object, the garbage collector @@ -95,8 +91,6 @@ public class FinalizableReferenceQueue implements Closeable { */ final ReferenceQueue<Object> queue; - final PhantomReference<Object> frqRef; - /** * Whether or not the background thread started successfully. */ @@ -108,28 +102,24 @@ public class FinalizableReferenceQueue implements Closeable { @SuppressWarnings("unchecked") public FinalizableReferenceQueue() { // We could start the finalizer lazily, but I'd rather it blow up early. - queue = new ReferenceQueue<Object>(); - frqRef = new PhantomReference<Object>(this, queue); + ReferenceQueue<Object> queue; boolean threadStarted = false; try { - startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef); + queue = (ReferenceQueue<Object>) + startFinalizer.invoke(null, FinalizableReference.class, this); threadStarted = true; } catch (IllegalAccessException impossible) { throw new AssertionError(impossible); // startFinalizer() is public } catch (Throwable t) { logger.log(Level.INFO, "Failed to start reference finalizer thread." + " Reference cleanup will only occur when new references are created.", t); + queue = new ReferenceQueue<Object>(); } + this.queue = queue; this.threadStarted = threadStarted; } - @Override - public void close() { - frqRef.enqueue(); - cleanUp(); - } - /** * Repeatedly dequeues references from the queue and invokes {@link * FinalizableReference#finalizeReferent()} on them until the queue is empty. This method is a @@ -189,16 +179,8 @@ public class FinalizableReferenceQueue implements Closeable { * we needn't create a separate loader. */ static class SystemLoader implements FinalizerLoader { - // This is used by the ClassLoader-leak test in FinalizableReferenceQueueTest to disable - // finding Finalizer on the system class path even if it is there. - @VisibleForTesting - static boolean disabled; - @Override public Class<?> loadFinalizer() { - if (disabled) { - return null; - } ClassLoader systemLoader; try { systemLoader = ClassLoader.getSystemClassLoader(); @@ -272,10 +254,7 @@ public class FinalizableReferenceQueue implements Closeable { /** Creates a class loader with the given base URL as its classpath. */ URLClassLoader newLoader(URL base) { - // We use the bootstrap class loader as the parent because Finalizer by design uses - // only standard Java classes. That also means that FinalizableReferenceQueueTest - // doesn't pick up the wrong version of the Finalizer class. - return new URLClassLoader(new URL[] {base}, null); + return new URLClassLoader(new URL[] {base}); } } @@ -299,11 +278,7 @@ public class FinalizableReferenceQueue implements Closeable { */ static Method getStartFinalizer(Class<?> finalizer) { try { - return finalizer.getMethod( - "startFinalizer", - Class.class, - ReferenceQueue.class, - PhantomReference.class); + return finalizer.getMethod("startFinalizer", Class.class, Object.class); } catch (NoSuchMethodException e) { throw new AssertionError(e); } diff --git a/guava/src/com/google/common/base/Function.java b/guava/src/com/google/common/base/Function.java index f969b4a..6289fa4 100644 --- a/guava/src/com/google/common/base/Function.java +++ b/guava/src/com/google/common/base/Function.java @@ -23,12 +23,6 @@ import javax.annotation.Nullable; /** * Determines an output value based on an input value. * - * <p>The {@link Functions} class provides common functions and related utilites. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/FunctionalExplained">the use of {@code - * Function}</a>. - * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) */ @@ -48,7 +42,7 @@ public interface Function<F, T> { * @throws NullPointerException if {@code input} is null and this function does not accept null * arguments */ - @Nullable T apply(@Nullable F input); + T apply(@Nullable F input); /** * Indicates whether another object is equal to this function. diff --git a/guava/src/com/google/common/base/Functions.java b/guava/src/com/google/common/base/Functions.java index 9336e02..cca629a 100644 --- a/guava/src/com/google/common/base/Functions.java +++ b/guava/src/com/google/common/base/Functions.java @@ -30,11 +30,7 @@ import javax.annotation.Nullable; /** * Static utility methods pertaining to {@code Function} instances. * - * <p>All methods return serializable functions 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 - * Function}</a>. + * <p>All methods returns serializable functions as long as they're given serializable parameters. * * @author Mike Bostock * @author Jared Levy @@ -84,8 +80,7 @@ public final class Functions { INSTANCE; @Override - @Nullable - public Object apply(@Nullable Object o) { + public Object apply(Object o) { return o; } @@ -110,7 +105,7 @@ public final class Functions { } @Override - public V apply(@Nullable K key) { + public V apply(K key) { V result = map.get(key); checkArgument(result != null || map.containsKey(key), "Key '%s' not present in map", key); return result; @@ -159,7 +154,7 @@ public final class Functions { } @Override - public V apply(@Nullable K key) { + public V apply(K key) { V result = map.get(key); return (result != null || map.containsKey(key)) ? result : defaultValue; } @@ -206,7 +201,7 @@ public final class Functions { } @Override - public C apply(@Nullable A a) { + public C apply(A a) { return g.apply(f.apply(a)); } @@ -248,7 +243,7 @@ public final class Functions { } @Override - public Boolean apply(@Nullable T t) { + public Boolean apply(T t) { return predicate.apply(t); } diff --git a/guava/src/com/google/common/base/Joiner.java b/guava/src/com/google/common/base/Joiner.java index 8a2e9a2..048e477 100644 --- a/guava/src/com/google/common/base/Joiner.java +++ b/guava/src/com/google/common/base/Joiner.java @@ -56,9 +56,6 @@ import javax.annotation.Nullable; * joiner.skipNulls(); // does nothing! * return joiner.join("wrong", null, "wrong");}</pre> * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/StringsExplained#Joiner">{@code Joiner}</a>. - * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) */ @@ -98,8 +95,7 @@ public class Joiner { */ @Beta @Deprecated - public - final <A extends Appendable, I extends Object & Iterable<?> & Iterator<?>> A + public final <A extends Appendable, I extends Object & Iterable<?> & Iterator<?>> A appendTo(A appendable, I parts) throws IOException { return appendTo(appendable, (Iterator<?>) parts); } @@ -118,6 +114,7 @@ public class Joiner { * * @since 11.0 */ + @Beta public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException { checkNotNull(appendable); if (parts.hasNext()) { @@ -157,8 +154,7 @@ public class Joiner { */ @Beta @Deprecated - public - final <I extends Object & Iterable<?> & Iterator<?>> StringBuilder + public final <I extends Object & Iterable<?> & Iterator<?>> StringBuilder appendTo(StringBuilder builder, I parts) { return appendTo(builder, (Iterator<?>) parts); } @@ -179,6 +175,7 @@ public class Joiner { * * @since 11.0 */ + @Beta public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) { try { appendTo((Appendable) builder, parts); @@ -217,8 +214,7 @@ public class Joiner { */ @Beta @Deprecated - public - final <I extends Object & Iterable<?> & Iterator<?>> String join(I parts) { + public final <I extends Object & Iterable<?> & Iterator<?>> String join(I parts) { return join((Iterator<?>) parts); } @@ -236,6 +232,7 @@ public class Joiner { * * @since 11.0 */ + @Beta public final String join(Iterator<?> parts) { return appendTo(new StringBuilder(), parts).toString(); } @@ -264,7 +261,7 @@ public class Joiner { public Joiner useForNull(final String nullText) { checkNotNull(nullText); return new Joiner(this) { - @Override CharSequence toString(@Nullable Object part) { + @Override CharSequence toString(Object part) { return (part == null) ? nullText : Joiner.this.toString(part); } @@ -391,8 +388,7 @@ public class Joiner { */ @Beta @Deprecated - public - <A extends Appendable, + public <A extends Appendable, I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>> A appendTo(A appendable, I entries) throws IOException { Iterator<? extends Entry<?, ?>> iterator = entries; @@ -448,8 +444,7 @@ public class Joiner { */ @Beta @Deprecated - public - <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>> + public <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>> StringBuilder appendTo(StringBuilder builder, I entries) throws IOException { Iterator<? extends Entry<?, ?>> iterator = entries; return appendTo(builder, iterator); @@ -495,8 +490,7 @@ public class Joiner { */ @Beta @Deprecated - public - <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>> + public <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>> String join(I entries) throws IOException { Iterator<? extends Entry<?, ?>> iterator = entries; return join(iterator); diff --git a/guava/src/com/google/common/base/Objects.java b/guava/src/com/google/common/base/Objects.java index c65f84b..ffac9c4 100644 --- a/guava/src/com/google/common/base/Objects.java +++ b/guava/src/com/google/common/base/Objects.java @@ -27,10 +27,6 @@ import javax.annotation.Nullable; /** * Helper functions that can operate on any {@code Object}. * - * <p>See the Guava User Guide on <a - * href="http://code.google.com/p/guava-libraries/wiki/CommonObjectUtilitiesExplained">writing - * {@code Object} methods with {@code Objects}</a>. - * * @author Laurence Gonsalves * @since 2.0 (imported from Google Collections Library) */ @@ -98,14 +94,6 @@ public final class Objects { * .add("x", 1) * .add("y", "foo") * .toString(); - * }} - * - * // Returns "ClassName{x=1}" - * Objects.toStringHelper(this) - * .omitNullValues() - * .add("x", 1) - * .add("y", null) - * .toString(); * }}</pre> * * <p>Note that in GWT, class names are often obfuscated. @@ -193,38 +181,25 @@ public final class Objects { * @since 2.0 */ public static final class ToStringHelper { - private final String className; - private ValueHolder holderHead = new ValueHolder(); - private ValueHolder holderTail = holderHead; - private boolean omitNullValues = false; + private final StringBuilder builder; + private boolean needsSeparator = false; /** * Use {@link Objects#toStringHelper(Object)} to create an instance. */ private ToStringHelper(String className) { - this.className = checkNotNull(className); - } - - /** - * Configures the {@link ToStringHelper} so {@link #toString()} will ignore - * properties with null value. The order of calling this method, relative - * to the {@code add()}/{@code addValue()} methods, is not significant. - * - * @since 12.0 - */ - public ToStringHelper omitNullValues() { - omitNullValues = true; - return this; + checkNotNull(className); + this.builder = new StringBuilder(32).append(className).append('{'); } /** * Adds a name/value pair to the formatted output in {@code name=value} * format. If {@code value} is {@code null}, the string {@code "null"} - * is used, unless {@link #omitNullValues()} is called, in which case this - * name/value pair will not be added. + * is used. */ public ToStringHelper add(String name, @Nullable Object value) { - return addHolder(name, value); + checkNameAndAppend(name).append(value); + return this; } /** @@ -234,7 +209,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper add(String name, boolean value) { - return addHolder(name, String.valueOf(value)); + checkNameAndAppend(name).append(value); + return this; } /** @@ -244,7 +220,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper add(String name, char value) { - return addHolder(name, String.valueOf(value)); + checkNameAndAppend(name).append(value); + return this; } /** @@ -254,7 +231,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper add(String name, double value) { - return addHolder(name, String.valueOf(value)); + checkNameAndAppend(name).append(value); + return this; } /** @@ -264,7 +242,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper add(String name, float value) { - return addHolder(name, String.valueOf(value)); + checkNameAndAppend(name).append(value); + return this; } /** @@ -274,7 +253,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper add(String name, int value) { - return addHolder(name, String.valueOf(value)); + checkNameAndAppend(name).append(value); + return this; } /** @@ -284,7 +264,13 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper add(String name, long value) { - return addHolder(name, String.valueOf(value)); + checkNameAndAppend(name).append(value); + return this; + } + + private StringBuilder checkNameAndAppend(String name) { + checkNotNull(name); + return maybeAppendSeparator().append(name).append('='); } /** @@ -294,7 +280,8 @@ public final class Objects { * and give value a readable name. */ public ToStringHelper addValue(@Nullable Object value) { - return addHolder(value); + maybeAppendSeparator().append(value); + return this; } /** @@ -306,7 +293,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper addValue(boolean value) { - return addHolder(String.valueOf(value)); + maybeAppendSeparator().append(value); + return this; } /** @@ -318,7 +306,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper addValue(char value) { - return addHolder(String.valueOf(value)); + maybeAppendSeparator().append(value); + return this; } /** @@ -330,7 +319,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper addValue(double value) { - return addHolder(String.valueOf(value)); + maybeAppendSeparator().append(value); + return this; } /** @@ -342,7 +332,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper addValue(float value) { - return addHolder(String.valueOf(value)); + maybeAppendSeparator().append(value); + return this; } /** @@ -354,7 +345,8 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper addValue(int value) { - return addHolder(String.valueOf(value)); + maybeAppendSeparator().append(value); + return this; } /** @@ -366,63 +358,31 @@ public final class Objects { * @since 11.0 (source-compatible since 2.0) */ public ToStringHelper addValue(long value) { - return addHolder(String.valueOf(value)); + maybeAppendSeparator().append(value); + return this; } /** * Returns a string in the format specified by {@link * Objects#toStringHelper(Object)}. - * - * <p>After calling this method, you can keep adding more properties to later - * call toString() again and get a more complete representation of the - * same object; but properties cannot be removed, so this only allows - * limited reuse of the helper instance. The helper allows duplication of - * properties (multiple name/value pairs with the same name can be added). */ @Override public String toString() { - // create a copy to keep it consistent in case value changes - boolean omitNullValuesSnapshot = omitNullValues; - String nextSeparator = ""; - StringBuilder builder = new StringBuilder(32).append(className) - .append('{'); - for (ValueHolder valueHolder = holderHead.next; valueHolder != null; - valueHolder = valueHolder.next) { - if (!omitNullValuesSnapshot || valueHolder.value != null) { - builder.append(nextSeparator); - nextSeparator = ", "; - - if (valueHolder.name != null) { - builder.append(valueHolder.name).append('='); - } - builder.append(valueHolder.value); - } + try { + return builder.append('}').toString(); + } finally { + // Slice off the closing brace in case there are additional calls to + // #add or #addValue. + builder.setLength(builder.length() - 1); } - return builder.append('}').toString(); } - private ValueHolder addHolder() { - ValueHolder valueHolder = new ValueHolder(); - holderTail = holderTail.next = valueHolder; - return valueHolder; - } - - private ToStringHelper addHolder(@Nullable Object value) { - ValueHolder valueHolder = addHolder(); - valueHolder.value = value; - return this; - } - - private ToStringHelper addHolder(String name, @Nullable Object value) { - ValueHolder valueHolder = addHolder(); - valueHolder.value = value; - valueHolder.name = checkNotNull(name); - return this; - } - - private static final class ValueHolder { - String name; - Object value; - ValueHolder next; + private StringBuilder maybeAppendSeparator() { + if (needsSeparator) { + return builder.append(", "); + } else { + needsSeparator = true; + return builder; + } } } } diff --git a/guava/src/com/google/common/base/Optional.java b/guava/src/com/google/common/base/Optional.java index de3139c..035f96c 100644 --- a/guava/src/com/google/common/base/Optional.java +++ b/guava/src/com/google/common/base/Optional.java @@ -22,6 +22,7 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; +import java.util.Collections; import java.util.Iterator; import java.util.Set; @@ -58,10 +59,6 @@ import javax.annotation.Nullable; * <p>This class is not intended as a direct analogue of any existing "option" or "maybe" * construct from other programming environments, though it may bear some similarities. * - * <p>See the Guava User Guide article on <a - * href="http://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional"> - * using {@code Optional}</a>. - * * @param <T> the type of instance that can be contained. {@code Optional} is naturally * covariant on this type, so it is safe to cast an {@code Optional<T>} to {@code * Optional<S>} for any supertype {@code S} of {@code T}. @@ -69,7 +66,8 @@ import javax.annotation.Nullable; * @author Kevin Bourrillion * @since 10.0 */ -@GwtCompatible(serializable = true) +@Beta +@GwtCompatible public abstract class Optional<T> implements Serializable { /** * Returns an {@code Optional} instance with no contained reference. @@ -96,7 +94,7 @@ public abstract class Optional<T> implements Serializable { : new Present<T>(nullableReference); } - Optional() {} + private Optional() {} /** * Returns {@code true} if this holder contains a (non-null) instance. @@ -116,30 +114,6 @@ public abstract class Optional<T> implements Serializable { * Returns the contained instance if it is present; {@code defaultValue} otherwise. If * no default value should be required because the instance is known to be present, use * {@link #get()} instead. For a default value of {@code null}, use {@link #orNull}. - * - * <p>Note about generics: The signature {@code public T or(T defaultValue)} is overly - * restrictive. However, the ideal signature, {@code public <S super T> S or(S)}, is not legal - * Java. As a result, some sensible operations involving subtypes are compile errors: - * <pre> {@code - * - * Optional<Integer> optionalInt = getSomeOptionalInt(); - * Number value = optionalInt.or(0.5); // error - * - * FluentIterable<? extends Number> numbers = getSomeNumbers(); - * Optional<? extends Number> first = numbers.first(); - * Number value = first.or(0.5); // error}</pre> - * - * As a workaround, it is always safe to cast an {@code Optional<? extends T>} to {@code - * Optional<T>}. Casting either of the above example {@code Optional} instances to {@code - * Optional<Number>} (where {@code Number} is the desired output type) solves the problem: - * <pre> {@code - * - * Optional<Number> optionalInt = (Optional) getSomeOptionalInt(); - * Number value = optionalInt.or(0.5); // fine - * - * FluentIterable<? extends Number> numbers = getSomeNumbers(); - * Optional<Number> first = (Optional) numbers.first(); - * Number value = first.or(0.5); // fine}</pre> */ public abstract T or(T defaultValue); @@ -151,83 +125,63 @@ public abstract class Optional<T> implements Serializable { /** * Returns the contained instance if it is present; {@code supplier.get()} otherwise. If the - * supplier returns {@code null}, a {@link NullPointerException} is thrown. + * supplier returns {@code null}, a {@link NullPointerException} will be thrown. * * @throws NullPointerException if the supplier returns {@code null} */ - @Beta public abstract T or(Supplier<? extends T> supplier); /** * Returns the contained instance if it is present; {@code null} otherwise. If the * instance is known to be present, use {@link #get()} instead. */ - @Nullable - public abstract T orNull(); + @Nullable public abstract T orNull(); /** - * Returns an immutable singleton {@link Set} whose only element is the contained instance - * if it is present; an empty immutable {@link Set} otherwise. + * Returns an immutable singleton {@link Set} whose only element is the + * contained instance if it is present; an empty immutable {@link Set} + * otherwise. * * @since 11.0 */ public abstract Set<T> asSet(); /** - * If the instance is present, it is transformed with the given {@link Function}; otherwise, - * {@link Optional#absent} is returned. If the function returns {@code null}, a - * {@link NullPointerException} is thrown. - * - * @throws NullPointerException if the function returns {@code null} - * - * @since 12.0 - */ - public abstract <V> Optional<V> transform(Function<? super T, V> function); - - /** * Returns {@code true} if {@code object} is an {@code Optional} instance, and either * the contained references are {@linkplain Object#equals equal} to each other or both * are absent. Note that {@code Optional} instances of differing parameterized types can * be equal. */ - @Override - public abstract boolean equals(@Nullable Object object); + @Override public abstract boolean equals(@Nullable Object object); /** * Returns a hash code for this instance. */ - @Override - public abstract int hashCode(); + @Override public abstract int hashCode(); /** * Returns a string representation for this instance. The form of this string * representation is unspecified. */ - @Override - public abstract String toString(); + @Override public abstract String toString(); /** * Returns the value of each present instance from the supplied {@code optionals}, in order, * skipping over occurrences of {@link Optional#absent}. Iterators are unmodifiable and are * evaluated lazily. * - * @since 11.0 (generics widened in 13.0) + * @since 11.0 */ - @Beta - public static <T> Iterable<T> presentInstances( - final Iterable<? extends Optional<? extends T>> optionals) { + public static <T> Iterable<T> presentInstances(final Iterable<Optional<T>> optionals) { checkNotNull(optionals); return new Iterable<T>() { - @Override - public Iterator<T> iterator() { + @Override public Iterator<T> iterator() { return new AbstractIterator<T>() { - private final Iterator<? extends Optional<? extends T>> iterator = - checkNotNull(optionals.iterator()); + private final Iterator<Optional<T>> iterator = checkNotNull(optionals.iterator()); - @Override - protected T computeNext() { + @Override protected T computeNext() { while (iterator.hasNext()) { - Optional<? extends T> optional = iterator.next(); + Optional<T> optional = iterator.next(); if (optional.isPresent()) { return optional.get(); } @@ -235,9 +189,118 @@ public abstract class Optional<T> implements Serializable { return endOfData(); } }; - } + }; }; } private static final long serialVersionUID = 0; + + private static final class Present<T> extends Optional<T> { + private final T reference; + + Present(T reference) { + this.reference = reference; + } + + @Override public boolean isPresent() { + return true; + } + + @Override public T get() { + return reference; + } + + @Override public T or(T defaultValue) { + checkNotNull(defaultValue, "use orNull() instead of or(null)"); + return reference; + } + + @Override public Optional<T> or(Optional<? extends T> secondChoice) { + checkNotNull(secondChoice); + return this; + } + + @Override public T or(Supplier<? extends T> supplier) { + checkNotNull(supplier); + return reference; + } + + @Override public T orNull() { + return reference; + } + + @Override public Set<T> asSet() { + return Collections.singleton(reference); + } + + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Present) { + Present<?> other = (Present<?>) object; + return reference.equals(other.reference); + } + return false; + } + + @Override public int hashCode() { + return 0x598df91c + reference.hashCode(); + } + + @Override public String toString() { + return "Optional.of(" + reference + ")"; + } + + private static final long serialVersionUID = 0; + } + + private static final class Absent extends Optional<Object> { + private static final Absent INSTANCE = new Absent(); + + @Override public boolean isPresent() { + return false; + } + + @Override public Object get() { + throw new IllegalStateException("value is absent"); + } + + @Override public Object or(Object defaultValue) { + return checkNotNull(defaultValue, "use orNull() instead of or(null)"); + } + + @SuppressWarnings("unchecked") // safe covariant cast + @Override public Optional<Object> or(Optional<?> secondChoice) { + return (Optional) checkNotNull(secondChoice); + } + + @Override public Object or(Supplier<?> supplier) { + return checkNotNull(supplier.get(), + "use orNull() instead of a Supplier that returns null"); + } + + @Override @Nullable public Object orNull() { + return null; + } + + @Override public Set<Object> asSet() { + return Collections.emptySet(); + } + + @Override public boolean equals(@Nullable Object object) { + return object == this; + } + + @Override public int hashCode() { + return 0x598df91c; + } + + @Override public String toString() { + return "Optional.absent()"; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; + } } diff --git a/guava/src/com/google/common/base/Preconditions.java b/guava/src/com/google/common/base/Preconditions.java index 802a309..4c22f1e 100644 --- a/guava/src/com/google/common/base/Preconditions.java +++ b/guava/src/com/google/common/base/Preconditions.java @@ -53,10 +53,6 @@ import javax.annotation.Nullable; * perhaps ever. Postcondition or other invariant failures should not throw * these types of exceptions. * - * <p>See the Guava User Guide on <a href= - * "http://code.google.com/p/guava-libraries/wiki/PreconditionsExplained"> - * using {@code Preconditions}</a>. - * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) */ diff --git a/guava/src/com/google/common/base/Predicate.java b/guava/src/com/google/common/base/Predicate.java index 7345742..08d9f64 100644 --- a/guava/src/com/google/common/base/Predicate.java +++ b/guava/src/com/google/common/base/Predicate.java @@ -23,12 +23,6 @@ import javax.annotation.Nullable; /** * Determines a true or false value for a given input. * - * <p>The {@link Predicates} class provides common predicates and related utilities. - * - * <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) */ diff --git a/guava/src/com/google/common/base/Predicates.java b/guava/src/com/google/common/base/Predicates.java index aa12750..5c77a28 100644 --- a/guava/src/com/google/common/base/Predicates.java +++ b/guava/src/com/google/common/base/Predicates.java @@ -37,10 +37,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) */ @@ -200,12 +196,12 @@ public final class Predicates { public static Predicate<Object> instanceOf(Class<?> clazz) { return new InstanceOfPredicate(clazz); } - + /** * Returns a predicate that evaluates to {@code true} if the class being * tested is assignable from the given class. The returned predicate * does not allow null inputs. - * + * * @since 10.0 */ @GwtIncompatible("Class.isAssignableFrom") @@ -293,7 +289,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; @@ -308,7 +304,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() { @@ -337,8 +333,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; @@ -347,7 +342,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) { @@ -371,8 +366,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; @@ -381,7 +375,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) { @@ -453,7 +447,7 @@ public final class Predicates { } private static final long serialVersionUID = 0; } - + /** @see Predicates#assignableFrom(Class) */ @GwtIncompatible("Class.isAssignableFrom") private static class AssignableFromPredicate @@ -492,7 +486,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) { @@ -532,7 +526,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/src/com/google/common/base/Present.java b/guava/src/com/google/common/base/Present.java deleted file mode 100644 index bcde922..0000000 --- a/guava/src/com/google/common/base/Present.java +++ /dev/null @@ -1,92 +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.GwtCompatible; - -import java.util.Collections; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * Implementation of an {@link Optional} containing a reference. - */ -@GwtCompatible -final class Present<T> extends Optional<T> { - private final T reference; - - Present(T reference) { - this.reference = reference; - } - - @Override public boolean isPresent() { - return true; - } - - @Override public T get() { - return reference; - } - - @Override public T or(T defaultValue) { - checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); - return reference; - } - - @Override public Optional<T> or(Optional<? extends T> secondChoice) { - checkNotNull(secondChoice); - return this; - } - - @Override public T or(Supplier<? extends T> supplier) { - checkNotNull(supplier); - return reference; - } - - @Override public T orNull() { - return reference; - } - - @Override public Set<T> asSet() { - return Collections.singleton(reference); - } - - @Override public <V> Optional<V> transform(Function<? super T, V> function) { - return new Present<V>(checkNotNull(function.apply(reference), - "the Function passed to Optional.transform() must not return null.")); - } - - @Override public boolean equals(@Nullable Object object) { - if (object instanceof Present) { - Present<?> other = (Present<?>) object; - return reference.equals(other.reference); - } - return false; - } - - @Override public int hashCode() { - return 0x598df91c + reference.hashCode(); - } - - @Override public String toString() { - return "Optional.of(" + reference + ")"; - } - - private static final long serialVersionUID = 0; -} diff --git a/guava/src/com/google/common/base/SmallCharMatcher.java b/guava/src/com/google/common/base/SmallCharMatcher.java deleted file mode 100644 index 10234c5..0000000 --- a/guava/src/com/google/common/base/SmallCharMatcher.java +++ /dev/null @@ -1,155 +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.GwtIncompatible; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.CharMatcher.FastMatcher; - -import java.util.BitSet; - -/** - * An immutable version of CharMatcher for smallish sets of characters that uses a hash table - * with linear probing to check for matches. - * - * @author Christopher Swenson - */ -@GwtIncompatible("no precomputation is done in GWT") -final class SmallCharMatcher extends FastMatcher { - static final int MAX_SIZE = 1023; - private final char[] table; - private final boolean containsZero; - private final long filter; - - private SmallCharMatcher(char[] table, long filter, boolean containsZero, - String description) { - super(description); - this.table = table; - this.filter = filter; - this.containsZero = containsZero; - } - - private static final int C1 = 0xcc9e2d51; - private static final int C2 = 0x1b873593; - - /* - * This method was rewritten in Java from an intermediate step of the Murmur hash function in - * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the - * following header: - * - * MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author - * hereby disclaims copyright to this source code. - */ - static int smear(int hashCode) { - return C2 * Integer.rotateLeft(hashCode * C1, 15); - } - - private boolean checkFilter(int c) { - return 1 == (1 & (filter >> c)); - } - - // This is all essentially copied from ImmutableSet, but we have to duplicate because - // of dependencies. - - // Represents how tightly we can pack things, as a maximum. - private static final double DESIRED_LOAD_FACTOR = 0.5; - - /** - * Returns an array size suitable for the backing array of a hash table that - * uses open addressing with linear probing in its implementation. The - * returned size is the smallest power of two that can hold setSize elements - * with the desired load factor. - */ - @VisibleForTesting static int chooseTableSize(int setSize) { - if (setSize == 1) { - return 2; - } - // Correct the size for open addressing to match desired load factor. - // Round up to the next highest power of 2. - int tableSize = Integer.highestOneBit(setSize - 1) << 1; - while (tableSize * DESIRED_LOAD_FACTOR < setSize) { - tableSize <<= 1; - } - return tableSize; - } - - @GwtIncompatible("java.util.BitSet") - static CharMatcher from(BitSet chars, String description) { - // Compute the filter. - long filter = 0; - int size = chars.cardinality(); - boolean containsZero = chars.get(0); - // Compute the hash table. - char[] table = new char[chooseTableSize(size)]; - int mask = table.length - 1; - for (int c = chars.nextSetBit(0); c != -1; c = chars.nextSetBit(c + 1)) { - // Compute the filter at the same time. - filter |= 1L << c; - int index = smear(c) & mask; - while (true) { - // Check for empty. - if (table[index] == 0) { - table[index] = (char) c; - break; - } - // Linear probing. - index = (index + 1) & mask; - } - } - return new SmallCharMatcher(table, filter, containsZero, description); - } - - @Override - public boolean matches(char c) { - if (c == 0) { - return containsZero; - } - if (!checkFilter(c)) { - return false; - } - int mask = table.length - 1; - int startingIndex = smear(c) & mask; - int index = startingIndex; - do { - // Check for empty. - if (table[index] == 0) { - return false; - // Check for match. - } else if (table[index] == c) { - return true; - } else { - // Linear probing. - index = (index + 1) & mask; - } - // Check to see if we wrapped around the whole table. - } while (index != startingIndex); - return false; - } - - @GwtIncompatible("java.util.BitSet") - @Override - void setBits(BitSet table) { - if (containsZero) { - table.set(0); - } - for (char c : this.table) { - if (c != 0) { - table.set(c); - } - } - } -} diff --git a/guava/src/com/google/common/base/Splitter.java b/guava/src/com/google/common/base/Splitter.java index b9e820f..da7da95 100644 --- a/guava/src/com/google/common/base/Splitter.java +++ b/guava/src/com/google/common/base/Splitter.java @@ -33,63 +33,60 @@ import java.util.regex.Pattern; 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 @@ -159,8 +156,8 @@ public final class Splitter { /** * Returns a splitter that uses the given fixed string as a separator. For - * example, {@code Splitter.on(", ").split("foo, bar,baz")} 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 @@ -258,18 +255,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"); @@ -386,12 +374,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(); - } }; } @@ -413,18 +395,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. * @@ -493,7 +463,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; @@ -522,15 +493,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); @@ -541,20 +505,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++; @@ -564,8 +514,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/src/com/google/common/base/Stopwatch.java b/guava/src/com/google/common/base/Stopwatch.java index bcd898a..ec9cc9c 100644 --- a/guava/src/com/google/common/base/Stopwatch.java +++ b/guava/src/com/google/common/base/Stopwatch.java @@ -50,7 +50,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> @@ -69,7 +69,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; @@ -89,7 +89,7 @@ public final class Stopwatch { * source. */ public Stopwatch(Ticker ticker) { - this.ticker = checkNotNull(ticker, "ticker"); + this.ticker = checkNotNull(ticker); } /** @@ -108,8 +108,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; @@ -124,8 +123,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; @@ -154,44 +152,23 @@ 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); } /** - * Returns a string representation of the current elapsed time. + * Returns a string representation of the current elapsed time; equivalent to + * {@code toString(4)} (four significant figures). */ @GwtIncompatible("String.format()") @Override public String toString() { @@ -201,13 +178,9 @@ public final class Stopwatch { /** * Returns a string representation of the current elapsed time, choosing an * appropriate unit and using the specified number of significant figures. - * For example, at the instant when {@code elapsed(NANOSECONDS)} would + * For example, at the instant when {@code elapsedTime(NANOSECONDS)} would * return {1234567}, {@code toString(4)} returns {@code "1.235 ms"}. - * - * @deprecated Use {@link #toString()} instead. This method is scheduled - * to be removed in Guava release 15.0. */ - @Deprecated @GwtIncompatible("String.format()") public String toString(int significantDigits) { long nanos = elapsedNanos(); diff --git a/guava/src/com/google/common/base/Strings.java b/guava/src/com/google/common/base/Strings.java index 45007fd..7697452 100644 --- a/guava/src/com/google/common/base/Strings.java +++ b/guava/src/com/google/common/base/Strings.java @@ -19,6 +19,7 @@ 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 com.google.common.annotations.VisibleForTesting; @@ -185,6 +186,7 @@ public final class Strings { * * @since 11.0 */ + @Beta public static String commonPrefix(CharSequence a, CharSequence b) { checkNotNull(a); checkNotNull(b); @@ -208,6 +210,7 @@ public final class Strings { * * @since 11.0 */ + @Beta public static String commonSuffix(CharSequence a, CharSequence b) { checkNotNull(a); checkNotNull(b); diff --git a/guava/src/com/google/common/base/Suppliers.java b/guava/src/com/google/common/base/Suppliers.java index 468ce8b..5f418c0 100644 --- a/guava/src/com/google/common/base/Suppliers.java +++ b/guava/src/com/google/common/base/Suppliers.java @@ -62,27 +62,10 @@ public final class Suppliers { this.function = function; this.supplier = supplier; } - - @Override public T get() { + @Override + public T get() { return function.apply(supplier.get()); } - - @Override public boolean equals(@Nullable Object obj) { - if (obj instanceof SupplierComposition) { - SupplierComposition<?, ?> that = (SupplierComposition<?, ?>) obj; - return function.equals(that.function) && supplier.equals(that.supplier); - } - return false; - } - - @Override public int hashCode() { - return Objects.hashCode(function, supplier); - } - - @Override public String toString() { - return "Suppliers.compose(" + function + ", " + supplier + ")"; - } - private static final long serialVersionUID = 0; } @@ -117,7 +100,8 @@ public final class Suppliers { this.delegate = delegate; } - @Override public T get() { + @Override + public T get() { // A 2-field variant of Double Checked Locking. if (!initialized) { synchronized (this) { @@ -132,10 +116,6 @@ public final class Suppliers { return value; } - @Override public String toString() { - return "Suppliers.memoize(" + delegate + ")"; - } - private static final long serialVersionUID = 0; } @@ -177,7 +157,8 @@ public final class Suppliers { Preconditions.checkArgument(duration > 0); } - @Override public T get() { + @Override + public T get() { // Another variant of Double Checked Locking. // // We use two volatile reads. We could reduce this to one by @@ -202,13 +183,6 @@ public final class Suppliers { return value; } - @Override public String toString() { - // This is a little strange if the unit the user provided was not NANOS, - // but we don't want to store the unit just for toString - return "Suppliers.memoizeWithExpiration(" + delegate + ", " + - durationNanos + ", NANOS)"; - } - private static final long serialVersionUID = 0; } @@ -226,27 +200,10 @@ public final class Suppliers { SupplierOfInstance(@Nullable T instance) { this.instance = instance; } - - @Override public T get() { + @Override + public T get() { return instance; } - - @Override public boolean equals(@Nullable Object obj) { - if (obj instanceof SupplierOfInstance) { - SupplierOfInstance<?> that = (SupplierOfInstance<?>) obj; - return Objects.equal(instance, that.instance); - } - return false; - } - - @Override public int hashCode() { - return Objects.hashCode(instance); - } - - @Override public String toString() { - return "Suppliers.ofInstance(" + instance + ")"; - } - private static final long serialVersionUID = 0; } @@ -265,17 +222,12 @@ public final class Suppliers { ThreadSafeSupplier(Supplier<T> delegate) { this.delegate = delegate; } - - @Override public T get() { + @Override + public T get() { synchronized (delegate) { return delegate.get(); } } - - @Override public String toString() { - return "Suppliers.synchronizedSupplier(" + delegate + ")"; - } - private static final long serialVersionUID = 0; } @@ -286,8 +238,7 @@ public final class Suppliers { * @since 8.0 */ @Beta - //SupplierFunction works for any T. - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings("unchecked") // SupplierFunction works for any T. public static <T> Function<Supplier<T>, T> supplierFunction() { return (Function) SupplierFunction.INSTANCE; } @@ -295,12 +246,9 @@ public final class Suppliers { private enum SupplierFunction implements Function<Supplier<?>, Object> { INSTANCE; - @Override public Object apply(Supplier<?> input) { + @Override + public Object apply(Supplier<?> input) { return input.get(); } - - @Override public String toString() { - return "Suppliers.supplierFunction()"; - } } } diff --git a/guava/src/com/google/common/base/Throwables.java b/guava/src/com/google/common/base/Throwables.java index 5e4d6ec..793c5f9 100644 --- a/guava/src/com/google/common/base/Throwables.java +++ b/guava/src/com/google/common/base/Throwables.java @@ -31,10 +31,6 @@ import javax.annotation.Nullable; /** * Static utility methods pertaining to instances of {@link Throwable}. * - * <p>See the Guava User Guide entry on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ThrowablesExplained"> - * Throwables</a>. - * * @author Kevin Bourrillion * @author Ben Yu * @since 1.0 diff --git a/guava/src/com/google/common/base/Ticker.java b/guava/src/com/google/common/base/Ticker.java index 6c34aef..e074cf1 100644 --- a/guava/src/com/google/common/base/Ticker.java +++ b/guava/src/com/google/common/base/Ticker.java @@ -20,11 +20,8 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** - * A time source; returns a time value representing the number of nanoseconds elapsed since some - * fixed but arbitrary point in time. Note that most users should use {@link Stopwatch} instead of - * interacting with this class directly. - * - * <p><b>Warning:</b> this interface can only be used to measure elapsed time, not wall time. + * A time source; returns a time value representing the number of nanoseconds + * elapsed since some fixed but arbitrary point in time. * * @author Kevin Bourrillion * @since 10.0 diff --git a/guava/src/com/google/common/base/internal/Finalizer.java b/guava/src/com/google/common/base/internal/Finalizer.java index ebef272..6be4eec 100644 --- a/guava/src/com/google/common/base/internal/Finalizer.java +++ b/guava/src/com/google/common/base/internal/Finalizer.java @@ -46,7 +46,7 @@ import java.util.logging.Logger; * class loader from getting garbage collected, and this class can detect when * the main class loader has been garbage collected and stop itself. */ -public class Finalizer implements Runnable { +public class Finalizer extends Thread { private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); @@ -59,16 +59,13 @@ public class Finalizer implements Runnable { * Starts the Finalizer thread. FinalizableReferenceQueue calls this method * reflectively. * - * @param finalizableReferenceClass FinalizableReference.class. - * @param queue a reference queue that the thread will poll. - * @param frqReference a phantom reference to the FinalizableReferenceQueue, which will be - * queued either when the FinalizableReferenceQueue is no longer referenced anywhere, or when - * its close() method is called. + * @param finalizableReferenceClass FinalizableReference.class + * @param frq reference to instance of FinalizableReferenceQueue that started + * this thread + * @return ReferenceQueue which Finalizer will poll */ - public static void startFinalizer( - Class<?> finalizableReferenceClass, - ReferenceQueue<Object> queue, - PhantomReference<Object> frqReference) { + public static ReferenceQueue<Object> startFinalizer( + Class<?> finalizableReferenceClass, Object frq) { /* * We use FinalizableReference.class for two things: * @@ -82,42 +79,40 @@ public class Finalizer implements Runnable { "Expected " + FINALIZABLE_REFERENCE + "."); } - Finalizer finalizer = new Finalizer(finalizableReferenceClass, queue, frqReference); - Thread thread = new Thread(finalizer); - thread.setName(Finalizer.class.getName()); - thread.setDaemon(true); - - try { - if (inheritableThreadLocals != null) { - inheritableThreadLocals.set(thread, null); - } - } catch (Throwable t) { - logger.log(Level.INFO, "Failed to clear thread local values inherited" - + " by reference finalizer thread.", t); - } - - thread.start(); + Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq); + finalizer.start(); + return finalizer.queue; } private final WeakReference<Class<?>> finalizableReferenceClassReference; private final PhantomReference<Object> frqReference; - private final ReferenceQueue<Object> queue; + private final ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); private static final Field inheritableThreadLocals = getInheritableThreadLocalsField(); /** Constructs a new finalizer thread. */ - private Finalizer( - Class<?> finalizableReferenceClass, - ReferenceQueue<Object> queue, - PhantomReference<Object> frqReference) { - this.queue = queue; + private Finalizer(Class<?> finalizableReferenceClass, Object frq) { + super(Finalizer.class.getName()); this.finalizableReferenceClassReference = new WeakReference<Class<?>>(finalizableReferenceClass); // Keep track of the FRQ that started us so we know when to stop. - this.frqReference = frqReference; + this.frqReference = new PhantomReference<Object>(frq, queue); + + setDaemon(true); + + try { + if (inheritableThreadLocals != null) { + inheritableThreadLocals.set(this, null); + } + } catch (Throwable t) { + logger.log(Level.INFO, "Failed to clear thread local values inherited" + + " by reference finalizer thread.", t); + } + + // TODO(fry): Priority? } /** @@ -208,5 +203,5 @@ public class Finalizer implements Runnable { /** Indicates that it's time to shut down the Finalizer. */ @SuppressWarnings("serial") // Never serialized or thrown out of this class. - private static class ShutDown extends Exception {} + private static class ShutDown extends Exception { } } diff --git a/guava/src/com/google/common/base/package-info.java b/guava/src/com/google/common/base/package-info.java index c18bd58..66e7177 100644 --- a/guava/src/com/google/common/base/package-info.java +++ b/guava/src/com/google/common/base/package-info.java @@ -41,7 +41,8 @@ * {@link com.google.common.base.Functions} * <li>{@link com.google.common.base.Predicate}, * {@link com.google.common.base.Predicates} - * <li>{@link com.google.common.base.Equivalence} + * <li>{@link com.google.common.base.Equivalence}, + * {@link com.google.common.base.Equivalences} * <li>{@link com.google.common.base.Supplier}, * {@link com.google.common.base.Suppliers} * </ul> diff --git a/guava/src/com/google/common/cache/AbstractCache.java b/guava/src/com/google/common/cache/AbstractCache.java index a8af810..8ce941d 100644 --- a/guava/src/com/google/common/cache/AbstractCache.java +++ b/guava/src/com/google/common/cache/AbstractCache.java @@ -20,21 +20,22 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import com.google.common.util.concurrent.UncheckedExecutionException; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicLong; /** * This class provides a skeletal implementation of the {@code Cache} interface to minimize the * effort required to implement this interface. * * <p>To implement a cache, the programmer needs only to extend this class and provide an - * implementation for the {@link #put} and {@link #getIfPresent} methods. {@link #getAllPresent} is - * implemented in terms of {@link #getIfPresent}; {@link #putAll} is implemented in terms of - * {@link #put}, {@link #invalidateAll(Iterable)} is implemented in terms of {@link #invalidate}. - * The method {@link #cleanUp} is a no-op. All other methods throw an + * implementation for the {@link #getIfPresent} method. {@link #getAllPresent} is implemented in + * terms of {@code getIfPresent}; {@link #invalidateAll(Iterable)} is implemented in terms of + * {@link #invalidate}. The method {@link #cleanUp} is a no-op. All other methods throw an * {@link UnsupportedOperationException}. * * @author Charles Fry @@ -56,22 +57,14 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { } /** - * This implementation of {@code getAllPresent} lacks any insight into the internal cache data - * structure, and is thus forced to return the query keys instead of the cached keys. This is only - * possible with an unsafe cast which requires {@code keys} to actually be of type {@code K}. - * - * {@inheritDoc} - * * @since 11.0 */ @Override - public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) { + public ImmutableMap<K, V> getAllPresent(Iterable<? extends K> keys) { Map<K, V> result = Maps.newLinkedHashMap(); - for (Object key : keys) { + for (K key : keys) { if (!result.containsKey(key)) { - @SuppressWarnings("unchecked") - K castKey = (K) key; - result.put(castKey, getIfPresent(key)); + result.put(key, getIfPresent(key)); } } return ImmutableMap.copyOf(result); @@ -85,16 +78,6 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { throw new UnsupportedOperationException(); } - /** - * @since 12.0 - */ - @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 cleanUp() {} @@ -133,6 +116,22 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { throw new UnsupportedOperationException(); } + @Deprecated + @Override + public V getUnchecked(K key) { + try { + return get(key); + } catch (ExecutionException e) { + throw new UncheckedExecutionException(e.getCause()); + } + } + + @Deprecated + @Override + public V apply(K key) { + return getUnchecked(key); + } + /** * Accumulates statistics during the operation of a {@link Cache} for presentation by {@link * Cache#stats}. This is solely intended for consumption by {@code Cache} implementors. @@ -165,7 +164,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { /** * Records the successful load of a new entry. This should be called when a cache request * causes an entry to be loaded, and the loading completes successfully. In contrast to - * {@link #recordMisses}, this method should only be called by the loading thread. + * {@link #recordConcurrentMiss}, this method should only be called by the loading thread. * * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new * value @@ -175,7 +174,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { /** * Records the failed load of a new entry. This should be called when a cache request causes * an entry to be loaded, but an exception is thrown while loading the entry. In contrast to - * {@link #recordMisses}, this method should only be called by the loading thread. + * {@link #recordConcurrentMiss}, this method should only be called by the loading thread. * * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new * value prior to an exception being thrown @@ -202,25 +201,20 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { * @since 10.0 */ @Beta - public static final class SimpleStatsCounter implements StatsCounter { - private final LongAddable hitCount = LongAddables.create(); - private final LongAddable missCount = LongAddables.create(); - private final LongAddable loadSuccessCount = LongAddables.create(); - private final LongAddable loadExceptionCount = LongAddables.create(); - private final LongAddable totalLoadTime = LongAddables.create(); - private final LongAddable evictionCount = LongAddables.create(); - - /** - * Constructs an instance with all counts initialized to zero. - */ - public SimpleStatsCounter() {} + public static class SimpleStatsCounter implements StatsCounter { + private final AtomicLong hitCount = new AtomicLong(); + private final AtomicLong missCount = new AtomicLong(); + private final AtomicLong loadSuccessCount = new AtomicLong(); + private final AtomicLong loadExceptionCount = new AtomicLong(); + private final AtomicLong totalLoadTime = new AtomicLong(); + private final AtomicLong evictionCount = new AtomicLong(); /** * @since 11.0 */ @Override public void recordHits(int count) { - hitCount.add(count); + hitCount.addAndGet(count); } /** @@ -228,35 +222,35 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { */ @Override public void recordMisses(int count) { - missCount.add(count); + missCount.addAndGet(count); } @Override public void recordLoadSuccess(long loadTime) { - loadSuccessCount.increment(); - totalLoadTime.add(loadTime); + loadSuccessCount.incrementAndGet(); + totalLoadTime.addAndGet(loadTime); } @Override public void recordLoadException(long loadTime) { - loadExceptionCount.increment(); - totalLoadTime.add(loadTime); + loadExceptionCount.incrementAndGet(); + totalLoadTime.addAndGet(loadTime); } @Override public void recordEviction() { - evictionCount.increment(); + evictionCount.incrementAndGet(); } @Override public CacheStats snapshot() { return new CacheStats( - hitCount.sum(), - missCount.sum(), - loadSuccessCount.sum(), - loadExceptionCount.sum(), - totalLoadTime.sum(), - evictionCount.sum()); + hitCount.get(), + missCount.get(), + loadSuccessCount.get(), + loadExceptionCount.get(), + totalLoadTime.get(), + evictionCount.get()); } /** @@ -264,12 +258,12 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> { */ public void incrementBy(StatsCounter other) { CacheStats otherStats = other.snapshot(); - hitCount.add(otherStats.hitCount()); - missCount.add(otherStats.missCount()); - loadSuccessCount.add(otherStats.loadSuccessCount()); - loadExceptionCount.add(otherStats.loadExceptionCount()); - totalLoadTime.add(otherStats.totalLoadTime()); - evictionCount.add(otherStats.evictionCount()); + hitCount.addAndGet(otherStats.hitCount()); + missCount.addAndGet(otherStats.missCount()); + loadSuccessCount.addAndGet(otherStats.loadSuccessCount()); + loadExceptionCount.addAndGet(otherStats.loadExceptionCount()); + totalLoadTime.addAndGet(otherStats.totalLoadTime()); + evictionCount.addAndGet(otherStats.evictionCount()); } } } diff --git a/guava/src/com/google/common/cache/AbstractLoadingCache.java b/guava/src/com/google/common/cache/AbstractLoadingCache.java index 6a12c40..22ef81b 100644 --- a/guava/src/com/google/common/cache/AbstractLoadingCache.java +++ b/guava/src/com/google/common/cache/AbstractLoadingCache.java @@ -30,11 +30,10 @@ import java.util.concurrent.ExecutionException; * effort required to implement this interface. * * <p>To implement a cache, the programmer needs only to extend this class and provide an - * implementation for the {@link #get(Object)} and {@link #getIfPresent} methods. - * {@link #getUnchecked}, {@link #get(Object, Callable)}, and {@link #getAll} are implemented in - * terms of {@code get}; {@link #getAllPresent} is implemented in terms of {@code getIfPresent}; - * {@link #putAll} is implemented in terms of {@link #put}, {@link #invalidateAll(Iterable)} is - * implemented in terms of {@link #invalidate}. The method {@link #cleanUp} is a no-op. All other + * implementation for the {@link #get} and {@link #getIfPresent} methods. {@link #getUnchecked}, + * {@link #get(K, Callable)}, and {@link #getAll} are implemented in terms of {@code get}; + * {@link #getAllPresent} is implemented in terms of {@code get}; {@link #invalidateAll(Iterable)} + * is implemented in terms of {@link #invalidate}. The method {@link #cleanUp} is a no-op. All other * methods throw an {@link UnsupportedOperationException}. * * @author Charles Fry diff --git a/guava/src/com/google/common/cache/Cache.java b/guava/src/com/google/common/cache/Cache.java index cfe5764..f243adc 100644 --- a/guava/src/com/google/common/cache/Cache.java +++ b/guava/src/com/google/common/cache/Cache.java @@ -18,11 +18,11 @@ package com.google.common.cache; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ExecutionError; import com.google.common.util.concurrent.UncheckedExecutionException; -import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; @@ -31,23 +31,24 @@ import javax.annotation.Nullable; /** * A semi-persistent mapping from keys to values. Cache entries are manually added using - * {@link #get(Object, Callable)} or {@link #put(Object, Object)}, and are stored in the cache until - * either evicted or manually invalidated. + * {@link #get(K, Callable)} or {@link #put(K, V)}, and are stored in the cache until either + * evicted or manually invalidated. + * + * <p><b>Note:</b> in release 12.0, all methods moved from {@code Cache} to {@link LoadingCache} + * will be deleted from {@code Cache}. As part of this transition {@code Cache} will no longer + * extend {@link Function}. * * <p>Implementations of this interface are expected to be thread-safe, and can be safely accessed * by multiple concurrent threads. * - * <p>Note that while this class is still annotated as {@link Beta}, the API is frozen from a - * consumer's standpoint. In other words existing methods are all considered {@code non-Beta} and - * won't be changed without going through an 18 month deprecation cycle; however new methods may be - * added at any time. + * <p>All methods other than {@link #getIfPresent} are optional. * * @author Charles Fry * @since 10.0 */ @Beta @GwtCompatible -public interface Cache<K, V> { +public interface Cache<K, V> extends Function<K, V> { /** * Returns the value associated with {@code key} in this cache, or {@code null} if there is no @@ -56,7 +57,7 @@ public interface Cache<K, V> { * @since 11.0 */ @Nullable - V getIfPresent(Object key); + V getIfPresent(K key); /** * Returns the value associated with {@code key} in this cache, obtaining that value from @@ -82,7 +83,7 @@ public interface Cache<K, V> { * * @since 11.0 */ - ImmutableMap<K, V> getAllPresent(Iterable<?> keys); + ImmutableMap<K, V> getAllPresent(Iterable<? extends K> keys); /** * Associates {@code value} with {@code key} in this cache. If the cache previously contained a @@ -96,16 +97,6 @@ public interface Cache<K, V> { void put(K key, V value); /** - * Copies all of the mappings from the specified map to the cache. The effect of this call is - * equivalent to that of calling {@code put(k, v)} on this map once for each mapping from key - * {@code k} to value {@code v} in the specified map. The behavior of this operation is undefined - * if the specified map is modified while the operation is in progress. - * - * @since 12.0 - */ - void putAll(Map<? extends K,? extends V> m); - - /** * Discards any cached value for key {@code key}. */ void invalidate(Object key); @@ -144,4 +135,55 @@ public interface Cache<K, V> { * performed -- if any -- is implementation-dependent. */ void cleanUp(); + + /** + * Returns the value associated with {@code key} in this cache, first loading that value if + * necessary. No observable state associated with this cache is modified until loading completes. + * + * @throws ExecutionException if a checked exception was thrown while loading the value + * @throws UncheckedExecutionException if an unchecked exception was thrown while loading the + * value + * @throws ExecutionError if an error was thrown while loading the value + * @deprecated This method has been split out into the {@link LoadingCache} interface, and will be + * removed from {@code Cache} in Guava release 12.0. Note that + * {@link CacheBuilder#build(CacheLoader)} now returns a {@code LoadingCache}, so this deprecation + * (migration) can be dealt with by simply changing the type of references to the results of + * {@link CacheBuilder#build(CacheLoader)}. + */ + @Deprecated V get(K key) throws ExecutionException; + + /** + * Returns the value associated with {@code key} in this cache, first loading that value if + * necessary. No observable state associated with this cache is modified until computation + * completes. Unlike {@link #get}, this method does not throw a checked exception, and thus should + * only be used in situations where checked exceptions are not thrown by the cache loader. + * + * <p><b>Warning:</b> this method silently converts checked exceptions to unchecked exceptions, + * and should not be used with cache loaders which throw checked exceptions. + * + * @throws UncheckedExecutionException if an exception was thrown while loading the value, + * regardless of whether the exception was checked or unchecked + * @throws ExecutionError if an error was thrown while loading the value + * @deprecated This method has been split out into the {@link LoadingCache} interface, and will be + * removed from {@code Cache} in Guava release 12.0. Note that + * {@link CacheBuilder#build(CacheLoader)} now returns a {@code LoadingCache}, so this deprecation + * (migration) can be dealt with by simply changing the type of references to the results of + * {@link CacheBuilder#build(CacheLoader)}. + */ + @Deprecated V getUnchecked(K key); + + /** + * Discouraged. Provided to satisfy the {@code Function} interface; use {@link #get} or + * {@link #getUnchecked} instead. + * + * @throws UncheckedExecutionException if an exception was thrown while loading the value, + * regardless of whether the exception was checked or unchecked + * @throws ExecutionError if an error was thrown while loading the value + * @deprecated This method has been split out into the {@link LoadingCache} interface, and will be + * removed from {@code Cache} in Guava release 12.0. Note that + * {@link CacheBuilder#build(CacheLoader)} now returns a {@code LoadingCache}, so this deprecation + * (migration) can be dealt with by simply changing the type of references to the results of + * {@link CacheBuilder#build(CacheLoader)}. + */ + @Deprecated V apply(K key); } diff --git a/guava/src/com/google/common/cache/CacheBuilder.java b/guava/src/com/google/common/cache/CacheBuilder.java index 105ff73..8eef200 100644 --- a/guava/src/com/google/common/cache/CacheBuilder.java +++ b/guava/src/com/google/common/cache/CacheBuilder.java @@ -26,6 +26,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Objects; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -56,12 +57,8 @@ import javax.annotation.CheckReturnValue; * <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() @@ -75,19 +72,8 @@ import javax.annotation.CheckReturnValue; * } * });}</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> + * These features are all optional. * * <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 @@ -139,16 +125,13 @@ import javax.annotation.CheckReturnValue; * 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 */ +@Beta @GwtCompatible(emulated = true) public final class CacheBuilder<K, V> { private static final int DEFAULT_INITIAL_CAPACITY = 16; @@ -180,10 +163,10 @@ public final class CacheBuilder<K, V> { }); static final CacheStats EMPTY_STATS = new CacheStats(0, 0, 0, 0, 0, 0); - static final Supplier<StatsCounter> CACHE_STATS_COUNTER = - new Supplier<StatsCounter>() { + static final Supplier<SimpleStatsCounter> CACHE_STATS_COUNTER = + new Supplier<SimpleStatsCounter>() { @Override - public StatsCounter get() { + public SimpleStatsCounter get() { return new SimpleStatsCounter(); } }; @@ -236,7 +219,7 @@ public final class CacheBuilder<K, V> { RemovalListener<? super K, ? super V> removalListener; Ticker ticker; - Supplier<? extends StatsCounter> statsCounterSupplier = NULL_STATS_COUNTER; + Supplier<? extends StatsCounter> statsCounterSupplier = CACHE_STATS_COUNTER; // TODO(fry): make constructor private and update tests to use newBuilder CacheBuilder() {} @@ -250,34 +233,8 @@ public final class CacheBuilder<K, V> { } /** - * Constructs a new {@code CacheBuilder} instance with the settings specified in {@code spec}. - * - * @since 12.0 - */ - @Beta - @GwtIncompatible("To be supported") - public static CacheBuilder<Object, Object> from(CacheBuilderSpec spec) { - return spec.toCacheBuilder() - .lenientParsing(); - } - - /** - * Constructs a new {@code CacheBuilder} instance with the settings specified in {@code spec}. - * This is especially useful for command-line configuration of a {@code CacheBuilder}. - * - * @param spec a String in the format specified by {@link CacheBuilderSpec} - * @since 12.0 - */ - @Beta - @GwtIncompatible("To be supported") - public static CacheBuilder<Object, Object> from(String spec) { - return from(CacheBuilderSpec.parse(spec)); - } - - /** * Enables lenient parsing. Useful for tests and spec parsing. */ - @GwtIncompatible("To be supported") CacheBuilder<K, V> lenientParsing() { strictParsing = false; return this; @@ -286,10 +243,9 @@ public final class CacheBuilder<K, V> { /** * Sets a custom {@code Equivalence} strategy for comparing keys. * - * <p>By default, the cache uses {@link Equivalence#identity} to determine key equality when - * {@link #weakKeys} is specified, and {@link Equivalence#equals()} otherwise. + * <p>By default, the cache uses {@link Equivalences#identity} to determine key equality when + * {@link #weakKeys} is specified, and {@link Equivalences#equals()} otherwise. */ - @GwtIncompatible("To be supported") CacheBuilder<K, V> keyEquivalence(Equivalence<Object> equivalence) { checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence); keyEquivalence = checkNotNull(equivalence); @@ -303,11 +259,10 @@ public final class CacheBuilder<K, V> { /** * Sets a custom {@code Equivalence} strategy for comparing values. * - * <p>By default, the cache uses {@link Equivalence#identity} to determine value equality when - * {@link #weakValues} or {@link #softValues} is specified, and {@link Equivalence#equals()} + * <p>By default, the cache uses {@link Equivalences#identity} to determine value equality when + * {@link #weakValues} or {@link #softValues} is specified, and {@link Equivalences#equals()} * otherwise. */ - @GwtIncompatible("To be supported") CacheBuilder<K, V> valueEquivalence(Equivalence<Object> equivalence) { checkState(valueEquivalence == null, "value equivalence was already set to %s", valueEquivalence); @@ -350,23 +305,11 @@ public final class CacheBuilder<K, V> { * 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. + * at a time, but since read operations can proceed concurrently, this still yields higher + * concurrency than full synchronization. Defaults to 4. * - * <p>Note that future implementations may abandon segment locking in favor of more advanced - * concurrency controls. + * <p><b>Note:</b>The default may change in the future. If you care about this value, you should + * always choose it explicitly. * * @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive * @throws IllegalStateException if a concurrency level was already set @@ -392,11 +335,9 @@ public final class CacheBuilder<K, V> { * <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 + * @throws IllegalStateException if a maximum size was already set */ public CacheBuilder<K, V> maximumSize(long size) { checkState(this.maximumSize == UNSET_INT, "maximum size was already set to %s", @@ -423,17 +364,11 @@ public final class CacheBuilder<K, V> { * cache. This can be useful in testing, or to disable caching temporarily without a code * change. * - * <p>Note that weight is only used to determine whether the cache is over capacity; it has no - * effect on selecting which entry should be evicted next. - * - * <p>This feature cannot be used in conjunction with {@link #maximumSize}. - * - * @param weight the maximum total weight of entries the cache may contain - * @throws IllegalArgumentException if {@code weight} is negative - * @throws IllegalStateException if a maximum weight or size was already set + * @param weight the maximum weight the cache may contain + * @throws IllegalArgumentException if {@code size} is negative + * @throws IllegalStateException if a maximum size was already set * @since 11.0 */ - @GwtIncompatible("To be supported") public CacheBuilder<K, V> maximumWeight(long weight) { checkState(this.maximumWeight == UNSET_INT, "maximum weight was already set to %s", this.maximumWeight); @@ -472,7 +407,6 @@ public final class CacheBuilder<K, V> { * @throws IllegalStateException if a maximum size was already set * @since 11.0 */ - @GwtIncompatible("To be supported") public <K1 extends K, V1 extends V> CacheBuilder<K1, V1> weigher( Weigher<? super K1, ? super V1> weigher) { checkState(this.weigher == null); @@ -502,6 +436,15 @@ public final class CacheBuilder<K, V> { } /** + * Specifies that each key (not value) stored in the cache should be strongly referenced. + * + * @throws IllegalStateException if the key strength was already set + */ + CacheBuilder<K, V> strongKeys() { + return setKeyStrength(Strength.STRONG); + } + + /** * Specifies that each key (not value) stored in the cache should be wrapped in a {@link * WeakReference} (by default, strong references are used). * @@ -530,6 +473,15 @@ public final class CacheBuilder<K, V> { } /** + * Specifies that each value (not key) stored in the cache should be strongly referenced. + * + * @throws IllegalStateException if the value strength was already set + */ + CacheBuilder<K, V> strongValues() { + return setValueStrength(Strength.STRONG); + } + + /** * Specifies that each value (not key) stored in the cache should be wrapped in a * {@link WeakReference} (by default, strong references are used). * @@ -657,9 +609,8 @@ public final class CacheBuilder<K, V> { * {@link CacheLoader#reload}. * * <p>As the default implementation of {@link CacheLoader#reload} is synchronous, it is - * recommended that users of this method override {@link CacheLoader#reload} with an asynchronous - * implementation; otherwise refreshes will be performed during unrelated cache read and write - * operations. + * recommended that users of this method override {@link CacheLoader#reload} with an asynchrnous + * implementation; otherwise refreshes will block other cache operations. * * <p>Currently automatic refreshes are performed when the first stale request for an entry * occurs. The request triggering refresh will make a blocking call to {@link CacheLoader#reload} @@ -675,8 +626,7 @@ public final class CacheBuilder<K, V> { * @throws IllegalStateException if the refresh interval was already set * @since 11.0 */ - @Beta - @GwtIncompatible("To be supported (synchronously).") + @GwtIncompatible("To be supported") public CacheBuilder<K, V> refreshAfterWrite(long duration, TimeUnit unit) { checkNotNull(unit); checkState(refreshNanos == UNSET_INT, "refresh was already set to %s ns", refreshNanos); @@ -698,6 +648,7 @@ public final class CacheBuilder<K, V> { * * @throws IllegalStateException if a ticker was already set */ + @GwtIncompatible("To be supported") public CacheBuilder<K, V> ticker(Ticker ticker) { checkState(this.ticker == null); this.ticker = checkNotNull(ticker); @@ -712,27 +663,34 @@ public final class CacheBuilder<K, V> { } /** - * 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 + * Specifies a listener instance, which all caches built using this {@code CacheBuilder} will + * notify each time an entry is removed from the cache by any means. + * + * <p>Each cache built by this {@code CacheBuilder} after this method is called invokes the + * supplied listener after removing an element for any reason (see removal causes in {@link + * RemovalCause}). It will invoke the listener as part of the routine maintenance described + * in the class javadoc. + * + * <p><b>Note:</b> <i>all exceptions thrown by {@code listener} will be logged (using + * {@link java.util.logging.Logger})and then swallowed</i>. + * + * <p><b>Important note:</b> Instead of returning <em>this</em> as a {@code CacheBuilder} + * instance, this method returns {@code CacheBuilder<K1, V1>}. From this point on, either the + * original reference or the returned reference may be used to complete configuration and build + * the cache, but only the "generic" one is type-safe. That is, it will properly prevent you from + * building caches whose key or value types are incompatible with the types accepted by the + * listener already provided; the {@code CacheBuilder} type cannot do this. For best results, + * simply use the standard method-chaining idiom, as illustrated in the documentation at top, + * configuring a {@code CacheBuilder} and building your {@link Cache} all in a single statement. + * + * <p><b>Warning:</b> if you ignore the above advice, and use this {@code CacheBuilder} to build + * a cache whose key or value type is incompatible with the listener, you will likely experience + * a {@link ClassCastException} at some <i>undefined</i> point in the future. + * * @throws IllegalStateException if a removal listener was already set */ @CheckReturnValue + @GwtIncompatible("To be supported") public <K1 extends K, V1 extends V> CacheBuilder<K1, V1> removalListener( RemovalListener<? super K1, ? super V1> listener) { checkState(this.removalListener == null); @@ -751,15 +709,11 @@ public final class CacheBuilder<K, V> { } /** - * 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) + * Disable the accumulation of {@link CacheStats} during the operation of the cache. */ - public CacheBuilder<K, V> recordStats() { - statsCounterSupplier = CACHE_STATS_COUNTER; + CacheBuilder<K, V> disableStats() { + checkState(statsCounterSupplier == CACHE_STATS_COUNTER); + statsCounterSupplier = NULL_STATS_COUNTER; return this; } @@ -834,11 +788,12 @@ public final class CacheBuilder<K, V> { if (concurrencyLevel != UNSET_INT) { s.add("concurrencyLevel", concurrencyLevel); } - if (maximumSize != UNSET_INT) { - s.add("maximumSize", maximumSize); - } if (maximumWeight != UNSET_INT) { - s.add("maximumWeight", maximumWeight); + if (weigher == null) { + s.add("maximumSize", maximumWeight); + } else { + s.add("maximumWeight", maximumWeight); + } } if (expireAfterWriteNanos != UNSET_INT) { s.add("expireAfterWrite", expireAfterWriteNanos + "ns"); diff --git a/guava/src/com/google/common/cache/CacheBuilderSpec.java b/guava/src/com/google/common/cache/CacheBuilderSpec.java deleted file mode 100644 index 1e03335..0000000 --- a/guava/src/com/google/common/cache/CacheBuilderSpec.java +++ /dev/null @@ -1,455 +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.cache; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; -import com.google.common.base.Splitter; -import com.google.common.cache.LocalCache.Strength; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nullable; - -/** - * A specification of a {@link CacheBuilder} configuration. - * - * <p>{@code CacheBuilderSpec} supports parsing configuration off of a string, which - * makes it especially useful for command-line configuration of a {@code CacheBuilder}. - * - * <p>The string syntax is a series of comma-separated keys or key-value pairs, - * each corresponding to a {@code CacheBuilder} method. - * <ul> - * <li>{@code concurrencyLevel=[integer]}: sets {@link CacheBuilder#concurrencyLevel}. - * <li>{@code initialCapacity=[integer]}: sets {@link CacheBuilder#initialCapacity}. - * <li>{@code maximumSize=[long]}: sets {@link CacheBuilder#maximumSize}. - * <li>{@code maximumWeight=[long]}: sets {@link CacheBuilder#maximumWeight}. - * <li>{@code expireAfterAccess=[duration]}: sets {@link CacheBuilder#expireAfterAccess}. - * <li>{@code expireAfterWrite=[duration]}: sets {@link CacheBuilder#expireAfterWrite}. - * <li>{@code refreshAfterWrite=[duration]}: sets {@link CacheBuilder#refreshAfterWrite}. - * <li>{@code weakKeys}: sets {@link CacheBuilder#weakKeys}. - * <li>{@code softValues}: sets {@link CacheBuilder#softValues}. - * <li>{@code weakValues}: sets {@link CacheBuilder#weakValues}. - * </ul> - * - * The set of supported keys will grow as {@code CacheBuilder} evolves, but existing keys - * will never be removed. - * - * <p>Durations are represented by an integer, followed by one of "d", "h", "m", - * or "s", representing days, hours, minutes, or seconds respectively. (There - * is currently no syntax to request expiration in milliseconds, microseconds, - * or nanoseconds.) - * - * <p>Whitespace before and after commas and equal signs is ignored. Keys may - * not be repeated; it is also illegal to use the following pairs of keys in - * a single value: - * <ul> - * <li>{@code maximumSize} and {@code maximumWeight} - * <li>{@code softValues} and {@code weakValues} - * </ul> - * - * <p>{@code CacheBuilderSpec} does not support configuring {@code CacheBuilder} methods - * with non-value parameters. These must be configured in code. - * - * <p>A new {@code CacheBuilder} can be instantiated from a {@code CacheBuilderSpec} using - * {@link CacheBuilder#from(CacheBuilderSpec)} or {@link CacheBuilder#from(String)}. - * - * @author Adam Winer - * @since 12.0 - */ -@Beta -public final class CacheBuilderSpec { - /** Parses a single value. */ - private interface ValueParser { - void parse(CacheBuilderSpec spec, String key, @Nullable String value); - } - - /** Splits each key-value pair. */ - private static final Splitter KEYS_SPLITTER = Splitter.on(',').trimResults(); - - /** Splits the key from the value. */ - private static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').trimResults(); - - /** Map of names to ValueParser. */ - private static final ImmutableMap<String, ValueParser> VALUE_PARSERS = - ImmutableMap.<String, ValueParser>builder() - .put("initialCapacity", new InitialCapacityParser()) - .put("maximumSize", new MaximumSizeParser()) - .put("maximumWeight", new MaximumWeightParser()) - .put("concurrencyLevel", new ConcurrencyLevelParser()) - .put("weakKeys", new KeyStrengthParser(Strength.WEAK)) - .put("softValues", new ValueStrengthParser(Strength.SOFT)) - .put("weakValues", new ValueStrengthParser(Strength.WEAK)) - .put("expireAfterAccess", new AccessDurationParser()) - .put("expireAfterWrite", new WriteDurationParser()) - .put("refreshAfterWrite", new RefreshDurationParser()) - .put("refreshInterval", new RefreshDurationParser()) - .build(); - - @VisibleForTesting Integer initialCapacity; - @VisibleForTesting Long maximumSize; - @VisibleForTesting Long maximumWeight; - @VisibleForTesting Integer concurrencyLevel; - @VisibleForTesting Strength keyStrength; - @VisibleForTesting Strength valueStrength; - @VisibleForTesting long writeExpirationDuration; - @VisibleForTesting TimeUnit writeExpirationTimeUnit; - @VisibleForTesting long accessExpirationDuration; - @VisibleForTesting TimeUnit accessExpirationTimeUnit; - @VisibleForTesting long refreshDuration; - @VisibleForTesting TimeUnit refreshTimeUnit; - /** Specification; used for toParseableString(). */ - private final String specification; - - private CacheBuilderSpec(String specification) { - this.specification = specification; - } - - /** - * Creates a CacheBuilderSpec from a string. - * - * @param cacheBuilderSpecification the string form - */ - public static CacheBuilderSpec parse(String cacheBuilderSpecification) { - CacheBuilderSpec spec = new CacheBuilderSpec(cacheBuilderSpecification); - if (!cacheBuilderSpecification.isEmpty()) { - for (String keyValuePair : KEYS_SPLITTER.split(cacheBuilderSpecification)) { - List<String> keyAndValue = ImmutableList.copyOf(KEY_VALUE_SPLITTER.split(keyValuePair)); - checkArgument(!keyAndValue.isEmpty(), "blank key-value pair"); - checkArgument(keyAndValue.size() <= 2, - "key-value pair %s with more than one equals sign", keyValuePair); - - // Find the ValueParser for the current key. - String key = keyAndValue.get(0); - ValueParser valueParser = VALUE_PARSERS.get(key); - checkArgument(valueParser != null, "unknown key %s", key); - - String value = keyAndValue.size() == 1 ? null : keyAndValue.get(1); - valueParser.parse(spec, key, value); - } - } - - return spec; - } - - /** - * Returns a CacheBuilderSpec that will prevent caching. - */ - public static CacheBuilderSpec disableCaching() { - // Maximum size of zero is one way to block caching - return CacheBuilderSpec.parse("maximumSize=0"); - } - - /** - * Returns a CacheBuilder configured according to this instance's specification. - */ - CacheBuilder<Object, Object> toCacheBuilder() { - CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder(); - if (initialCapacity != null) { - builder.initialCapacity(initialCapacity); - } - if (maximumSize != null) { - builder.maximumSize(maximumSize); - } - if (maximumWeight != null) { - builder.maximumWeight(maximumWeight); - } - if (concurrencyLevel != null) { - builder.concurrencyLevel(concurrencyLevel); - } - if (keyStrength != null) { - switch (keyStrength) { - case WEAK: - builder.weakKeys(); - break; - default: - throw new AssertionError(); - } - } - if (valueStrength != null) { - switch (valueStrength) { - case SOFT: - builder.softValues(); - break; - case WEAK: - builder.weakValues(); - break; - default: - throw new AssertionError(); - } - } - if (writeExpirationTimeUnit != null) { - builder.expireAfterWrite(writeExpirationDuration, writeExpirationTimeUnit); - } - if (accessExpirationTimeUnit != null) { - builder.expireAfterAccess(accessExpirationDuration, accessExpirationTimeUnit); - } - if (refreshTimeUnit != null) { - builder.refreshAfterWrite(refreshDuration, refreshTimeUnit); - } - - return builder; - } - - /** - * Returns a string that can be used to parse an equivalent - * {@code CacheBuilderSpec}. The order and form of this representation is - * not guaranteed, except that reparsing its output will produce - * a {@code CacheBuilderSpec} equal to this instance. - */ - public String toParsableString() { - return specification; - } - - /** - * Returns a string representation for this CacheBuilderSpec instance. - * The form of this representation is not guaranteed. - */ - @Override - public String toString() { - return Objects.toStringHelper(this).addValue(toParsableString()).toString(); - } - - @Override - public int hashCode() { - return Objects.hashCode( - initialCapacity, - maximumSize, - maximumWeight, - concurrencyLevel, - keyStrength, - valueStrength, - durationInNanos(writeExpirationDuration, writeExpirationTimeUnit), - durationInNanos(accessExpirationDuration, accessExpirationTimeUnit), - durationInNanos(refreshDuration, refreshTimeUnit)); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof CacheBuilderSpec)) { - return false; - } - CacheBuilderSpec that = (CacheBuilderSpec) obj; - return Objects.equal(initialCapacity, that.initialCapacity) - && Objects.equal(maximumSize, that.maximumSize) - && Objects.equal(maximumWeight, that.maximumWeight) - && Objects.equal(concurrencyLevel, that.concurrencyLevel) - && Objects.equal(keyStrength, that.keyStrength) - && Objects.equal(valueStrength, that.valueStrength) - && Objects.equal(durationInNanos(writeExpirationDuration, writeExpirationTimeUnit), - durationInNanos(that.writeExpirationDuration, that.writeExpirationTimeUnit)) - && Objects.equal(durationInNanos(accessExpirationDuration, accessExpirationTimeUnit), - durationInNanos(that.accessExpirationDuration, that.accessExpirationTimeUnit)) - && Objects.equal(durationInNanos(refreshDuration, refreshTimeUnit), - durationInNanos(that.refreshDuration, that.refreshTimeUnit)); - } - - /** - * Converts an expiration duration/unit pair into a single Long for hashing and equality. - * Uses nanos to match CacheBuilder implementation. - */ - @Nullable private static Long durationInNanos(long duration, @Nullable TimeUnit unit) { - return (unit == null) ? null : unit.toNanos(duration); - } - - /** Base class for parsing integers. */ - abstract static class IntegerParser implements ValueParser { - protected abstract void parseInteger(CacheBuilderSpec spec, int value); - - @Override - public void parse(CacheBuilderSpec spec, String key, String value) { - checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key); - try { - parseInteger(spec, Integer.parseInt(value)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException( - String.format("key %s value set to %s, must be integer", key, value), e); - } - } - } - - /** Base class for parsing integers. */ - abstract static class LongParser implements ValueParser { - protected abstract void parseLong(CacheBuilderSpec spec, long value); - - @Override - public void parse(CacheBuilderSpec spec, String key, String value) { - checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key); - try { - parseLong(spec, Long.parseLong(value)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException( - String.format("key %s value set to %s, must be integer", key, value), e); - } - } - } - - /** Parse initialCapacity */ - static class InitialCapacityParser extends IntegerParser { - @Override - protected void parseInteger(CacheBuilderSpec spec, int value) { - checkArgument(spec.initialCapacity == null, - "initial capacity was already set to ", spec.initialCapacity); - spec.initialCapacity = value; - } - } - - /** Parse maximumSize */ - static class MaximumSizeParser extends LongParser { - @Override - protected void parseLong(CacheBuilderSpec spec, long value) { - checkArgument(spec.maximumSize == null, - "maximum size was already set to ", spec.maximumSize); - checkArgument(spec.maximumWeight == null, - "maximum weight was already set to ", spec.maximumWeight); - spec.maximumSize = value; - } - } - - /** Parse maximumWeight */ - static class MaximumWeightParser extends LongParser { - @Override - protected void parseLong(CacheBuilderSpec spec, long value) { - checkArgument(spec.maximumWeight == null, - "maximum weight was already set to ", spec.maximumWeight); - checkArgument(spec.maximumSize == null, - "maximum size was already set to ", spec.maximumSize); - spec.maximumWeight = value; - } - } - - /** Parse concurrencyLevel */ - static class ConcurrencyLevelParser extends IntegerParser { - @Override - protected void parseInteger(CacheBuilderSpec spec, int value) { - checkArgument(spec.concurrencyLevel == null, - "concurrency level was already set to ", spec.concurrencyLevel); - spec.concurrencyLevel = value; - } - } - - /** Parse weakKeys */ - static class KeyStrengthParser implements ValueParser { - private final Strength strength; - - public KeyStrengthParser(Strength strength) { - this.strength = strength; - } - - @Override - public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { - checkArgument(value == null, "key %s does not take values", key); - checkArgument(spec.keyStrength == null, "%s was already set to %s", key, spec.keyStrength); - spec.keyStrength = strength; - } - } - - /** Parse weakValues and softValues */ - static class ValueStrengthParser implements ValueParser { - private final Strength strength; - - public ValueStrengthParser(Strength strength) { - this.strength = strength; - } - - @Override - public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { - checkArgument(value == null, "key %s does not take values", key); - checkArgument(spec.valueStrength == null, - "%s was already set to %s", key, spec.valueStrength); - - spec.valueStrength = strength; - } - } - - /** Base class for parsing times with durations */ - abstract static class DurationParser implements ValueParser { - protected abstract void parseDuration( - CacheBuilderSpec spec, - long duration, - TimeUnit unit); - - @Override - public void parse(CacheBuilderSpec spec, String key, String value) { - checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key); - try { - char lastChar = value.charAt(value.length() - 1); - TimeUnit timeUnit; - switch (lastChar) { - case 'd': - timeUnit = TimeUnit.DAYS; - break; - case 'h': - timeUnit = TimeUnit.HOURS; - break; - case 'm': - timeUnit = TimeUnit.MINUTES; - break; - case 's': - timeUnit = TimeUnit.SECONDS; - break; - default: - throw new IllegalArgumentException( - String.format("key %s invalid format. was %s, must end with one of [dDhHmMsS]", - key, value)); - } - - long duration = Long.parseLong(value.substring(0, value.length() - 1)); - parseDuration(spec, duration, timeUnit); - } catch (NumberFormatException e) { - throw new IllegalArgumentException( - String.format("key %s value set to %s, must be integer", key, value)); - } - } - } - - /** Parse expireAfterAccess */ - static class AccessDurationParser extends DurationParser { - @Override protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { - checkArgument(spec.accessExpirationTimeUnit == null, "expireAfterAccess already set"); - spec.accessExpirationDuration = duration; - spec.accessExpirationTimeUnit = unit; - } - } - - /** Parse expireAfterWrite */ - static class WriteDurationParser extends DurationParser { - @Override protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { - checkArgument(spec.writeExpirationTimeUnit == null, "expireAfterWrite already set"); - spec.writeExpirationDuration = duration; - spec.writeExpirationTimeUnit = unit; - } - } - - /** Parse refreshAfterWrite */ - static class RefreshDurationParser extends DurationParser { - @Override protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { - checkArgument(spec.refreshTimeUnit == null, "refreshAfterWrite already set"); - spec.refreshDuration = duration; - spec.refreshTimeUnit = unit; - } - } -} diff --git a/guava/src/com/google/common/cache/CacheLoader.java b/guava/src/com/google/common/cache/CacheLoader.java index 2f014a3..21811e8 100644 --- a/guava/src/com/google/common/cache/CacheLoader.java +++ b/guava/src/com/google/common/cache/CacheLoader.java @@ -30,23 +30,15 @@ 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 */ +@Beta @GwtCompatible(emulated = true) public abstract class CacheLoader<K, V> { /** @@ -59,17 +51,13 @@ 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 a replacement value corresponding to an already-cached {@code key}. This * method is called when an existing cache entry is refreshed by - * {@link CacheBuilder#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}. + * {@link CacheBuilder#refreshAfterWrite}, or through a call to {@link Cache#refresh}. * * <p>This implementation synchronously delegates to {@link #load}. It is recommended that it be * overridden with an asynchronous implementation when using @@ -81,22 +69,16 @@ public abstract class CacheLoader<K, V> { * @param oldValue the non-null old value corresponding to {@code key} * @return the future new value associated with {@code key}; * <b>must not be null, must not return null</b> - * @throws Exception if unable to reload 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 */ @GwtIncompatible("Futures") public ListenableFuture<V> reload(K key, V oldValue) throws Exception { - checkNotNull(key); - checkNotNull(oldValue); return Futures.immediateFuture(load(key)); } /** * 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 @@ -104,33 +86,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); } @@ -145,22 +116,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); } @@ -175,7 +140,6 @@ public abstract class CacheLoader<K, V> { @Override public V load(Object key) { - checkNotNull(key); return computingSupplier.get(); } diff --git a/guava/src/com/google/common/cache/CacheStats.java b/guava/src/com/google/common/cache/CacheStats.java index d8c9eb7..04d442c 100644 --- a/guava/src/com/google/common/cache/CacheStats.java +++ b/guava/src/com/google/common/cache/CacheStats.java @@ -22,8 +22,6 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; -import java.util.concurrent.Callable; - import javax.annotation.Nullable; /** @@ -46,14 +44,9 @@ import javax.annotation.Nullable; * </ul> * <li>When an entry is evicted from the cache, {@code evictionCount} is incremented. * <li>No stats are modified when a cache entry is invalidated or manually removed. - * <li>No stats are modified on a query to {@link Cache#getIfPresent}. * <li>No stats are modified by operations invoked on the {@linkplain Cache#asMap asMap} view of * the cache. * </ul> - * - * <p>A lookup is specifically defined as an invocation of one of the methods - * {@link LoadingCache#get(Object)}, {@link LoadingCache#getUnchecked(Object)}, - * {@link Cache#get(Object, Callable)}, or {@link LoadingCache#getAll(Iterable)}. * * @author Charles Fry * @since 10.0 diff --git a/guava/src/com/google/common/cache/ForwardingCache.java b/guava/src/com/google/common/cache/ForwardingCache.java index 44fe683..4404593 100644 --- a/guava/src/com/google/common/cache/ForwardingCache.java +++ b/guava/src/com/google/common/cache/ForwardingCache.java @@ -21,7 +21,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ForwardingObject; import com.google.common.collect.ImmutableMap; -import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; @@ -50,7 +49,7 @@ public abstract class ForwardingCache<K, V> extends ForwardingObject implements */ @Override @Nullable - public V getIfPresent(Object key) { + public V getIfPresent(K key) { return delegate().getIfPresent(key); } @@ -66,7 +65,7 @@ public abstract class ForwardingCache<K, V> extends ForwardingObject implements * @since 11.0 */ @Override - public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) { + public ImmutableMap<K, V> getAllPresent(Iterable<? extends K> keys) { return delegate().getAllPresent(keys); } @@ -78,14 +77,6 @@ public abstract class ForwardingCache<K, V> extends ForwardingObject implements delegate().put(key, value); } - /** - * @since 12.0 - */ - @Override - public void putAll(Map<? extends K,? extends V> m) { - delegate().putAll(m); - } - @Override public void invalidate(Object key) { delegate().invalidate(key); @@ -124,6 +115,24 @@ public abstract class ForwardingCache<K, V> extends ForwardingObject implements delegate().cleanUp(); } + @Deprecated + @Override + public V get(K key) throws ExecutionException { + return delegate().get(key); + } + + @Deprecated + @Override + public V getUnchecked(K key) { + return delegate().getUnchecked(key); + } + + @Deprecated + @Override + public V apply(K key) { + return delegate().apply(key); + } + /** * A simplified version of {@link ForwardingCache} where subclasses can pass in an already * constructed {@link Cache} as the delegete. diff --git a/guava/src/com/google/common/cache/LoadingCache.java b/guava/src/com/google/common/cache/LoadingCache.java index 05b1312..471c31a 100644 --- a/guava/src/com/google/common/cache/LoadingCache.java +++ b/guava/src/com/google/common/cache/LoadingCache.java @@ -33,14 +33,11 @@ import java.util.concurrent.ExecutionException; * <p>Implementations of this interface are expected to be thread-safe, and can be safely accessed * by multiple concurrent threads. * + * <p>All methods other than {@link #get} and {@link #getUnchecked} are optional. + * * <p>When evaluated as a {@link Function}, a cache yields the same result as invoking * {@link #getUnchecked}. * - * <p>Note that while this class is still annotated as {@link Beta}, the API is frozen from a - * consumer's standpoint. In other words existing methods are all considered {@code non-Beta} and - * won't be changed without going through an 18 month deprecation cycle; however new methods may be - * added at any time. - * * @author Charles Fry * @since 11.0 */ @@ -141,8 +138,6 @@ public interface LoadingCache<K, V> extends Cache<K, V>, Function<K, V> { * * <p>Caches loaded by a {@link CacheLoader} will call {@link CacheLoader#reload} if the * cache currently contains a value for {@code key}, and {@link CacheLoader#load} otherwise. - * Loading is asynchronous only if {@link CacheLoader#reload} was overridden with an - * asynchronous implementation. * * <p>Returns without doing anything if another thread is currently loading the value for * {@code key}. If the cache loader associated with this cache performs refresh asynchronously diff --git a/guava/src/com/google/common/cache/LocalCache.java b/guava/src/com/google/common/cache/LocalCache.java index d7c5bd7..4973429 100644 --- a/guava/src/com/google/common/cache/LocalCache.java +++ b/guava/src/com/google/common/cache/LocalCache.java @@ -23,10 +23,9 @@ import static com.google.common.cache.CacheBuilder.UNSET_INT; import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; import static java.util.concurrent.TimeUnit.NANOSECONDS; -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Stopwatch; import com.google.common.base.Ticker; import com.google.common.cache.AbstractCache.SimpleStatsCounter; @@ -35,7 +34,7 @@ import com.google.common.cache.CacheBuilder.NullListener; import com.google.common.cache.CacheBuilder.OneWeigher; import com.google.common.cache.CacheLoader.InvalidCacheLoadException; import com.google.common.cache.CacheLoader.UnsupportedLoadingOperationException; -import com.google.common.collect.AbstractSequentialIterator; +import com.google.common.collect.AbstractLinkedIterator; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; @@ -48,7 +47,6 @@ import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.UncheckedExecutionException; -import com.google.common.util.concurrent.Uninterruptibles; import java.io.IOException; import java.io.ObjectInputStream; @@ -57,6 +55,7 @@ import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; +import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractQueue; import java.util.AbstractSet; @@ -90,7 +89,6 @@ import javax.annotation.concurrent.GuardedBy; * @author Bob Lee ({@code com.google.common.collect.MapMaker}) * @author Doug Lea ({@code ConcurrentHashMap}) */ -@GwtCompatible(emulated = true) class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> { /* @@ -232,8 +230,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> /** * Creates a new, empty map with the specified strategy, initial capacity and concurrency level. */ - LocalCache( - CacheBuilder<? super K, ? super V> builder, @Nullable CacheLoader<? super K, V> loader) { + LocalCache(CacheBuilder<? super K, ? super V> builder, CacheLoader<? super K, V> loader) { concurrencyLevel = Math.min(builder.getConcurrencyLevel(), MAX_SEGMENTS); keyStrength = builder.getKeyStrength(); @@ -263,15 +260,13 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> initialCapacity = Math.min(initialCapacity, (int) maxWeight); } - // Find the lowest power-of-two segmentCount that exceeds concurrencyLevel, unless - // maximumSize/Weight is specified in which case ensure that each segment gets at least 10 - // entries. The special casing for size-based eviction is only necessary because that eviction - // happens per segment instead of globally, so too many segments compared to the maximum size - // will result in random eviction behavior. + // Find power-of-two sizes best matching arguments. Constraints: + // (segmentCount <= maxWeight) + // && (concurrencyLevel > maxWeight || segmentCount > concurrencyLevel) int segmentShift = 0; int segmentCount = 1; while (segmentCount < concurrencyLevel - && (!evictsBySize() || segmentCount * 20 <= maxWeight)) { + && (!evictsBySize() || customWeigher() || segmentCount * 2 <= maxWeight)) { ++segmentShift; segmentCount <<= 1; } @@ -386,7 +381,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.equals(); + return Equivalences.equals(); } }, @@ -402,7 +397,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.identity(); + return Equivalences.identity(); } }, @@ -418,7 +413,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.identity(); + return Equivalences.identity(); } }; @@ -652,11 +647,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> /** * Creates a copy of this reference for the given entry. - * - * <p>{@code value} may be null only for a loading reference. */ - ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, @Nullable V value, ReferenceEntry<K, V> entry); + ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry); /** * Notifify pending loads that a new value was set. This is only relevant to loading @@ -701,8 +693,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } @Override - public ValueReference<Object, Object> copyFor(ReferenceQueue<Object> queue, - @Nullable Object value, ReferenceEntry<Object, Object> entry) { + public ValueReference<Object, Object> copyFor( + ReferenceQueue<Object> queue, ReferenceEntry<Object, Object> entry) { return this; } @@ -1664,8 +1656,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Override public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new WeakValueReference<K, V>(queue, value, entry); + ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new WeakValueReference<K, V>(queue, get(), entry); } @Override @@ -1710,9 +1702,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> public void notifyNewValue(V newValue) {} @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new SoftValueReference<K, V>(queue, value, entry); + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new SoftValueReference<K, V>(queue, get(), entry); } @Override @@ -1757,8 +1748,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } @@ -1800,8 +1790,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Override public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new WeightedWeakValueReference<K, V>(queue, value, entry, weight); + ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new WeightedWeakValueReference<K, V>(queue, get(), entry, weight); } } @@ -1822,9 +1812,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> return weight; } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new WeightedSoftValueReference<K, V>(queue, value, entry, weight); + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new WeightedSoftValueReference<K, V>(queue, get(), entry, weight); } } @@ -1892,10 +1881,10 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @VisibleForTesting ValueReference<K, V> newValueReference(ReferenceEntry<K, V> entry, V value, int weight) { int hash = entry.getHash(); - return valueStrength.referenceValue(segmentFor(hash), entry, checkNotNull(value), weight); + return valueStrength.referenceValue(segmentFor(hash), entry, value, weight); } - int hash(@Nullable Object key) { + int hash(Object key) { int h = keyEquivalence.hash(key); return rehash(h); } @@ -1964,13 +1953,12 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> * Returns true if the entry has expired. */ boolean isExpired(ReferenceEntry<K, V> entry, long now) { - checkNotNull(entry); if (expiresAfterAccess() - && (now - entry.getAccessTime() >= expireAfterAccessNanos)) { + && (now - entry.getAccessTime() > expireAfterAccessNanos)) { return true; } if (expiresAfterWrite() - && (now - entry.getWriteTime() >= expireAfterWriteNanos)) { + && (now - entry.getWriteTime() > expireAfterWriteNanos)) { return true; } return false; @@ -2150,7 +2138,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> StatsCounter statsCounter) { this.map = map; this.maxSegmentWeight = maxSegmentWeight; - this.statsCounter = checkNotNull(statsCounter); + this.statsCounter = statsCounter; initTable(newEntryArray(initialCapacity)); keyReferenceQueue = map.usesKeyReferences() @@ -2187,29 +2175,14 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @GuardedBy("Segment.this") ReferenceEntry<K, V> newEntry(K key, int hash, @Nullable ReferenceEntry<K, V> next) { - return map.entryFactory.newEntry(this, checkNotNull(key), hash, next); + return map.entryFactory.newEntry(this, key, hash, next); } - /** - * Copies {@code original} into a new entry chained to {@code newNext}. Returns the new entry, - * or {@code null} if {@code original} was already garbage collected. - */ @GuardedBy("Segment.this") ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { - if (original.getKey() == null) { - // key collected - return null; - } - ValueReference<K, V> valueReference = original.getValueReference(); - V value = valueReference.get(); - if ((value == null) && valueReference.isActive()) { - // value collected - return null; - } - ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext); - newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, newEntry)); return newEntry; } @@ -2232,8 +2205,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> // loading V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException { - checkNotNull(key); - checkNotNull(loader); try { if (count != 0) { // read-volatile // don't call getLiveEntry, which would ignore loading values @@ -2424,9 +2395,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> V scheduleRefresh(ReferenceEntry<K, V> entry, K key, int hash, V oldValue, long now, CacheLoader<? super K, V> loader) { - if (map.refreshes() && (now - entry.getWriteTime() > map.refreshNanos) - && !entry.getValueReference().isLoading()) { - V newValue = refresh(key, hash, loader, true); + if (map.refreshes() && (now - entry.getWriteTime() > map.refreshNanos)) { + V newValue = refresh(key, hash, loader); if (newValue != null) { return newValue; } @@ -2441,9 +2411,9 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> * refresh. */ @Nullable - V refresh(K key, int hash, CacheLoader<? super K, V> loader, boolean checkTime) { + V refresh(K key, int hash, CacheLoader<? super K, V> loader) { final LoadingValueReference<K, V> loadingValueReference = - insertLoadingValueReference(key, hash, checkTime); + insertLoadingValueReference(key, hash); if (loadingValueReference == null) { return null; } @@ -2451,7 +2421,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> ListenableFuture<V> result = loadAsync(key, hash, loadingValueReference, loader); if (result.isDone()) { try { - return Uninterruptibles.getUninterruptibly(result); + return result.get(); } catch (Throwable t) { // don't let refresh exceptions propagate; error was already logged } @@ -2464,8 +2434,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> * is already loading. */ @Nullable - LoadingValueReference<K, V> insertLoadingValueReference(final K key, final int hash, - boolean checkTime) { + LoadingValueReference<K, V> insertLoadingValueReference(final K key, final int hash) { ReferenceEntry<K, V> e = null; lock(); try { @@ -2484,11 +2453,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> // We found an existing entry. ValueReference<K, V> valueReference = e.getValueReference(); - if (valueReference.isLoading() - || (checkTime && (now - e.getWriteTime() < map.refreshNanos))) { + if (valueReference.isLoading()) { // refresh is a no-op if loading is pending - // if checkTime, we want to check *after* acquiring the lock if refresh still needs - // to be scheduled return null; } @@ -3008,14 +2974,14 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> // Clone nodes leading up to the tail. for (ReferenceEntry<K, V> e = head; e != tail; e = e.getNext()) { - int newIndex = e.getHash() & newMask; - ReferenceEntry<K, V> newNext = newTable.get(newIndex); - ReferenceEntry<K, V> newFirst = copyEntry(e, newNext); - if (newFirst != null) { - newTable.set(newIndex, newFirst); - } else { + if (isCollected(e)) { removeCollectedEntry(e); newCount--; + } else { + int newIndex = e.getHash() & newMask; + ReferenceEntry<K, V> newNext = newTable.get(newIndex); + ReferenceEntry<K, V> newFirst = copyEntry(e, newNext); + newTable.set(newIndex, newFirst); } } } @@ -3177,11 +3143,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> preWriteCleanup(now); int newCount = this.count + 1; - if (newCount > this.threshold) { // ensure capacity - expand(); - newCount = this.count + 1; - } - AtomicReferenceArray<ReferenceEntry<K, V>> table = this.table; int index = hash & (table.length() - 1); ReferenceEntry<K, V> first = table.get(index); @@ -3192,10 +3153,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> && map.keyEquivalence.equivalent(key, entryKey)) { ValueReference<K, V> valueReference = e.getValueReference(); V entryValue = valueReference.get(); - // replace the old LoadingValueReference if it's live, otherwise - // perform a putIfAbsent - if (oldValueReference == valueReference - || (entryValue == null && valueReference != UNSET)) { + if (entryValue == null || oldValueReference == valueReference) { ++modCount; if (oldValueReference.isActive()) { RemovalCause cause = @@ -3328,12 +3286,11 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> int newCount = count; ReferenceEntry<K, V> newFirst = entry.getNext(); for (ReferenceEntry<K, V> e = first; e != entry; e = e.getNext()) { - ReferenceEntry<K, V> next = copyEntry(e, newFirst); - if (next != null) { - newFirst = next; - } else { + if (isCollected(e)) { removeCollectedEntry(e); newCount--; + } else { + newFirst = copyEntry(e, newFirst); } } this.count = newCount; @@ -3470,6 +3427,18 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } /** + * Returns true if the entry has been partially collected, meaning that either the key is null, + * or the value is active but null. + */ + boolean isCollected(ReferenceEntry<K, V> entry) { + if (entry.getKey() == null) { + return true; + } + ValueReference<K, V> valueReference = entry.getValueReference(); + return (valueReference.get() == null) && valueReference.isActive(); + } + + /** * Performs routine cleanup following a read. Normally cleanup happens during writes. If cleanup * is not observed after a sufficient number of reads, try cleaning up from the read thread. */ @@ -3604,15 +3573,12 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> return newValue != null ? newValue : Futures.<V>immediateFuture(null); } } catch (Throwable t) { - if (t instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } return setException(t) ? futureValue : fullyFailedFuture(t); } } public long elapsedNanos() { - return stopwatch.elapsed(NANOSECONDS); + return stopwatch.elapsedTime(NANOSECONDS); } @Override @@ -3635,8 +3601,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, @Nullable V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } } @@ -3770,7 +3735,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Override public Iterator<ReferenceEntry<K, V>> iterator() { - return new AbstractSequentialIterator<ReferenceEntry<K, V>>(peek()) { + return new AbstractLinkedIterator<ReferenceEntry<K, V>>(peek()) { @Override protected ReferenceEntry<K, V> computeNext(ReferenceEntry<K, V> previous) { ReferenceEntry<K, V> next = previous.getNextInWriteQueue(); @@ -3907,7 +3872,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Override public Iterator<ReferenceEntry<K, V>> iterator() { - return new AbstractSequentialIterator<ReferenceEntry<K, V>>(peek()) { + return new AbstractLinkedIterator<ReferenceEntry<K, V>>(peek()) { @Override protected ReferenceEntry<K, V> computeNext(ReferenceEntry<K, V> previous) { ReferenceEntry<K, V> next = previous.getNextInAccessQueue(); @@ -4004,20 +3969,17 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> return get(key, defaultLoader); } - ImmutableMap<K, V> getAllPresent(Iterable<?> keys) { + ImmutableMap<K, V> getAllPresent(Iterable<? extends K> keys) { int hits = 0; int misses = 0; Map<K, V> result = Maps.newLinkedHashMap(); - for (Object key : keys) { + for (K key : keys) { V value = get(key); if (value == null) { misses++; } else { - // TODO(fry): store entry key instead of query key - @SuppressWarnings("unchecked") - K castKey = (K) key; - result.put(castKey, value); + result.put(key, value); hits++; } } @@ -4026,7 +3988,8 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> return ImmutableMap.copyOf(result); } - ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException { + ImmutableMap<K, V> getAll(Iterable<? extends K> keys) + throws ExecutionException { int hits = 0; int misses = 0; @@ -4078,8 +4041,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> @Nullable Map<K, V> loadAll(Set<? extends K> keys, CacheLoader<? super K, V> loader) throws ExecutionException { - checkNotNull(loader); - checkNotNull(keys); Stopwatch stopwatch = new Stopwatch().start(); Map<K, V> result; boolean success = false; @@ -4091,9 +4052,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } catch (UnsupportedLoadingOperationException e) { success = true; throw e; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new ExecutionException(e); } catch (RuntimeException e) { throw new UncheckedExecutionException(e); } catch (Exception e) { @@ -4102,12 +4060,12 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> throw new ExecutionError(e); } finally { if (!success) { - globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS)); + globalStatsCounter.recordLoadException(stopwatch.elapsedTime(NANOSECONDS)); } } if (result == null) { - globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS)); + globalStatsCounter.recordLoadException(stopwatch.elapsedTime(NANOSECONDS)); throw new InvalidCacheLoadException(loader + " returned null map from loadAll"); } @@ -4126,12 +4084,12 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } if (nullsPresent) { - globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS)); + globalStatsCounter.recordLoadException(stopwatch.elapsedTime(NANOSECONDS)); throw new InvalidCacheLoadException(loader + " returned null keys or values from loadAll"); } // TODO(fry): record count of loaded entries - globalStatsCounter.recordLoadSuccess(stopwatch.elapsed(NANOSECONDS)); + globalStatsCounter.recordLoadSuccess(stopwatch.elapsedTime(NANOSECONDS)); return result; } @@ -4148,9 +4106,21 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> return segmentFor(hash).getEntry(key, hash); } + /** + * Returns the live internal entry for the specified key. + */ + ReferenceEntry<K, V> getLiveEntry(@Nullable Object key) { + // does not impact recency ordering + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).getLiveEntry(key, hash, ticker.read()); + } + void refresh(K key) { int hash = hash(checkNotNull(key)); - segmentFor(hash).refresh(key, hash, defaultLoader, false); + segmentFor(hash).refresh(key, hash, defaultLoader); } @Override @@ -4284,7 +4254,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> public Set<K> keySet() { // does not impact recency ordering Set<K> ks = keySet; - return (ks != null) ? ks : (keySet = new KeySet(this)); + return (ks != null) ? ks : (keySet = new KeySet()); } Collection<V> values; @@ -4293,22 +4263,21 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> public Collection<V> values() { // does not impact recency ordering Collection<V> vs = values; - return (vs != null) ? vs : (values = new Values(this)); + return (vs != null) ? vs : (values = new Values()); } Set<Entry<K, V>> entrySet; @Override - @GwtIncompatible("Not supported.") 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)); + return (es != null) ? es : (entrySet = new EntrySet()); } // Iterator Support - abstract class HashIterator<T> implements Iterator<T> { + abstract class HashIterator { int nextSegmentIndex; int nextTableIndex; @@ -4324,9 +4293,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> advance(); } - @Override - public abstract T next(); - final void advance() { nextExternal = null; @@ -4399,7 +4365,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } } - @Override public boolean hasNext() { return nextExternal != null; } @@ -4413,7 +4378,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> return lastReturned; } - @Override public void remove() { checkState(lastReturned != null); LocalCache.this.remove(lastReturned.getKey()); @@ -4421,7 +4385,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } } - final class KeyIterator extends HashIterator<K> { + final class KeyIterator extends HashIterator implements Iterator<K> { @Override public K next() { @@ -4429,7 +4393,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } } - final class ValueIterator extends HashIterator<V> { + final class ValueIterator extends HashIterator implements Iterator<V> { @Override public V next() { @@ -4489,7 +4453,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } } - final class EntryIterator extends HashIterator<Entry<K, V>> { + final class EntryIterator extends HashIterator implements Iterator<Entry<K, V>> { @Override public Entry<K, V> next() { @@ -4497,73 +4461,68 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } } - abstract class AbstractCacheSet<T> extends AbstractSet<T> { - final ConcurrentMap<?, ?> map; + final class KeySet extends AbstractSet<K> { - AbstractCacheSet(ConcurrentMap<?, ?> map) { - this.map = map; + @Override + public Iterator<K> iterator() { + return new KeyIterator(); } @Override public int size() { - return map.size(); + return LocalCache.this.size(); } @Override public boolean isEmpty() { - return map.isEmpty(); + return LocalCache.this.isEmpty(); } @Override - public void clear() { - map.clear(); + public boolean contains(Object o) { + return LocalCache.this.containsKey(o); } - } - final class KeySet extends AbstractCacheSet<K> { - - KeySet(ConcurrentMap<?, ?> map) { - super(map); + @Override + public boolean remove(Object o) { + return LocalCache.this.remove(o) != null; } @Override - public Iterator<K> iterator() { - return new KeyIterator(); + public void clear() { + LocalCache.this.clear(); } + } + + final class Values extends AbstractCollection<V> { @Override - public boolean contains(Object o) { - return map.containsKey(o); + public Iterator<V> iterator() { + return new ValueIterator(); } @Override - public boolean remove(Object o) { - return map.remove(o) != null; + public int size() { + return LocalCache.this.size(); } - } - - final class Values extends AbstractCacheSet<V> { - Values(ConcurrentMap<?, ?> map) { - super(map); + @Override + public boolean isEmpty() { + return LocalCache.this.isEmpty(); } @Override - public Iterator<V> iterator() { - return new ValueIterator(); + public boolean contains(Object o) { + return LocalCache.this.containsValue(o); } @Override - public boolean contains(Object o) { - return map.containsValue(o); + public void clear() { + LocalCache.this.clear(); } } - final class EntrySet extends AbstractCacheSet<Entry<K, V>> { - - EntrySet(ConcurrentMap<?, ?> map) { - super(map); - } + final class EntrySet extends AbstractSet<Entry<K, V>> { @Override public Iterator<Entry<K, V>> iterator() { @@ -4594,6 +4553,21 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> Object key = e.getKey(); return key != null && LocalCache.this.remove(key, e.getValue()); } + + @Override + public int size() { + return LocalCache.this.size(); + } + + @Override + public boolean isEmpty() { + return LocalCache.this.isEmpty(); + } + + @Override + public void clear() { + LocalCache.this.clear(); + } } // Serialization Support @@ -4663,15 +4637,15 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> this.loader = loader; } - CacheBuilder<K, V> recreateCacheBuilder() { - CacheBuilder<K, V> builder = CacheBuilder.newBuilder() + CacheBuilder<Object, Object> recreateCacheBuilder() { + CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder() .setKeyStrength(keyStrength) .setValueStrength(valueStrength) .keyEquivalence(keyEquivalence) .valueEquivalence(valueEquivalence) - .concurrencyLevel(concurrencyLevel) - .removalListener(removalListener); + .concurrencyLevel(concurrencyLevel); builder.strictParsing = false; + builder.removalListener(removalListener); if (expireAfterWriteNanos > 0) { builder.expireAfterWrite(expireAfterWriteNanos, TimeUnit.NANOSECONDS); } @@ -4696,7 +4670,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); - CacheBuilder<K, V> builder = recreateCacheBuilder(); + CacheBuilder<Object, Object> builder = recreateCacheBuilder(); this.delegate = builder.build(); } @@ -4730,7 +4704,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); - CacheBuilder<K, V> builder = recreateCacheBuilder(); + CacheBuilder<Object, Object> builder = recreateCacheBuilder(); this.autoDelegate = builder.build(loader); } @@ -4768,18 +4742,19 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> final LocalCache<K, V> localCache; LocalManualCache(CacheBuilder<? super K, ? super V> builder) { - this(new LocalCache<K, V>(builder, null)); + this(builder, null); } - private LocalManualCache(LocalCache<K, V> localCache) { - this.localCache = localCache; + protected LocalManualCache(CacheBuilder<? super K, ? super V> builder, + CacheLoader<? super K, V> loader) { + this.localCache = new LocalCache<K, V>(builder, loader); } // Cache methods @Override @Nullable - public V getIfPresent(Object key) { + public V getIfPresent(K key) { return localCache.getIfPresent(key); } @@ -4795,7 +4770,7 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } @Override - public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) { + public ImmutableMap<K, V> getAllPresent(Iterable<? extends K> keys) { return localCache.getAllPresent(keys); } @@ -4805,11 +4780,6 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> } @Override - public void putAll(Map<? extends K, ? extends V> m) { - localCache.putAll(m); - } - - @Override public void invalidate(Object key) { checkNotNull(key); localCache.remove(key); @@ -4850,6 +4820,27 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> localCache.cleanUp(); } + /* + * These methods have been moved to LoadingCache, but they temporarily + * remain in Cache in Guava. + */ + + public V get(K key) throws ExecutionException { + return localCache.getOrLoad(key); + } + + public V getUnchecked(K key) { + try { + return get(key); + } catch (ExecutionException e) { + throw new UncheckedExecutionException(e.getCause()); + } + } + + public final V apply(K key) { + return getUnchecked(key); + } + // Serialization Support private static final long serialVersionUID = 1; @@ -4864,24 +4855,10 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> LocalLoadingCache(CacheBuilder<? super K, ? super V> builder, CacheLoader<? super K, V> loader) { - super(new LocalCache<K, V>(builder, checkNotNull(loader))); + super(builder, checkNotNull(loader)); } - // LoadingCache 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()); - } - } + // Cache methods @Override public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException { @@ -4893,16 +4870,10 @@ class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> localCache.refresh(key); } - @Override - public final V apply(K key) { - return getUnchecked(key); - } - // Serialization Support private static final long serialVersionUID = 1; - @Override Object writeReplace() { return new LoadingSerializationProxy<K, V>(localCache); } diff --git a/guava/src/com/google/common/cache/LongAddable.java b/guava/src/com/google/common/cache/LongAddable.java deleted file mode 100644 index 48ddbfc..0000000 --- a/guava/src/com/google/common/cache/LongAddable.java +++ /dev/null @@ -1,33 +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 com.google.common.annotations.GwtCompatible; - -/** - * Abstract interface for objects that can concurrently add longs. - * - * @author Louis Wasserman - */ -@GwtCompatible -interface LongAddable { - void increment(); - - void add(long x); - - long sum(); -} diff --git a/guava/src/com/google/common/cache/LongAddables.java b/guava/src/com/google/common/cache/LongAddables.java deleted file mode 100644 index a110c6c..0000000 --- a/guava/src/com/google/common/cache/LongAddables.java +++ /dev/null @@ -1,75 +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 com.google.common.annotations.GwtCompatible; -import com.google.common.base.Supplier; - -import java.util.concurrent.atomic.AtomicLong; - -/** - * Source of {@link LongAddable} objects that deals with GWT, Unsafe, and all - * that. - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -final class LongAddables { - private static final Supplier<LongAddable> SUPPLIER; - - static { - Supplier<LongAddable> supplier; - try { - new LongAdder(); - supplier = new Supplier<LongAddable>() { - @Override - public LongAddable get() { - return new LongAdder(); - } - }; - } catch (Throwable t) { // we really want to catch *everything* - supplier = new Supplier<LongAddable>() { - @Override - public LongAddable get() { - return new PureJavaLongAddable(); - } - }; - } - SUPPLIER = supplier; - } - - public static LongAddable create() { - return SUPPLIER.get(); - } - - private static final class PureJavaLongAddable extends AtomicLong implements LongAddable { - @Override - public void increment() { - getAndIncrement(); - } - - @Override - public void add(long x) { - getAndAdd(x); - } - - @Override - public long sum() { - return get(); - } - } -} diff --git a/guava/src/com/google/common/cache/LongAdder.java b/guava/src/com/google/common/cache/LongAdder.java deleted file mode 100644 index 5af81e1..0000000 --- a/guava/src/com/google/common/cache/LongAdder.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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/ - */ - -/* - * Source: - * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8 - */ - -package com.google.common.cache; - -import com.google.common.annotations.GwtCompatible; - -import java.util.concurrent.atomic.AtomicLong; -import java.io.IOException; -import java.io.Serializable; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -/** - * One or more variables that together maintain an initially zero - * {@code long} sum. When updates (method {@link #add}) are contended - * across threads, the set of variables may grow dynamically to reduce - * contention. Method {@link #sum} (or, equivalently, {@link - * #longValue}) returns the current total combined across the - * variables maintaining the sum. - * - * <p> This class is usually preferable to {@link AtomicLong} when - * multiple threads update a common sum that is used for purposes such - * as collecting statistics, not for fine-grained synchronization - * control. Under low update contention, the two classes have similar - * characteristics. But under high contention, expected throughput of - * this class is significantly higher, at the expense of higher space - * consumption. - * - * <p>This class extends {@link Number}, but does <em>not</em> define - * methods such as {@code hashCode} and {@code compareTo} because - * instances are expected to be mutated, and so are not useful as - * collection keys. - * - * <p><em>jsr166e note: This class is targeted to be placed in - * java.util.concurrent.atomic<em> - * - * @since 1.8 - * @author Doug Lea - */ -@GwtCompatible(emulated = true) -final class LongAdder extends Striped64 implements Serializable, LongAddable { - private static final long serialVersionUID = 7249069246863182397L; - - /** - * Version of plus for use in retryUpdate - */ - final long fn(long v, long x) { return v + x; } - - /** - * Creates a new adder with initial sum of zero. - */ - public LongAdder() { - } - - /** - * Adds the given value. - * - * @param x the value to add - */ - public void add(long x) { - Cell[] as; long b, v; HashCode hc; Cell a; int n; - if ((as = cells) != null || !casBase(b = base, b + x)) { - boolean uncontended = true; - int h = (hc = threadHashCode.get()).code; - if (as == null || (n = as.length) < 1 || - (a = as[(n - 1) & h]) == null || - !(uncontended = a.cas(v = a.value, v + x))) - retryUpdate(x, hc, uncontended); - } - } - - /** - * Equivalent to {@code add(1)}. - */ - public void increment() { - add(1L); - } - - /** - * Equivalent to {@code add(-1)}. - */ - public void decrement() { - add(-1L); - } - - /** - * Returns the current sum. The returned value is <em>NOT</em> an - * atomic snapshot: Invocation in the absence of concurrent - * updates returns an accurate result, but concurrent updates that - * occur while the sum is being calculated might not be - * incorporated. - * - * @return the sum - */ - public long sum() { - long sum = base; - Cell[] as = cells; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) - sum += a.value; - } - } - return sum; - } - - /** - * Resets variables maintaining the sum to zero. This method may - * be a useful alternative to creating a new adder, but is only - * effective if there are no concurrent updates. Because this - * method is intrinsically racy, it should only be used when it is - * known that no threads are concurrently updating. - */ - public void reset() { - internalReset(0L); - } - - /** - * Equivalent in effect to {@link #sum} followed by {@link - * #reset}. This method may apply for example during quiescent - * points between multithreaded computations. If there are - * updates concurrent with this method, the returned value is - * <em>not</em> guaranteed to be the final value occurring before - * the reset. - * - * @return the sum - */ - public long sumThenReset() { - long sum = base; - Cell[] as = cells; - base = 0L; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) { - sum += a.value; - a.value = 0L; - } - } - } - return sum; - } - - /** - * Returns the String representation of the {@link #sum}. - * @return the String representation of the {@link #sum} - */ - public String toString() { - return Long.toString(sum()); - } - - /** - * Equivalent to {@link #sum}. - * - * @return the sum - */ - public long longValue() { - return sum(); - } - - /** - * Returns the {@link #sum} as an {@code int} after a narrowing - * primitive conversion. - */ - public int intValue() { - return (int)sum(); - } - - /** - * Returns the {@link #sum} as a {@code float} - * after a widening primitive conversion. - */ - public float floatValue() { - return (float)sum(); - } - - /** - * Returns the {@link #sum} as a {@code double} after a widening - * primitive conversion. - */ - public double doubleValue() { - return (double)sum(); - } - - private void writeObject(ObjectOutputStream s) - throws java.io.IOException { - s.defaultWriteObject(); - s.writeLong(sum()); - } - - private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { - s.defaultReadObject(); - busy = 0; - cells = null; - base = s.readLong(); - } - -} diff --git a/guava/src/com/google/common/cache/RemovalCause.java b/guava/src/com/google/common/cache/RemovalCause.java index 0be9b5b..6574b0e 100644 --- a/guava/src/com/google/common/cache/RemovalCause.java +++ b/guava/src/com/google/common/cache/RemovalCause.java @@ -17,7 +17,6 @@ package com.google.common.cache; import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; import java.util.Iterator; import java.util.Map; @@ -30,7 +29,6 @@ import java.util.concurrent.ConcurrentMap; * @since 10.0 */ @Beta -@GwtCompatible public enum RemovalCause { /** * The entry was manually removed by the user. This can result from the user invoking diff --git a/guava/src/com/google/common/cache/RemovalListener.java b/guava/src/com/google/common/cache/RemovalListener.java index 270a7c4..e9b6c2c 100644 --- a/guava/src/com/google/common/cache/RemovalListener.java +++ b/guava/src/com/google/common/cache/RemovalListener.java @@ -17,7 +17,6 @@ package com.google.common.cache; import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; /** * An object that can receive a notification when an entry is removed from a cache. The removal @@ -37,7 +36,6 @@ import com.google.common.annotations.GwtCompatible; * @since 10.0 */ @Beta -@GwtCompatible public interface RemovalListener<K, V> { /** * Notifies the listener that a removal occurred at some point in the past. diff --git a/guava/src/com/google/common/cache/RemovalListeners.java b/guava/src/com/google/common/cache/RemovalListeners.java index dbb9efc..18292fd 100644 --- a/guava/src/com/google/common/cache/RemovalListeners.java +++ b/guava/src/com/google/common/cache/RemovalListeners.java @@ -16,8 +16,6 @@ package com.google.common.cache; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.Beta; import java.util.concurrent.Executor; @@ -43,8 +41,6 @@ public final class RemovalListeners { */ public static <K, V> RemovalListener<K, V> asynchronous( final RemovalListener<K, V> listener, final Executor executor) { - checkNotNull(listener); - checkNotNull(executor); return new RemovalListener<K, V>() { @Override public void onRemoval(final RemovalNotification<K, V> notification) { diff --git a/guava/src/com/google/common/cache/RemovalNotification.java b/guava/src/com/google/common/cache/RemovalNotification.java index 2565dba..8e0066e 100644 --- a/guava/src/com/google/common/cache/RemovalNotification.java +++ b/guava/src/com/google/common/cache/RemovalNotification.java @@ -19,7 +19,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.Objects; import java.util.Map.Entry; @@ -38,7 +37,6 @@ import javax.annotation.Nullable; * @since 10.0 */ @Beta -@GwtCompatible public final class RemovalNotification<K, V> implements Entry<K, V> { @Nullable private final K key; @Nullable private final V value; diff --git a/guava/src/com/google/common/cache/Striped64.java b/guava/src/com/google/common/cache/Striped64.java deleted file mode 100644 index e045453..0000000 --- a/guava/src/com/google/common/cache/Striped64.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * 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/ - */ - -/* - * Source: - * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.7 - */ - -package com.google.common.cache; - -import java.util.Random; - -/** - * A package-local class holding common representation and mechanics - * for classes supporting dynamic striping on 64bit values. The class - * extends Number so that concrete subclasses must publicly do so. - */ -abstract class Striped64 extends Number { - /* - * This class maintains a lazily-initialized table of atomically - * updated variables, plus an extra "base" field. The table size - * is a power of two. Indexing uses masked per-thread hash codes. - * Nearly all declarations in this class are package-private, - * accessed directly by subclasses. - * - * Table entries are of class Cell; a variant of AtomicLong padded - * to reduce cache contention on most processors. Padding is - * overkill for most Atomics because they are usually irregularly - * scattered in memory and thus don't interfere much with each - * other. But Atomic objects residing in arrays will tend to be - * placed adjacent to each other, and so will most often share - * cache lines (with a huge negative performance impact) without - * this precaution. - * - * In part because Cells are relatively large, we avoid creating - * them until they are needed. When there is no contention, all - * updates are made to the base field. Upon first contention (a - * failed CAS on base update), the table is initialized to size 2. - * The table size is doubled upon further contention until - * reaching the nearest power of two greater than or equal to the - * number of CPUS. Table slots remain empty (null) until they are - * needed. - * - * A single spinlock ("busy") is used for initializing and - * resizing the table, as well as populating slots with new Cells. - * There is no need for a blocking lock: When the lock is not - * available, threads try other slots (or the base). During these - * retries, there is increased contention and reduced locality, - * which is still better than alternatives. - * - * Per-thread hash codes are initialized to random values. - * Contention and/or table collisions are indicated by failed - * CASes when performing an update operation (see method - * retryUpdate). Upon a collision, if the table size is less than - * the capacity, it is doubled in size unless some other thread - * holds the lock. If a hashed slot is empty, and lock is - * available, a new Cell is created. Otherwise, if the slot - * exists, a CAS is tried. Retries proceed by "double hashing", - * using a secondary hash (Marsaglia XorShift) to try to find a - * free slot. - * - * The table size is capped because, when there are more threads - * than CPUs, supposing that each thread were bound to a CPU, - * there would exist a perfect hash function mapping threads to - * slots that eliminates collisions. When we reach capacity, we - * search for this mapping by randomly varying the hash codes of - * colliding threads. Because search is random, and collisions - * only become known via CAS failures, convergence can be slow, - * and because threads are typically not bound to CPUS forever, - * may not occur at all. However, despite these limitations, - * observed contention rates are typically low in these cases. - * - * It is possible for a Cell to become unused when threads that - * once hashed to it terminate, as well as in the case where - * doubling the table causes no thread to hash to it under - * expanded mask. We do not try to detect or remove such cells, - * under the assumption that for long-running instances, observed - * contention levels will recur, so the cells will eventually be - * needed again; and for short-lived ones, it does not matter. - */ - - /** - * Padded variant of AtomicLong supporting only raw accesses plus CAS. - * The value field is placed between pads, hoping that the JVM doesn't - * reorder them. - * - * JVM intrinsics note: It would be possible to use a release-only - * form of CAS here, if it were provided. - */ - static final class Cell { - volatile long p0, p1, p2, p3, p4, p5, p6; - volatile long value; - volatile long q0, q1, q2, q3, q4, q5, q6; - Cell(long x) { value = x; } - - final boolean cas(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); - } - - // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long valueOffset; - static { - try { - UNSAFE = getUnsafe(); - Class<?> ak = Cell.class; - valueOffset = UNSAFE.objectFieldOffset - (ak.getDeclaredField("value")); - } catch (Exception e) { - throw new Error(e); - } - } - - } - - /** - * Holder for the thread-local hash code. The code is initially - * random, but may be set to a different value upon collisions. - */ - static final class HashCode { - static final Random rng = new Random(); - int code; - HashCode() { - int h = rng.nextInt(); // Avoid zero to allow xorShift rehash - code = (h == 0) ? 1 : h; - } - } - - /** - * The corresponding ThreadLocal class - */ - static final class ThreadHashCode extends ThreadLocal<HashCode> { - public HashCode initialValue() { return new HashCode(); } - } - - /** - * Static per-thread hash codes. Shared across all instances to - * reduce ThreadLocal pollution and because adjustments due to - * collisions in one table are likely to be appropriate for - * others. - */ - static final ThreadHashCode threadHashCode = new ThreadHashCode(); - - /** Number of CPUS, to place bound on table size */ - static final int NCPU = Runtime.getRuntime().availableProcessors(); - - /** - * Table of cells. When non-null, size is a power of 2. - */ - transient volatile Cell[] cells; - - /** - * Base value, used mainly when there is no contention, but also as - * a fallback during table initialization races. Updated via CAS. - */ - transient volatile long base; - - /** - * Spinlock (locked via CAS) used when resizing and/or creating Cells. - */ - transient volatile int busy; - - /** - * Package-private default constructor - */ - Striped64() { - } - - /** - * CASes the base field. - */ - final boolean casBase(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); - } - - /** - * CASes the busy field from 0 to 1 to acquire lock. - */ - final boolean casBusy() { - return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); - } - - /** - * Computes the function of current and new value. Subclasses - * should open-code this update function for most uses, but the - * virtualized form is needed within retryUpdate. - * - * @param currentValue the current value (of either base or a cell) - * @param newValue the argument from a user update call - * @return result of the update function - */ - abstract long fn(long currentValue, long newValue); - - /** - * Handles cases of updates involving initialization, resizing, - * creating new Cells, and/or contention. See above for - * explanation. This method suffers the usual non-modularity - * problems of optimistic retry code, relying on rechecked sets of - * reads. - * - * @param x the value - * @param hc the hash code holder - * @param wasUncontended false if CAS failed before call - */ - final void retryUpdate(long x, HashCode hc, boolean wasUncontended) { - int h = hc.code; - boolean collide = false; // True if last slot nonempty - for (;;) { - Cell[] as; Cell a; int n; long v; - if ((as = cells) != null && (n = as.length) > 0) { - if ((a = as[(n - 1) & h]) == null) { - if (busy == 0) { // Try to attach new Cell - Cell r = new Cell(x); // Optimistically create - if (busy == 0 && casBusy()) { - boolean created = false; - try { // Recheck under lock - Cell[] rs; int m, j; - if ((rs = cells) != null && - (m = rs.length) > 0 && - rs[j = (m - 1) & h] == null) { - rs[j] = r; - created = true; - } - } finally { - busy = 0; - } - if (created) - break; - continue; // Slot is now non-empty - } - } - collide = false; - } - else if (!wasUncontended) // CAS already known to fail - wasUncontended = true; // Continue after rehash - else if (a.cas(v = a.value, fn(v, x))) - break; - else if (n >= NCPU || cells != as) - collide = false; // At max size or stale - else if (!collide) - collide = true; - else if (busy == 0 && casBusy()) { - try { - if (cells == as) { // Expand table unless stale - Cell[] rs = new Cell[n << 1]; - for (int i = 0; i < n; ++i) - rs[i] = as[i]; - cells = rs; - } - } finally { - busy = 0; - } - collide = false; - continue; // Retry with expanded table - } - h ^= h << 13; // Rehash - h ^= h >>> 17; - h ^= h << 5; - } - else if (busy == 0 && cells == as && casBusy()) { - boolean init = false; - try { // Initialize table - if (cells == as) { - Cell[] rs = new Cell[2]; - rs[h & 1] = new Cell(x); - cells = rs; - init = true; - } - } finally { - busy = 0; - } - if (init) - break; - } - else if (casBase(v = base, fn(v, x))) - break; // Fall back on using base - } - hc.code = h; // Record index for next time - } - - /** - * Sets base and all cells to the given value. - */ - final void internalReset(long initialValue) { - Cell[] as = cells; - base = initialValue; - if (as != null) { - int n = as.length; - for (int i = 0; i < n; ++i) { - Cell a = as[i]; - if (a != null) - a.value = initialValue; - } - } - } - - // Unsafe mechanics - private static final sun.misc.Unsafe UNSAFE; - private static final long baseOffset; - private static final long busyOffset; - static { - try { - UNSAFE = getUnsafe(); - Class<?> sk = Striped64.class; - baseOffset = UNSAFE.objectFieldOffset - (sk.getDeclaredField("base")); - busyOffset = UNSAFE.objectFieldOffset - (sk.getDeclaredField("busy")); - } catch (Exception e) { - throw new Error(e); - } - } - - /** - * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. - * Replace with a simple call to Unsafe.getUnsafe when integrating - * into a jdk. - * - * @return a sun.misc.Unsafe - */ - private static sun.misc.Unsafe getUnsafe() { - try { - return sun.misc.Unsafe.getUnsafe(); - } catch (SecurityException tryReflectionInstead) {} - try { - return java.security.AccessController.doPrivileged - (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() { - public sun.misc.Unsafe run() throws Exception { - Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) - return k.cast(x); - } - throw new NoSuchFieldError("the Unsafe"); - }}); - } catch (java.security.PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", - e.getCause()); - } - } - -} diff --git a/guava/src/com/google/common/cache/Weigher.java b/guava/src/com/google/common/cache/Weigher.java index 5720cb8..bbb0a33 100644 --- a/guava/src/com/google/common/cache/Weigher.java +++ b/guava/src/com/google/common/cache/Weigher.java @@ -1,30 +1,30 @@ /* * 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 + * 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. + * 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 com.google.common.annotations.Beta; -import com.google.common.annotations.GwtCompatible; /** * Calculates the weights of cache entries. * - * @author Charles Fry + * @author fry@google.com (Charles Fry) * @since 11.0 */ @Beta -@GwtCompatible public interface Weigher<K, V> { /** @@ -34,4 +34,5 @@ public interface Weigher<K, V> { * @return the weight of the entry; must be non-negative */ int weigh(K key, V value); + } diff --git a/guava/src/com/google/common/cache/package-info.java b/guava/src/com/google/common/cache/package-info.java index ea0297b..1c5391c 100644 --- a/guava/src/com/google/common/cache/package-info.java +++ b/guava/src/com/google/common/cache/package-info.java @@ -22,9 +22,6 @@ * {@link com.google.common.cache.CacheBuilder}, with cache entries being loaded by * {@link com.google.common.cache.CacheLoader}. Statistics about cache performance are exposed using * {@link com.google.common.cache.CacheStats}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CachesExplained">caches</a>. * * <p>This package is a part of the open-source * <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. @@ -35,4 +32,3 @@ package com.google.common.cache; import javax.annotation.ParametersAreNonnullByDefault; - diff --git a/guava/src/com/google/common/collect/AbstractBiMap.java b/guava/src/com/google/common/collect/AbstractBiMap.java index 44ab8c7..2a94f88 100644 --- a/guava/src/com/google/common/collect/AbstractBiMap.java +++ b/guava/src/com/google/common/collect/AbstractBiMap.java @@ -49,7 +49,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) { @@ -67,20 +67,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. */ @@ -100,24 +86,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; @@ -140,7 +124,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; } @@ -207,7 +191,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); + } + }; } } @@ -230,7 +234,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() { @@ -363,16 +383,6 @@ abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> * 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); - } - /** * @serialData the forward bimap */ diff --git a/guava/src/com/google/common/collect/AbstractSequentialIterator.java b/guava/src/com/google/common/collect/AbstractLinkedIterator.java index c6567f5..e796b9b 100644 --- a/guava/src/com/google/common/collect/AbstractSequentialIterator.java +++ b/guava/src/com/google/common/collect/AbstractLinkedIterator.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.NoSuchElementException; @@ -30,18 +31,18 @@ import javax.annotation.Nullable; * * <p>Example: <pre> {@code * - * Iterator<Integer> powersOfTwo = - * new AbstractSequentialIterator<Integer>(1) { - * protected Integer computeNext(Integer previous) { - * return (previous == 1 << 30) ? null : previous * 2; - * } - * };}</pre> + * Iterator<Integer> powersOfTwo = new AbstractLinkedIterator<Integer>(1) { + * protected Integer computeNext(Integer previous) { + * return (previous == 1 << 30) ? null : previous * 2; + * } + * };}</pre> * * @author Chris Povirk - * @since 12.0 (in Guava as {@code AbstractLinkedIterator} since 8.0) + * @since 8.0 */ +@Beta @GwtCompatible -public abstract class AbstractSequentialIterator<T> +public abstract class AbstractLinkedIterator<T> extends UnmodifiableIterator<T> { private T nextOrNull; @@ -49,7 +50,7 @@ public abstract class AbstractSequentialIterator<T> * Creates a new iterator with the given first element, or, if {@code * firstOrNull} is null, creates a new empty iterator. */ - protected AbstractSequentialIterator(@Nullable T firstOrNull) { + protected AbstractLinkedIterator(@Nullable T firstOrNull) { this.nextOrNull = firstOrNull; } diff --git a/guava/src/com/google/common/collect/AbstractListMultimap.java b/guava/src/com/google/common/collect/AbstractListMultimap.java index 3759c93..ad24011 100644 --- a/guava/src/com/google/common/collect/AbstractListMultimap.java +++ b/guava/src/com/google/common/collect/AbstractListMultimap.java @@ -26,7 +26,7 @@ import javax.annotation.Nullable; /** * Basic implementation of the {@link ListMultimap} interface. It's a wrapper - * around {@link AbstractMapBasedMultimap} that converts the returned collections into + * around {@link AbstractMultimap} that converts the returned collections into * {@code Lists}. The {@link #createCollection} method must return a {@code * List}. * @@ -35,7 +35,7 @@ import javax.annotation.Nullable; */ @GwtCompatible abstract class AbstractListMultimap<K, V> - extends AbstractMapBasedMultimap<K, V> implements ListMultimap<K, V> { + extends AbstractMultimap<K, V> implements ListMultimap<K, V> { /** * Creates a new multimap that uses the provided map. * @@ -48,11 +48,6 @@ abstract class AbstractListMultimap<K, V> @Override abstract List<V> createCollection(); - @Override - List<V> createUnmodifiableEmptyCollection() { - return ImmutableList.of(); - } - // Following Javadoc copied from ListMultimap. /** diff --git a/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java b/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java deleted file mode 100644 index 0a1edf3..0000000 --- a/guava/src/com/google/common/collect/AbstractMapBasedMultimap.java +++ /dev/null @@ -1,1567 +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 com.google.common.annotations.GwtIncompatible; - -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.NavigableMap; -import java.util.NavigableSet; -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()); - } - } - - @GwtIncompatible("NavigableSet") - class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet<V> { - WrappedNavigableSet( - @Nullable K key, NavigableSet<V> delegate, @Nullable WrappedCollection ancestor) { - super(key, delegate, ancestor); - } - - @Override - NavigableSet<V> getSortedSetDelegate() { - return (NavigableSet<V>) super.getSortedSetDelegate(); - } - - @Override - public V lower(V v) { - return getSortedSetDelegate().lower(v); - } - - @Override - public V floor(V v) { - return getSortedSetDelegate().floor(v); - } - - @Override - public V ceiling(V v) { - return getSortedSetDelegate().ceiling(v); - } - - @Override - public V higher(V v) { - return getSortedSetDelegate().higher(v); - } - - @Override - public V pollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - public V pollLast() { - return Iterators.pollNext(descendingIterator()); - } - - private NavigableSet<V> wrap(NavigableSet<V> wrapped) { - return new WrappedNavigableSet(key, wrapped, - (getAncestor() == null) ? this : getAncestor()); - } - - @Override - public NavigableSet<V> descendingSet() { - return wrap(getSortedSetDelegate().descendingSet()); - } - - @Override - public Iterator<V> descendingIterator() { - return new WrappedIterator(getSortedSetDelegate().descendingIterator()); - } - - @Override - public NavigableSet<V> subSet( - V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { - return wrap( - getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet<V> headSet(V toElement, boolean inclusive) { - return wrap(getSortedSetDelegate().headSet(toElement, inclusive)); - } - - @Override - public NavigableSet<V> tailSet(V fromElement, boolean inclusive) { - return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive)); - } - } - - /** 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)); - } - } - - @GwtIncompatible("NavigableSet") - class NavigableKeySet extends SortedKeySet implements NavigableSet<K> { - NavigableKeySet(NavigableMap<K, Collection<V>> subMap) { - super(subMap); - } - - @Override - NavigableMap<K, Collection<V>> sortedMap() { - return (NavigableMap<K, Collection<V>>) super.sortedMap(); - } - - @Override - public K lower(K k) { - return sortedMap().lowerKey(k); - } - - @Override - public K floor(K k) { - return sortedMap().floorKey(k); - } - - @Override - public K ceiling(K k) { - return sortedMap().ceilingKey(k); - } - - @Override - public K higher(K k) { - return sortedMap().higherKey(k); - } - - @Override - public K pollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - public K pollLast() { - return Iterators.pollNext(descendingIterator()); - } - - @Override - public NavigableSet<K> descendingSet() { - return new NavigableKeySet(sortedMap().descendingMap()); - } - - @Override - public Iterator<K> descendingIterator() { - return descendingSet().iterator(); - } - - @Override - public NavigableSet<K> headSet(K toElement) { - return headSet(toElement, false); - } - - @Override - public NavigableSet<K> headSet(K toElement, boolean inclusive) { - return new NavigableKeySet(sortedMap().headMap(toElement, inclusive)); - } - - @Override - public NavigableSet<K> subSet(K fromElement, K toElement) { - return subSet(fromElement, true, toElement, false); - } - - @Override - public NavigableSet<K> subSet( - K fromElement, boolean fromInclusive, K toElement, boolean toInclusive) { - return new NavigableKeySet( - sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet<K> tailSet(K fromElement) { - return tailSet(fromElement, true); - } - - @Override - public NavigableSet<K> tailSet(K fromElement, boolean inclusive) { - return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive)); - } - } - - /** - * 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()); - } - } - - @GwtIncompatible("NavigableAsMap") - class NavigableAsMap extends SortedAsMap implements NavigableMap<K, Collection<V>> { - - NavigableAsMap(NavigableMap<K, Collection<V>> submap) { - super(submap); - } - - @Override - NavigableMap<K, Collection<V>> sortedMap() { - return (NavigableMap<K, Collection<V>>) super.sortedMap(); - } - - @Override - public Entry<K, Collection<V>> lowerEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().lowerEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K lowerKey(K key) { - return sortedMap().lowerKey(key); - } - - @Override - public Entry<K, Collection<V>> floorEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().floorEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K floorKey(K key) { - return sortedMap().floorKey(key); - } - - @Override - public Entry<K, Collection<V>> ceilingEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().ceilingEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K ceilingKey(K key) { - return sortedMap().ceilingKey(key); - } - - @Override - public Entry<K, Collection<V>> higherEntry(K key) { - Entry<K, Collection<V>> entry = sortedMap().higherEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public K higherKey(K key) { - return sortedMap().higherKey(key); - } - - @Override - public Entry<K, Collection<V>> firstEntry() { - Entry<K, Collection<V>> entry = sortedMap().firstEntry(); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public Entry<K, Collection<V>> lastEntry() { - Entry<K, Collection<V>> entry = sortedMap().lastEntry(); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - public Entry<K, Collection<V>> pollFirstEntry() { - return pollAsMapEntry(entrySet().iterator()); - } - - @Override - public Entry<K, Collection<V>> pollLastEntry() { - return pollAsMapEntry(descendingMap().entrySet().iterator()); - } - - Map.Entry<K, Collection<V>> pollAsMapEntry(Iterator<Entry<K, Collection<V>>> entryIterator) { - if (!entryIterator.hasNext()) { - return null; - } - Entry<K, Collection<V>> entry = entryIterator.next(); - Collection<V> output = createCollection(); - output.addAll(entry.getValue()); - entryIterator.remove(); - return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); - } - - @Override - public NavigableMap<K, Collection<V>> descendingMap() { - return new NavigableAsMap(sortedMap().descendingMap()); - } - - @Override - public NavigableSet<K> keySet() { - return (NavigableSet<K>) super.keySet(); - } - - @Override - NavigableSet<K> createKeySet() { - return new NavigableKeySet(sortedMap()); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return keySet(); - } - - @Override - public NavigableSet<K> descendingKeySet() { - return descendingMap().navigableKeySet(); - } - - @Override - public NavigableMap<K, Collection<V>> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public NavigableMap<K, Collection<V>> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); - } - - @Override - public NavigableMap<K, Collection<V>> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public NavigableMap<K, Collection<V>> headMap(K toKey, boolean inclusive) { - return new NavigableAsMap(sortedMap().headMap(toKey, false)); - } - - @Override - public NavigableMap<K, Collection<V>> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public NavigableMap<K, Collection<V>> tailMap(K fromKey, boolean inclusive) { - return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive)); - } - } - - private static final long serialVersionUID = 2447537837011683357L; -} diff --git a/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java b/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java index 6f14380..362386c 100644 --- a/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java +++ b/guava/src/com/google/common/collect/AbstractMapBasedMultiset.java @@ -28,6 +28,7 @@ import com.google.common.primitives.Ints; import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.io.Serializable; +import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; @@ -37,7 +38,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. @@ -63,6 +64,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; @@ -119,7 +124,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; @@ -153,7 +159,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> { @@ -199,8 +205,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 @@ -261,7 +273,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; @@ -290,6 +302,98 @@ 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. @GwtIncompatible("java.io.ObjectStreamException") @SuppressWarnings("unused") // actually used during deserialization diff --git a/guava/src/com/google/common/collect/AbstractMultimap.java b/guava/src/com/google/common/collect/AbstractMultimap.java index 13fdd00..38f69ec 100644 --- a/guava/src/com/google/common/collect/AbstractMultimap.java +++ b/guava/src/com/google/common/collect/AbstractMultimap.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Guava Authors + * 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. @@ -16,33 +16,170 @@ 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 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.Map.Entry; +import java.util.RandomAccess; import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; import javax.annotation.Nullable; /** - * A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}. - * + * 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 AbstractMultimap} 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 -abstract class AbstractMultimap<K, V> implements Multimap<K, V> { +abstract class AbstractMultimap<K, V> implements Multimap<K, V>, 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 AbstractMultimap(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 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 isEmpty() { - return size() == 0; + return totalSize == 0; + } + + @Override + public boolean containsKey(@Nullable Object key) { + return map.containsKey(key); } @Override public boolean containsValue(@Nullable Object value) { - for (Collection<V> collection : asMap().values()) { + for (Collection<V> collection : map.values()) { if (collection.contains(value)) { return true; } @@ -53,25 +190,72 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { @Override public boolean containsEntry(@Nullable Object key, @Nullable Object value) { - Collection<V> collection = asMap().get(key); + Collection<V> collection = map.get(key); return collection != null && collection.contains(value); } - + + // Modification Operations + @Override - public boolean remove(@Nullable Object key, @Nullable Object value) { - Collection<V> collection = asMap().get(key); - return collection != null && collection.remove(value); + public boolean put(@Nullable K key, @Nullable V value) { + Collection<V> collection = getOrCreateCollection(key); + + 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; } @Override - public boolean put(@Nullable K key, @Nullable V value) { - return get(key).add(value); + public boolean remove(@Nullable Object key, @Nullable Object value) { + Collection<V> collection = map.get(key); + if (collection == null) { + return false; + } + + boolean changed = collection.remove(value); + if (changed) { + totalSize--; + if (collection.isEmpty()) { + map.remove(key); + } + } + return changed; } + // Bulk Operations + @Override public boolean putAll(@Nullable K key, Iterable<? extends V> values) { - checkNotNull(values); - return values.iterator().hasNext() && Iterables.addAll(get(key), values); + if (!values.iterator().hasNext()) { + return false; + } + Collection<V> collection = getOrCreateCollection(key); + int oldSize = collection.size(); + + boolean changed = false; + if (values instanceof Collection) { + Collection<? extends V> c = Collections2.cast(values); + changed = collection.addAll(c); + } else { + for (V value : values) { + changed |= collection.add(value); + } + } + + totalSize += (collection.size() - oldSize); + return changed; } @Override @@ -83,50 +267,597 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { return changed; } + /** + * {@inheritDoc} + * + * <p>The returned collection is immutable. + */ @Override - public Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values) { - checkNotNull(values); - Collection<V> result = removeAll(key); - putAll(key, values); - return result; + public Collection<V> replaceValues( + @Nullable K key, Iterable<? extends V> values) { + Iterator<? extends V> iterator = values.iterator(); + if (!iterator.hasNext()) { + return removeAll(key); + } + + 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); } - - private transient Collection<Entry<K, V>> entries; + /** + * {@inheritDoc} + * + * <p>The returned collection is immutable. + */ @Override - public Collection<Entry<K, V>> entries() { - Collection<Entry<K, V>> result = entries; - return (result == null) ? entries = createEntries() : result; + public Collection<V> removeAll(@Nullable Object key) { + Collection<V> collection = map.remove(key); + Collection<V> output = createCollection(); + + if (collection != null) { + output.addAll(collection); + totalSize -= collection.size(); + collection.clear(); + } + + return unmodifiableCollectionSubclass(output); } - - Collection<Entry<K, V>> createEntries() { - if (this instanceof SetMultimap) { - return new Multimaps.EntrySet<K, V>() { - @Override - Multimap<K, V> multimap() { - return AbstractMultimap.this; + + private Collection<V> unmodifiableCollectionSubclass( + Collection<V> collection) { + 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. + */ + private Collection<V> wrapCollection( + @Nullable K key, Collection<V> collection) { + 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 + * AbstractMultimap#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; + } + } + } - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); + /** + * If collection is empty, remove it from {@code AbstractMultimap.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; + } } - return new Multimaps.Entries<K, V>() { + + @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); + } + } + + /** + * 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 - Multimap<K, V> multimap() { - return AbstractMultimap.this; + public boolean hasPrevious() { + return getDelegateListIterator().hasPrevious(); } @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); + 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); + } } - - abstract Iterator<Entry<K, V>> entryIterator(); private transient Set<K> keySet; @@ -136,48 +867,484 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { return (result == null) ? keySet = createKeySet() : result; } - Set<K> createKeySet() { - return new Maps.KeySet<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return asMap(); + private Set<K> createKeySet() { + 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() { + return new Iterator<K>() { + final Iterator<Map.Entry<K, Collection<V>>> entryIterator + = subMap.entrySet().iterator(); + 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() { + checkState(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)); + } } - - private transient Multiset<K> keys; - + + private transient Multiset<K> multiset; + @Override public Multiset<K> keys() { - Multiset<K> result = keys; - return (result == null) ? keys = createKeys() : result; + Multiset<K> result = multiset; + if (result == null) { + return multiset = new Multimaps.Keys<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + }; + } + return result; } - - Multiset<K> createKeys() { - return new Multimaps.Keys<K, V>(this); + + /** + * 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; + try { + collection = map.remove(key); + } catch (NullPointerException e) { + return 0; + } catch (ClassCastException e) { + return 0; + } + + int count = 0; + if (collection != null) { + count = collection.size(); + collection.clear(); + totalSize -= count; + } + return count; } - - private transient Collection<V> values; - + + private transient Collection<V> valuesCollection; + + /** + * {@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() { + Collection<V> result = valuesCollection; + if (result == null) { + return valuesCollection = new Multimaps.Values<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + }; + } + return result; + } + + private transient Collection<Map.Entry<K, V>> entries; + + /* + * 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<V> values() { - Collection<V> result = values; - return (result == null) ? values = createValues() : result; + public Collection<Map.Entry<K, V>> entries() { + Collection<Map.Entry<K, V>> result = entries; + return (result == null) ? entries = createEntries() : result; } - - Collection<V> createValues() { - return new Multimaps.Values<K, V>(this); + + Collection<Map.Entry<K, V>> createEntries() { + if (this instanceof SetMultimap) { + return new Multimaps.EntrySet<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + + @Override public Iterator<Entry<K, V>> iterator() { + return createEntryIterator(); + } + }; + } + return new Multimaps.Entries<K, V>() { + @Override Multimap<K, V> multimap() { + return AbstractMultimap.this; + } + + @Override public Iterator<Entry<K, V>> iterator() { + return createEntryIterator(); + } + }; } - + + /** + * 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 AbstractMultimap} implementations. + * + * @return an iterator across map entries + */ + Iterator<Map.Entry<K, V>> createEntryIterator() { + 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--; + } + } + private transient Map<K, Collection<V>> asMap; - + @Override public Map<K, Collection<V>> asMap() { Map<K, Collection<V>> result = asMap; return (result == null) ? asMap = createAsMap() : result; } - - abstract Map<K, Collection<V>> createAsMap(); + + private Map<K, Collection<V>> createAsMap() { + 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 AbstractMultimap.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) { + AbstractMultimap.this.clear(); + } else { + + Iterators.clear(new AsMapIterator()); + } + } + + 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(); + K key = entry.getKey(); + collection = entry.getValue(); + return Maps.immutableEntry(key, wrapCollection(key, collection)); + } + + @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 = new SortedKeySet(sortedMap()) : result; + } + } // Comparison and hashing @@ -187,7 +1354,7 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { } if (object instanceof Multimap) { Multimap<?, ?> that = (Multimap<?, ?>) object; - return this.asMap().equals(that.asMap()); + return this.map.equals(that.asMap()); } return false; } @@ -201,7 +1368,7 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { * @see Map#hashCode */ @Override public int hashCode() { - return asMap().hashCode(); + return map.hashCode(); } /** @@ -212,6 +1379,9 @@ abstract class AbstractMultimap<K, V> implements Multimap<K, V> { */ @Override public String toString() { - return asMap().toString(); + return map.toString(); } + + private static final long serialVersionUID = 2447537837011683357L; } + diff --git a/guava/src/com/google/common/collect/AbstractMultiset.java b/guava/src/com/google/common/collect/AbstractMultiset.java index 20ea93f..c0d2c4a 100644 --- a/guava/src/com/google/common/collect/AbstractMultiset.java +++ b/guava/src/com/google/common/collect/AbstractMultiset.java @@ -65,7 +65,7 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> } @Override - public int count(@Nullable Object element) { + public int count(Object element) { for (Entry<E> entry : entrySet()) { if (Objects.equal(entry.getElement(), element)) { return entry.getCount(); @@ -82,26 +82,26 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> } @Override - public int add(@Nullable E element, int occurrences) { + public int add(E element, int occurrences) { throw new UnsupportedOperationException(); } - @Override public boolean remove(@Nullable Object element) { + @Override public boolean remove(Object element) { return remove(element, 1) > 0; } @Override - public int remove(@Nullable Object element, int occurrences) { + public int remove(Object element, int occurrences) { throw new UnsupportedOperationException(); } @Override - public int setCount(@Nullable E element, int count) { + public int setCount(E element, int count) { return setCountImpl(this, element, count); } @Override - public boolean setCount(@Nullable E element, int oldCount, int newCount) { + public boolean setCount(E element, int oldCount, int newCount) { return setCountImpl(this, element, oldCount, newCount); } @@ -109,7 +109,7 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> /** * {@inheritDoc} - * + * * <p>This implementation is highly efficient when {@code elementsToAdd} * is itself a {@link Multiset}. */ @@ -158,11 +158,11 @@ abstract class AbstractMultiset<E> extends AbstractCollection<E> } abstract Iterator<Entry<E>> entryIterator(); - + abstract int distinctElements(); private transient Set<Entry<E>> entrySet; - + @Override public Set<Entry<E>> entrySet() { Set<Entry<E>> result = entrySet; return (result == null) ? entrySet = createEntrySet() : result; diff --git a/guava/src/com/google/common/collect/AbstractNavigableMap.java b/guava/src/com/google/common/collect/AbstractNavigableMap.java deleted file mode 100644 index f6defe6..0000000 --- a/guava/src/com/google/common/collect/AbstractNavigableMap.java +++ /dev/null @@ -1,198 +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.AbstractMap; -import java.util.Iterator; -import java.util.Map; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.SortedMap; - -import javax.annotation.Nullable; - -/** - * Skeletal implementation of {@link NavigableMap}. - * - * @author Louis Wasserman - */ -abstract class AbstractNavigableMap<K, V> extends AbstractMap<K, V> implements NavigableMap<K, V> { - - @Override - @Nullable - public abstract V get(@Nullable Object key); - - @Override - @Nullable - public Entry<K, V> firstEntry() { - return Iterators.getNext(entryIterator(), null); - } - - @Override - @Nullable - public Entry<K, V> lastEntry() { - return Iterators.getNext(descendingEntryIterator(), null); - } - - @Override - @Nullable - public Entry<K, V> pollFirstEntry() { - return Iterators.pollNext(entryIterator()); - } - - @Override - @Nullable - public Entry<K, V> pollLastEntry() { - return Iterators.pollNext(descendingEntryIterator()); - } - - @Override - public K firstKey() { - Entry<K, V> entry = firstEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - public K lastKey() { - Entry<K, V> entry = lastEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - @Nullable - public Entry<K, V> lowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - @Nullable - public Entry<K, V> floorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - @Nullable - public Entry<K, V> ceilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - @Nullable - public Entry<K, V> higherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K lowerKey(K key) { - return Maps.keyOrNull(lowerEntry(key)); - } - - @Override - public K floorKey(K key) { - return Maps.keyOrNull(floorEntry(key)); - } - - @Override - public K ceilingKey(K key) { - return Maps.keyOrNull(ceilingEntry(key)); - } - - @Override - public K higherKey(K key) { - return Maps.keyOrNull(higherEntry(key)); - } - - abstract Iterator<Entry<K, V>> entryIterator(); - - abstract Iterator<Entry<K, V>> descendingEntryIterator(); - - @Override - public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return new Maps.NavigableKeySet<K, V>(this); - } - - @Override - public Set<K> keySet() { - return navigableKeySet(); - } - - @Override - public abstract int size(); - - @Override - public Set<Entry<K, V>> entrySet() { - return new Maps.EntrySet<K, V>() { - @Override - Map<K, V> map() { - return AbstractNavigableMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); - } - }; - } - - @Override - public NavigableSet<K> descendingKeySet() { - return descendingMap().navigableKeySet(); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return new DescendingMap(); - } - - private final class DescendingMap extends Maps.DescendingMap<K, V> { - @Override - NavigableMap<K, V> forward() { - return AbstractNavigableMap.this; - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - return descendingEntryIterator(); - } - } - -} diff --git a/guava/src/com/google/common/collect/AbstractRangeSet.java b/guava/src/com/google/common/collect/AbstractRangeSet.java deleted file mode 100644 index e02f5da..0000000 --- a/guava/src/com/google/common/collect/AbstractRangeSet.java +++ /dev/null @@ -1,123 +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 javax.annotation.Nullable; - -/** - * A skeletal implementation of {@code RangeSet}. - * - * @author Louis Wasserman - */ -abstract class AbstractRangeSet<C extends Comparable> implements RangeSet<C> { - AbstractRangeSet() {} - - @Override - public boolean contains(C value) { - return rangeContaining(value) != null; - } - - @Override - public Range<C> rangeContaining(C value) { - checkNotNull(value); - for (Range<C> range : asRanges()) { - if (range.contains(value)) { - return range; - } - } - return null; - } - - @Override - public boolean isEmpty() { - return asRanges().isEmpty(); - } - - @Override - public void add(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - remove(Range.<C>all()); - } - - @Override - public boolean enclosesAll(RangeSet<C> other) { - for (Range<C> range : other.asRanges()) { - if (!encloses(range)) { - return false; - } - } - return true; - } - - @Override - public void addAll(RangeSet<C> other) { - for (Range<C> range : other.asRanges()) { - add(range); - } - } - - @Override - public void removeAll(RangeSet<C> other) { - for (Range<C> range : other.asRanges()) { - remove(range); - } - } - - @Override - public boolean encloses(Range<C> otherRange) { - for (Range<C> range : asRanges()) { - if (range.encloses(otherRange)) { - return true; - } - } - return false; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj instanceof RangeSet) { - RangeSet<?> other = (RangeSet<?>) obj; - return this.asRanges().equals(other.asRanges()); - } - return false; - } - - @Override - public final int hashCode() { - return asRanges().hashCode(); - } - - @Override - public final String toString() { - StringBuilder builder = new StringBuilder(); - builder.append('{'); - for (Range<C> range : asRanges()) { - builder.append(range); - } - builder.append('}'); - return builder.toString(); - } -} diff --git a/guava/src/com/google/common/collect/AbstractSetMultimap.java b/guava/src/com/google/common/collect/AbstractSetMultimap.java index e43d8b1..fe68470 100644 --- a/guava/src/com/google/common/collect/AbstractSetMultimap.java +++ b/guava/src/com/google/common/collect/AbstractSetMultimap.java @@ -26,14 +26,14 @@ import javax.annotation.Nullable; /** * Basic implementation of the {@link SetMultimap} interface. It's a wrapper - * around {@link AbstractMapBasedMultimap} that converts the returned collections into + * around {@link AbstractMultimap} that converts the returned collections into * {@code Sets}. The {@link #createCollection} method must return a {@code Set}. * * @author Jared Levy */ @GwtCompatible abstract class AbstractSetMultimap<K, V> - extends AbstractMapBasedMultimap<K, V> implements SetMultimap<K, V> { + extends AbstractMultimap<K, V> implements SetMultimap<K, V> { /** * Creates a new multimap that uses the provided map. * @@ -46,10 +46,6 @@ abstract class AbstractSetMultimap<K, V> @Override abstract Set<V> createCollection(); - @Override Set<V> createUnmodifiableEmptyCollection() { - return ImmutableSet.of(); - } - // Following Javadoc copied from SetMultimap. /** @@ -117,7 +113,7 @@ abstract class AbstractSetMultimap<K, V> * @return {@code true} if the method increased the size of the multimap, or * {@code false} if the multimap already contained the key-value pair */ - @Override public boolean put(@Nullable K key, @Nullable V value) { + @Override public boolean put(K key, V value) { return super.put(key, value); } diff --git a/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java b/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java deleted file mode 100644 index c561b87..0000000 --- a/guava/src/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java +++ /dev/null @@ -1,56 +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.Collection; -import java.util.SortedMap; -import java.util.SortedSet; - -/** - * Basic implementation of a {@link SortedSetMultimap} with a sorted key set. - * - * This superclass allows {@code TreeMultimap} to override methods to return - * navigable set and map types in non-GWT only, while GWT code will inherit the - * SortedMap/SortedSet overrides. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class AbstractSortedKeySortedSetMultimap<K, V> extends AbstractSortedSetMultimap<K, V> { - - AbstractSortedKeySortedSetMultimap(SortedMap<K, Collection<V>> map) { - super(map); - } - - @Override - public SortedMap<K, Collection<V>> asMap() { - return (SortedMap<K, Collection<V>>) super.asMap(); - } - - @Override - SortedMap<K, Collection<V>> backingMap() { - return (SortedMap<K, Collection<V>>) super.backingMap(); - } - - @Override - public SortedSet<K> keySet() { - return (SortedSet<K>) super.keySet(); - } - -} diff --git a/guava/src/com/google/common/collect/AbstractSortedMultiset.java b/guava/src/com/google/common/collect/AbstractSortedMultiset.java index 7c277f8..b1a1d54 100644 --- a/guava/src/com/google/common/collect/AbstractSortedMultiset.java +++ b/guava/src/com/google/common/collect/AbstractSortedMultiset.java @@ -1,11 +1,11 @@ /* * 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 @@ -20,9 +20,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Comparator; import java.util.Iterator; -import java.util.NavigableSet; - -import javax.annotation.Nullable; +import java.util.SortedSet; /** * This class provides a skeletal implementation of the {@link SortedMultiset} interface. @@ -33,28 +31,33 @@ import javax.annotation.Nullable; * * @author Louis Wasserman */ -@GwtCompatible(emulated = true) +@GwtCompatible abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements SortedMultiset<E> { - @GwtTransient final Comparator<? super E> comparator; + 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 NavigableSet<E> elementSet() { - return (NavigableSet<E>) super.elementSet(); + public SortedSet<E> elementSet() { + return (SortedSet<E>) super.elementSet(); } @Override - NavigableSet<E> createElementSet() { - return new SortedMultisets.NavigableElementSet<E>(this); + SortedSet<E> createElementSet() { + return new SortedMultisets.ElementSet<E>() { + @Override + SortedMultiset<E> multiset() { + return AbstractSortedMultiset.this; + } + }; } @Override @@ -99,11 +102,8 @@ abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements } @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); + public SortedMultiset<E> subMultiset(E fromElement, BoundType fromBoundType, E toElement, + BoundType toBoundType) { return tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType); } @@ -122,7 +122,7 @@ abstract class AbstractSortedMultiset<E> extends AbstractMultiset<E> implements } SortedMultiset<E> createDescendingMultiset() { - return new DescendingMultiset<E>() { + return new SortedMultisets.DescendingMultiset<E>() { @Override SortedMultiset<E> forwardMultiset() { return AbstractSortedMultiset.this; diff --git a/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java b/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java index ac74155..2be5f4b 100644 --- a/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java +++ b/guava/src/com/google/common/collect/AbstractSortedSetMultimap.java @@ -19,8 +19,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.Map; import java.util.SortedSet; @@ -28,7 +26,7 @@ import javax.annotation.Nullable; /** * Basic implementation of the {@link SortedSetMultimap} interface. It's a - * wrapper around {@link AbstractMapBasedMultimap} that converts the returned + * wrapper around {@link AbstractMultimap} that converts the returned * collections into sorted sets. The {@link #createCollection} method * must return a {@code SortedSet}. * @@ -47,18 +45,7 @@ abstract class AbstractSortedSetMultimap<K, V> super(map); } - @Override - abstract SortedSet<V> createCollection(); - - @Override - SortedSet<V> createUnmodifiableEmptyCollection() { - Comparator<? super V> comparator = valueComparator(); - if (comparator == null) { - return Collections.unmodifiableSortedSet(createCollection()); - } else { - return ImmutableSortedSet.emptySet(valueComparator()); - } - } + @Override abstract SortedSet<V> createCollection(); // Following Javadoc copied from Multimap and SortedSetMultimap. @@ -101,7 +88,7 @@ abstract class AbstractSortedSetMultimap<K, V> * <p>Any duplicates in {@code values} will be stored in the multimap once. */ @Override public SortedSet<V> replaceValues( - @Nullable K key, Iterable<? extends V> values) { + K key, Iterable<? extends V> values) { return (SortedSet<V>) super.replaceValues(key, values); } diff --git a/guava/src/com/google/common/collect/AllEqualOrdering.java b/guava/src/com/google/common/collect/AllEqualOrdering.java deleted file mode 100644 index c30164b..0000000 --- a/guava/src/com/google/common/collect/AllEqualOrdering.java +++ /dev/null @@ -1,66 +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.io.Serializable; -import java.util.List; - -import javax.annotation.Nullable; - -/** - * An ordering that treats all references as equals, even nulls. - * - * @author Emily Soldal - */ -@GwtCompatible(serializable = true) -final class AllEqualOrdering extends Ordering<Object> implements Serializable { - static final AllEqualOrdering INSTANCE = new AllEqualOrdering(); - - @Override - public int compare(@Nullable Object left, @Nullable Object right) { - return 0; - } - - @Override - public <E> List<E> sortedCopy(Iterable<E> iterable) { - return Lists.newArrayList(iterable); - } - - @Override - public <E> ImmutableList<E> immutableSortedCopy(Iterable<E> iterable) { - return ImmutableList.copyOf(iterable); - } - - @SuppressWarnings("unchecked") - @Override - public <S> Ordering<S> reverse() { - return (Ordering<S>) this; - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "Ordering.allEqual()"; - } - - private static final long serialVersionUID = 0; -} diff --git a/guava/src/com/google/common/collect/ArrayListMultimap.java b/guava/src/com/google/common/collect/ArrayListMultimap.java index 759c073..43d42c3 100644 --- a/guava/src/com/google/common/collect/ArrayListMultimap.java +++ b/guava/src/com/google/common/collect/ArrayListMultimap.java @@ -55,10 +55,6 @@ import java.util.Map; * 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) @@ -66,7 +62,7 @@ import java.util.Map; @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/src/com/google/common/collect/ArrayTable.java b/guava/src/com/google/common/collect/ArrayTable.java index 554265c..28eb5b8 100644 --- a/guava/src/com/google/common/collect/ArrayTable.java +++ b/guava/src/com/google/common/collect/ArrayTable.java @@ -17,23 +17,21 @@ 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.annotations.GwtIncompatible; import com.google.common.base.Objects; import java.io.Serializable; import java.lang.reflect.Array; import java.util.AbstractCollection; +import java.util.AbstractMap; 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.Map.Entry; import java.util.Set; import javax.annotation.Nullable; @@ -76,15 +74,10 @@ import javax.annotation.Nullable; * 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 { /** @@ -162,23 +155,22 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * 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); + ImmutableMap.Builder<R, Integer> rowBuilder = ImmutableMap.builder(); + for (int i = 0; i < rowList.size(); i++) { + rowBuilder.put(rowList.get(i), i); + } + rowKeyToIndex = rowBuilder.build(); + + ImmutableMap.Builder<C, Integer> columnBuilder = ImmutableMap.builder(); + for (int i = 0; i < columnList.size(); i++) { + columnBuilder.put(columnList.get(i), i); + } + columnKeyToIndex = columnBuilder.build(); @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) { @@ -194,116 +186,11 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { @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. @@ -335,9 +222,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * 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]; } @@ -357,9 +241,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * 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; @@ -375,7 +256,6 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { * * @param valueClass class of values stored in the returned array */ - @GwtIncompatible("reflection") public V[][] toArray(Class<V> valueClass) { // Can change to use varargs in JDK 1.6 if we want @SuppressWarnings("unchecked") // TODO: safe? @@ -451,8 +331,12 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { public V get(@Nullable Object rowKey, @Nullable Object columnKey) { Integer rowIndex = rowKeyToIndex.get(rowKey); Integer columnIndex = columnKeyToIndex.get(columnKey); + return getIndexed(rowIndex, columnIndex); + } + + private V getIndexed(Integer rowIndex, Integer columnIndex) { return (rowIndex == null || columnIndex == null) - ? null : at(rowIndex, columnIndex); + ? null : array[rowIndex][columnIndex]; } /** @@ -602,7 +486,7 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { } @Override public V getValue() { - return at(rowIndex, columnIndex); + return array[rowIndex][columnIndex]; } }; } @@ -620,7 +504,7 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { Integer columnIndex = columnKeyToIndex.get(cell.getColumnKey()); return rowIndex != null && columnIndex != null - && Objects.equal(at(rowIndex, columnIndex), cell.getValue()); + && Objects.equal(array[rowIndex][columnIndex], cell.getValue()); } return false; } @@ -646,27 +530,68 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { ? ImmutableMap.<R, V>of() : new Column(columnIndex); } - private class Column extends ArrayMap<R, V> { + private class Column extends AbstractMap<R, V> { final int columnIndex; Column(int columnIndex) { - super(rowKeyToIndex); this.columnIndex = columnIndex; } - @Override - String getKeyRole() { - return "Row"; + ColumnEntrySet entrySet; + + @Override public Set<Entry<R, V>> entrySet() { + ColumnEntrySet set = entrySet; + return (set == null) ? entrySet = new ColumnEntrySet(columnIndex) : set; + } + + @Override public V get(Object rowKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + return getIndexed(rowIndex, columnIndex); + } + + @Override public boolean containsKey(Object rowKey) { + return rowKeyToIndex.containsKey(rowKey); + } + + @Override public V put(R rowKey, V value) { + checkNotNull(rowKey); + Integer rowIndex = rowKeyToIndex.get(rowKey); + checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList); + return set(rowIndex, columnIndex, value); + } + + @Override public Set<R> keySet() { + return rowKeySet(); + } + } + + private class ColumnEntrySet extends AbstractSet<Entry<R, V>> { + final int columnIndex; + + ColumnEntrySet(int columnIndex) { + this.columnIndex = columnIndex; } - @Override - V getValue(int index) { - return at(index, columnIndex); + @Override public Iterator<Entry<R, V>> iterator() { + return new AbstractIndexedListIterator<Entry<R, V>>(size()) { + @Override protected Entry<R, V> get(final int rowIndex) { + return new AbstractMapEntry<R, V>() { + @Override public R getKey() { + return rowList.get(rowIndex); + } + @Override public V getValue() { + return array[rowIndex][columnIndex]; + } + @Override public V setValue(V value) { + return ArrayTable.this.set(rowIndex, columnIndex, value); + } + }; + } + }; } - @Override - V setValue(int index, V newValue) { - return set(index, columnIndex, newValue); + @Override public int size() { + return rowList.size(); } } @@ -689,32 +614,47 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (map == null) ? columnMap = new ColumnMap() : map; } - private class ColumnMap extends ArrayMap<C, Map<R, V>> { - private ColumnMap() { - super(columnKeyToIndex); + private class ColumnMap extends AbstractMap<C, Map<R, V>> { + transient ColumnMapEntrySet entrySet; + + @Override public Set<Entry<C, Map<R, V>>> entrySet() { + ColumnMapEntrySet set = entrySet; + return (set == null) ? entrySet = new ColumnMapEntrySet() : set; } - @Override - String getKeyRole() { - return "Column"; + @Override public Map<R, V> get(Object columnKey) { + Integer columnIndex = columnKeyToIndex.get(columnKey); + return (columnIndex == null) ? null : new Column(columnIndex); } - @Override - Map<R, V> getValue(int index) { - return new Column(index); + @Override public boolean containsKey(Object columnKey) { + return containsColumn(columnKey); } - @Override - Map<R, V> setValue(int index, Map<R, V> newValue) { - throw new UnsupportedOperationException(); + @Override public Set<C> keySet() { + return columnKeySet(); } - @Override - public Map<R, V> put(C key, Map<R, V> value) { + @Override public Map<R, V> remove(Object columnKey) { throw new UnsupportedOperationException(); } } + private class ColumnMapEntrySet extends AbstractSet<Entry<C, Map<R, V>>> { + @Override public Iterator<Entry<C, Map<R, V>>> iterator() { + return new AbstractIndexedListIterator<Entry<C, Map<R, V>>>(size()) { + @Override protected Entry<C, Map<R, V>> get(int index) { + return Maps.<C, Map<R, V>>immutableEntry(columnList.get(index), + new Column(index)); + } + }; + } + + @Override public int size() { + return columnList.size(); + } + } + /** * 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 @@ -735,27 +675,69 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (rowIndex == null) ? ImmutableMap.<C, V>of() : new Row(rowIndex); } - private class Row extends ArrayMap<C, V> { + private class Row extends AbstractMap<C, V> { final int rowIndex; Row(int rowIndex) { - super(columnKeyToIndex); this.rowIndex = rowIndex; } - @Override - String getKeyRole() { - return "Column"; + RowEntrySet entrySet; + + @Override public Set<Entry<C, V>> entrySet() { + RowEntrySet set = entrySet; + return (set == null) ? entrySet = new RowEntrySet(rowIndex) : set; + } + + @Override public V get(Object columnKey) { + Integer columnIndex = columnKeyToIndex.get(columnKey); + return getIndexed(rowIndex, columnIndex); + } + + @Override public boolean containsKey(Object columnKey) { + return containsColumn(columnKey); + } + + @Override public V put(C columnKey, V value) { + checkNotNull(columnKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + checkArgument(columnIndex != null, + "Column %s not in %s", columnKey, columnList); + return set(rowIndex, columnIndex, value); } - @Override - V getValue(int index) { - return at(rowIndex, index); + @Override public Set<C> keySet() { + return columnKeySet(); } + } + + private class RowEntrySet extends AbstractSet<Entry<C, V>> { + final int rowIndex; - @Override - V setValue(int index, V newValue) { - return set(rowIndex, index, newValue); + RowEntrySet(int rowIndex) { + this.rowIndex = rowIndex; + } + + @Override public Iterator<Entry<C, V>> iterator() { + return new AbstractIndexedListIterator<Entry<C, V>>(size()) { + @Override protected Entry<C, V> get(final int columnIndex) { + return new AbstractMapEntry<C, V>() { + @Override public C getKey() { + return columnList.get(columnIndex); + } + @Override public V getValue() { + return array[rowIndex][columnIndex]; + } + @Override public V setValue(V value) { + return ArrayTable.this.set(rowIndex, columnIndex, value); + } + }; + } + }; + } + + @Override public int size() { + return columnList.size(); } } @@ -778,32 +760,47 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { return (map == null) ? rowMap = new RowMap() : map; } - private class RowMap extends ArrayMap<R, Map<C, V>> { - private RowMap() { - super(rowKeyToIndex); + private class RowMap extends AbstractMap<R, Map<C, V>> { + transient RowMapEntrySet entrySet; + + @Override public Set<Entry<R, Map<C, V>>> entrySet() { + RowMapEntrySet set = entrySet; + return (set == null) ? entrySet = new RowMapEntrySet() : set; } - @Override - String getKeyRole() { - return "Row"; + @Override public Map<C, V> get(Object rowKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + return (rowIndex == null) ? null : new Row(rowIndex); } - @Override - Map<C, V> getValue(int index) { - return new Row(index); + @Override public boolean containsKey(Object rowKey) { + return containsRow(rowKey); } - @Override - Map<C, V> setValue(int index, Map<C, V> newValue) { - throw new UnsupportedOperationException(); + @Override public Set<R> keySet() { + return rowKeySet(); } - @Override - public Map<C, V> put(R key, Map<C, V> value) { + @Override public Map<C, V> remove(Object rowKey) { throw new UnsupportedOperationException(); } } + private class RowMapEntrySet extends AbstractSet<Entry<R, Map<C, V>>> { + @Override public Iterator<Entry<R, Map<C, V>>> iterator() { + return new AbstractIndexedListIterator<Entry<R, Map<C, V>>>(size()) { + @Override protected Entry<R, Map<C, V>> get(int index) { + return Maps.<R, Map<C, V>>immutableEntry(rowList.get(index), + new Row(index)); + } + }; + } + + @Override public int size() { + return rowList.size(); + } + } + private transient Collection<V> values; /** @@ -823,10 +820,11 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { 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(); + return new AbstractIndexedListIterator<V>(size()) { + @Override protected V get(int index) { + int rowIndex = index / columnList.size(); + int columnIndex = index % columnList.size(); + return array[rowIndex][columnIndex]; } }; } @@ -834,6 +832,10 @@ public final class ArrayTable<R, C, V> implements Table<R, C, V>, Serializable { @Override public int size() { return ArrayTable.this.size(); } + + @Override public boolean contains(Object value) { + return containsValue(value); + } } private static final long serialVersionUID = 0; diff --git a/guava/src/com/google/common/collect/AsynchronousComputationException.java b/guava/src/com/google/common/collect/AsynchronousComputationException.java new file mode 100644 index 0000000..e64e17b --- /dev/null +++ b/guava/src/com/google/common/collect/AsynchronousComputationException.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.collect; + +/** + * Wraps an exception that occurred during a computation in a different thread. + * + * @author Bob Lee + * @since 2.0 (imported from Google Collections Library) + * @deprecated this class is unused by com.google.common.collect. <b>This class + * is scheduled for deletion in November 2012.</b> + */ +@Deprecated +public +class AsynchronousComputationException extends ComputationException { + /** + * Creates a new instance with the given cause. + */ + public AsynchronousComputationException(Throwable cause) { + super(cause); + } + private static final long serialVersionUID = 0; +} diff --git a/guava/src/com/google/common/collect/BiMap.java b/guava/src/com/google/common/collect/BiMap.java index a6203e0..34d6677 100644 --- a/guava/src/com/google/common/collect/BiMap.java +++ b/guava/src/com/google/common/collect/BiMap.java @@ -28,10 +28,6 @@ import javax.annotation.Nullable; * its values as well as that of its keys. This constraint enables bimaps to * support an "inverse view", which is another bimap containing the same entries * as this bimap but with reversed keys and values. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap"> - * {@code BiMap}</a>. * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/BoundType.java b/guava/src/com/google/common/collect/BoundType.java index 7b8f34b..3632b32 100644 --- a/guava/src/com/google/common/collect/BoundType.java +++ b/guava/src/com/google/common/collect/BoundType.java @@ -14,6 +14,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** @@ -23,26 +24,18 @@ import com.google.common.annotations.GwtCompatible; * * @since 10.0 */ +@Beta @GwtCompatible public enum BoundType { /** * The endpoint value <i>is not</i> considered part of the set ("exclusive"). */ - OPEN { - @Override - BoundType flip() { - return CLOSED; - } - }, + OPEN, + /** * The endpoint value <i>is</i> considered part of the set ("inclusive"). */ - CLOSED { - @Override - BoundType flip() { - return OPEN; - } - }; + CLOSED; /** * Returns the bound type corresponding to a boolean value for inclusivity. @@ -50,6 +43,4 @@ public enum BoundType { static BoundType forBoolean(boolean inclusive) { return inclusive ? CLOSED : OPEN; } - - abstract BoundType flip(); } diff --git a/guava/src/com/google/common/collect/BstAggregate.java b/guava/src/com/google/common/collect/BstAggregate.java new file mode 100644 index 0000000..84d150f --- /dev/null +++ b/guava/src/com/google/common/collect/BstAggregate.java @@ -0,0 +1,41 @@ +/* + * 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.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * An integer-valued function on binary search tree nodes that adds between nodes. + * + * <p>The value of individual entries must fit into an {@code int}, but the value of an entire + * tree can require a {@code long}. + * + * @author Louis Wasserman + */ +@GwtCompatible +interface BstAggregate<N extends BstNode<?, N>> { + /** + * The total value on an entire subtree. Must be equal to the sum of the {@link #entryValue + * entryValue} of this node and all its descendants. + */ + long treeValue(@Nullable N tree); + + /** + * The value on a single entry, ignoring its descendants. + */ + int entryValue(N entry); +} diff --git a/guava/src/com/google/common/collect/BstBalancePolicy.java b/guava/src/com/google/common/collect/BstBalancePolicy.java new file mode 100644 index 0000000..d1e93d0 --- /dev/null +++ b/guava/src/com/google/common/collect/BstBalancePolicy.java @@ -0,0 +1,45 @@ +/* + * 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.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A local balancing policy for modified nodes in binary search trees. + * + * @author Louis Wasserman + * @param <N> The type of the nodes in the trees that this {@code BstRebalancePolicy} can + * rebalance. + */ +@GwtCompatible +interface BstBalancePolicy<N extends BstNode<?, N>> { + /** + * Constructs a locally balanced tree around the key and value data in {@code source}, and the + * subtrees {@code left} and {@code right}. It is guaranteed that the resulting tree will have + * the same inorder traversal order as the subtree {@code left}, then the entry {@code source}, + * then the subtree {@code right}. + */ + N balance(BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right); + + /** + * Constructs a locally balanced tree around the subtrees {@code left} and {@code right}. It is + * guaranteed that the resulting tree will have the same inorder traversal order as the subtree + * {@code left}, then the subtree {@code right}. + */ + @Nullable + N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right); +} diff --git a/guava/src/com/google/common/collect/BstCountBasedBalancePolicies.java b/guava/src/com/google/common/collect/BstCountBasedBalancePolicies.java new file mode 100644 index 0000000..5b98b91 --- /dev/null +++ b/guava/src/com/google/common/collect/BstCountBasedBalancePolicies.java @@ -0,0 +1,212 @@ +/* + * 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 static com.google.common.collect.BstOperations.extractMax; +import static com.google.common.collect.BstOperations.extractMin; +import static com.google.common.collect.BstOperations.insertMax; +import static com.google.common.collect.BstOperations.insertMin; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A tree-size-based set of balancing policies, based on <a + * href="http://www.swiss.ai.mit.edu/~adams/BB/"> Stephen Adams, "Efficient sets: a balancing + * act."</a>. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstCountBasedBalancePolicies { + private BstCountBasedBalancePolicies() {} + + private static final int SINGLE_ROTATE_RATIO = 4; + private static final int SECOND_ROTATE_RATIO = 2; + + /** + * Returns a balance policy that does no balancing or the bare minimum (for {@code combine}). + */ + public static <N extends BstNode<?, N>> BstBalancePolicy<N> noRebalancePolicy( + final BstAggregate<N> countAggregate) { + checkNotNull(countAggregate); + return new BstBalancePolicy<N>() { + @Override + public N balance( + BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right) { + return checkNotNull(nodeFactory).createNode(source, left, right); + } + + @Nullable + @Override + public N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right) { + if (left == null) { + return right; + } else if (right == null) { + return left; + } else if (countAggregate.treeValue(left) > countAggregate.treeValue(right)) { + return nodeFactory.createNode( + left, left.childOrNull(LEFT), combine(nodeFactory, left.childOrNull(RIGHT), right)); + } else { + return nodeFactory.createNode(right, combine(nodeFactory, left, right.childOrNull(LEFT)), + right.childOrNull(RIGHT)); + } + } + }; + } + + /** + * Returns a balance policy that expects the sizes of each side to be at most one node (added or + * removed) away from being balanced. {@code balance} takes {@code O(1)} time, and {@code + * combine} takes {@code O(log n)} time. + */ + public static <K, N extends BstNode<K, N>> BstBalancePolicy<N> singleRebalancePolicy( + final BstAggregate<N> countAggregate) { + checkNotNull(countAggregate); + return new BstBalancePolicy<N>() { + @Override + public N balance( + BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right) { + long countL = countAggregate.treeValue(left); + long countR = countAggregate.treeValue(right); + if (countL + countR > 1) { + if (countR >= SINGLE_ROTATE_RATIO * countL) { + return rotateL(nodeFactory, source, left, right); + } else if (countL >= SINGLE_ROTATE_RATIO * countR) { + return rotateR(nodeFactory, source, left, right); + } + } + return nodeFactory.createNode(source, left, right); + } + + private N rotateL(BstNodeFactory<N> nodeFactory, N source, @Nullable N left, N right) { + checkNotNull(right); + N rl = right.childOrNull(LEFT); + N rr = right.childOrNull(RIGHT); + if (countAggregate.treeValue(rl) >= SECOND_ROTATE_RATIO * countAggregate.treeValue(rr)) { + right = singleR(nodeFactory, right, rl, rr); + } + return singleL(nodeFactory, source, left, right); + } + + private N rotateR(BstNodeFactory<N> nodeFactory, N source, N left, @Nullable N right) { + checkNotNull(left); + N lr = left.childOrNull(RIGHT); + N ll = left.childOrNull(LEFT); + if (countAggregate.treeValue(lr) >= SECOND_ROTATE_RATIO * countAggregate.treeValue(ll)) { + left = singleL(nodeFactory, left, ll, lr); + } + return singleR(nodeFactory, source, left, right); + } + + private N singleL(BstNodeFactory<N> nodeFactory, N source, @Nullable N left, N right) { + checkNotNull(right); + return nodeFactory.createNode(right, + nodeFactory.createNode(source, left, right.childOrNull(LEFT)), + right.childOrNull(RIGHT)); + } + + private N singleR(BstNodeFactory<N> nodeFactory, N source, N left, @Nullable N right) { + checkNotNull(left); + return nodeFactory.createNode(left, left.childOrNull(LEFT), + nodeFactory.createNode(source, left.childOrNull(RIGHT), right)); + } + + @Nullable + @Override + public N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right) { + if (left == null) { + return right; + } else if (right == null) { + return left; + } + N newRootSource; + if (countAggregate.treeValue(left) > countAggregate.treeValue(right)) { + BstMutationResult<K, N> extractLeftMax = extractMax(left, nodeFactory, this); + newRootSource = extractLeftMax.getOriginalTarget(); + left = extractLeftMax.getChangedRoot(); + } else { + BstMutationResult<K, N> extractRightMin = extractMin(right, nodeFactory, this); + newRootSource = extractRightMin.getOriginalTarget(); + right = extractRightMin.getChangedRoot(); + } + return nodeFactory.createNode(newRootSource, left, right); + } + }; + } + + /** + * Returns a balance policy that makes no assumptions on the relative balance of the two sides + * and performs a full rebalancing as necessary. Both {@code balance} and {@code combine} take + * {@code O(log n)} time. + */ + public static <K, N extends BstNode<K, N>> BstBalancePolicy<N> fullRebalancePolicy( + final BstAggregate<N> countAggregate) { + checkNotNull(countAggregate); + final BstBalancePolicy<N> singleBalancePolicy = + BstCountBasedBalancePolicies.<K, N>singleRebalancePolicy(countAggregate); + return new BstBalancePolicy<N>() { + @Override + public N balance( + BstNodeFactory<N> nodeFactory, N source, @Nullable N left, @Nullable N right) { + if (left == null) { + return insertMin(right, source, nodeFactory, singleBalancePolicy); + } else if (right == null) { + return insertMax(left, source, nodeFactory, singleBalancePolicy); + } + long countL = countAggregate.treeValue(left); + long countR = countAggregate.treeValue(right); + if (SINGLE_ROTATE_RATIO * countL <= countR) { + N resultLeft = balance(nodeFactory, source, left, right.childOrNull(LEFT)); + return singleBalancePolicy.balance( + nodeFactory, right, resultLeft, right.childOrNull(RIGHT)); + } else if (SINGLE_ROTATE_RATIO * countR <= countL) { + N resultRight = balance(nodeFactory, source, left.childOrNull(RIGHT), right); + return singleBalancePolicy.balance( + nodeFactory, left, left.childOrNull(LEFT), resultRight); + } else { + return nodeFactory.createNode(source, left, right); + } + } + + @Nullable + @Override + public N combine(BstNodeFactory<N> nodeFactory, @Nullable N left, @Nullable N right) { + if (left == null) { + return right; + } else if (right == null) { + return left; + } + long countL = countAggregate.treeValue(left); + long countR = countAggregate.treeValue(right); + if (SINGLE_ROTATE_RATIO * countL <= countR) { + N resultLeft = combine(nodeFactory, left, right.childOrNull(LEFT)); + return singleBalancePolicy.balance( + nodeFactory, right, resultLeft, right.childOrNull(RIGHT)); + } else if (SINGLE_ROTATE_RATIO * countR <= countL) { + N resultRight = combine(nodeFactory, left.childOrNull(RIGHT), right); + return singleBalancePolicy.balance( + nodeFactory, left, left.childOrNull(LEFT), resultRight); + } else { + return singleBalancePolicy.combine(nodeFactory, left, right); + } + } + }; + } +} diff --git a/guava/src/com/google/common/collect/BstInOrderPath.java b/guava/src/com/google/common/collect/BstInOrderPath.java new file mode 100644 index 0000000..de14c39 --- /dev/null +++ b/guava/src/com/google/common/collect/BstInOrderPath.java @@ -0,0 +1,126 @@ +/* + * 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 com.google.common.base.Optional; + +import java.util.NoSuchElementException; + +import javax.annotation.Nullable; + +/** + * A {@code BstPath} supporting inorder traversal operations. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstInOrderPath<N extends BstNode<?, N>> extends BstPath<N, BstInOrderPath<N>> { + /** + * The factory to use to construct {@code BstInOrderPath} values. + */ + public static <N extends BstNode<?, N>> BstPathFactory<N, BstInOrderPath<N>> inOrderFactory() { + return new BstPathFactory<N, BstInOrderPath<N>>() { + @Override + public BstInOrderPath<N> extension(BstInOrderPath<N> path, BstSide side) { + return BstInOrderPath.extension(path, side); + } + + @Override + public BstInOrderPath<N> initialPath(N root) { + return new BstInOrderPath<N>(root, null, null); + } + }; + } + + private static <N extends BstNode<?, N>> BstInOrderPath<N> extension( + BstInOrderPath<N> path, BstSide side) { + checkNotNull(path); + N tip = path.getTip(); + return new BstInOrderPath<N>(tip.getChild(side), side, path); + } + + private final BstSide sideExtension; + private transient Optional<BstInOrderPath<N>> prevInOrder; + private transient Optional<BstInOrderPath<N>> nextInOrder; + + private BstInOrderPath( + N tip, @Nullable BstSide sideExtension, @Nullable BstInOrderPath<N> tail) { + super(tip, tail); + this.sideExtension = sideExtension; + assert (sideExtension == null) == (tail == null); + } + + private Optional<BstInOrderPath<N>> computeNextInOrder(BstSide side) { + if (getTip().hasChild(side)) { + BstInOrderPath<N> path = extension(this, side); + BstSide otherSide = side.other(); + while (path.getTip().hasChild(otherSide)) { + path = extension(path, otherSide); + } + return Optional.of(path); + } else { + BstInOrderPath<N> current = this; + while (current.sideExtension == side) { + current = current.getPrefix(); + } + current = current.prefixOrNull(); + return Optional.fromNullable(current); + } + } + + private Optional<BstInOrderPath<N>> nextInOrder(BstSide side) { + Optional<BstInOrderPath<N>> result; + switch (side) { + case LEFT: + result = prevInOrder; + return (result == null) ? prevInOrder = computeNextInOrder(side) : result; + case RIGHT: + result = nextInOrder; + return (result == null) ? nextInOrder = computeNextInOrder(side) : result; + default: + throw new AssertionError(); + } + } + + /** + * Returns {@code true} if there is a next path in an in-order traversal in the given direction. + */ + public boolean hasNext(BstSide side) { + return nextInOrder(side).isPresent(); + } + + /** + * Returns the next path in an in-order traversal in the given direction. + * + * @throws NoSuchElementException if this would be the last path in an in-order traversal + */ + public BstInOrderPath<N> next(BstSide side) { + if (!hasNext(side)) { + throw new NoSuchElementException(); + } + return nextInOrder(side).get(); + } + + /** + * Returns the direction this path went in relative to its tail path, or {@code null} if this + * path has no tail. + */ + public BstSide getSideOfExtension() { + return sideExtension; + } +} diff --git a/guava/src/com/google/common/collect/BstModificationResult.java b/guava/src/com/google/common/collect/BstModificationResult.java new file mode 100644 index 0000000..2c7c036 --- /dev/null +++ b/guava/src/com/google/common/collect/BstModificationResult.java @@ -0,0 +1,74 @@ +/* + * 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 javax.annotation.Nullable; + +/** + * The result of a {@code BstModifier}. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstModificationResult<N extends BstNode<?, N>> { + enum ModificationType { + IDENTITY, REBUILDING_CHANGE, REBALANCING_CHANGE; + } + + static <N extends BstNode<?, N>> BstModificationResult<N> identity(@Nullable N target) { + return new BstModificationResult<N>(target, target, ModificationType.IDENTITY); + } + + static <N extends BstNode<?, N>> BstModificationResult<N> rebuildingChange( + @Nullable N originalTarget, @Nullable N changedTarget) { + return new BstModificationResult<N>( + originalTarget, changedTarget, ModificationType.REBUILDING_CHANGE); + } + + static <N extends BstNode<?, N>> BstModificationResult<N> rebalancingChange( + @Nullable N originalTarget, @Nullable N changedTarget) { + return new BstModificationResult<N>( + originalTarget, changedTarget, ModificationType.REBALANCING_CHANGE); + } + + @Nullable private final N originalTarget; + @Nullable private final N changedTarget; + private final ModificationType type; + + private BstModificationResult( + @Nullable N originalTarget, @Nullable N changedTarget, ModificationType type) { + this.originalTarget = originalTarget; + this.changedTarget = changedTarget; + this.type = checkNotNull(type); + } + + @Nullable + N getOriginalTarget() { + return originalTarget; + } + + @Nullable + N getChangedTarget() { + return changedTarget; + } + + ModificationType getType() { + return type; + } +} diff --git a/guava/src/com/google/common/collect/BstModifier.java b/guava/src/com/google/common/collect/BstModifier.java new file mode 100644 index 0000000..d972800 --- /dev/null +++ b/guava/src/com/google/common/collect/BstModifier.java @@ -0,0 +1,49 @@ +/* + * 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.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A specification for a local change to an entry in a binary search tree. + * + * @author Louis Wasserman + */ +@GwtCompatible +interface BstModifier<K, N extends BstNode<K, N>> { + + /** + * Given a target key and the original entry (if any) with the specified key, returns the entry + * with key {@code key} after this mutation has been performed. The result must either be {@code + * null} or must have a key that compares as equal to {@code key}. A deletion operation, for + * example, would always return {@code null}, or an insertion operation would always return a + * non-null {@code insertedEntry}. + * + * <p>If this method returns a non-null entry of type {@code N}, any children it has will be + * ignored. + * + * <p>This method may return {@code originalEntry} itself to indicate that no change is made. + * + * @param key The key being targeted for modification. + * @param originalEntry The original entry in the binary search tree with the specified key, if + * any. No guarantees are made about the children of this entry when treated as a node; in + * particular, they are not necessarily the children of the corresponding node in the + * binary search tree. + * @return the entry (if any) with the specified key after this modification is performed + */ + BstModificationResult<N> modify(K key, @Nullable N originalEntry); +} diff --git a/guava/src/com/google/common/collect/BstMutationResult.java b/guava/src/com/google/common/collect/BstMutationResult.java new file mode 100644 index 0000000..68309a8 --- /dev/null +++ b/guava/src/com/google/common/collect/BstMutationResult.java @@ -0,0 +1,156 @@ +/* + * 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 static com.google.common.collect.BstModificationResult.ModificationType.IDENTITY; +import static com.google.common.collect.BstModificationResult.ModificationType.REBUILDING_CHANGE; +import static com.google.common.collect.BstModificationResult.ModificationType.REBALANCING_CHANGE; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.BstModificationResult.ModificationType; + +import javax.annotation.Nullable; + +/** + * The result of a mutation operation performed at a single location in a binary search tree. + * + * @author Louis Wasserman + * @param <K> The key type of the nodes in the modified binary search tree. + * @param <N> The type of the nodes in the modified binary search tree. + */ +@GwtCompatible +final class BstMutationResult<K, N extends BstNode<K, N>> { + /** + * Creates a {@code BstMutationResult}. + * + * @param targetKey The key targeted for modification. If {@code originalTarget} or {@code + * changedTarget} are non-null, their keys must compare as equal to {@code targetKey}. + * @param originalRoot The root of the subtree that was modified. + * @param changedRoot The root of the subtree, after the modification and any rebalancing. + * @param modificationResult The result of the local modification to an entry. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutationResult( + @Nullable K targetKey, @Nullable N originalRoot, @Nullable N changedRoot, + BstModificationResult<N> modificationResult) { + return new BstMutationResult<K, N>(targetKey, originalRoot, changedRoot, modificationResult); + } + + private final K targetKey; + + @Nullable + private N originalRoot; + + @Nullable + private N changedRoot; + + private final BstModificationResult<N> modificationResult; + + private BstMutationResult(@Nullable K targetKey, @Nullable N originalRoot, + @Nullable N changedRoot, BstModificationResult<N> modificationResult) { + this.targetKey = targetKey; + this.originalRoot = originalRoot; + this.changedRoot = changedRoot; + this.modificationResult = checkNotNull(modificationResult); + } + + /** + * Returns the key which was the target of this modification. + */ + public K getTargetKey() { + return targetKey; + } + + /** + * Returns the root of the subtree that was modified. + */ + @Nullable + public N getOriginalRoot() { + return originalRoot; + } + + /** + * Returns the root of the subtree, after the modification and any rebalancing was performed. + */ + @Nullable + public N getChangedRoot() { + return changedRoot; + } + + /** + * Returns the entry in the original subtree with key {@code targetKey}, if any. This should not + * be treated as a subtree, but only as an entry, and no guarantees are made about its children + * when viewed as a subtree. + */ + @Nullable + public N getOriginalTarget() { + return modificationResult.getOriginalTarget(); + } + + /** + * Returns the result of the modification to {@link #getOriginalTarget()}. This should not be + * treated as a subtree, but only as an entry, and no guarantees are made about its children when + * viewed as a subtree. + */ + @Nullable + public N getChangedTarget() { + return modificationResult.getChangedTarget(); + } + + ModificationType modificationType() { + return modificationResult.getType(); + } + + /** + * If this mutation was to an immediate child subtree of the specified root on the specified + * side, returns the {@code BstMutationResult} of applying the mutation to the appropriate child + * of the specified root and rebalancing using the specified mutation rule. + */ + public BstMutationResult<K, N> lift(N liftOriginalRoot, BstSide side, + BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + assert liftOriginalRoot != null & side != null & nodeFactory != null & balancePolicy != null; + switch (modificationType()) { + case IDENTITY: + this.originalRoot = this.changedRoot = liftOriginalRoot; + return this; + case REBUILDING_CHANGE: + case REBALANCING_CHANGE: + this.originalRoot = liftOriginalRoot; + N resultLeft = liftOriginalRoot.childOrNull(LEFT); + N resultRight = liftOriginalRoot.childOrNull(RIGHT); + switch (side) { + case LEFT: + resultLeft = changedRoot; + break; + case RIGHT: + resultRight = changedRoot; + break; + default: + throw new AssertionError(); + } + if (modificationType() == REBUILDING_CHANGE) { + this.changedRoot = nodeFactory.createNode(liftOriginalRoot, resultLeft, resultRight); + } else { + this.changedRoot = + balancePolicy.balance(nodeFactory, liftOriginalRoot, resultLeft, resultRight); + } + return this; + default: + throw new AssertionError(); + } + } +} diff --git a/guava/src/com/google/common/collect/BstMutationRule.java b/guava/src/com/google/common/collect/BstMutationRule.java new file mode 100644 index 0000000..7f90f8c --- /dev/null +++ b/guava/src/com/google/common/collect/BstMutationRule.java @@ -0,0 +1,77 @@ +/* + * 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; + +/** + * A rule for a local mutation to a binary search tree, that changes at most one entry. In addition + * to specifying how it modifies a particular entry via a {@code BstModifier}, it specifies a + * {@link BstBalancePolicy} for rebalancing the tree after the modification is performed and a + * {@link BstNodeFactory} for constructing newly rebalanced nodes. + * + * @author Louis Wasserman + * @param <K> The key type of the nodes in binary search trees that this rule can modify. + * @param <N> The type of the nodes in binary search trees that this rule can modify. + */ +@GwtCompatible +final class BstMutationRule<K, N extends BstNode<K, N>> { + /** + * Constructs a {@code BstMutationRule} with the specified modifier, balance policy, and node + * factory. + */ + public static <K, N extends BstNode<K, N>> BstMutationRule<K, N> createRule( + BstModifier<K, N> modifier, BstBalancePolicy<N> balancePolicy, + BstNodeFactory<N> nodeFactory) { + return new BstMutationRule<K, N>(modifier, balancePolicy, nodeFactory); + } + + private final BstModifier<K, N> modifier; + private final BstBalancePolicy<N> balancePolicy; + private final BstNodeFactory<N> nodeFactory; + + private BstMutationRule(BstModifier<K, N> modifier, BstBalancePolicy<N> balancePolicy, + BstNodeFactory<N> nodeFactory) { + this.balancePolicy = checkNotNull(balancePolicy); + this.nodeFactory = checkNotNull(nodeFactory); + this.modifier = checkNotNull(modifier); + } + + /** + * Returns the {@link BstModifier} that specifies the change to a targeted entry in a binary + * search tree. + */ + public BstModifier<K, N> getModifier() { + return modifier; + } + + /** + * Returns the policy used to rebalance nodes in the tree after this modification has been + * performed. + */ + public BstBalancePolicy<N> getBalancePolicy() { + return balancePolicy; + } + + /** + * Returns the node factory used to create new nodes in the tree after this modification has been + * performed. + */ + public BstNodeFactory<N> getNodeFactory() { + return nodeFactory; + } +} diff --git a/guava/src/com/google/common/collect/BstNode.java b/guava/src/com/google/common/collect/BstNode.java new file mode 100644 index 0000000..818f85a --- /dev/null +++ b/guava/src/com/google/common/collect/BstNode.java @@ -0,0 +1,125 @@ +/* + * 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 static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import java.util.Comparator; + +import javax.annotation.Nullable; + +/** + * A reusable abstraction for a node in a binary search tree. Null keys are allowed. + * + * <p>The node is considered to be immutable. Any subclass with mutable fields must create a new + * {@code BstNode} object upon any mutation, as the {@code Bst} classes assume that two nodes + * {@code a} and {@code b} represent exactly the same tree if and only if {@code a == b}. + * + * <p>A {@code BstNode} can be considered to be an <i>entry</i>, containing a key and possibly some + * value data, or it can be considered to be a <i>subtree</i>, representative of it and all its + * descendants. + * + * @author Louis Wasserman + * @param <K> The key type associated with this tree. + * @param <N> The type of the nodes in this tree. + */ +@GwtCompatible +class BstNode<K, N extends BstNode<K, N>> { + /** + * The key on which this binary search tree is ordered. All descendants of the left subtree of + * this node must have keys strictly less than {@code this.key}. + */ + private final K key; + + /** + * The left child of this node. A null value indicates that this node has no left child. + */ + @Nullable + private final N left; + + /** + * The right child of this node. A null value indicates that this node has no right child. + */ + @Nullable + private final N right; + + BstNode(@Nullable K key, @Nullable N left, @Nullable N right) { + this.key = key; + this.left = left; + this.right = right; + } + + /** + * Returns the ordered key associated with this node. + */ + @Nullable + public final K getKey() { + return key; + } + + /** + * Returns the child on the specified side, or {@code null} if there is no such child. + */ + @Nullable + public final N childOrNull(BstSide side) { + switch (side) { + case LEFT: + return left; + case RIGHT: + return right; + default: + throw new AssertionError(); + } + } + + /** + * Returns {@code true} if this node has a child on the specified side. + */ + public final boolean hasChild(BstSide side) { + return childOrNull(side) != null; + } + + /** + * Returns this node's child on the specified side. + * + * @throws IllegalStateException if this node has no such child + */ + public final N getChild(BstSide side) { + N child = childOrNull(side); + checkState(child != null); + return child; + } + + /** + * Returns {@code true} if the traditional binary search tree ordering invariant holds with + * respect to the specified {@code comparator}. + */ + protected final boolean orderingInvariantHolds(Comparator<? super K> comparator) { + checkNotNull(comparator); + boolean result = true; + if (hasChild(LEFT)) { + result &= comparator.compare(getChild(LEFT).getKey(), key) < 0; + } + if (hasChild(RIGHT)) { + result &= comparator.compare(getChild(RIGHT).getKey(), key) > 0; + } + return result; + } +} diff --git a/guava/src/com/google/common/collect/BstNodeFactory.java b/guava/src/com/google/common/collect/BstNodeFactory.java new file mode 100644 index 0000000..8e1476c --- /dev/null +++ b/guava/src/com/google/common/collect/BstNodeFactory.java @@ -0,0 +1,46 @@ +/* + * 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.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A factory for copying nodes in binary search trees with different children. + * + * <p>Typically, nodes will carry more information than the fields in the {@link BstNode} class, + * often some kind of value or some aggregate data for the subtree. This factory is responsible for + * copying this additional data between nodes. + * + * @author Louis Wasserman + * @param <N> The type of the tree nodes constructed with this {@code BstNodeFactory}. + */ +@GwtCompatible +abstract class BstNodeFactory<N extends BstNode<?, N>> { + /** + * Returns a new {@code N} with the key and value data from {@code source}, with left child + * {@code left}, and right child {@code right}. If {@code left} or {@code right} is null, the + * returned node will not have a child on the corresponding side. + */ + public abstract N createNode(N source, @Nullable N left, @Nullable N right); + + /** + * Returns a new {@code N} with the key and value data from {@code source} that is a leaf. + */ + public final N createLeaf(N source) { + return createNode(source, null, null); + } +} diff --git a/guava/src/com/google/common/collect/BstOperations.java b/guava/src/com/google/common/collect/BstOperations.java new file mode 100644 index 0000000..1f933d4 --- /dev/null +++ b/guava/src/com/google/common/collect/BstOperations.java @@ -0,0 +1,228 @@ +/* + * 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 static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import java.util.Comparator; + +import javax.annotation.Nullable; + +/** + * Tools to perform single-key queries and mutations in binary search trees. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstOperations { + private BstOperations() {} + + /** + * Returns the node with key {@code key} in {@code tree}, if any. + */ + @Nullable + public static <K, N extends BstNode<K, N>> N seek( + Comparator<? super K> comparator, @Nullable N tree, @Nullable K key) { + checkNotNull(comparator); + if (tree == null) { + return null; + } + int cmp = comparator.compare(key, tree.getKey()); + if (cmp == 0) { + return tree; + } else { + BstSide side = (cmp < 0) ? LEFT : RIGHT; + return seek(comparator, tree.childOrNull(side), key); + } + } + + /** + * Returns the result of performing the mutation specified by {@code mutationRule} in {@code + * tree} at the location with key {@code key}. + * + * <ul> + * <li>If the returned {@link BstModificationResult} has type {@code IDENTITY}, the exact + * original tree is returned. + * <li>If the returned {@code BstModificationResult} has type {@code REBUILDING_CHANGE}, + * the tree will be rebuilt with the node factory of the mutation rule, but not rebalanced. + * <li>If the returned {@code BstModificationResult} has type {@code REBALANCING_CHANGE}, + * the tree will be rebalanced using the balance policy of the mutation rule. + * </ul> + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutate( + Comparator<? super K> comparator, BstMutationRule<K, N> mutationRule, @Nullable N tree, + @Nullable K key) { + checkNotNull(comparator); + checkNotNull(mutationRule); + + if (tree != null) { + int cmp = comparator.compare(key, tree.getKey()); + if (cmp != 0) { + BstSide side = (cmp < 0) ? LEFT : RIGHT; + BstMutationResult<K, N> mutation = + mutate(comparator, mutationRule, tree.childOrNull(side), key); + return mutation.lift( + tree, side, mutationRule.getNodeFactory(), mutationRule.getBalancePolicy()); + } + } + return modify(tree, key, mutationRule); + } + + /** + * Perform the local mutation at the tip of the specified path. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> mutate( + BstInOrderPath<N> path, BstMutationRule<K, N> mutationRule) { + checkNotNull(path); + checkNotNull(mutationRule); + BstBalancePolicy<N> balancePolicy = mutationRule.getBalancePolicy(); + BstNodeFactory<N> nodeFactory = mutationRule.getNodeFactory(); + BstModifier<K, N> modifier = mutationRule.getModifier(); + + N target = path.getTip(); + K key = target.getKey(); + BstMutationResult<K, N> result = modify(target, key, mutationRule); + while (path.hasPrefix()) { + BstInOrderPath<N> prefix = path.getPrefix(); + result = result.lift(prefix.getTip(), path.getSideOfExtension(), nodeFactory, balancePolicy); + path = prefix; + } + return result; + } + + /** + * Perform the local mutation right here, at the specified node. + */ + private static <K, N extends BstNode<K, N>> BstMutationResult<K, N> modify( + @Nullable N tree, K key, BstMutationRule<K, N> mutationRule) { + BstBalancePolicy<N> rebalancePolicy = mutationRule.getBalancePolicy(); + BstNodeFactory<N> nodeFactory = mutationRule.getNodeFactory(); + BstModifier<K, N> modifier = mutationRule.getModifier(); + + N originalRoot = tree; + N changedRoot; + N originalTarget = (tree == null) ? null : nodeFactory.createLeaf(tree); + BstModificationResult<N> modResult = modifier.modify(key, originalTarget); + N originalLeft = null; + N originalRight = null; + if (tree != null) { + originalLeft = tree.childOrNull(LEFT); + originalRight = tree.childOrNull(RIGHT); + } + switch (modResult.getType()) { + case IDENTITY: + changedRoot = tree; + break; + case REBUILDING_CHANGE: + if (modResult.getChangedTarget() != null) { + changedRoot = + nodeFactory.createNode(modResult.getChangedTarget(), originalLeft, originalRight); + } else if (tree == null) { + changedRoot = null; + } else { + throw new AssertionError( + "Modification result is a REBUILDING_CHANGE, but rebalancing required"); + } + break; + case REBALANCING_CHANGE: + if (modResult.getChangedTarget() != null) { + changedRoot = rebalancePolicy.balance( + nodeFactory, modResult.getChangedTarget(), originalLeft, originalRight); + } else if (tree != null) { + changedRoot = rebalancePolicy.combine(nodeFactory, originalLeft, originalRight); + } else { + changedRoot = null; + } + break; + default: + throw new AssertionError(); + } + return BstMutationResult.mutationResult(key, originalRoot, changedRoot, modResult); + } + + /** + * Returns the result of removing the minimum element from the specified subtree. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> extractMin( + N root, BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(root); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root.hasChild(LEFT)) { + BstMutationResult<K, N> subResult = + extractMin(root.getChild(LEFT), nodeFactory, balancePolicy); + return subResult.lift(root, LEFT, nodeFactory, balancePolicy); + } + return BstMutationResult.mutationResult( + root.getKey(), root, root.childOrNull(RIGHT), + BstModificationResult.rebalancingChange(root, null)); + } + + /** + * Returns the result of removing the maximum element from the specified subtree. + */ + public static <K, N extends BstNode<K, N>> BstMutationResult<K, N> extractMax( + N root, BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(root); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root.hasChild(RIGHT)) { + BstMutationResult<K, N> subResult = + extractMax(root.getChild(RIGHT), nodeFactory, balancePolicy); + return subResult.lift(root, RIGHT, nodeFactory, balancePolicy); + } + return BstMutationResult.mutationResult(root.getKey(), root, root.childOrNull(LEFT), + BstModificationResult.rebalancingChange(root, null)); + } + + /** + * Inserts the specified entry into the tree as the minimum entry. Assumes that {@code + * entry.getKey()} is less than the key of all nodes in the subtree {@code root}. + */ + public static <N extends BstNode<?, N>> N insertMin(@Nullable N root, N entry, + BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(entry); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root == null) { + return nodeFactory.createLeaf(entry); + } else { + return balancePolicy.balance(nodeFactory, root, + insertMin(root.childOrNull(LEFT), entry, nodeFactory, balancePolicy), + root.childOrNull(RIGHT)); + } + } + + /** + * Inserts the specified entry into the tree as the maximum entry. Assumes that {@code + * entry.getKey()} is greater than the key of all nodes in the subtree {@code root}. + */ + public static <N extends BstNode<?, N>> N insertMax(@Nullable N root, N entry, + BstNodeFactory<N> nodeFactory, BstBalancePolicy<N> balancePolicy) { + checkNotNull(entry); + checkNotNull(nodeFactory); + checkNotNull(balancePolicy); + if (root == null) { + return nodeFactory.createLeaf(entry); + } else { + return balancePolicy.balance(nodeFactory, root, root.childOrNull(LEFT), + insertMax(root.childOrNull(RIGHT), entry, nodeFactory, balancePolicy)); + } + } +} diff --git a/guava/src/com/google/common/collect/BstPath.java b/guava/src/com/google/common/collect/BstPath.java new file mode 100644 index 0000000..dd564c7 --- /dev/null +++ b/guava/src/com/google/common/collect/BstPath.java @@ -0,0 +1,74 @@ +/* + * 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 static com.google.common.base.Preconditions.checkState; + +import com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A path to a node in a binary search tree, originating at the root. + * + * @author Louis Wasserman + * @param <N> The type of nodes in this binary search tree. + * @param <P> This path type, and the path type of all suffix paths. + */ +@GwtCompatible +abstract class BstPath<N extends BstNode<?, N>, P extends BstPath<N, P>> { + private final N tip; + @Nullable + private final P prefix; + + BstPath(N tip, @Nullable P prefix) { + this.tip = checkNotNull(tip); + this.prefix = prefix; + } + + /** + * Return the end of this {@code BstPath}, the deepest node in the path. + */ + public final N getTip() { + return tip; + } + + /** + * Returns {@code true} if this path has a prefix. + */ + public final boolean hasPrefix() { + return prefix != null; + } + + /** + * Returns the prefix of this path, which reaches to the parent of the end of this path. Returns + * {@code null} if this path has no prefix. + */ + @Nullable + public final P prefixOrNull() { + return prefix; + } + + /** + * Returns the prefix of this path, which reaches to the parent of the end of this path. + * + * @throws IllegalStateException if this path has no prefix. + */ + public final P getPrefix() { + checkState(hasPrefix()); + return prefix; + } +} diff --git a/guava/src/com/google/common/collect/BstPathFactory.java b/guava/src/com/google/common/collect/BstPathFactory.java new file mode 100644 index 0000000..92086ae --- /dev/null +++ b/guava/src/com/google/common/collect/BstPathFactory.java @@ -0,0 +1,38 @@ +/* + * 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.GwtCompatible; + +/** + * A factory for extending paths in a binary search tree. + * + * @author Louis Wasserman + * @param <N> The type of binary search tree nodes used in the paths generated by this {@code + * BstPathFactory}. + * @param <P> The type of paths constructed by this {@code BstPathFactory}. + */ +@GwtCompatible +interface BstPathFactory<N extends BstNode<?, N>, P extends BstPath<N, P>> { + /** + * Returns this path extended by one node to the specified {@code side}. + */ + P extension(P path, BstSide side); + + /** + * Returns the trivial path that starts at {@code root} and goes no further. + */ + P initialPath(N root); +} diff --git a/guava/src/com/google/common/collect/BstRangeOps.java b/guava/src/com/google/common/collect/BstRangeOps.java new file mode 100644 index 0000000..10d5931 --- /dev/null +++ b/guava/src/com/google/common/collect/BstRangeOps.java @@ -0,0 +1,175 @@ +/* + * 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 static com.google.common.collect.BstSide.LEFT; +import static com.google.common.collect.BstSide.RIGHT; + +import com.google.common.annotations.GwtCompatible; + +import javax.annotation.Nullable; + +/** + * A utility class with operations on binary search trees that operate on some interval. + * + * @author Louis Wasserman + */ +@GwtCompatible +final class BstRangeOps { + /** + * Returns the total value of the specified aggregation function on the specified tree restricted + * to the specified range. Assumes that the tree satisfies the binary search ordering property + * relative to {@code range.comparator()}. + */ + public static <K, N extends BstNode<K, N>> long totalInRange( + BstAggregate<? super N> aggregate, GeneralRange<K> range, @Nullable N root) { + checkNotNull(aggregate); + checkNotNull(range); + if (root == null || range.isEmpty()) { + return 0; + } + long total = aggregate.treeValue(root); + if (range.hasLowerBound()) { + total -= totalBeyondRangeToSide(aggregate, range, LEFT, root); + } + if (range.hasUpperBound()) { + total -= totalBeyondRangeToSide(aggregate, range, RIGHT, root); + } + return total; + } + + // Returns total value strictly to the specified side of the specified range. + private static <K, N extends BstNode<K, N>> long totalBeyondRangeToSide( + BstAggregate<? super N> aggregate, GeneralRange<K> range, BstSide side, @Nullable N root) { + long accum = 0; + while (root != null) { + if (beyond(range, root.getKey(), side)) { + accum += aggregate.entryValue(root); + accum += aggregate.treeValue(root.childOrNull(side)); + root = root.childOrNull(side.other()); + } else { + root = root.childOrNull(side); + } + } + return accum; + } + + /** + * Returns a balanced tree containing all nodes from the specified tree that were <i>not</i> in + * the specified range, using the specified balance policy. Assumes that the tree satisfies the + * binary search ordering property relative to {@code range.comparator()}. + */ + @Nullable + public static <K, N extends BstNode<K, N>> N minusRange(GeneralRange<K> range, + BstBalancePolicy<N> balancePolicy, BstNodeFactory<N> nodeFactory, @Nullable N root) { + checkNotNull(range); + checkNotNull(balancePolicy); + checkNotNull(nodeFactory); + N higher = range.hasUpperBound() + ? subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, RIGHT, root) + : null; + N lower = range.hasLowerBound() + ? subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, LEFT, root) + : null; + return balancePolicy.combine(nodeFactory, lower, higher); + } + + /* + * Returns a balanced tree containing all nodes in the specified tree that are strictly to the + * specified side of the specified range. + */ + @Nullable + private static <K, N extends BstNode<K, N>> N subTreeBeyondRangeToSide(GeneralRange<K> range, + BstBalancePolicy<N> balancePolicy, BstNodeFactory<N> nodeFactory, BstSide side, + @Nullable N root) { + if (root == null) { + return null; + } + if (beyond(range, root.getKey(), side)) { + N left = root.childOrNull(LEFT); + N right = root.childOrNull(RIGHT); + switch (side) { + case LEFT: + right = subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, LEFT, right); + break; + case RIGHT: + left = subTreeBeyondRangeToSide(range, balancePolicy, nodeFactory, RIGHT, left); + break; + default: + throw new AssertionError(); + } + return balancePolicy.balance(nodeFactory, root, left, right); + } else { + return subTreeBeyondRangeToSide( + range, balancePolicy, nodeFactory, side, root.childOrNull(side)); + } + } + + /** + * Returns the furthest path to the specified side in the specified tree that falls into the + * specified range. + */ + @Nullable + public static <K, N extends BstNode<K, N>, P extends BstPath<N, P>> P furthestPath( + GeneralRange<K> range, BstSide side, BstPathFactory<N, P> pathFactory, @Nullable N root) { + checkNotNull(range); + checkNotNull(pathFactory); + checkNotNull(side); + if (root == null) { + return null; + } + P path = pathFactory.initialPath(root); + return furthestPath(range, side, pathFactory, path); + } + + private static <K, N extends BstNode<K, N>, P extends BstPath<N, P>> P furthestPath( + GeneralRange<K> range, BstSide side, BstPathFactory<N, P> pathFactory, P currentPath) { + N tip = currentPath.getTip(); + K tipKey = tip.getKey(); + if (beyond(range, tipKey, side)) { + if (tip.hasChild(side.other())) { + currentPath = pathFactory.extension(currentPath, side.other()); + return furthestPath(range, side, pathFactory, currentPath); + } else { + return null; + } + } else if (tip.hasChild(side)) { + P alphaPath = pathFactory.extension(currentPath, side); + alphaPath = furthestPath(range, side, pathFactory, alphaPath); + if (alphaPath != null) { + return alphaPath; + } + } + return beyond(range, tipKey, side.other()) ? null : currentPath; + } + + /** + * Returns {@code true} if {@code key} is beyond the specified side of the specified range. + */ + public static <K> boolean beyond(GeneralRange<K> range, @Nullable K key, BstSide side) { + checkNotNull(range); + switch (side) { + case LEFT: + return range.tooLow(key); + case RIGHT: + return range.tooHigh(key); + default: + throw new AssertionError(); + } + } + + private BstRangeOps() {} +} diff --git a/guava/src/com/google/common/collect/BstSide.java b/guava/src/com/google/common/collect/BstSide.java new file mode 100644 index 0000000..5dec1bc --- /dev/null +++ b/guava/src/com/google/common/collect/BstSide.java @@ -0,0 +1,40 @@ +/* + * 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.GwtCompatible; + +/** + * A side of a binary search tree node, used to index its children. + * + * @author Louis Wasserman + */ +@GwtCompatible +enum BstSide { + LEFT { + @Override + public BstSide other() { + return RIGHT; + } + }, + RIGHT { + @Override + public BstSide other() { + return LEFT; + } + }; + + abstract BstSide other(); +} diff --git a/guava/src/com/google/common/collect/CartesianList.java b/guava/src/com/google/common/collect/CartesianList.java deleted file mode 100644 index 62f9227..0000000 --- a/guava/src/com/google/common/collect/CartesianList.java +++ /dev/null @@ -1,164 +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 static com.google.common.base.Preconditions.checkElementIndex; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.math.IntMath; - -import java.util.AbstractList; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link Lists#cartesianProduct(List)}. - * - * @author Louis Wasserman - */ -@GwtCompatible -final class CartesianList<E> extends AbstractList<List<E>> { - - private transient final ImmutableList<List<E>> axes; - private transient final int[] axesSizeProduct; - - static <E> List<List<E>> create(List<? extends List<? extends E>> lists) { - ImmutableList.Builder<List<E>> axesBuilder = - new ImmutableList.Builder<List<E>>(lists.size()); - for (List<? extends E> list : lists) { - List<E> copy = ImmutableList.copyOf(list); - if (copy.isEmpty()) { - return ImmutableList.of(); - } - axesBuilder.add(copy); - } - return new CartesianList<E>(axesBuilder.build()); - } - - CartesianList(ImmutableList<List<E>> axes) { - this.axes = axes; - int[] axesSizeProduct = new int[axes.size() + 1]; - axesSizeProduct[axes.size()] = 1; - try { - for (int i = axes.size() - 1; i >= 0; i--) { - axesSizeProduct[i] = - IntMath.checkedMultiply(axesSizeProduct[i + 1], axes.get(i).size()); - } - } catch (ArithmeticException e) { - throw new IllegalArgumentException( - "Cartesian product too large; must have size at most Integer.MAX_VALUE"); - } - this.axesSizeProduct = axesSizeProduct; - } - - private int getAxisIndexForProductIndex(int index, int axis) { - return (index / axesSizeProduct[axis + 1]) % axes.get(axis).size(); - } - - @Override - public ImmutableList<E> get(final int index) { - checkElementIndex(index, size()); - return new ImmutableList<E>() { - - @Override - public int size() { - return axes.size(); - } - - @Override - public E get(int axis) { - checkElementIndex(axis, size()); - int axisIndex = getAxisIndexForProductIndex(index, axis); - return axes.get(axis).get(axisIndex); - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - - @Override - public int size() { - return axesSizeProduct[0]; - } - - @Override - public boolean contains(@Nullable Object o) { - if (!(o instanceof List)) { - return false; - } - List<?> list = (List<?>) o; - if (list.size() != axes.size()) { - return false; - } - ListIterator<?> itr = list.listIterator(); - while (itr.hasNext()) { - int index = itr.nextIndex(); - if (!axes.get(index).contains(itr.next())) { - return false; - } - } - return true; - } - - @Override - public int indexOf(Object o) { - if (!(o instanceof List)) { - return -1; - } - List<?> l = (List<?>) o; - if (l.size() != axes.size()) { - return -1; - } - Iterator<?> lIterator = l.iterator(); - int i = 0; - for (List<E> axis : axes) { - Object lElement = lIterator.next(); - int axisIndex = axis.indexOf(lElement); - if (axisIndex == -1) { - return -1; - } - i = (i * axis.size()) + axisIndex; - } - return i; - } - - @Override - public int lastIndexOf(Object o) { - if (!(o instanceof List)) { - return -1; - } - List<?> l = (List<?>) o; - if (l.size() != axes.size()) { - return -1; - } - Iterator<?> lIterator = l.iterator(); - int i = 0; - for (List<E> axis : axes) { - Object lElement = lIterator.next(); - int axisIndex = axis.lastIndexOf(lElement); - if (axisIndex == -1) { - return -1; - } - i = (i * axis.size()) + axisIndex; - } - return i; - } -} diff --git a/guava/src/com/google/common/collect/ClassToInstanceMap.java b/guava/src/com/google/common/collect/ClassToInstanceMap.java index b3f535c..6b6fb5b 100644 --- a/guava/src/com/google/common/collect/ClassToInstanceMap.java +++ b/guava/src/com/google/common/collect/ClassToInstanceMap.java @@ -31,13 +31,6 @@ import javax.annotation.Nullable; * <p>Like any other {@code Map<Class, Object>}, this map may contain entries * for primitive types, and a primitive type and its corresponding wrapper type * may map to different values. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#ClassToInstanceMap"> - * {@code ClassToInstanceMap}</a>. - * - * <p>To map a generic type to an instance of that type, use {@link - * com.google.common.reflect.TypeToInstanceMap} instead. * * @param <B> the common supertype that all entries must share; often this is * simply {@link Object} diff --git a/guava/src/com/google/common/collect/Collections2.java b/guava/src/com/google/common/collect/Collections2.java index 7805e0b..603fa8b 100644 --- a/guava/src/com/google/common/collect/Collections2.java +++ b/guava/src/com/google/common/collect/Collections2.java @@ -18,27 +18,21 @@ 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.math.LongMath.binomial; -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.Predicate; import com.google.common.base.Predicates; -import com.google.common.math.IntMath; import com.google.common.primitives.Ints; import java.util.AbstractCollection; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; - /** * Provides static methods for working with {@code Collection} instances. * @@ -95,33 +89,13 @@ public final class Collections2 { /** * Delegates to {@link Collection#contains}. Returns {@code false} if the - * {@code contains} method throws a {@code ClassCastException} or - * {@code NullPointerException}. + * {@code contains} method throws a {@code ClassCastException}. */ static boolean safeContains(Collection<?> collection, Object object) { - checkNotNull(collection); try { return collection.contains(object); } catch (ClassCastException e) { return false; - } catch (NullPointerException e) { - return false; - } - } - - /** - * Delegates to {@link Collection#remove}. Returns {@code false} if the - * {@code remove} method throws a {@code ClassCastException} or - * {@code NullPointerException}. - */ - static boolean safeRemove(Collection<?> collection, Object object) { - checkNotNull(collection); - try { - return collection.remove(object); - } catch (ClassCastException e) { - return false; - } catch (NullPointerException e) { - return false; } } @@ -163,9 +137,6 @@ public final class Collections2 { @Override public boolean contains(Object element) { try { - // TODO(user): consider doing the predicate after unfiltered.contains, - // which would reduce the risk of CCE here - // unsafe cast can result in a CCE from predicate.apply(), which we // will catch @SuppressWarnings("unchecked") @@ -207,9 +178,6 @@ public final class Collections2 { @Override public boolean remove(Object element) { try { - // TODO(user): consider doing the predicate after unfiltered.contains, - // which would reduce the risk of CCE here - // unsafe cast can result in a CCE from predicate.apply(), which we // will catch @SuppressWarnings("unchecked") @@ -373,357 +341,8 @@ public final class Collections2 { return (Collection<T>) iterable; } - static final Joiner STANDARD_JOINER = Joiner.on(", ").useForNull("null"); - - /** - * Returns a {@link Collection} of all the permutations of the specified - * {@link Iterable}. - * - * <p><i>Notes:</i> This is an implementation of the algorithm for - * Lexicographical Permutations Generation, described in Knuth's "The Art of - * Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The - * iteration order follows the lexicographical order. This means that - * the first permutation will be in ascending order, and the last will be in - * descending order. - * - * <p>Duplicate elements are considered equal. For example, the list [1, 1] - * will have only one permutation, instead of two. This is why the elements - * have to implement {@link Comparable}. - * - * <p>An empty iterable has only one permutation, which is an empty list. - * - * <p>This method is equivalent to - * {@code Collections2.orderedPermutations(list, Ordering.natural())}. - * - * @param elements the original iterable whose elements have to be permuted. - * @return an immutable {@link Collection} containing all the different - * permutations of the original iterable. - * @throws NullPointerException if the specified iterable is null or has any - * null elements. - * @since 12.0 - */ - @Beta public static <E extends Comparable<? super E>> - Collection<List<E>> orderedPermutations(Iterable<E> elements) { - return orderedPermutations(elements, Ordering.natural()); - } - - /** - * Returns a {@link Collection} of all the permutations of the specified - * {@link Iterable} using the specified {@link Comparator} for establishing - * the lexicographical ordering. - * - * <p>Examples: <pre> {@code - * - * for (List<String> perm : orderedPermutations(asList("b", "c", "a"))) { - * println(perm); - * } - * // -> ["a", "b", "c"] - * // -> ["a", "c", "b"] - * // -> ["b", "a", "c"] - * // -> ["b", "c", "a"] - * // -> ["c", "a", "b"] - * // -> ["c", "b", "a"] - * - * for (List<Integer> perm : orderedPermutations(asList(1, 2, 2, 1))) { - * println(perm); - * } - * // -> [1, 1, 2, 2] - * // -> [1, 2, 1, 2] - * // -> [1, 2, 2, 1] - * // -> [2, 1, 1, 2] - * // -> [2, 1, 2, 1] - * // -> [2, 2, 1, 1]}</pre> - * - * <p><i>Notes:</i> This is an implementation of the algorithm for - * Lexicographical Permutations Generation, described in Knuth's "The Art of - * Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The - * iteration order follows the lexicographical order. This means that - * the first permutation will be in ascending order, and the last will be in - * descending order. - * - * <p>Elements that compare equal are considered equal and no new permutations - * are created by swapping them. - * - * <p>An empty iterable has only one permutation, which is an empty list. - * - * @param elements the original iterable whose elements have to be permuted. - * @param comparator a comparator for the iterable's elements. - * @return an immutable {@link Collection} containing all the different - * permutations of the original iterable. - * @throws NullPointerException If the specified iterable is null, has any - * null elements, or if the specified comparator is null. - * @since 12.0 - */ - @Beta public static <E> Collection<List<E>> orderedPermutations( - Iterable<E> elements, Comparator<? super E> comparator) { - return new OrderedPermutationCollection<E>(elements, comparator); - } - - private static final class OrderedPermutationCollection<E> - extends AbstractCollection<List<E>> { - final ImmutableList<E> inputList; - final Comparator<? super E> comparator; - final int size; - - OrderedPermutationCollection(Iterable<E> input, - Comparator<? super E> comparator) { - this.inputList = Ordering.from(comparator).immutableSortedCopy(input); - this.comparator = comparator; - this.size = calculateSize(inputList, comparator); - } - - /** - * The number of permutations with repeated elements is calculated as - * follows: - * <ul> - * <li>For an empty list, it is 1 (base case).</li> - * <li>When r numbers are added to a list of n-r elements, the number of - * permutations is increased by a factor of (n choose r).</li> - * </ul> - */ - private static <E> int calculateSize( - List<E> sortedInputList, Comparator<? super E> comparator) { - long permutations = 1; - int n = 1; - int r = 1; - while (n < sortedInputList.size()) { - int comparison = comparator.compare( - sortedInputList.get(n - 1), sortedInputList.get(n)); - if (comparison < 0) { - // We move to the next non-repeated element. - permutations *= binomial(n, r); - r = 0; - if (!isPositiveInt(permutations)) { - return Integer.MAX_VALUE; - } - } - n++; - r++; - } - permutations *= binomial(n, r); - if (!isPositiveInt(permutations)) { - return Integer.MAX_VALUE; - } - return (int) permutations; - } - - @Override public int size() { - return size; - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Iterator<List<E>> iterator() { - return new OrderedPermutationIterator<E>(inputList, comparator); - } - - @Override public boolean contains(@Nullable Object obj) { - if (obj instanceof List) { - List<?> list = (List<?>) obj; - return isPermutation(inputList, list); - } - return false; - } - - @Override public String toString() { - return "orderedPermutationCollection(" + inputList + ")"; - } - } - - private static final class OrderedPermutationIterator<E> - extends AbstractIterator<List<E>> { - - List<E> nextPermutation; - final Comparator<? super E> comparator; - - OrderedPermutationIterator(List<E> list, - Comparator<? super E> comparator) { - this.nextPermutation = Lists.newArrayList(list); - this.comparator = comparator; - } - - @Override protected List<E> computeNext() { - if (nextPermutation == null) { - return endOfData(); - } - ImmutableList<E> next = ImmutableList.copyOf(nextPermutation); - calculateNextPermutation(); - return next; - } - - void calculateNextPermutation() { - int j = findNextJ(); - if (j == -1) { - nextPermutation = null; - return; - } - - int l = findNextL(j); - Collections.swap(nextPermutation, j, l); - int n = nextPermutation.size(); - Collections.reverse(nextPermutation.subList(j + 1, n)); - } - - int findNextJ() { - for (int k = nextPermutation.size() - 2; k >= 0; k--) { - if (comparator.compare(nextPermutation.get(k), - nextPermutation.get(k + 1)) < 0) { - return k; - } - } - return -1; - } - - int findNextL(int j) { - E ak = nextPermutation.get(j); - for (int l = nextPermutation.size() - 1; l > j; l--) { - if (comparator.compare(ak, nextPermutation.get(l)) < 0) { - return l; - } - } - throw new AssertionError("this statement should be unreachable"); - } - } - - /** - * Returns a {@link Collection} of all the permutations of the specified - * {@link Collection}. - * - * <p><i>Notes:</i> This is an implementation of the Plain Changes algorithm - * for permutations generation, described in Knuth's "The Art of Computer - * Programming", Volume 4, Chapter 7, Section 7.2.1.2. - * - * <p>If the input list contains equal elements, some of the generated - * permutations will be equal. - * - * <p>An empty collection has only one permutation, which is an empty list. - * - * @param elements the original collection whose elements have to be permuted. - * @return an immutable {@link Collection} containing all the different - * permutations of the original collection. - * @throws NullPointerException if the specified collection is null or has any - * null elements. - * @since 12.0 - */ - @Beta public static <E> Collection<List<E>> permutations( - Collection<E> elements) { - return new PermutationCollection<E>(ImmutableList.copyOf(elements)); - } - - private static final class PermutationCollection<E> - extends AbstractCollection<List<E>> { - final ImmutableList<E> inputList; - - PermutationCollection(ImmutableList<E> input) { - this.inputList = input; - } - - @Override public int size() { - return IntMath.factorial(inputList.size()); - } - - @Override public boolean isEmpty() { - return false; - } - - @Override public Iterator<List<E>> iterator() { - return new PermutationIterator<E>(inputList); - } - - @Override public boolean contains(@Nullable Object obj) { - if (obj instanceof List) { - List<?> list = (List<?>) obj; - return isPermutation(inputList, list); - } - return false; - } - - @Override public String toString() { - return "permutations(" + inputList + ")"; - } - } - - private static class PermutationIterator<E> - extends AbstractIterator<List<E>> { - final List<E> list; - final int[] c; - final int[] o; - int j; - - PermutationIterator(List<E> list) { - this.list = new ArrayList<E>(list); - int n = list.size(); - c = new int[n]; - o = new int[n]; - for (int i = 0; i < n; i++) { - c[i] = 0; - o[i] = 1; - } - j = Integer.MAX_VALUE; - } - - @Override protected List<E> computeNext() { - if (j <= 0) { - return endOfData(); - } - ImmutableList<E> next = ImmutableList.copyOf(list); - calculateNextPermutation(); - return next; - } + static final Joiner STANDARD_JOINER = Joiner.on(", "); - void calculateNextPermutation() { - j = list.size() - 1; - int s = 0; - - // Handle the special case of an empty list. Skip the calculation of the - // next permutation. - if (j == -1) { - return; - } - - while (true) { - int q = c[j] + o[j]; - if (q < 0) { - switchDirection(); - continue; - } - if (q == j + 1) { - if (j == 0) { - break; - } - s++; - switchDirection(); - continue; - } - - Collections.swap(list, j - c[j] + s, j - q + s); - c[j] = q; - break; - } - } - - void switchDirection() { - o[j] = -o[j]; - j--; - } - } - - /** - * Returns {@code true} if the second list is a permutation of the first. - */ - private static boolean isPermutation(List<?> first, - List<?> second) { - if (first.size() != second.size()) { - return false; - } - Multiset<?> firstSet = HashMultiset.create(first); - Multiset<?> secondSet = HashMultiset.create(second); - return firstSet.equals(secondSet); - } - - private static boolean isPositiveInt(long n) { - return n >= 0 && n <= Integer.MAX_VALUE; - } + // TODO(user): Maybe move the mathematical methods to a separate + // package-permission class. } diff --git a/guava/src/com/google/common/collect/ComparatorOrdering.java b/guava/src/com/google/common/collect/ComparatorOrdering.java index 5eb7612..77fe58d 100644 --- a/guava/src/com/google/common/collect/ComparatorOrdering.java +++ b/guava/src/com/google/common/collect/ComparatorOrdering.java @@ -21,7 +21,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -53,17 +52,6 @@ final class ComparatorOrdering<T> extends Ordering<T> implements Serializable { return list; } - // Override just to remove a level of indirection from inner loops - @Override public <E extends T> ImmutableList<E> immutableSortedCopy(Iterable<E> iterable) { - @SuppressWarnings("unchecked") // we'll only ever have E's in here - E[] elements = (E[]) Iterables.toArray(iterable); - for (E e : elements) { - checkNotNull(e); - } - Arrays.sort(elements, comparator); - return ImmutableList.asImmutableList(elements); - } - @Override public boolean equals(@Nullable Object object) { if (object == this) { return true; diff --git a/guava/src/com/google/common/collect/ComparisonChain.java b/guava/src/com/google/common/collect/ComparisonChain.java index 2ed8cc4..cc90357 100644 --- a/guava/src/com/google/common/collect/ComparisonChain.java +++ b/guava/src/com/google/common/collect/ComparisonChain.java @@ -44,10 +44,6 @@ import javax.annotation.Nullable; * * <p>Once any comparison returns a nonzero value, remaining comparisons are * "short-circuited". - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CommonObjectUtilitiesExplained#compare/compareTo"> - * {@code ComparisonChain}</a>. * * @author Mark Davis * @author Kevin Bourrillion @@ -87,10 +83,7 @@ public abstract class ComparisonChain { @Override public ComparisonChain compare(double left, double right) { return classify(Double.compare(left, right)); } - @Override public ComparisonChain compareTrueFirst(boolean left, boolean right) { - return classify(Booleans.compare(right, left)); // reversed - } - @Override public ComparisonChain compareFalseFirst(boolean left, boolean right) { + @Override public ComparisonChain compare(boolean left, boolean right) { return classify(Booleans.compare(left, right)); } ComparisonChain classify(int result) { @@ -131,10 +124,7 @@ public abstract class ComparisonChain { @Override public ComparisonChain compare(double left, double right) { return this; } - @Override public ComparisonChain compareTrueFirst(boolean left, boolean right) { - return this; - } - @Override public ComparisonChain compareFalseFirst(boolean left, boolean right) { + @Override public ComparisonChain compare(boolean left, boolean right) { return this; } @Override public int result() { @@ -186,35 +176,11 @@ public abstract class ComparisonChain { public abstract ComparisonChain compare(double left, double right); /** - * Compares two {@code boolean} values, considering {@code true} to be less - * than {@code false}, <i>if</i> the result of this comparison chain has not + * Compares two {@code boolean} values as specified by {@link + * Booleans#compare}, <i>if</i> the result of this comparison chain has not * already been determined. - * - * @since 12.0 */ - public abstract ComparisonChain compareTrueFirst(boolean left, boolean right); - - /** - * Compares two {@code boolean} values, considering {@code false} to be less - * than {@code true}, <i>if</i> the result of this comparison chain has not - * already been determined. - * - * @since 12.0 (present as {@code compare} since 2.0) - */ - public abstract ComparisonChain compareFalseFirst(boolean left, boolean right); - - /** - * Old name of {@link #compareFalseFirst}. - * - * @deprecated Use {@link #compareFalseFirst}; or, if the parameters passed - * are being either negated or reversed, undo the negation or reversal and - * use {@link #compareTrueFirst}. <b>This method is scheduled for deletion - * in September 2013.</b> - */ - @Deprecated - public final ComparisonChain compare(boolean left, boolean right) { - return compareFalseFirst(left, right); - } + public abstract ComparisonChain compare(boolean left, boolean right); /** * Ends this comparison chain and returns its result: a value having the diff --git a/guava/src/com/google/common/collect/CompoundOrdering.java b/guava/src/com/google/common/collect/CompoundOrdering.java index 26ebf54..f669a62 100644 --- a/guava/src/com/google/common/collect/CompoundOrdering.java +++ b/guava/src/com/google/common/collect/CompoundOrdering.java @@ -20,6 +20,7 @@ import com.google.common.annotations.GwtCompatible; import java.io.Serializable; import java.util.Comparator; +import java.util.List; /** An ordering that tries several comparators in order. */ @GwtCompatible(serializable = true) @@ -36,11 +37,15 @@ final class CompoundOrdering<T> extends Ordering<T> implements Serializable { this.comparators = ImmutableList.copyOf(comparators); } + CompoundOrdering(List<? extends Comparator<? super T>> comparators, + Comparator<? super T> lastComparator) { + this.comparators = new ImmutableList.Builder<Comparator<? super T>>() + .addAll(comparators).add(lastComparator).build(); + } + @Override public int compare(T left, T right) { - // Avoid using the Iterator to avoid generating garbage (issue 979). - int size = comparators.size(); - for (int i = 0; i < size; i++) { - int result = comparators.get(i).compare(left, right); + for (Comparator<? super T> comparator : comparators) { + int result = comparator.compare(left, right); if (result != 0) { return result; } diff --git a/guava/src/com/google/common/collect/ComputationException.java b/guava/src/com/google/common/collect/ComputationException.java index ac80d6a..5401aff 100644 --- a/guava/src/com/google/common/collect/ComputationException.java +++ b/guava/src/com/google/common/collect/ComputationException.java @@ -18,8 +18,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import javax.annotation.Nullable; - /** * Wraps an exception that occurred during a computation. * @@ -31,7 +29,7 @@ public class ComputationException extends RuntimeException { /** * Creates a new instance with the given cause. */ - public ComputationException(@Nullable Throwable cause) { + public ComputationException(Throwable cause) { super(cause); } private static final long serialVersionUID = 0; diff --git a/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java b/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java index eb7363a..1e37edc 100644 --- a/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java +++ b/guava/src/com/google/common/collect/ComputingConcurrentHashMap.java @@ -223,8 +223,7 @@ class ComputingConcurrentHashMap<K, V> extends MapMakerInternalMap<K, V> { } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } @@ -263,8 +262,7 @@ class ComputingConcurrentHashMap<K, V> extends MapMakerInternalMap<K, V> { } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } @@ -305,8 +303,7 @@ class ComputingConcurrentHashMap<K, V> extends MapMakerInternalMap<K, V> { } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, @Nullable V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } diff --git a/guava/src/com/google/common/collect/ConcurrentHashMultiset.java b/guava/src/com/google/common/collect/ConcurrentHashMultiset.java index 1a1331b..46b8d6e 100644 --- a/guava/src/com/google/common/collect/ConcurrentHashMultiset.java +++ b/guava/src/com/google/common/collect/ConcurrentHashMultiset.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 static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Multisets.checkNonnegative; @@ -31,7 +30,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; -import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -46,10 +44,6 @@ import javax.annotation.Nullable; * A multiset that supports concurrent modifications and that provides atomic versions of most * {@code Multiset} operations (exceptions where noted). Null elements are not supported. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. - * * @author Cliff L. Biffle * @author mike nonemacher * @since 2.0 (imported from Google Collections Library) @@ -118,7 +112,7 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * <p>Finally, soft/weak values can be used but are not very useful: the values are created * internally and not exposed externally, so no one else will have a strong reference to the * values. Weak keys on the other hand can be useful in some scenarios. - * + * * @since 7.0 */ @Beta @@ -151,11 +145,26 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @return the nonnegative number of occurrences of the element */ @Override public int count(@Nullable Object element) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); return (existingCounter == null) ? 0 : existingCounter.get(); } /** + * Depending on the type of the underlying map, map.get may throw NullPointerException or + * ClassCastException, if the object is null or of the wrong type. We usually just want to treat + * those cases as if the element isn't in the map, by catching the exceptions and returning null. + */ + private AtomicInteger safeGet(Object element) { + try { + return countMap.get(element); + } catch (NullPointerException e) { + return null; + } catch (ClassCastException e) { + return null; + } + } + + /** * {@inheritDoc} * * <p>If the data in the multiset is modified by any other threads during this method, @@ -209,14 +218,13 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * the resulting amount would exceed {@link Integer#MAX_VALUE} */ @Override public int add(E element, int occurrences) { - checkNotNull(element); if (occurrences == 0) { return count(element); } checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); while (true) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { existingCounter = countMap.putIfAbsent(element, new AtomicInteger(occurrences)); if (existingCounter == null) { @@ -264,22 +272,13 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @return the count of the element before the operation; possibly zero * @throws IllegalArgumentException if {@code occurrences} is negative */ - /* - * TODO(cpovirk): remove and removeExactly currently accept null inputs only - * if occurrences == 0. This satisfies both NullPointerTester and - * CollectionRemoveTester.testRemove_nullAllowed, but it's not clear that it's - * a good policy, especially because, in order for the test to pass, the - * parameter must be misleadingly annotated as @Nullable. I suspect that - * we'll want to remove @Nullable, add an eager checkNotNull, and loosen up - * testRemove_nullAllowed. - */ @Override public int remove(@Nullable Object element, int occurrences) { if (occurrences == 0) { return count(element); } checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { return 0; } @@ -318,7 +317,7 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme } checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { return false; } @@ -347,10 +346,9 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @throws IllegalArgumentException if {@code count} is negative */ @Override public int setCount(E element, int count) { - checkNotNull(element); checkNonnegative(count, "count"); while (true) { - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { if (count == 0) { return 0; @@ -402,11 +400,10 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme * @throws IllegalArgumentException if {@code expectedOldCount} or {@code newCount} is negative */ @Override public boolean setCount(E element, int expectedOldCount, int newCount) { - checkNotNull(element); checkNonnegative(expectedOldCount, "oldCount"); checkNonnegative(newCount, "newCount"); - AtomicInteger existingCounter = Maps.safeGet(countMap, element); + AtomicInteger existingCounter = safeGet(element); if (existingCounter == null) { if (expectedOldCount != 0) { return false; @@ -451,23 +448,14 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme @Override protected Set<E> delegate() { return delegate; } - - @Override - public boolean contains(@Nullable Object object) { - return object != null && Collections2.safeContains(delegate, object); - } - - @Override - public boolean containsAll(Collection<?> collection) { - return standardContainsAll(collection); - } - @Override public boolean remove(Object object) { - return object != null && Collections2.safeRemove(delegate, object); - } - - @Override public boolean removeAll(Collection<?> c) { - return standardRemoveAll(c); + try { + return delegate.remove(object); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } } }; } @@ -559,6 +547,21 @@ public final class ConcurrentHashMultiset<E> extends AbstractMultiset<E> impleme Iterators.addAll(list, iterator()); return list; } + + @Override public boolean remove(Object object) { + if (object instanceof Multiset.Entry) { + Multiset.Entry<?> entry = (Multiset.Entry<?>) object; + Object element = entry.getElement(); + int entryCount = entry.getCount(); + if (entryCount != 0) { + // Safe as long as we never add a new entry, which we won't. + @SuppressWarnings("unchecked") + Multiset<Object> multiset = (Multiset) multiset(); + return multiset.setCount(element, entryCount, 0); + } + } + return false; + } } /** diff --git a/guava/src/com/google/common/collect/ContiguousSet.java b/guava/src/com/google/common/collect/ContiguousSet.java index dc9aa83..439f675 100644 --- a/guava/src/com/google/common/collect/ContiguousSet.java +++ b/guava/src/com/google/common/collect/ContiguousSet.java @@ -19,63 +19,19 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; -import java.util.Collections; import java.util.NoSuchElementException; -import java.util.Set; /** * 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 +@GwtCompatible +@SuppressWarnings("unchecked") // 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) { @@ -84,14 +40,10 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte } @Override public ContiguousSet<C> headSet(C toElement) { - return headSetImpl(checkNotNull(toElement), false); + return headSet(checkNotNull(toElement), false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override public ContiguousSet<C> headSet(C toElement, boolean inclusive) { + @Override ContiguousSet<C> headSet(C toElement, boolean inclusive) { return headSetImpl(checkNotNull(toElement), inclusive); } @@ -99,14 +51,10 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte checkNotNull(fromElement); checkNotNull(toElement); checkArgument(comparator().compare(fromElement, toElement) <= 0); - return subSetImpl(fromElement, true, toElement, false); + return subSet(fromElement, true, toElement, false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override public ContiguousSet<C> subSet(C fromElement, boolean fromInclusive, C toElement, + @Override ContiguousSet<C> subSet(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { checkNotNull(fromElement); checkNotNull(toElement); @@ -115,14 +63,10 @@ public abstract class ContiguousSet<C extends Comparable> extends ImmutableSorte } @Override public ContiguousSet<C> tailSet(C fromElement) { - return tailSetImpl(checkNotNull(fromElement), true); + return tailSet(checkNotNull(fromElement), true); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override public ContiguousSet<C> tailSet(C fromElement, boolean inclusive) { + @Override ContiguousSet<C> tailSet(C fromElement, boolean inclusive){ return tailSetImpl(checkNotNull(fromElement), inclusive); } diff --git a/guava/src/com/google/common/collect/Count.java b/guava/src/com/google/common/collect/Count.java index 768e298..a095119 100644 --- a/guava/src/com/google/common/collect/Count.java +++ b/guava/src/com/google/common/collect/Count.java @@ -29,6 +29,10 @@ import javax.annotation.Nullable; final class Count implements Serializable { private int value; + Count() { + this(0); + } + Count(int value) { this.value = value; } diff --git a/guava/src/com/google/common/collect/Cut.java b/guava/src/com/google/common/collect/Cut.java index 44e3450..204ea0c 100644 --- a/guava/src/com/google/common/collect/Cut.java +++ b/guava/src/com/google/common/collect/Cut.java @@ -161,9 +161,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int compareTo(Cut<Comparable<?>> o) { return (o == this) ? 0 : -1; } - @Override public String toString() { - return "-\u221e"; - } private Object readResolve() { return INSTANCE; } @@ -222,9 +219,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int compareTo(Cut<Comparable<?>> o) { return (o == this) ? 0 : 1; } - @Override public String toString() { - return "+\u221e"; - } private Object readResolve() { return INSTANCE; } @@ -286,9 +280,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int hashCode() { return endpoint.hashCode(); } - @Override public String toString() { - return "\\" + endpoint + "/"; - } private static final long serialVersionUID = 0; } @@ -351,9 +342,6 @@ abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializ @Override public int hashCode() { return ~endpoint.hashCode(); } - @Override public String toString() { - return "/" + endpoint + "\\"; - } private static final long serialVersionUID = 0; } } diff --git a/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java b/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java index d2d0088..f5630dd 100644 --- a/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/DescendingImmutableSortedMultiset.java @@ -1,11 +1,11 @@ /* * 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 @@ -18,14 +18,14 @@ import javax.annotation.Nullable; /** * A descending wrapper around an {@code ImmutableSortedMultiset} - * + * * @author Louis Wasserman */ -@SuppressWarnings("serial") // uses writeReplace, not default serialization final class DescendingImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { private final transient ImmutableSortedMultiset<E> forward; DescendingImmutableSortedMultiset(ImmutableSortedMultiset<E> forward) { + super(forward.reverseComparator()); this.forward = forward; } @@ -50,29 +50,18 @@ final class DescendingImmutableSortedMultiset<E> extends ImmutableSortedMultiset } @Override - public ImmutableSortedSet<E> elementSet() { - return forward.elementSet().descendingSet(); + ImmutableSortedSet<E> createElementSet() { + return forward.createDescendingElementSet(); } @Override - ImmutableSet<Entry<E>> createEntrySet() { - final ImmutableSet<Entry<E>> forwardEntrySet = forward.entrySet(); - return new EntrySet() { - @Override - public int size() { - return forwardEntrySet.size(); - } - - @Override - public UnmodifiableIterator<Entry<E>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<E>> createAsList() { - return forwardEntrySet.asList().reverse(); - } - }; + ImmutableSortedSet<E> createDescendingElementSet() { + return forward.elementSet(); + } + + @Override + UnmodifiableIterator<Entry<E>> descendingEntryIterator() { + return forward.entryIterator(); } @Override @@ -91,6 +80,16 @@ final class DescendingImmutableSortedMultiset<E> extends ImmutableSortedMultiset } @Override + UnmodifiableIterator<Entry<E>> entryIterator() { + return forward.descendingEntryIterator(); + } + + @Override + int distinctElements() { + return forward.distinctElements(); + } + + @Override boolean isPartialView() { return forward.isPartialView(); } diff --git a/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java b/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java deleted file mode 100644 index 340d8b9..0000000 --- a/guava/src/com/google/common/collect/DescendingImmutableSortedSet.java +++ /dev/null @@ -1,114 +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.GwtIncompatible; - -import javax.annotation.Nullable; - -/** - * Skeletal implementation of {@link ImmutableSortedSet#descendingSet()}. - * - * @author Louis Wasserman - */ -class DescendingImmutableSortedSet<E> extends ImmutableSortedSet<E> { - private final ImmutableSortedSet<E> forward; - - DescendingImmutableSortedSet(ImmutableSortedSet<E> forward) { - super(Ordering.from(forward.comparator()).reverse()); - this.forward = forward; - } - - @Override - public int size() { - return forward.size(); - } - - @Override - public UnmodifiableIterator<E> iterator() { - return forward.descendingIterator(); - } - - @Override - ImmutableSortedSet<E> headSetImpl(E toElement, boolean inclusive) { - return forward.tailSet(toElement, inclusive).descendingSet(); - } - - @Override - ImmutableSortedSet<E> subSetImpl( - E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { - return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); - } - - @Override - ImmutableSortedSet<E> tailSetImpl(E fromElement, boolean inclusive) { - return forward.headSet(fromElement, inclusive).descendingSet(); - } - - @Override - @GwtIncompatible("NavigableSet") - public ImmutableSortedSet<E> descendingSet() { - return forward; - } - - @Override - @GwtIncompatible("NavigableSet") - public UnmodifiableIterator<E> descendingIterator() { - return forward.iterator(); - } - - @Override - @GwtIncompatible("NavigableSet") - ImmutableSortedSet<E> createDescendingSet() { - throw new AssertionError("should never be called"); - } - - @Override - public E lower(E element) { - return forward.higher(element); - } - - @Override - public E floor(E element) { - return forward.ceiling(element); - } - - @Override - public E ceiling(E element) { - return forward.floor(element); - } - - @Override - public E higher(E element) { - return forward.lower(element); - } - - @Override - int indexOf(@Nullable Object target) { - int index = forward.indexOf(target); - if (index == -1) { - return index; - } else { - return size() - 1 - index; - } - } - - @Override - boolean isPartialView() { - return forward.isPartialView(); - } -} diff --git a/guava/src/com/google/common/collect/DescendingMultiset.java b/guava/src/com/google/common/collect/DescendingMultiset.java deleted file mode 100644 index d83f782..0000000 --- a/guava/src/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.NavigableSet; -import java.util.Set; - -/** - * 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 NavigableSet<E> elementSet; - - @Override public NavigableSet<E> elementSet() { - NavigableSet<E> result = elementSet; - if (result == null) { - return elementSet = new SortedMultisets.NavigableElementSet<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/src/com/google/common/collect/DiscreteDomain.java b/guava/src/com/google/common/collect/DiscreteDomain.java index f2a2f9a..893bbbb 100644 --- a/guava/src/com/google/common/collect/DiscreteDomain.java +++ b/guava/src/com/google/common/collect/DiscreteDomain.java @@ -19,13 +19,11 @@ package com.google.common.collect; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import java.io.Serializable; -import java.math.BigInteger; import java.util.NoSuchElementException; /** * A descriptor for a <i>discrete</i> {@code Comparable} domain such as all - * {@link Integer} instances. A discrete domain is one that supports the three basic + * {@link Integer}s. A discrete domain is one that supports the three basic * operations: {@link #next}, {@link #previous} and {@link #distance}, according * to their specifications. The methods {@link #minValue} and {@link #maxValue} * should also be overridden for bounded types. @@ -34,159 +32,13 @@ import java.util.NoSuchElementException; * type; it cannot represent partial domains such as "prime integers" or * "strings of length 5." * - * <p>See the Guava User Guide section on <a href= - * "http://code.google.com/p/guava-libraries/wiki/RangesExplained#Discrete_Domains"> - * {@code DiscreteDomain}</a>. - * * @author Kevin Bourrillion * @since 10.0 + * @see DiscreteDomains */ @GwtCompatible @Beta public abstract class DiscreteDomain<C extends Comparable> { - - /** - * Returns the discrete domain for values of type {@code Integer}. - * - * @since 14.0 (since 10.0 as {@code DiscreteDomains.integers()}) - */ - public static DiscreteDomain<Integer> integers() { - return IntegerDomain.INSTANCE; - } - - private static final class IntegerDomain extends DiscreteDomain<Integer> - implements Serializable { - private static final IntegerDomain INSTANCE = new IntegerDomain(); - - @Override public Integer next(Integer value) { - int i = value; - return (i == Integer.MAX_VALUE) ? null : i + 1; - } - - @Override public Integer previous(Integer value) { - int i = value; - return (i == Integer.MIN_VALUE) ? null : i - 1; - } - - @Override public long distance(Integer start, Integer end) { - return (long) end - start; - } - - @Override public Integer minValue() { - return Integer.MIN_VALUE; - } - - @Override public Integer maxValue() { - return Integer.MAX_VALUE; - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "DiscreteDomains.integers()"; - } - - private static final long serialVersionUID = 0; - } - - /** - * Returns the discrete domain for values of type {@code Long}. - * - * @since 14.0 (since 10.0 as {@code DiscreteDomains.longs()}) - */ - public static DiscreteDomain<Long> longs() { - return LongDomain.INSTANCE; - } - - private static final class LongDomain extends DiscreteDomain<Long> - implements Serializable { - private static final LongDomain INSTANCE = new LongDomain(); - - @Override public Long next(Long value) { - long l = value; - return (l == Long.MAX_VALUE) ? null : l + 1; - } - - @Override public Long previous(Long value) { - long l = value; - return (l == Long.MIN_VALUE) ? null : l - 1; - } - - @Override public long distance(Long start, Long end) { - long result = end - start; - if (end > start && result < 0) { // overflow - return Long.MAX_VALUE; - } - if (end < start && result > 0) { // underflow - return Long.MIN_VALUE; - } - return result; - } - - @Override public Long minValue() { - return Long.MIN_VALUE; - } - - @Override public Long maxValue() { - return Long.MAX_VALUE; - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "DiscreteDomains.longs()"; - } - - private static final long serialVersionUID = 0; - } - - /** - * Returns the discrete domain for values of type {@code BigInteger}. - */ - // TODO(kevinb): make sure it's tested, and make it public - static DiscreteDomain<BigInteger> bigIntegers() { - return BigIntegerDomain.INSTANCE; - } - - private static final class BigIntegerDomain extends DiscreteDomain<BigInteger> - implements Serializable { - private static final BigIntegerDomain INSTANCE = new BigIntegerDomain(); - - private static final BigInteger MIN_LONG = - BigInteger.valueOf(Long.MIN_VALUE); - private static final BigInteger MAX_LONG = - BigInteger.valueOf(Long.MAX_VALUE); - - @Override public BigInteger next(BigInteger value) { - return value.add(BigInteger.ONE); - } - - @Override public BigInteger previous(BigInteger value) { - return value.subtract(BigInteger.ONE); - } - - @Override public long distance(BigInteger start, BigInteger end) { - return end.subtract(start).max(MIN_LONG).min(MAX_LONG).longValue(); - } - - private Object readResolve() { - return INSTANCE; - } - - @Override - public String toString() { - return "DiscreteDomains.bigIntegers()"; - } - - private static final long serialVersionUID = 0; - } - /** Constructor for use by subclasses. */ protected DiscreteDomain() {} @@ -224,7 +76,7 @@ public abstract class DiscreteDomain<C extends Comparable> { * type. * * @return the distance as described above, or {@link Long#MIN_VALUE} or - * {@link Long#MAX_VALUE} if the distance is too small or too large, + * {@link Long#MIN_VALUE} if the distance is too small or too large, * respectively. */ public abstract long distance(C start, C end); @@ -258,5 +110,4 @@ public abstract class DiscreteDomain<C extends Comparable> { public C maxValue() { throw new NoSuchElementException(); } - } diff --git a/guava/src/com/google/common/collect/DiscreteDomains.java b/guava/src/com/google/common/collect/DiscreteDomains.java index dac4628..8cb2ae7 100644 --- a/guava/src/com/google/common/collect/DiscreteDomains.java +++ b/guava/src/com/google/common/collect/DiscreteDomains.java @@ -16,22 +16,20 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.math.BigInteger; + /** * Factories for common {@link DiscreteDomain} instances. * - * <p>See the Guava User Guide section on <a href= - * "http://code.google.com/p/guava-libraries/wiki/RangesExplained#Discrete_Domains"> - * {@code DiscreteDomain}</a>. - * * @author Gregory Kick * @since 10.0 - * @deprecated Merged into {@link DiscreteDomain}. This class is scheduled for deletion in release - * 15.0. */ @GwtCompatible -@Deprecated +@Beta public final class DiscreteDomains { private DiscreteDomains() {} @@ -39,13 +37,122 @@ public final class DiscreteDomains { * Returns the discrete domain for values of type {@code Integer}. */ public static DiscreteDomain<Integer> integers() { - return DiscreteDomain.integers(); + return IntegerDomain.INSTANCE; + } + + private static final class IntegerDomain extends DiscreteDomain<Integer> + implements Serializable { + private static final IntegerDomain INSTANCE = new IntegerDomain(); + + @Override public Integer next(Integer value) { + int i = value; + return (i == Integer.MAX_VALUE) ? null : i + 1; + } + + @Override public Integer previous(Integer value) { + int i = value; + return (i == Integer.MIN_VALUE) ? null : i - 1; + } + + @Override public long distance(Integer start, Integer end) { + return (long) end - start; + } + + @Override public Integer minValue() { + return Integer.MIN_VALUE; + } + + @Override public Integer maxValue() { + return Integer.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; } /** * Returns the discrete domain for values of type {@code Long}. */ public static DiscreteDomain<Long> longs() { - return DiscreteDomain.longs(); + return LongDomain.INSTANCE; + } + + private static final class LongDomain extends DiscreteDomain<Long> + implements Serializable { + private static final LongDomain INSTANCE = new LongDomain(); + + @Override public Long next(Long value) { + long l = value; + return (l == Long.MAX_VALUE) ? null : l + 1; + } + + @Override public Long previous(Long value) { + long l = value; + return (l == Long.MIN_VALUE) ? null : l - 1; + } + + @Override public long distance(Long start, Long end) { + long result = end - start; + if (end > start && result < 0) { // overflow + return Long.MAX_VALUE; + } + if (end < start && result > 0) { // underflow + return Long.MIN_VALUE; + } + return result; + } + + @Override public Long minValue() { + return Long.MIN_VALUE; + } + + @Override public Long maxValue() { + return Long.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; + } + + /** + * Returns the discrete domain for values of type {@code BigInteger}. + */ + // TODO(kevinb): make sure it's tested, and make it public + static DiscreteDomain<BigInteger> bigIntegers() { + return BigIntegerDomain.INSTANCE; + } + + private static final class BigIntegerDomain extends DiscreteDomain<BigInteger> + implements Serializable { + private static final BigIntegerDomain INSTANCE = new BigIntegerDomain(); + + private static final BigInteger MIN_LONG = + BigInteger.valueOf(Long.MIN_VALUE); + private static final BigInteger MAX_LONG = + BigInteger.valueOf(Long.MAX_VALUE); + + @Override public BigInteger next(BigInteger value) { + return value.add(BigInteger.ONE); + } + + @Override public BigInteger previous(BigInteger value) { + return value.subtract(BigInteger.ONE); + } + + @Override public long distance(BigInteger start, BigInteger end) { + return start.subtract(end).max(MIN_LONG).min(MAX_LONG).longValue(); + } + + private Object readResolve() { + return INSTANCE; + } + + private static final long serialVersionUID = 0; } } diff --git a/guava/src/com/google/common/collect/EmptyContiguousSet.java b/guava/src/com/google/common/collect/EmptyContiguousSet.java index 4546349..2bec7bd 100644 --- a/guava/src/com/google/common/collect/EmptyContiguousSet.java +++ b/guava/src/com/google/common/collect/EmptyContiguousSet.java @@ -71,8 +71,8 @@ final class EmptyContiguousSet<C extends Comparable> extends ContiguousSet<C> { return this; } - @GwtIncompatible("not used by GWT emulation") - @Override int indexOf(Object target) { + //Abstract method doesn't exist in GWT emulation + /* @Override */ int indexOf(Object target) { return -1; } @@ -80,11 +80,6 @@ final class EmptyContiguousSet<C extends Comparable> extends ContiguousSet<C> { return Iterators.emptyIterator(); } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<C> descendingIterator() { - return Iterators.emptyIterator(); - } - @Override boolean isPartialView() { return false; } @@ -133,9 +128,4 @@ final class EmptyContiguousSet<C extends Comparable> extends ContiguousSet<C> { Object writeReplace() { return new SerializedForm<C>(domain); } - - @GwtIncompatible("NavigableSet") - ImmutableSortedSet<C> createDescendingSet() { - return new EmptyImmutableSortedSet<C>(Ordering.natural().reverse()); - } } diff --git a/guava/src/com/google/common/collect/EmptyImmutableBiMap.java b/guava/src/com/google/common/collect/EmptyImmutableBiMap.java deleted file mode 100644 index 5b862b3..0000000 --- a/guava/src/com/google/common/collect/EmptyImmutableBiMap.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 javax.annotation.Nullable; - -/** - * Bimap with no mappings. - * - * @author Jared Levy - */ -@GwtCompatible(emulated = true) -@SuppressWarnings("serial") // uses writeReplace(), not default serialization -final class EmptyImmutableBiMap extends ImmutableBiMap<Object, Object> { - static final EmptyImmutableBiMap INSTANCE = new EmptyImmutableBiMap(); - - private EmptyImmutableBiMap() {} - - @Override public ImmutableBiMap<Object, Object> inverse() { - return this; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Object get(@Nullable Object key) { - return null; - } - - @Override - public ImmutableSet<Entry<Object, Object>> entrySet() { - return ImmutableSet.of(); - } - - @Override - ImmutableSet<Entry<Object, Object>> createEntrySet() { - throw new AssertionError("should never be called"); - } - - @Override - public ImmutableSet<Object> keySet() { - return ImmutableSet.of(); - } - - @Override - boolean isPartialView() { - return false; - } - - Object readResolve() { - return INSTANCE; // preserve singleton property - } -} diff --git a/guava/src/com/google/common/collect/EmptyImmutableList.java b/guava/src/com/google/common/collect/EmptyImmutableList.java index b854d2b..ec685dd 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableList.java +++ b/guava/src/com/google/common/collect/EmptyImmutableList.java @@ -24,17 +24,45 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.List; +import java.util.NoSuchElementException; import javax.annotation.Nullable; /** * An empty immutable list. - * + * * @author Kevin Bourrillion */ @GwtCompatible(serializable = true, emulated = true) final class EmptyImmutableList extends ImmutableList<Object> { static final EmptyImmutableList INSTANCE = new EmptyImmutableList(); + static final UnmodifiableListIterator<Object> ITERATOR = + new UnmodifiableListIterator<Object>() { + + @Override public boolean hasNext() { + return false; + } + + @Override public boolean hasPrevious() { + return false; + } + + @Override public Object next() { + throw new NoSuchElementException(); + } + + @Override public int nextIndex() { + return 0; + } + + @Override public Object previous() { + throw new NoSuchElementException(); + } + + @Override public int previousIndex() { + return -1; + } + }; private EmptyImmutableList() {} @@ -51,20 +79,18 @@ final class EmptyImmutableList extends ImmutableList<Object> { return false; } - @Override public boolean contains(@Nullable Object target) { + @Override public boolean contains(Object target) { return false; } - @Override public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - @Override public UnmodifiableIterator<Object> iterator() { - return listIterator(); + return Iterators.emptyIterator(); } + private static final Object[] EMPTY_ARRAY = new Object[0]; + @Override public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; + return EMPTY_ARRAY; } @Override public <T> T[] toArray(T[] a) { @@ -98,13 +124,17 @@ final class EmptyImmutableList extends ImmutableList<Object> { return this; } - @Override public UnmodifiableListIterator<Object> listIterator() { - return Iterators.EMPTY_LIST_ITERATOR; + @Override public UnmodifiableListIterator<Object> listIterator(){ + return ITERATOR; } @Override public UnmodifiableListIterator<Object> listIterator(int start) { checkPositionIndex(start, 0); - return Iterators.EMPTY_LIST_ITERATOR; + return ITERATOR; + } + + @Override public boolean containsAll(Collection<?> targets) { + return targets.isEmpty(); } @Override public boolean equals(@Nullable Object object) { diff --git a/guava/src/com/google/common/collect/EmptyImmutableMap.java b/guava/src/com/google/common/collect/EmptyImmutableMap.java new file mode 100644 index 0000000..8d58021 --- /dev/null +++ b/guava/src/com/google/common/collect/EmptyImmutableMap.java @@ -0,0 +1,95 @@ +/* + * 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; + +import javax.annotation.Nullable; + +/** + * An empty immutable map. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +final class EmptyImmutableMap extends ImmutableMap<Object, Object> { + static final EmptyImmutableMap INSTANCE = new EmptyImmutableMap(); + + private EmptyImmutableMap() {} + + @Override public Object get(@Nullable Object key) { + return null; + } + + @Override + public int size() { + return 0; + } + + @Override public boolean isEmpty() { + return true; + } + + @Override public boolean containsKey(@Nullable Object key) { + return false; + } + + @Override public boolean containsValue(@Nullable Object value) { + return false; + } + + @Override public ImmutableSet<Entry<Object, Object>> entrySet() { + return ImmutableSet.of(); + } + + @Override public ImmutableSet<Object> keySet() { + return ImmutableSet.of(); + } + + @Override public ImmutableCollection<Object> values() { + return ImmutableCollection.EMPTY_IMMUTABLE_COLLECTION; + } + + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Map) { + Map<?, ?> that = (Map<?, ?>) object; + return that.isEmpty(); + } + return false; + } + + @Override boolean isPartialView() { + return false; + } + + @Override public int hashCode() { + return 0; + } + + @Override public String toString() { + return "{}"; + } + + Object readResolve() { + return INSTANCE; // preserve singleton property + } + + private static final long serialVersionUID = 0; +} diff --git a/guava/src/com/google/common/collect/EmptyImmutableMultiset.java b/guava/src/com/google/common/collect/EmptyImmutableMultiset.java index 1931342..2a72a2b 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableMultiset.java +++ b/guava/src/com/google/common/collect/EmptyImmutableMultiset.java @@ -1,12 +1,12 @@ /* * 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 @@ -18,13 +18,11 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import java.util.Collection; - import javax.annotation.Nullable; /** * An empty immutable multiset. - * + * * @author Jared Levy * @author Louis Wasserman */ @@ -38,51 +36,22 @@ final class EmptyImmutableMultiset extends ImmutableMultiset<Object> { } @Override - public boolean contains(@Nullable Object object) { - return false; - } - - @Override - public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - - @Override - public UnmodifiableIterator<Object> iterator() { - return Iterators.emptyIterator(); - } - - @Override - public boolean equals(@Nullable Object object) { - if (object instanceof Multiset) { - Multiset<?> other = (Multiset<?>) object; - return other.isEmpty(); - } - return false; - } - - @Override - public int hashCode() { - return 0; - } - - @Override public ImmutableSet<Object> elementSet() { return ImmutableSet.of(); } @Override - public ImmutableSet<Entry<Object>> entrySet() { - return ImmutableSet.of(); + public int size() { + return 0; } @Override - ImmutableSet<Entry<Object>> createEntrySet() { - throw new AssertionError("should never be called"); + UnmodifiableIterator<Entry<Object>> entryIterator() { + return Iterators.emptyIterator(); } @Override - public int size() { + int distinctElements() { return 0; } @@ -92,22 +61,8 @@ final class EmptyImmutableMultiset extends ImmutableMultiset<Object> { } @Override - public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; - } - - @Override - public <T> T[] toArray(T[] other) { - return asList().toArray(other); - } - - @Override - public ImmutableList<Object> asList() { - return ImmutableList.of(); - } - - Object readResolve() { - return INSTANCE; // preserve singleton property + ImmutableSet<Entry<Object>> createEntrySet() { + return ImmutableSet.of(); } private static final long serialVersionUID = 0; diff --git a/guava/src/com/google/common/collect/EmptyImmutableSet.java b/guava/src/com/google/common/collect/EmptyImmutableSet.java index e70b051..8722bdf 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableSet.java +++ b/guava/src/com/google/common/collect/EmptyImmutableSet.java @@ -25,7 +25,7 @@ import javax.annotation.Nullable; /** * An empty immutable set. - * + * * @author Kevin Bourrillion */ @GwtCompatible(serializable = true, emulated = true) @@ -43,14 +43,10 @@ final class EmptyImmutableSet extends ImmutableSet<Object> { return true; } - @Override public boolean contains(@Nullable Object target) { + @Override public boolean contains(Object target) { return false; } - @Override public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - @Override public UnmodifiableIterator<Object> iterator() { return Iterators.emptyIterator(); } @@ -59,17 +55,21 @@ final class EmptyImmutableSet extends ImmutableSet<Object> { return false; } + private static final Object[] EMPTY_ARRAY = new Object[0]; + @Override public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; + return EMPTY_ARRAY; } @Override public <T> T[] toArray(T[] a) { - return asList().toArray(a); + if (a.length > 0) { + a[0] = null; + } + return a; } - @Override - public ImmutableList<Object> asList() { - return ImmutableList.of(); + @Override public boolean containsAll(Collection<?> targets) { + return targets.isEmpty(); } @Override public boolean equals(@Nullable Object object) { diff --git a/guava/src/com/google/common/collect/EmptyImmutableSortedMap.java b/guava/src/com/google/common/collect/EmptyImmutableSortedMap.java deleted file mode 100644 index 0a1e854..0000000 --- a/guava/src/com/google/common/collect/EmptyImmutableSortedMap.java +++ /dev/null @@ -1,107 +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 static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.GwtCompatible; - -import java.util.Comparator; - -import javax.annotation.Nullable; - -/** - * An empty immutable sorted map. - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -@SuppressWarnings("serial") // uses writeReplace, not default serialization -final class EmptyImmutableSortedMap<K, V> extends ImmutableSortedMap<K, V> { - private final transient ImmutableSortedSet<K> keySet; - - EmptyImmutableSortedMap(Comparator<? super K> comparator) { - this.keySet = ImmutableSortedSet.emptySet(comparator); - } - - EmptyImmutableSortedMap( - Comparator<? super K> comparator, ImmutableSortedMap<K, V> descendingMap) { - super(descendingMap); - this.keySet = ImmutableSortedSet.emptySet(comparator); - } - - @Override - public V get(@Nullable Object key) { - return null; - } - - @Override - public ImmutableSortedSet<K> keySet() { - return keySet; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public ImmutableCollection<V> values() { - return ImmutableList.of(); - } - - @Override - public String toString() { - return "{}"; - } - - @Override - boolean isPartialView() { - return false; - } - - @Override - public ImmutableSet<Entry<K, V>> entrySet() { - return ImmutableSet.of(); - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - throw new AssertionError("should never be called"); - } - - @Override - public ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive) { - checkNotNull(toKey); - return this; - } - - @Override - public ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) { - checkNotNull(fromKey); - return this; - } - - @Override - ImmutableSortedMap<K, V> createDescendingMap() { - return new EmptyImmutableSortedMap<K, V>(Ordering.from(comparator()).reverse(), this); - } -} diff --git a/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java b/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java index a7ddf28..623050c 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/EmptyImmutableSortedMultiset.java @@ -1,11 +1,11 @@ /* * 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 @@ -16,7 +16,6 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.Collection; import java.util.Comparator; import javax.annotation.Nullable; @@ -26,12 +25,9 @@ import javax.annotation.Nullable; * * @author Louis Wasserman */ -@SuppressWarnings("serial") // Uses writeReplace, not default serialization final class EmptyImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { - private final ImmutableSortedSet<E> elementSet; - EmptyImmutableSortedMultiset(Comparator<? super E> comparator) { - this.elementSet = ImmutableSortedSet.emptySet(comparator); + super(comparator); } @Override @@ -50,33 +46,28 @@ final class EmptyImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { } @Override - public boolean contains(@Nullable Object object) { - return false; - } - - @Override - public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); + public int size() { + return 0; } @Override - public int size() { - return 0; + ImmutableSortedSet<E> createElementSet() { + return ImmutableSortedSet.emptySet(comparator()); } @Override - public ImmutableSortedSet<E> elementSet() { - return elementSet; + ImmutableSortedSet<E> createDescendingElementSet() { + return ImmutableSortedSet.emptySet(reverseComparator()); } @Override - public ImmutableSet<Entry<E>> entrySet() { - return ImmutableSet.of(); + UnmodifiableIterator<Entry<E>> descendingEntryIterator() { + return Iterators.emptyIterator(); } @Override - ImmutableSet<Entry<E>> createEntrySet() { - throw new AssertionError("should never be called"); + UnmodifiableIterator<Entry<E>> entryIterator() { + return Iterators.emptyIterator(); } @Override @@ -94,46 +85,12 @@ final class EmptyImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { } @Override - public UnmodifiableIterator<E> iterator() { - return Iterators.emptyIterator(); - } - - @Override - public boolean equals(@Nullable Object object) { - if (object instanceof Multiset) { - Multiset<?> other = (Multiset<?>) object; - return other.isEmpty(); - } - return false; - } - - @Override - public int hashCode() { + int distinctElements() { return 0; } @Override - public String toString() { - return "[]"; - } - - @Override boolean isPartialView() { return false; } - - @Override - public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; - } - - @Override - public <T> T[] toArray(T[] other) { - return asList().toArray(other); - } - - @Override - public ImmutableList<E> asList() { - return ImmutableList.of(); - } } diff --git a/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java b/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java index 9f5d522..e406163 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java +++ b/guava/src/com/google/common/collect/EmptyImmutableSortedSet.java @@ -17,7 +17,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.util.Collection; import java.util.Comparator; @@ -47,37 +46,33 @@ class EmptyImmutableSortedSet<E> extends ImmutableSortedSet<E> { return true; } - @Override public boolean contains(@Nullable Object target) { + @Override public boolean contains(Object target) { return false; } - @Override public boolean containsAll(Collection<?> targets) { - return targets.isEmpty(); - } - @Override public UnmodifiableIterator<E> iterator() { return Iterators.emptyIterator(); } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<E> descendingIterator() { - return Iterators.emptyIterator(); - } - @Override boolean isPartialView() { return false; } - @Override public ImmutableList<E> asList() { - return ImmutableList.of(); - } + private static final Object[] EMPTY_ARRAY = new Object[0]; @Override public Object[] toArray() { - return ObjectArrays.EMPTY_ARRAY; + return EMPTY_ARRAY; } @Override public <T> T[] toArray(T[] a) { - return asList().toArray(a); + if (a.length > 0) { + a[0] = null; + } + return a; + } + + @Override public boolean containsAll(Collection<?> targets) { + return targets.isEmpty(); } @Override public boolean equals(@Nullable Object object) { @@ -125,9 +120,4 @@ class EmptyImmutableSortedSet<E> extends ImmutableSortedSet<E> { @Override int indexOf(@Nullable Object target) { return -1; } - - @Override - ImmutableSortedSet<E> createDescendingSet() { - return new EmptyImmutableSortedSet<E>(Ordering.from(comparator).reverse()); - } } diff --git a/guava/src/com/google/common/collect/EmptyImmutableTable.java b/guava/src/com/google/common/collect/EmptyImmutableTable.java index 65b8042..61949ca 100644 --- a/guava/src/com/google/common/collect/EmptyImmutableTable.java +++ b/guava/src/com/google/common/collect/EmptyImmutableTable.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. @@ -28,7 +28,7 @@ import javax.annotation.concurrent.Immutable; /** * An empty implementation of {@link ImmutableTable}. * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) */ @GwtCompatible @Immutable @@ -53,7 +53,7 @@ final class EmptyImmutableTable extends ImmutableTable<Object, Object, Object> { @Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; - } else if (obj instanceof Table) { + } else if (obj instanceof Table<?, ?, ?>) { Table<?, ?, ?> that = (Table<?, ?, ?>) obj; return that.isEmpty(); } else { diff --git a/guava/src/com/google/common/collect/EnumBiMap.java b/guava/src/com/google/common/collect/EnumBiMap.java index 05d84ed..9a94ddd 100644 --- a/guava/src/com/google/common/collect/EnumBiMap.java +++ b/guava/src/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; import com.google.common.annotations.GwtIncompatible; @@ -32,10 +31,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) @@ -111,16 +106,6 @@ public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>> return valueType; } - @Override - K checkKey(K key) { - return checkNotNull(key); - } - - @Override - V checkValue(V value) { - return checkNotNull(value); - } - /** * @serialData the key class, value class, number of entries, first key, first * value, second key, second value, and so on. diff --git a/guava/src/com/google/common/collect/EnumHashBiMap.java b/guava/src/com/google/common/collect/EnumHashBiMap.java index c43daf0..8f32515 100644 --- a/guava/src/com/google/common/collect/EnumHashBiMap.java +++ b/guava/src/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 com.google.common.annotations.GwtIncompatible; @@ -35,10 +33,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) @@ -83,12 +77,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/src/com/google/common/collect/EnumMultiset.java b/guava/src/com/google/common/collect/EnumMultiset.java index 2bb121c..560bf7c 100644 --- a/guava/src/com/google/common/collect/EnumMultiset.java +++ b/guava/src/com/google/common/collect/EnumMultiset.java @@ -27,10 +27,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) @@ -58,19 +54,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/src/com/google/common/collect/FilteredEntryMultimap.java b/guava/src/com/google/common/collect/FilteredEntryMultimap.java deleted file mode 100644 index 9120893..0000000 --- a/guava/src/com/google/common/collect/FilteredEntryMultimap.java +++ /dev/null @@ -1,406 +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 static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Predicates.compose; -import static com.google.common.base.Predicates.in; -import static com.google.common.base.Predicates.not; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; - -import java.util.AbstractMap; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link Multimaps#filterEntries(Multimap, Predicate)}. - * - * @author Jared Levy - * @author Louis Wasserman - */ -@GwtCompatible -class FilteredEntryMultimap<K, V> extends FilteredMultimap<K, V> { - final Predicate<? super Entry<K, V>> predicate; - - FilteredEntryMultimap(Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> predicate) { - super(unfiltered); - this.predicate = checkNotNull(predicate); - } - - @Override - Predicate<? super Entry<K, V>> entryPredicate() { - return predicate; - } - - @Override - public int size() { - return entries().size(); - } - - private boolean satisfies(K key, V value) { - return predicate.apply(Maps.immutableEntry(key, value)); - } - - - final class ValuePredicate implements Predicate<V> { - private final K key; - - ValuePredicate(K key) { - this.key = key; - } - - @Override - public boolean apply(@Nullable V value) { - return satisfies(key, value); - } - } - - static <E> Collection<E> filterCollection( - Collection<E> collection, Predicate<? super E> predicate) { - if (collection instanceof Set) { - return Sets.filter((Set<E>) collection, predicate); - } else { - return Collections2.filter(collection, predicate); - } - } - - @Override - public boolean containsKey(@Nullable Object key) { - return asMap().get(key) != null; - } - - @Override - public Collection<V> removeAll(@Nullable Object key) { - return Objects.firstNonNull(asMap().remove(key), unmodifiableEmptyCollection()); - } - - Collection<V> unmodifiableEmptyCollection() { - // These return false, rather than throwing a UOE, on remove calls. - return (unfiltered instanceof SetMultimap) - ? Collections.<V>emptySet() - : Collections.<V>emptyList(); - } - - @Override - public void clear() { - entries().clear(); - } - - @Override - public Collection<V> get(final K key) { - return filterCollection(unfiltered.get(key), new ValuePredicate(key)); - } - - @Override - Collection<Entry<K, V>> createEntries() { - return filterCollection(unfiltered.entries(), predicate); - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - throw new AssertionError("should never be called"); - } - - @Override - Map<K, Collection<V>> createAsMap() { - return new AsMap(); - } - - @Override - public Set<K> keySet() { - return asMap().keySet(); - } - - boolean removeIf(Predicate<? super Entry<K, Collection<V>>> predicate) { - Iterator<Entry<K, Collection<V>>> entryIterator = unfiltered.asMap().entrySet().iterator(); - boolean changed = false; - while (entryIterator.hasNext()) { - Entry<K, Collection<V>> entry = entryIterator.next(); - K key = entry.getKey(); - Collection<V> collection = filterCollection(entry.getValue(), new ValuePredicate(key)); - if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) { - if (collection.size() == entry.getValue().size()) { - entryIterator.remove(); - } else { - collection.clear(); - } - changed = true; - } - } - return changed; - } - - class AsMap extends AbstractMap<K, Collection<V>> { - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - public void clear() { - FilteredEntryMultimap.this.clear(); - } - - @Override - public Collection<V> get(@Nullable Object key) { - Collection<V> result = unfiltered.asMap().get(key); - if (result == null) { - return null; - } - @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself - K k = (K) key; - result = filterCollection(result, new ValuePredicate(k)); - return result.isEmpty() ? null : result; - } - - @Override - public Collection<V> remove(@Nullable Object key) { - Collection<V> collection = unfiltered.asMap().get(key); - if (collection == null) { - return null; - } - @SuppressWarnings("unchecked") // it's definitely equal to a K - K k = (K) key; - List<V> result = Lists.newArrayList(); - Iterator<V> itr = collection.iterator(); - while (itr.hasNext()) { - V v = itr.next(); - if (satisfies(k, v)) { - itr.remove(); - result.add(v); - } - } - if (result.isEmpty()) { - return null; - } else if (unfiltered instanceof SetMultimap) { - return Collections.unmodifiableSet(Sets.newLinkedHashSet(result)); - } else { - return Collections.unmodifiableList(result); - } - } - - private Set<K> keySet; - - @Override - public Set<K> keySet() { - Set<K> result = keySet; - if (result == null) { - return keySet = new Maps.KeySet<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return AsMap.this; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(compose(in(c), Maps.<K>keyFunction())); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<K>keyFunction())); - } - - @Override - public boolean remove(@Nullable Object o) { - return AsMap.this.remove(o) != null; - } - }; - } - return result; - } - - @Override - public Set<Entry<K, Collection<V>>> entrySet() { - return new Maps.EntrySet<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return AsMap.this; - } - - @Override - public Iterator<Entry<K, Collection<V>>> iterator() { - return new AbstractIterator<Entry<K, Collection<V>>>() { - final Iterator<Entry<K, Collection<V>>> backingIterator - = unfiltered.asMap().entrySet().iterator(); - - @Override - protected Entry<K, Collection<V>> computeNext() { - while (backingIterator.hasNext()) { - Entry<K, Collection<V>> entry = backingIterator.next(); - K key = entry.getKey(); - Collection<V> collection - = filterCollection(entry.getValue(), new ValuePredicate(key)); - if (!collection.isEmpty()) { - return Maps.immutableEntry(key, collection); - } - } - return endOfData(); - } - }; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(in(c)); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(not(in(c))); - } - - @Override - public int size() { - return Iterators.size(iterator()); - } - }; - } - - @Override - public Collection<Collection<V>> values() { - return new Maps.Values<K, Collection<V>>() { - @Override - Map<K, Collection<V>> map() { - return AsMap.this; - } - - @Override - public boolean remove(@Nullable Object o) { - if (o instanceof Collection) { - Collection<?> c = (Collection<?>) o; - Iterator<Entry<K, Collection<V>>> entryIterator - = unfiltered.asMap().entrySet().iterator(); - while (entryIterator.hasNext()) { - Entry<K, Collection<V>> entry = entryIterator.next(); - K key = entry.getKey(); - Collection<V> collection - = filterCollection(entry.getValue(), new ValuePredicate(key)); - if (!collection.isEmpty() && c.equals(collection)) { - if (collection.size() == entry.getValue().size()) { - entryIterator.remove(); - } else { - collection.clear(); - } - return true; - } - } - } - return false; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(compose(in(c), Maps.<Collection<V>>valueFunction())); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<Collection<V>>valueFunction())); - } - }; - } - } - - @Override - Multiset<K> createKeys() { - return new Keys(); - } - - class Keys extends Multimaps.Keys<K, V> { - Keys() { - super(FilteredEntryMultimap.this); - } - - @Override - public int remove(@Nullable Object key, int occurrences) { - Multisets.checkNonnegative(occurrences, "occurrences"); - if (occurrences == 0) { - return count(key); - } - Collection<V> collection = unfiltered.asMap().get(key); - if (collection == null) { - return 0; - } - @SuppressWarnings("unchecked") // key is equal to a K, if not a K itself - K k = (K) key; - int oldCount = 0; - Iterator<V> itr = collection.iterator(); - while (itr.hasNext()) { - V v = itr.next(); - if (satisfies(k, v)) { - oldCount++; - if (oldCount <= occurrences) { - itr.remove(); - } - } - } - return oldCount; - } - - @Override - public Set<Multiset.Entry<K>> entrySet() { - return new Multisets.EntrySet<K>() { - - @Override - Multiset<K> multiset() { - return Keys.this; - } - - @Override - public Iterator<Multiset.Entry<K>> iterator() { - return Keys.this.entryIterator(); - } - - @Override - public int size() { - return FilteredEntryMultimap.this.keySet().size(); - } - - private boolean removeIf(final Predicate<? super Multiset.Entry<K>> predicate) { - return FilteredEntryMultimap.this.removeIf(new Predicate<Map.Entry<K, Collection<V>>>() { - @Override - public boolean apply(Map.Entry<K, Collection<V>> entry) { - return predicate.apply( - Multisets.immutableEntry(entry.getKey(), entry.getValue().size())); - } - }); - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(in(c)); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(not(in(c))); - } - }; - } - } -} diff --git a/guava/src/com/google/common/collect/FilteredKeyMultimap.java b/guava/src/com/google/common/collect/FilteredKeyMultimap.java deleted file mode 100644 index 0a07333..0000000 --- a/guava/src/com/google/common/collect/FilteredKeyMultimap.java +++ /dev/null @@ -1,228 +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 static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkPositionIndex; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link Multimaps#filterKeys(Multimap, Predicate)}. - * - * @author Louis Wasserman - */ -@GwtCompatible -class FilteredKeyMultimap<K, V> extends FilteredMultimap<K, V> { - final Predicate<? super K> keyPredicate; - - FilteredKeyMultimap(Multimap<K, V> unfiltered, Predicate<? super K> keyPredicate) { - super(unfiltered); - this.keyPredicate = checkNotNull(keyPredicate); - } - - @Override - Predicate<? super Entry<K, V>> entryPredicate() { - return Predicates.compose(keyPredicate, Maps.<K>keyFunction()); - } - - @Override - public int size() { - int size = 0; - for (Collection<V> collection : asMap().values()) { - size += collection.size(); - } - return size; - } - - @Override - public boolean containsKey(@Nullable Object key) { - if (unfiltered.containsKey(key)) { - @SuppressWarnings("unchecked") // k is equal to a K, if not one itself - K k = (K) key; - return keyPredicate.apply(k); - } - return false; - } - - @Override - public Collection<V> removeAll(Object key) { - return containsKey(key) ? unfiltered.removeAll(key) : unmodifiableEmptyCollection(); - } - - Collection<V> unmodifiableEmptyCollection() { - if (unfiltered instanceof SetMultimap) { - return ImmutableSet.of(); - } else { - return ImmutableList.of(); - } - } - - @Override - public void clear() { - keySet().clear(); - } - - @Override - Set<K> createKeySet() { - return Sets.filter(unfiltered.keySet(), keyPredicate); - } - - @Override - public Collection<V> get(K key) { - if (keyPredicate.apply(key)) { - return unfiltered.get(key); - } else if (unfiltered instanceof SetMultimap) { - return new AddRejectingSet<K, V>(key); - } else { - return new AddRejectingList<K, V>(key); - } - } - - static class AddRejectingSet<K, V> extends ForwardingSet<V> { - final K key; - - AddRejectingSet(K key) { - this.key = key; - } - - @Override - public boolean add(V element) { - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - public boolean addAll(Collection<? extends V> collection) { - checkNotNull(collection); - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - protected Set<V> delegate() { - return Collections.emptySet(); - } - } - - static class AddRejectingList<K, V> extends ForwardingList<V> { - final K key; - - AddRejectingList(K key) { - this.key = key; - } - - @Override - public boolean add(V v) { - add(0, v); - return true; - } - - @Override - public boolean addAll(Collection<? extends V> collection) { - addAll(0, collection); - return true; - } - - @Override - public void add(int index, V element) { - checkPositionIndex(index, 0); - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - public boolean addAll(int index, Collection<? extends V> elements) { - checkNotNull(elements); - checkPositionIndex(index, 0); - throw new IllegalArgumentException("Key does not satisfy predicate: " + key); - } - - @Override - protected List<V> delegate() { - return Collections.emptyList(); - } - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - return Iterators.filter( - unfiltered.entries().iterator(), Predicates.compose(keyPredicate, Maps.<K>keyFunction())); - } - - @Override - Collection<Entry<K, V>> createEntries() { - return new Multimaps.Entries<K, V>() { - @Override - Multimap<K, V> multimap() { - return FilteredKeyMultimap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); - } - - @Override - @SuppressWarnings("unchecked") - public boolean remove(@Nullable Object o) { - if (o instanceof Entry) { - Entry<?, ?> entry = (Entry<?, ?>) o; - if (unfiltered.containsEntry(entry.getKey(), entry.getValue()) - && keyPredicate.apply((K) entry.getKey())) { - return unfiltered.remove(entry.getKey(), entry.getValue()); - } - } - return false; - } - - @Override - public boolean removeAll(Collection<?> c) { - Predicate<Entry<K, ?>> combinedPredicate = Predicates.and( - Predicates.compose(keyPredicate, Maps.<K>keyFunction()), Predicates.in(c)); - return Iterators.removeIf(unfiltered.entries().iterator(), combinedPredicate); - } - - @Override - public boolean retainAll(Collection<?> c) { - Predicate<Entry<K, ?>> combinedPredicate = Predicates.and( - Predicates.compose(keyPredicate, Maps.<K>keyFunction()), - Predicates.not(Predicates.in(c))); - return Iterators.removeIf(unfiltered.entries().iterator(), combinedPredicate); - } - }; - } - - @Override - Map<K, Collection<V>> createAsMap() { - return Maps.filterKeys(unfiltered.asMap(), keyPredicate); - } - - @Override - Multiset<K> createKeys() { - return Multisets.filter(unfiltered.keys(), keyPredicate); - } -} diff --git a/guava/src/com/google/common/collect/FilteredMultimap.java b/guava/src/com/google/common/collect/FilteredMultimap.java deleted file mode 100644 index d7d2b3b..0000000 --- a/guava/src/com/google/common/collect/FilteredMultimap.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.collect; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Predicate; - -import java.util.Map.Entry; - -/** - * A superclass of all filtered multimap types. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class FilteredMultimap<K, V> extends AbstractMultimap<K, V> { - final Multimap<K, V> unfiltered; - - FilteredMultimap(Multimap<K, V> unfiltered) { - this.unfiltered = checkNotNull(unfiltered); - } - - abstract Predicate<? super Entry<K, V>> entryPredicate(); -} diff --git a/guava/src/com/google/common/collect/FluentIterable.java b/guava/src/com/google/common/collect/FluentIterable.java deleted file mode 100644 index 2469180..0000000 --- a/guava/src/com/google/common/collect/FluentIterable.java +++ /dev/null @@ -1,531 +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.annotations.GwtIncompatible; -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 the elements from this fluent iterable that are instances of class {@code type}. - * - * @param type the type of elements desired - */ - @GwtIncompatible("Class.isInstance") - public final <T> FluentIterable<T> filter(Class<T> type) { - return from(Iterables.filter(iterable, type)); - } - - /** - * 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); - } - - /** - * Returns an array containing all of the elements from this fluent iterable in iteration order. - * - * @param type the type of the elements - * @return a newly-allocated array into which all the elements of this fluent iterable have - * been copied - */ - @GwtIncompatible("Array.newArray(Class, int)") - public final E[] toArray(Class<E> type) { - return Iterables.toArray(iterable, type); - } - - /** - * 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/src/com/google/common/collect/ForwardingBlockingDeque.java b/guava/src/com/google/common/collect/ForwardingBlockingDeque.java deleted file mode 100644 index a6fb43d..0000000 --- a/guava/src/com/google/common/collect/ForwardingBlockingDeque.java +++ /dev/null @@ -1,123 +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.Collection; -import java.util.concurrent.BlockingDeque; -import java.util.concurrent.TimeUnit; - -/** - * A {@link BlockingDeque} which forwards all its method calls to another {@code BlockingDeque}. - * Subclasses should override one or more methods to modify the behavior of the backing deque as - * desired per the <a href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><b>Warning:</b> The methods of {@code ForwardingBlockingDeque} forward - * <b>indiscriminately</b> to the methods of the delegate. For example, overriding {@link #add} - * alone <b>will not</b> change the behaviour of {@link #offer} which can lead to unexpected - * behaviour. In this case, you should override {@code offer} as well, either providing your own - * implementation, or delegating to the provided {@code standardOffer} method. - * - * <p> - * The {@code standard} methods are not guaranteed to be thread-safe, even when all of the methods - * that they depend on are thread-safe. - * - * @author Emily Soldal - * @since 14.0 - */ -public abstract class ForwardingBlockingDeque<E> - extends ForwardingDeque<E> implements BlockingDeque<E> { - - /** Constructor for use by subclasses. */ - protected ForwardingBlockingDeque() {} - - @Override protected abstract BlockingDeque<E> delegate(); - - @Override - public int remainingCapacity() { - return delegate().remainingCapacity(); - } - - @Override - public void putFirst(E e) throws InterruptedException { - delegate().putFirst(e); - } - - @Override - public void putLast(E e) throws InterruptedException { - delegate().putLast(e); - } - - @Override - public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException { - return delegate().offerFirst(e, timeout, unit); - } - - @Override - public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException { - return delegate().offerLast(e, timeout, unit); - } - - @Override - public E takeFirst() throws InterruptedException { - return delegate().takeFirst(); - } - - @Override - public E takeLast() throws InterruptedException { - return delegate().takeLast(); - } - - @Override - public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { - return delegate().pollFirst(timeout, unit); - } - - @Override - public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { - return delegate().pollLast(timeout, unit); - } - - @Override - public void put(E e) throws InterruptedException { - delegate().put(e); - } - - @Override - public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - return delegate().offer(e, timeout, unit); - } - - @Override - public E take() throws InterruptedException { - return delegate().take(); - } - - @Override - public E poll(long timeout, TimeUnit unit) throws InterruptedException { - return delegate().poll(timeout, unit); - } - - @Override - public int drainTo(Collection<? super E> c) { - return delegate().drainTo(c); - } - - @Override - public int drainTo(Collection<? super E> c, int maxElements) { - return delegate().drainTo(c, maxElements); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingCollection.java b/guava/src/com/google/common/collect/ForwardingCollection.java index 79d7860..a6a46f0 100644 --- a/guava/src/com/google/common/collect/ForwardingCollection.java +++ b/guava/src/com/google/common/collect/ForwardingCollection.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; @@ -126,7 +127,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardContains(@Nullable Object object) { + @Beta protected boolean standardContains(@Nullable Object object) { return Iterators.contains(iterator(), object); } @@ -137,7 +138,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardContainsAll(Collection<?> collection) { + @Beta protected boolean standardContainsAll(Collection<?> collection) { for (Object o : collection) { if (!contains(o)) { return false; @@ -153,7 +154,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardAddAll(Collection<? extends E> collection) { + @Beta protected boolean standardAddAll(Collection<? extends E> collection) { return Iterators.addAll(this, collection.iterator()); } @@ -165,7 +166,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardRemove(@Nullable Object object) { + @Beta protected boolean standardRemove(@Nullable Object object) { Iterator<E> iterator = iterator(); while (iterator.hasNext()) { if (Objects.equal(iterator.next(), object)) { @@ -184,7 +185,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardRemoveAll(Collection<?> collection) { + @Beta protected boolean standardRemoveAll(Collection<?> collection) { return Iterators.removeAll(iterator(), collection); } @@ -196,7 +197,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardRetainAll(Collection<?> collection) { + @Beta protected boolean standardRetainAll(Collection<?> collection) { return Iterators.retainAll(iterator(), collection); } @@ -208,8 +209,12 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected void standardClear() { - Iterators.clear(iterator()); + @Beta protected void standardClear() { + Iterator<E> iterator = iterator(); + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } } /** @@ -220,7 +225,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected boolean standardIsEmpty() { + @Beta protected boolean standardIsEmpty() { return !iterator().hasNext(); } @@ -231,7 +236,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected String standardToString() { + @Beta protected String standardToString() { return Collections2.toStringImpl(this); } @@ -242,7 +247,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected Object[] standardToArray() { + @Beta protected Object[] standardToArray() { Object[] newArray = new Object[size()]; return toArray(newArray); } @@ -254,7 +259,7 @@ public abstract class ForwardingCollection<E> extends ForwardingObject * * @since 7.0 */ - protected <T> T[] standardToArray(T[] array) { + @Beta protected <T> T[] standardToArray(T[] array) { return ObjectArrays.toArrayImpl(this, array); } } diff --git a/guava/src/com/google/common/collect/ForwardingDeque.java b/guava/src/com/google/common/collect/ForwardingDeque.java deleted file mode 100644 index 070f622..0000000 --- a/guava/src/com/google/common/collect/ForwardingDeque.java +++ /dev/null @@ -1,129 +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.Deque; -import java.util.Iterator; - -/** - * A deque which forwards all its method calls to another deque. Subclasses - * should override one or more methods to modify the behavior of the backing - * deque as desired per the <a - * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><b>Warning:</b> The methods of {@code ForwardingDeque} forward - * <b>indiscriminately</b> to the methods of the delegate. For example, - * overriding {@link #add} alone <b>will not</b> change the behavior of {@link - * #offer} which can lead to unexpected behavior. In this case, you should - * override {@code offer} as well. - * - * @author Kurt Alfred Kluever - * @since 12.0 - */ -public abstract class ForwardingDeque<E> extends ForwardingQueue<E> - implements Deque<E> { - - /** Constructor for use by subclasses. */ - protected ForwardingDeque() {} - - @Override protected abstract Deque<E> delegate(); - - @Override - public void addFirst(E e) { - delegate().addFirst(e); - } - - @Override - public void addLast(E e) { - delegate().addLast(e); - } - - @Override - public Iterator<E> descendingIterator() { - return delegate().descendingIterator(); - } - - @Override - public E getFirst() { - return delegate().getFirst(); - } - - @Override - public E getLast() { - return delegate().getLast(); - } - - @Override - public boolean offerFirst(E e) { - return delegate().offerFirst(e); - } - - @Override - public boolean offerLast(E e) { - return delegate().offerLast(e); - } - - @Override - public E peekFirst() { - return delegate().peekFirst(); - } - - @Override - public E peekLast() { - return delegate().peekLast(); - } - - @Override - public E pollFirst() { - return delegate().pollFirst(); - } - - @Override - public E pollLast() { - return delegate().pollLast(); - } - - @Override - public E pop() { - return delegate().pop(); - } - - @Override - public void push(E e) { - delegate().push(e); - } - - @Override - public E removeFirst() { - return delegate().removeFirst(); - } - - @Override - public E removeLast() { - return delegate().removeLast(); - } - - @Override - public boolean removeFirstOccurrence(Object o) { - return delegate().removeFirstOccurrence(o); - } - - @Override - public boolean removeLastOccurrence(Object o) { - return delegate().removeLastOccurrence(o); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableCollection.java b/guava/src/com/google/common/collect/ForwardingImmutableCollection.java deleted file mode 100644 index 90489a4..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableCollection.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.collect; - -import com.google.common.annotations.GwtCompatible; - -/** - * Dummy class that makes the GWT serialization policy happy. It isn't used - * on the server-side. - * - * @author Hayward Chan - */ -@GwtCompatible(emulated = true) -class ForwardingImmutableCollection { - private ForwardingImmutableCollection() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableList.java b/guava/src/com/google/common/collect/ForwardingImmutableList.java deleted file mode 100644 index 2b9092e..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableList.java +++ /dev/null @@ -1,29 +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; - -/** - * Unused stub class, unreferenced under Java and manually emulated under GWT. - * - * @author Chris Povirk - */ -@GwtCompatible(emulated = true) -abstract class ForwardingImmutableList<E> { - private ForwardingImmutableList() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableMap.java b/guava/src/com/google/common/collect/ForwardingImmutableMap.java deleted file mode 100644 index a367157..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableMap.java +++ /dev/null @@ -1,29 +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; - -/** - * Unused stub class, unreferenced under Java and manually emulated under GWT. - * - * @author Chris Povirk - */ -@GwtCompatible(emulated = true) -abstract class ForwardingImmutableMap<K, V> { - private ForwardingImmutableMap() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingImmutableSet.java b/guava/src/com/google/common/collect/ForwardingImmutableSet.java deleted file mode 100644 index c7d7bf6..0000000 --- a/guava/src/com/google/common/collect/ForwardingImmutableSet.java +++ /dev/null @@ -1,29 +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; - -/** - * Unused stub class, unreferenced under Java and manually emulated under GWT. - * - * @author Chris Povirk - */ -@GwtCompatible(emulated = true) -abstract class ForwardingImmutableSet<E> { - private ForwardingImmutableSet() {} -} diff --git a/guava/src/com/google/common/collect/ForwardingList.java b/guava/src/com/google/common/collect/ForwardingList.java index 9f3cf1c..e59e662 100644 --- a/guava/src/com/google/common/collect/ForwardingList.java +++ b/guava/src/com/google/common/collect/ForwardingList.java @@ -127,7 +127,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardAdd(E element){ + @Beta protected boolean standardAdd(E element){ add(size(), element); return true; } @@ -140,7 +140,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardAddAll( + @Beta protected boolean standardAddAll( int index, Iterable<? extends E> elements) { return Lists.addAllImpl(this, index, elements); } @@ -152,7 +152,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardIndexOf(@Nullable Object element) { + @Beta protected int standardIndexOf(@Nullable Object element) { return Lists.indexOfImpl(this, element); } @@ -164,7 +164,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardLastIndexOf(@Nullable Object element) { + @Beta protected int standardLastIndexOf(@Nullable Object element) { return Lists.lastIndexOfImpl(this, element); } @@ -175,7 +175,7 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected Iterator<E> standardIterator() { + @Beta protected Iterator<E> standardIterator() { return listIterator(); } @@ -187,15 +187,14 @@ public abstract class ForwardingList<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected ListIterator<E> standardListIterator() { + @Beta protected ListIterator<E> standardListIterator(){ return listIterator(0); } /** * A sensible default implementation of {@link #listIterator(int)}, in terms - * of {@link #size}, {@link #get(int)}, {@link #set(int, Object)}, {@link - * #add(int, Object)}, and {@link #remove(int)}. If you override any of these - * methods, you may wish to override {@link #listIterator(int)} to forward to + * of {@link #size} and {@link #get(int)}. If you override either of these + * methods you may wish to override {@link #listIterator(int)} to forward to * this implementation. * * @since 7.0 diff --git a/guava/src/com/google/common/collect/ForwardingMap.java b/guava/src/com/google/common/collect/ForwardingMap.java index be22230..9b3a489 100644 --- a/guava/src/com/google/common/collect/ForwardingMap.java +++ b/guava/src/com/google/common/collect/ForwardingMap.java @@ -86,17 +86,17 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject } @Override - public boolean containsKey(@Nullable Object key) { + public boolean containsKey(Object key) { return delegate().containsKey(key); } @Override - public boolean containsValue(@Nullable Object value) { + public boolean containsValue(Object value) { return delegate().containsValue(value); } @Override - public V get(@Nullable Object key) { + public V get(Object key) { return delegate().get(key); } @@ -141,7 +141,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected void standardPutAll(Map<? extends K, ? extends V> map) { + @Beta protected void standardPutAll(Map<? extends K, ? extends V> map) { Maps.putAllImpl(this, map); } @@ -177,8 +177,12 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected void standardClear() { - Iterators.clear(entrySet().iterator()); + @Beta protected void standardClear() { + Iterator<Entry<K, V>> entryIterator = entrySet().iterator(); + while (entryIterator.hasNext()) { + entryIterator.next(); + entryIterator.remove(); + } } /** @@ -194,7 +198,6 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject */ @Beta protected class StandardKeySet extends Maps.KeySet<K, V> { - /** Constructor for use by subclasses. */ public StandardKeySet() {} @Override @@ -227,7 +230,6 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject */ @Beta protected class StandardValues extends Maps.Values<K, V> { - /** Constructor for use by subclasses. */ public StandardValues() {} @Override @@ -244,7 +246,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected boolean standardContainsValue(@Nullable Object value) { + @Beta protected boolean standardContainsValue(@Nullable Object value) { return Maps.containsValueImpl(this, value); } @@ -260,7 +262,6 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject */ @Beta protected abstract class StandardEntrySet extends Maps.EntrySet<K, V> { - /** Constructor for use by subclasses. */ public StandardEntrySet() {} @Override @@ -276,7 +277,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected boolean standardIsEmpty() { + @Beta protected boolean standardIsEmpty() { return !entrySet().iterator().hasNext(); } @@ -287,7 +288,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { return Maps.equalsImpl(this, object); } @@ -298,7 +299,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { return Sets.hashCodeImpl(entrySet()); } @@ -309,7 +310,7 @@ public abstract class ForwardingMap<K, V> extends ForwardingObject * * @since 7.0 */ - protected String standardToString() { + @Beta protected String standardToString() { return Maps.toStringImpl(this); } } diff --git a/guava/src/com/google/common/collect/ForwardingMapEntry.java b/guava/src/com/google/common/collect/ForwardingMapEntry.java index 4d63757..ff201a5 100644 --- a/guava/src/com/google/common/collect/ForwardingMapEntry.java +++ b/guava/src/com/google/common/collect/ForwardingMapEntry.java @@ -92,7 +92,7 @@ public abstract class ForwardingMapEntry<K, V> * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { if (object instanceof Entry) { Entry<?, ?> that = (Entry<?, ?>) object; return Objects.equal(this.getKey(), that.getKey()) @@ -108,7 +108,7 @@ public abstract class ForwardingMapEntry<K, V> * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { K k = getKey(); V v = getValue(); return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); diff --git a/guava/src/com/google/common/collect/ForwardingMultiset.java b/guava/src/com/google/common/collect/ForwardingMultiset.java index 9834751..4e1ceed 100644 --- a/guava/src/com/google/common/collect/ForwardingMultiset.java +++ b/guava/src/com/google/common/collect/ForwardingMultiset.java @@ -107,7 +107,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardContains(@Nullable Object object) { + @Override @Beta protected boolean standardContains(@Nullable Object object) { return count(object) > 0; } @@ -118,8 +118,12 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected void standardClear() { - Iterators.clear(entrySet().iterator()); + @Override @Beta protected void standardClear() { + Iterator<Entry<E>> entryIterator = entrySet().iterator(); + while (entryIterator.hasNext()) { + entryIterator.next(); + entryIterator.remove(); + } } /** @@ -145,7 +149,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardAdd(E element) { + @Beta protected boolean standardAdd(E element) { add(element, 1); return true; } @@ -171,7 +175,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardRemove(Object element) { + @Beta @Override protected boolean standardRemove(Object element) { return remove(element, 1) > 0; } @@ -183,7 +187,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardRemoveAll( + @Beta @Override protected boolean standardRemoveAll( Collection<?> elementsToRemove) { return Multisets.removeAllImpl(this, elementsToRemove); } @@ -196,7 +200,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected boolean standardRetainAll( + @Beta @Override protected boolean standardRetainAll( Collection<?> elementsToRetain) { return Multisets.retainAllImpl(this, elementsToRetain); } @@ -210,7 +214,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardSetCount(E element, int count) { + @Beta protected int standardSetCount(E element, int count) { return Multisets.setCountImpl(this, element, count); } @@ -222,7 +226,8 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardSetCount(E element, int oldCount, int newCount) { + @Beta protected boolean standardSetCount( + E element, int oldCount, int newCount) { return Multisets.setCountImpl(this, element, oldCount, newCount); } @@ -241,7 +246,6 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> */ @Beta protected class StandardElementSet extends Multisets.ElementSet<E> { - /** Constructor for use by subclasses. */ public StandardElementSet() {} @Override @@ -257,7 +261,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected Iterator<E> standardIterator() { + @Beta protected Iterator<E> standardIterator() { return Multisets.iteratorImpl(this); } @@ -268,7 +272,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardSize() { + @Beta protected int standardSize() { return Multisets.sizeImpl(this); } @@ -280,7 +284,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { return Multisets.equalsImpl(this, object); } @@ -291,7 +295,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { return entrySet().hashCode(); } @@ -302,7 +306,7 @@ public abstract class ForwardingMultiset<E> extends ForwardingCollection<E> * * @since 7.0 */ - @Override protected String standardToString() { + @Beta @Override protected String standardToString() { return entrySet().toString(); } } diff --git a/guava/src/com/google/common/collect/ForwardingNavigableMap.java b/guava/src/com/google/common/collect/ForwardingNavigableMap.java deleted file mode 100644 index 8f371ff..0000000 --- a/guava/src/com/google/common/collect/ForwardingNavigableMap.java +++ /dev/null @@ -1,397 +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 static com.google.common.collect.Maps.keyOrNull; - -import com.google.common.annotations.Beta; - -import java.util.Iterator; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.NoSuchElementException; -import java.util.SortedMap; - -/** - * A navigable map which forwards all its method calls to another navigable map. Subclasses should - * override one or more methods to modify the behavior of the backing map as desired per the <a - * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><i>Warning:</i> The methods of {@code ForwardingNavigableMap} forward <i>indiscriminately</i> - * to the methods of the delegate. For example, overriding {@link #put} alone <i>will not</i> - * change the behavior of {@link #putAll}, which can lead to unexpected behavior. In this case, you - * should override {@code putAll} as well, either providing your own implementation, or delegating - * to the provided {@code standardPutAll} method. - * - * <p>Each of the {@code standard} methods uses the map's comparator (or the natural ordering of - * the elements, if there is no comparator) to test element equality. As a result, if the comparator - * is not consistent with equals, some of the standard implementations may violate the {@code Map} - * contract. - * - * <p>The {@code standard} methods and the collection views they return are not guaranteed to be - * thread-safe, even when all of the methods that they depend on are thread-safe. - * - * @author Louis Wasserman - * @since 12.0 - */ -public abstract class ForwardingNavigableMap<K, V> - extends ForwardingSortedMap<K, V> implements NavigableMap<K, V> { - - /** Constructor for use by subclasses. */ - protected ForwardingNavigableMap() {} - - @Override - protected abstract NavigableMap<K, V> delegate(); - - @Override - public Entry<K, V> lowerEntry(K key) { - return delegate().lowerEntry(key); - } - - /** - * A sensible definition of {@link #lowerEntry} in terms of the {@code lastEntry()} of - * {@link #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override - * {@code lowerEntry} to forward to this implementation. - */ - protected Entry<K, V> standardLowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - public K lowerKey(K key) { - return delegate().lowerKey(key); - } - - /** - * A sensible definition of {@link #lowerKey} in terms of {@code lowerEntry}. If you override - * {@link #lowerEntry}, you may wish to override {@code lowerKey} to forward to this - * implementation. - */ - protected K standardLowerKey(K key) { - return keyOrNull(lowerEntry(key)); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return delegate().floorEntry(key); - } - - /** - * A sensible definition of {@link #floorEntry} in terms of the {@code lastEntry()} of - * {@link #headMap(Object, boolean)}. If you override {@code headMap}, you may wish to override - * {@code floorEntry} to forward to this implementation. - */ - protected Entry<K, V> standardFloorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - public K floorKey(K key) { - return delegate().floorKey(key); - } - - /** - * A sensible definition of {@link #floorKey} in terms of {@code floorEntry}. If you override - * {@code floorEntry}, you may wish to override {@code floorKey} to forward to this - * implementation. - */ - protected K standardFloorKey(K key) { - return keyOrNull(floorEntry(key)); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return delegate().ceilingEntry(key); - } - - /** - * A sensible definition of {@link #ceilingEntry} in terms of the {@code firstEntry()} of - * {@link #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override - * {@code ceilingEntry} to forward to this implementation. - */ - protected Entry<K, V> standardCeilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - public K ceilingKey(K key) { - return delegate().ceilingKey(key); - } - - /** - * A sensible definition of {@link #ceilingKey} in terms of {@code ceilingEntry}. If you override - * {@code ceilingEntry}, you may wish to override {@code ceilingKey} to forward to this - * implementation. - */ - protected K standardCeilingKey(K key) { - return keyOrNull(ceilingEntry(key)); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return delegate().higherEntry(key); - } - - /** - * A sensible definition of {@link #higherEntry} in terms of the {@code firstEntry()} of - * {@link #tailMap(Object, boolean)}. If you override {@code tailMap}, you may wish to override - * {@code higherEntry} to forward to this implementation. - */ - protected Entry<K, V> standardHigherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K higherKey(K key) { - return delegate().higherKey(key); - } - - /** - * A sensible definition of {@link #higherKey} in terms of {@code higherEntry}. If you override - * {@code higherEntry}, you may wish to override {@code higherKey} to forward to this - * implementation. - */ - protected K standardHigherKey(K key) { - return keyOrNull(higherEntry(key)); - } - - @Override - public Entry<K, V> firstEntry() { - return delegate().firstEntry(); - } - - /** - * A sensible definition of {@link #firstEntry} in terms of the {@code iterator()} of - * {@link #entrySet}. If you override {@code entrySet}, you may wish to override - * {@code firstEntry} to forward to this implementation. - */ - protected Entry<K, V> standardFirstEntry() { - return Iterables.getFirst(entrySet(), null); - } - - /** - * A sensible definition of {@link #firstKey} in terms of {@code firstEntry}. If you override - * {@code firstEntry}, you may wish to override {@code firstKey} to forward to this - * implementation. - */ - protected K standardFirstKey() { - Entry<K, V> entry = firstEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - public Entry<K, V> lastEntry() { - return delegate().lastEntry(); - } - - /** - * A sensible definition of {@link #lastEntry} in terms of the {@code iterator()} of the - * {@link #entrySet} of {@link #descendingMap}. If you override {@code descendingMap}, you may - * wish to override {@code lastEntry} to forward to this implementation. - */ - protected Entry<K, V> standardLastEntry() { - return Iterables.getFirst(descendingMap().entrySet(), null); - } - - /** - * A sensible definition of {@link #lastKey} in terms of {@code lastEntry}. If you override - * {@code lastEntry}, you may wish to override {@code lastKey} to forward to this implementation. - */ - protected K standardLastKey() { - Entry<K, V> entry = lastEntry(); - if (entry == null) { - throw new NoSuchElementException(); - } else { - return entry.getKey(); - } - } - - @Override - public Entry<K, V> pollFirstEntry() { - return delegate().pollFirstEntry(); - } - - /** - * A sensible definition of {@link #pollFirstEntry} in terms of the {@code iterator} of - * {@code entrySet}. If you override {@code entrySet}, you may wish to override - * {@code pollFirstEntry} to forward to this implementation. - */ - protected Entry<K, V> standardPollFirstEntry() { - return Iterators.pollNext(entrySet().iterator()); - } - - @Override - public Entry<K, V> pollLastEntry() { - return delegate().pollLastEntry(); - } - - /** - * A sensible definition of {@link #pollFirstEntry} in terms of the {@code iterator} of the - * {@code entrySet} of {@code descendingMap}. If you override {@code descendingMap}, you may wish - * to override {@code pollFirstEntry} to forward to this implementation. - */ - protected Entry<K, V> standardPollLastEntry() { - return Iterators.pollNext(descendingMap().entrySet().iterator()); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return delegate().descendingMap(); - } - - /** - * A sensible implementation of {@link NavigableMap#descendingMap} in terms of the methods of - * this {@code NavigableMap}. In many cases, you may wish to override - * {@link ForwardingNavigableMap#descendingMap} to forward to this implementation or a subclass - * thereof. - * - * <p>In particular, this map iterates over entries with repeated calls to - * {@link NavigableMap#lowerEntry}. If a more efficient means of iteration is available, you may - * wish to override the {@code entryIterator()} method of this class. - * - * @since 12.0 - */ - @Beta - protected class StandardDescendingMap extends Maps.DescendingMap<K, V> { - /** Constructor for use by subclasses. */ - public StandardDescendingMap() {} - - @Override - NavigableMap<K, V> forward() { - return ForwardingNavigableMap.this; - } - - @Override - protected Iterator<Entry<K, V>> entryIterator() { - return new Iterator<Entry<K, V>>() { - private Entry<K, V> toRemove = null; - private Entry<K, V> nextOrNull = forward().lastEntry(); - - @Override - public boolean hasNext() { - return nextOrNull != null; - } - - @Override - public java.util.Map.Entry<K, V> next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - try { - return nextOrNull; - } finally { - toRemove = nextOrNull; - nextOrNull = forward().lowerEntry(nextOrNull.getKey()); - } - } - - @Override - public void remove() { - Iterators.checkRemove(toRemove != null); - forward().remove(toRemove.getKey()); - toRemove = null; - } - }; - } - } - - @Override - public NavigableSet<K> navigableKeySet() { - return delegate().navigableKeySet(); - } - - /** - * A sensible implementation of {@link NavigableMap#navigableKeySet} in terms of the methods of - * this {@code NavigableMap}. In many cases, you may wish to override - * {@link ForwardingNavigableMap#navigableKeySet} to forward to this implementation or a subclass - * thereof. - * - * @since 12.0 - */ - @Beta - protected class StandardNavigableKeySet extends Maps.NavigableKeySet<K, V> { - /** Constructor for use by subclasses. */ - public StandardNavigableKeySet() { - super(ForwardingNavigableMap.this); - } - } - - @Override - public NavigableSet<K> descendingKeySet() { - return delegate().descendingKeySet(); - } - - /** - * A sensible definition of {@link #descendingKeySet} as the {@code navigableKeySet} of - * {@link #descendingMap}. (The {@link StandardDescendingMap} implementation implements - * {@code navigableKeySet} on its own, so as not to cause an infinite loop.) If you override - * {@code descendingMap}, you may wish to override {@code descendingKeySet} to forward to this - * implementation. - */ - @Beta - protected NavigableSet<K> standardDescendingKeySet() { - return descendingMap().navigableKeySet(); - } - - /** - * A sensible definition of {@link #subMap(Object, Object)} in terms of - * {@link #subMap(Object, boolean, Object, boolean)}. If you override - * {@code subMap(K, boolean, K, boolean)}, you may wish to override {@code subMap} to forward to - * this implementation. - */ - @Override - protected SortedMap<K, V> standardSubMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return delegate().subMap(fromKey, fromInclusive, toKey, toInclusive); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return delegate().headMap(toKey, inclusive); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return delegate().tailMap(fromKey, inclusive); - } - - /** - * A sensible definition of {@link #headMap(Object)} in terms of - * {@link #headMap(Object, boolean)}. If you override {@code headMap(K, boolean)}, you may wish - * to override {@code headMap} to forward to this implementation. - */ - protected SortedMap<K, V> standardHeadMap(K toKey) { - return headMap(toKey, false); - } - - /** - * A sensible definition of {@link #tailMap(Object)} in terms of - * {@link #tailMap(Object, boolean)}. If you override {@code tailMap(K, boolean)}, you may wish - * to override {@code tailMap} to forward to this implementation. - */ - protected SortedMap<K, V> standardTailMap(K fromKey) { - return tailMap(fromKey, true); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingNavigableSet.java b/guava/src/com/google/common/collect/ForwardingNavigableSet.java deleted file mode 100644 index dff5ea0..0000000 --- a/guava/src/com/google/common/collect/ForwardingNavigableSet.java +++ /dev/null @@ -1,238 +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.Beta; - -import java.util.Iterator; -import java.util.NavigableSet; -import java.util.SortedSet; - -/** - * A navigable set which forwards all its method calls to another navigable set. Subclasses should - * override one or more methods to modify the behavior of the backing set as desired per the <a - * href="http://en.wikipedia.org/wiki/Decorator_pattern">decorator pattern</a>. - * - * <p><i>Warning:</i> The methods of {@code ForwardingNavigableSet} forward <i>indiscriminately</i> - * to the methods of the delegate. For example, overriding {@link #add} alone <i>will not</i> - * change the behavior of {@link #addAll}, which can lead to unexpected behavior. In this case, you - * should override {@code addAll} as well, either providing your own implementation, or delegating - * to the provided {@code standardAddAll} method. - * - * <p>Each of the {@code standard} methods uses the set's comparator (or the natural ordering of - * the elements, if there is no comparator) to test element equality. As a result, if the - * comparator is not consistent with equals, some of the standard implementations may violate the - * {@code Set} contract. - * - * <p>The {@code standard} methods and the collection views they return are not guaranteed to be - * thread-safe, even when all of the methods that they depend on are thread-safe. - * - * @author Louis Wasserman - * @since 12.0 - */ -public abstract class ForwardingNavigableSet<E> - extends ForwardingSortedSet<E> implements NavigableSet<E> { - - /** Constructor for use by subclasses. */ - protected ForwardingNavigableSet() {} - - @Override - protected abstract NavigableSet<E> delegate(); - - @Override - public E lower(E e) { - return delegate().lower(e); - } - - /** - * A sensible definition of {@link #lower} in terms of the {@code descendingIterator} method of - * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may - * wish to override {@link #lower} to forward to this implementation. - */ - protected E standardLower(E e) { - return Iterators.getNext(headSet(e, false).descendingIterator(), null); - } - - @Override - public E floor(E e) { - return delegate().floor(e); - } - - /** - * A sensible definition of {@link #floor} in terms of the {@code descendingIterator} method of - * {@link #headSet(Object, boolean)}. If you override {@link #headSet(Object, boolean)}, you may - * wish to override {@link #floor} to forward to this implementation. - */ - protected E standardFloor(E e) { - return Iterators.getNext(headSet(e, true).descendingIterator(), null); - } - - @Override - public E ceiling(E e) { - return delegate().ceiling(e); - } - - /** - * A sensible definition of {@link #ceiling} in terms of the {@code iterator} method of - * {@link #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may - * wish to override {@link #ceiling} to forward to this implementation. - */ - protected E standardCeiling(E e) { - return Iterators.getNext(tailSet(e, true).iterator(), null); - } - - @Override - public E higher(E e) { - return delegate().higher(e); - } - - /** - * A sensible definition of {@link #higher} in terms of the {@code iterator} method of - * {@link #tailSet(Object, boolean)}. If you override {@link #tailSet(Object, boolean)}, you may - * wish to override {@link #higher} to forward to this implementation. - */ - protected E standardHigher(E e) { - return Iterators.getNext(tailSet(e, false).iterator(), null); - } - - @Override - public E pollFirst() { - return delegate().pollFirst(); - } - - /** - * A sensible definition of {@link #pollFirst} in terms of the {@code iterator} method. If you - * override {@link #iterator} you may wish to override {@link #pollFirst} to forward to this - * implementation. - */ - protected E standardPollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - public E pollLast() { - return delegate().pollLast(); - } - - /** - * A sensible definition of {@link #pollLast} in terms of the {@code descendingIterator} method. - * If you override {@link #descendingIterator} you may wish to override {@link #pollLast} to - * forward to this implementation. - */ - protected E standardPollLast() { - return Iterators.pollNext(descendingIterator()); - } - - protected E standardFirst() { - return iterator().next(); - } - - protected E standardLast() { - return descendingIterator().next(); - } - - @Override - public NavigableSet<E> descendingSet() { - return delegate().descendingSet(); - } - - /** - * A sensible implementation of {@link NavigableSet#descendingSet} in terms of the other methods - * of {@link NavigableSet}, notably including {@link NavigableSet#descendingIterator}. - * - * <p>In many cases, you may wish to override {@link ForwardingNavigableSet#descendingSet} to - * forward to this implementation or a subclass thereof. - * - * @since 12.0 - */ - @Beta - protected class StandardDescendingSet extends Sets.DescendingSet<E> { - /** Constructor for use by subclasses. */ - public StandardDescendingSet() { - super(ForwardingNavigableSet.this); - } - } - - @Override - public Iterator<E> descendingIterator() { - return delegate().descendingIterator(); - } - - @Override - public NavigableSet<E> subSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return delegate().subSet(fromElement, fromInclusive, toElement, toInclusive); - } - - /** - * A sensible definition of {@link #subSet(Object, boolean, Object, boolean)} in terms of the - * {@code headSet} and {@code tailSet} methods. In many cases, you may wish to override - * {@link #subSet(Object, boolean, Object, boolean)} to forward to this implementation. - */ - @Beta - protected NavigableSet<E> standardSubSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return tailSet(fromElement, fromInclusive).headSet(toElement, toInclusive); - } - - /** - * A sensible definition of {@link #subSet(Object, Object)} in terms of the - * {@link #subSet(Object, boolean, Object, boolean)} method. If you override - * {@link #subSet(Object, boolean, Object, boolean)}, you may wish to override - * {@link #subSet(Object, Object)} to forward to this implementation. - */ - @Override - protected SortedSet<E> standardSubSet(E fromElement, E toElement) { - return subSet(fromElement, true, toElement, false); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return delegate().headSet(toElement, inclusive); - } - - /** - * A sensible definition of {@link #headSet(Object)} in terms of the - * {@link #headSet(Object, boolean)} method. If you override - * {@link #headSet(Object, boolean)}, you may wish to override - * {@link #headSet(Object)} to forward to this implementation. - */ - protected SortedSet<E> standardHeadSet(E toElement) { - return headSet(toElement, false); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return delegate().tailSet(fromElement, inclusive); - } - - /** - * A sensible definition of {@link #tailSet(Object)} in terms of the - * {@link #tailSet(Object, boolean)} method. If you override - * {@link #tailSet(Object, boolean)}, you may wish to override - * {@link #tailSet(Object)} to forward to this implementation. - */ - protected SortedSet<E> standardTailSet(E fromElement) { - return tailSet(fromElement, true); - } -} diff --git a/guava/src/com/google/common/collect/ForwardingQueue.java b/guava/src/com/google/common/collect/ForwardingQueue.java index 569880a..3d30aaf 100644 --- a/guava/src/com/google/common/collect/ForwardingQueue.java +++ b/guava/src/com/google/common/collect/ForwardingQueue.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.NoSuchElementException; @@ -82,7 +83,7 @@ public abstract class ForwardingQueue<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected boolean standardOffer(E e) { + @Beta protected boolean standardOffer(E e) { try { return add(e); } catch (IllegalStateException caught) { @@ -97,7 +98,7 @@ public abstract class ForwardingQueue<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected E standardPeek() { + @Beta protected E standardPeek() { try { return element(); } catch (NoSuchElementException caught) { @@ -112,7 +113,7 @@ public abstract class ForwardingQueue<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected E standardPoll() { + @Beta protected E standardPoll() { try { return remove(); } catch (NoSuchElementException caught) { diff --git a/guava/src/com/google/common/collect/ForwardingSet.java b/guava/src/com/google/common/collect/ForwardingSet.java index e1a4485..4c3dccd 100644 --- a/guava/src/com/google/common/collect/ForwardingSet.java +++ b/guava/src/com/google/common/collect/ForwardingSet.java @@ -16,11 +16,9 @@ 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 java.util.Collection; import java.util.Set; import javax.annotation.Nullable; @@ -64,26 +62,13 @@ public abstract class ForwardingSet<E> extends ForwardingCollection<E> } /** - * A sensible definition of {@link #removeAll} in terms of {@link #iterator} - * and {@link #remove}. If you override {@code iterator} or {@code remove}, - * you may wish to override {@link #removeAll} to forward to this - * implementation. - * - * @since 7.0 (this version overrides the {@code ForwardingCollection} version as of 12.0) - */ - @Override - protected boolean standardRemoveAll(Collection<?> collection) { - return Sets.removeAllImpl(this, checkNotNull(collection)); // for GWT - } - - /** * A sensible definition of {@link #equals} in terms of {@link #size} and * {@link #containsAll}. If you override either of those methods, you may wish * to override {@link #equals} to forward to this implementation. * * @since 7.0 */ - protected boolean standardEquals(@Nullable Object object) { + @Beta protected boolean standardEquals(@Nullable Object object) { return Sets.equalsImpl(this, object); } @@ -94,7 +79,7 @@ public abstract class ForwardingSet<E> extends ForwardingCollection<E> * * @since 7.0 */ - protected int standardHashCode() { + @Beta protected int standardHashCode() { return Sets.hashCodeImpl(this); } } diff --git a/guava/src/com/google/common/collect/ForwardingTable.java b/guava/src/com/google/common/collect/ForwardingTable.java index 92cc876..2c59705 100644 --- a/guava/src/com/google/common/collect/ForwardingTable.java +++ b/guava/src/com/google/common/collect/ForwardingTable.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; @@ -31,6 +32,7 @@ import java.util.Set; * @author Gregory Kick * @since 7.0 */ +@Beta @GwtCompatible public abstract class ForwardingTable<R, C, V> extends ForwardingObject implements Table<R, C, V> { diff --git a/guava/src/com/google/common/collect/GeneralRange.java b/guava/src/com/google/common/collect/GeneralRange.java index cb3b2c8..5a3980f 100644 --- a/guava/src/com/google/common/collect/GeneralRange.java +++ b/guava/src/com/google/common/collect/GeneralRange.java @@ -1,11 +1,11 @@ /* * 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 @@ -19,21 +19,21 @@ import static com.google.common.base.Preconditions.checkNotNull; 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.base.Objects; - import java.io.Serializable; import java.util.Comparator; import javax.annotation.Nullable; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; + /** * A generalized interval on any ordering, for internal use. Supports {@code null}. Unlike * {@link Range}, this allows the use of an arbitrary comparator. This is designed for use in the * implementation of subcollections of sorted collection types. - * + * * <p>Whenever possible, use {@code Range} instead, which is better supported. - * + * * @author Louis Wasserman */ @GwtCompatible(serializable = true) @@ -138,26 +138,26 @@ final class GeneralRange<T> implements Serializable { } boolean isEmpty() { - return (hasUpperBound() && tooLow(getUpperEndpoint())) - || (hasLowerBound() && tooHigh(getLowerEndpoint())); + return (hasUpperBound() && tooLow(upperEndpoint)) + || (hasLowerBound() && tooHigh(lowerEndpoint)); } boolean tooLow(@Nullable T t) { if (!hasLowerBound()) { return false; } - T lbound = getLowerEndpoint(); + T lbound = lowerEndpoint; int cmp = comparator.compare(t, lbound); - return cmp < 0 | (cmp == 0 & getLowerBoundType() == OPEN); + return cmp < 0 | (cmp == 0 & lowerBoundType == OPEN); } boolean tooHigh(@Nullable T t) { if (!hasUpperBound()) { return false; } - T ubound = getUpperEndpoint(); + T ubound = upperEndpoint; int cmp = comparator.compare(t, ubound); - return cmp > 0 | (cmp == 0 & getUpperBoundType() == OPEN); + return cmp > 0 | (cmp == 0 & upperBoundType == OPEN); } boolean contains(@Nullable T t) { @@ -173,33 +173,33 @@ final class GeneralRange<T> implements Serializable { boolean hasLowBound = this.hasLowerBound; @Nullable - T lowEnd = getLowerEndpoint(); - BoundType lowType = getLowerBoundType(); + T lowEnd = lowerEndpoint; + BoundType lowType = lowerBoundType; if (!hasLowerBound()) { hasLowBound = other.hasLowerBound; - lowEnd = other.getLowerEndpoint(); - lowType = other.getLowerBoundType(); + lowEnd = other.lowerEndpoint; + lowType = other.lowerBoundType; } else if (other.hasLowerBound()) { - int cmp = comparator.compare(getLowerEndpoint(), other.getLowerEndpoint()); - if (cmp < 0 || (cmp == 0 && other.getLowerBoundType() == OPEN)) { - lowEnd = other.getLowerEndpoint(); - lowType = other.getLowerBoundType(); + int cmp = comparator.compare(lowerEndpoint, other.lowerEndpoint); + if (cmp < 0 || (cmp == 0 && other.lowerBoundType == OPEN)) { + lowEnd = other.lowerEndpoint; + lowType = other.lowerBoundType; } } boolean hasUpBound = this.hasUpperBound; @Nullable - T upEnd = getUpperEndpoint(); - BoundType upType = getUpperBoundType(); + T upEnd = upperEndpoint; + BoundType upType = upperBoundType; if (!hasUpperBound()) { hasUpBound = other.hasUpperBound; - upEnd = other.getUpperEndpoint(); - upType = other.getUpperBoundType(); + upEnd = other.upperEndpoint; + upType = other.upperBoundType; } else if (other.hasUpperBound()) { - int cmp = comparator.compare(getUpperEndpoint(), other.getUpperEndpoint()); - if (cmp > 0 || (cmp == 0 && other.getUpperBoundType() == OPEN)) { - upEnd = other.getUpperEndpoint(); - upType = other.getUpperBoundType(); + int cmp = comparator.compare(upperEndpoint, other.upperEndpoint); + if (cmp > 0 || (cmp == 0 && other.upperBoundType == OPEN)) { + upEnd = other.upperEndpoint; + upType = other.upperBoundType; } } @@ -221,18 +221,18 @@ final class GeneralRange<T> implements Serializable { if (obj instanceof GeneralRange) { GeneralRange<?> r = (GeneralRange<?>) obj; return comparator.equals(r.comparator) && hasLowerBound == r.hasLowerBound - && hasUpperBound == r.hasUpperBound && getLowerBoundType().equals(r.getLowerBoundType()) - && getUpperBoundType().equals(r.getUpperBoundType()) - && Objects.equal(getLowerEndpoint(), r.getLowerEndpoint()) - && Objects.equal(getUpperEndpoint(), r.getUpperEndpoint()); + && hasUpperBound == r.hasUpperBound && lowerBoundType.equals(r.lowerBoundType) + && upperBoundType.equals(r.upperBoundType) + && Objects.equal(lowerEndpoint, r.lowerEndpoint) + && Objects.equal(upperEndpoint, r.upperEndpoint); } return false; } @Override public int hashCode() { - return Objects.hashCode(comparator, getLowerEndpoint(), getLowerBoundType(), getUpperEndpoint(), - getUpperBoundType()); + return Objects.hashCode(comparator, lowerEndpoint, lowerBoundType, upperEndpoint, + upperBoundType); } private transient GeneralRange<T> reverse; @@ -240,12 +240,12 @@ final class GeneralRange<T> implements Serializable { /** * Returns the same range relative to the reversed comparator. */ - GeneralRange<T> reverse() { + public GeneralRange<T> reverse() { GeneralRange<T> result = reverse; if (result == null) { - result = new GeneralRange<T>( - Ordering.from(comparator).reverse(), hasUpperBound, getUpperEndpoint(), - getUpperBoundType(), hasLowerBound, getLowerEndpoint(), getLowerBoundType()); + result = + new GeneralRange<T>(Ordering.from(comparator).reverse(), hasUpperBound, upperEndpoint, + upperBoundType, hasLowerBound, lowerEndpoint, lowerBoundType); result.reverse = this; return this.reverse = result; } @@ -254,30 +254,35 @@ final class GeneralRange<T> implements Serializable { @Override public String toString() { - return new StringBuilder() - .append(comparator) - .append(":") - .append(lowerBoundType == CLOSED ? '[' : '(') - .append(hasLowerBound ? lowerEndpoint : "-\u221e") - .append(',') - .append(hasUpperBound ? upperEndpoint : "\u221e") - .append(upperBoundType == CLOSED ? ']' : ')') - .toString(); - } - - T getLowerEndpoint() { - return lowerEndpoint; - } - - BoundType getLowerBoundType() { - return lowerBoundType; - } - - T getUpperEndpoint() { - return upperEndpoint; - } - - BoundType getUpperBoundType() { - return upperBoundType; + StringBuilder builder = new StringBuilder(); + builder.append(comparator).append(":"); + switch (lowerBoundType) { + case CLOSED: + builder.append('['); + break; + case OPEN: + builder.append('('); + break; + } + if (hasLowerBound()) { + builder.append(lowerEndpoint); + } else { + builder.append("-\u221e"); + } + builder.append(','); + if (hasUpperBound()) { + builder.append(upperEndpoint); + } else { + builder.append("\u221e"); + } + switch (upperBoundType) { + case CLOSED: + builder.append(']'); + break; + case OPEN: + builder.append(')'); + break; + } + return builder.toString(); } } diff --git a/guava/src/com/google/common/collect/GenericMapMaker.java b/guava/src/com/google/common/collect/GenericMapMaker.java index 0b0a290..8c5bbeb 100644 --- a/guava/src/com/google/common/collect/GenericMapMaker.java +++ b/guava/src/com/google/common/collect/GenericMapMaker.java @@ -62,6 +62,12 @@ public abstract class GenericMapMaker<K0, V0> { abstract GenericMapMaker<K0, V0> keyEquivalence(Equivalence<Object> equivalence); /** + * See {@link MapMaker#valueEquivalence}. + */ + @GwtIncompatible("To be supported") + abstract GenericMapMaker<K0, V0> valueEquivalence(Equivalence<Object> equivalence); + + /** * See {@link MapMaker#initialCapacity}. */ public abstract GenericMapMaker<K0, V0> initialCapacity(int initialCapacity); @@ -72,6 +78,11 @@ public abstract class GenericMapMaker<K0, V0> { abstract GenericMapMaker<K0, V0> maximumSize(int maximumSize); /** + * See {@link MapMaker#strongKeys}. + */ + abstract GenericMapMaker<K0, V0> strongKeys(); + + /** * See {@link MapMaker#concurrencyLevel}. */ public abstract GenericMapMaker<K0, V0> concurrencyLevel(int concurrencyLevel); @@ -83,6 +94,18 @@ public abstract class GenericMapMaker<K0, V0> { public abstract GenericMapMaker<K0, V0> weakKeys(); /** + * See {@link MapMaker#strongValues}. + */ + abstract GenericMapMaker<K0, V0> strongValues(); + + /** + * See {@link MapMaker#softKeys}. + */ + @Deprecated + @GwtIncompatible("java.lang.ref.SoftReference") + public abstract GenericMapMaker<K0, V0> softKeys(); + + /** * See {@link MapMaker#weakValues}. */ @GwtIncompatible("java.lang.ref.WeakReference") @@ -95,6 +118,13 @@ public abstract class GenericMapMaker<K0, V0> { public abstract GenericMapMaker<K0, V0> softValues(); /** + * 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/src/com/google/common/collect/HashBasedTable.java b/guava/src/com/google/common/collect/HashBasedTable.java index 4944174..8c92ec1 100644 --- a/guava/src/com/google/common/collect/HashBasedTable.java +++ b/guava/src/com/google/common/collect/HashBasedTable.java @@ -18,6 +18,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Supplier; @@ -44,15 +45,12 @@ import javax.annotation.Nullable; * <p>Note that this implementation is not synchronized. If multiple threads * access this table concurrently and one of the threads modifies the table, it * must be synchronized externally. - * - * <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 7.0 */ @GwtCompatible(serializable = true) +@Beta public class HashBasedTable<R, C, V> extends StandardTable<R, C, V> { private static class Factory<C, V> implements Supplier<Map<C, V>>, Serializable { diff --git a/guava/src/com/google/common/collect/HashBiMap.java b/guava/src/com/google/common/collect/HashBiMap.java index 0620f45..26d11e1 100644 --- a/guava/src/com/google/common/collect/HashBiMap.java +++ b/guava/src/com/google/common/collect/HashBiMap.java @@ -1,657 +1,97 @@ /* * 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 + * 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. + * 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.checkState; - import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Objects; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractMap; -import java.util.Arrays; -import java.util.ConcurrentModificationException; -import java.util.Iterator; +import java.util.HashMap; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; import javax.annotation.Nullable; /** - * A {@link BiMap} backed by two hash tables. This implementation allows null keys and values. A - * {@code HashBiMap} 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>. + * 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 Louis Wasserman * @author Mike Bostock * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(emulated = true) -public final class HashBiMap<K, V> extends AbstractMap<K, V> implements BiMap<K, V>, Serializable { +public final class HashBiMap<K, V> extends AbstractBiMap<K, V> { /** - * Returns a new, empty {@code HashBiMap} with the default initial capacity (16). + * Returns a new, empty {@code HashBiMap} with the default initial capacity + * (16). */ public static <K, V> HashBiMap<K, V> create() { - return create(16); + return new HashBiMap<K, V>(); } /** * Constructs a new, empty bimap with the specified expected size. * * @param expectedSize the expected number of entries - * @throws IllegalArgumentException if the specified expected size is negative + * @throws IllegalArgumentException if the specified expected size is + * negative */ public static <K, V> HashBiMap<K, V> create(int expectedSize) { return new HashBiMap<K, V>(expectedSize); } /** - * Constructs a new bimap containing initial values from {@code map}. The bimap is created with an - * initial capacity sufficient to hold the mappings in the specified map. + * Constructs a new bimap containing initial values from {@code map}. The + * bimap is created with an initial capacity sufficient to hold the mappings + * in the specified map. */ - public static <K, V> HashBiMap<K, V> create(Map<? extends K, ? extends V> map) { + public static <K, V> HashBiMap<K, V> create( + Map<? extends K, ? extends V> map) { HashBiMap<K, V> bimap = create(map.size()); bimap.putAll(map); return bimap; } - private static final class BiEntry<K, V> { - final K key; - final int keyHash; - - final V value; - final int valueHash; - - @Nullable - BiEntry<K, V> nextInKToVBucket; - - @Nullable - BiEntry<K, V> nextInVToKBucket; - - BiEntry(K key, int keyHash, V value, int valueHash) { - this.key = key; - this.keyHash = keyHash; - this.value = value; - this.valueHash = valueHash; - } + private HashBiMap() { + super(new HashMap<K, V>(), new HashMap<V, K>()); } - private static final double LOAD_FACTOR = 1.0; - - private transient BiEntry<K, V>[] hashTableKToV; - private transient BiEntry<K, V>[] hashTableVToK; - private transient int size; - private transient int mask; - private transient int modCount; - private HashBiMap(int expectedSize) { - init(expectedSize); - } - - private void init(int expectedSize) { - checkArgument(expectedSize >= 0, "expectedSize must be >= 0 but was %s", expectedSize); - int tableSize = Hashing.closedTableSize(expectedSize, LOAD_FACTOR); - this.hashTableKToV = createTable(tableSize); - this.hashTableVToK = createTable(tableSize); - this.mask = tableSize - 1; - this.modCount = 0; - this.size = 0; - } - - /** - * Finds and removes {@code entry} from the bucket linked lists in both the - * key-to-value direction and the value-to-key direction. - */ - private void delete(BiEntry<K, V> entry) { - int keyBucket = entry.keyHash & mask; - BiEntry<K, V> prevBucketEntry = null; - for (BiEntry<K, V> bucketEntry = hashTableKToV[keyBucket]; true; - bucketEntry = bucketEntry.nextInKToVBucket) { - if (bucketEntry == entry) { - if (prevBucketEntry == null) { - hashTableKToV[keyBucket] = entry.nextInKToVBucket; - } else { - prevBucketEntry.nextInKToVBucket = entry.nextInKToVBucket; - } - break; - } - prevBucketEntry = bucketEntry; - } - - int valueBucket = entry.valueHash & mask; - prevBucketEntry = null; - for (BiEntry<K, V> bucketEntry = hashTableVToK[valueBucket];; - bucketEntry = bucketEntry.nextInVToKBucket) { - if (bucketEntry == entry) { - if (prevBucketEntry == null) { - hashTableVToK[valueBucket] = entry.nextInVToKBucket; - } else { - prevBucketEntry.nextInVToKBucket = entry.nextInVToKBucket; - } - break; - } - prevBucketEntry = bucketEntry; - } - - size--; - modCount++; - } - - private void insert(BiEntry<K, V> entry) { - int keyBucket = entry.keyHash & mask; - entry.nextInKToVBucket = hashTableKToV[keyBucket]; - hashTableKToV[keyBucket] = entry; - - int valueBucket = entry.valueHash & mask; - entry.nextInVToKBucket = hashTableVToK[valueBucket]; - hashTableVToK[valueBucket] = entry; - - size++; - modCount++; - } - - private static int hash(@Nullable Object o) { - return Hashing.smear((o == null) ? 0 : o.hashCode()); + super( + Maps.<K, V>newHashMapWithExpectedSize(expectedSize), + Maps.<V, K>newHashMapWithExpectedSize(expectedSize)); } - private BiEntry<K, V> seekByKey(@Nullable Object key, int keyHash) { - for (BiEntry<K, V> entry = hashTableKToV[keyHash & mask]; entry != null; - entry = entry.nextInKToVBucket) { - if (keyHash == entry.keyHash && Objects.equal(key, entry.key)) { - return entry; - } - } - return null; - } - - private BiEntry<K, V> seekByValue(@Nullable Object value, int valueHash) { - for (BiEntry<K, V> entry = hashTableVToK[valueHash & mask]; entry != null; - entry = entry.nextInVToKBucket) { - if (valueHash == entry.valueHash && Objects.equal(value, entry.value)) { - return entry; - } - } - return null; - } - - @Override - public boolean containsKey(@Nullable Object key) { - return seekByKey(key, hash(key)) != null; - } - - @Override - public boolean containsValue(@Nullable Object value) { - return seekByValue(value, hash(value)) != null; - } + // Override these two methods to show that keys and values may be null - @Nullable - @Override - public V get(@Nullable Object key) { - BiEntry<K, V> entry = seekByKey(key, hash(key)); - return (entry == null) ? null : entry.value; + @Override public V put(@Nullable K key, @Nullable V value) { + return super.put(key, value); } - @Override - public V put(@Nullable K key, @Nullable V value) { - return put(key, value, false); - } - - @Override - public V forcePut(@Nullable K key, @Nullable V value) { - return put(key, value, true); - } - - private V put(@Nullable K key, @Nullable V value, boolean force) { - int keyHash = hash(key); - int valueHash = hash(value); - - BiEntry<K, V> oldEntryForKey = seekByKey(key, keyHash); - if (oldEntryForKey != null && valueHash == oldEntryForKey.valueHash - && Objects.equal(value, oldEntryForKey.value)) { - return value; - } - - BiEntry<K, V> oldEntryForValue = seekByValue(value, valueHash); - if (oldEntryForValue != null) { - if (force) { - delete(oldEntryForValue); - } else { - throw new IllegalArgumentException("value already present: " + value); - } - } - - if (oldEntryForKey != null) { - delete(oldEntryForKey); - } - BiEntry<K, V> newEntry = new BiEntry<K, V>(key, keyHash, value, valueHash); - insert(newEntry); - rehashIfNecessary(); - return (oldEntryForKey == null) ? null : oldEntryForKey.value; - } - - @Nullable - private K putInverse(@Nullable V value, @Nullable K key, boolean force) { - int valueHash = hash(value); - int keyHash = hash(key); - - BiEntry<K, V> oldEntryForValue = seekByValue(value, valueHash); - if (oldEntryForValue != null && keyHash == oldEntryForValue.keyHash - && Objects.equal(key, oldEntryForValue.key)) { - return key; - } - - BiEntry<K, V> oldEntryForKey = seekByKey(key, keyHash); - if (oldEntryForKey != null) { - if (force) { - delete(oldEntryForKey); - } else { - throw new IllegalArgumentException("value already present: " + key); - } - } - - if (oldEntryForValue != null) { - delete(oldEntryForValue); - } - BiEntry<K, V> newEntry = new BiEntry<K, V>(key, keyHash, value, valueHash); - insert(newEntry); - rehashIfNecessary(); - return (oldEntryForValue == null) ? null : oldEntryForValue.key; - } - - private void rehashIfNecessary() { - BiEntry<K, V>[] oldKToV = hashTableKToV; - if (Hashing.needsResizing(size, oldKToV.length, LOAD_FACTOR)) { - int newTableSize = oldKToV.length * 2; - - this.hashTableKToV = createTable(newTableSize); - this.hashTableVToK = createTable(newTableSize); - this.mask = newTableSize - 1; - this.size = 0; - - for (int bucket = 0; bucket < oldKToV.length; bucket++) { - BiEntry<K, V> entry = oldKToV[bucket]; - while (entry != null) { - BiEntry<K, V> nextEntry = entry.nextInKToVBucket; - insert(entry); - entry = nextEntry; - } - } - this.modCount++; - } - } - - @SuppressWarnings("unchecked") - private BiEntry<K, V>[] createTable(int length) { - return new BiEntry[length]; - } - - @Override - public V remove(@Nullable Object key) { - BiEntry<K, V> entry = seekByKey(key, hash(key)); - if (entry == null) { - return null; - } else { - delete(entry); - return entry.value; - } - } - - @Override - public void clear() { - size = 0; - Arrays.fill(hashTableKToV, null); - Arrays.fill(hashTableVToK, null); - modCount++; - } - - @Override - public int size() { - return size; - } - - abstract class Itr<T> implements Iterator<T> { - int nextBucket = 0; - BiEntry<K, V> next = null; - BiEntry<K, V> toRemove = null; - int expectedModCount = modCount; - - private void checkForConcurrentModification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } - - @Override - public boolean hasNext() { - checkForConcurrentModification(); - if (next != null) { - return true; - } - while (nextBucket < hashTableKToV.length) { - if (hashTableKToV[nextBucket] != null) { - next = hashTableKToV[nextBucket++]; - return true; - } - nextBucket++; - } - return false; - } - - @Override - public T next() { - checkForConcurrentModification(); - if (!hasNext()) { - throw new NoSuchElementException(); - } - - BiEntry<K, V> entry = next; - next = entry.nextInKToVBucket; - toRemove = entry; - return output(entry); - } - - @Override - public void remove() { - checkForConcurrentModification(); - checkState(toRemove != null, "Only one remove() call allowed per call to next"); - delete(toRemove); - expectedModCount = modCount; - toRemove = null; - } - - abstract T output(BiEntry<K, V> entry); - } - - @Override - public Set<K> keySet() { - return new KeySet(); - } - - private final class KeySet extends Maps.KeySet<K, V> { - @Override - Map<K, V> map() { - return HashBiMap.this; - } - - @Override - public Iterator<K> iterator() { - return new Itr<K>() { - @Override - K output(BiEntry<K, V> entry) { - return entry.key; - } - }; - } - - @Override - public boolean remove(@Nullable Object o) { - BiEntry<K, V> entry = seekByKey(o, hash(o)); - if (entry == null) { - return false; - } else { - delete(entry); - return true; - } - } - } - - @Override - public Set<V> values() { - return inverse().keySet(); - } - - @Override - public Set<Entry<K, V>> entrySet() { - return new EntrySet(); - } - - private final class EntrySet extends Maps.EntrySet<K, V> { - @Override - Map<K, V> map() { - return HashBiMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return new Itr<Entry<K, V>>() { - @Override - Entry<K, V> output(BiEntry<K, V> entry) { - return new MapEntry(entry); - } - - class MapEntry extends AbstractMapEntry<K, V> { - BiEntry<K, V> delegate; - - MapEntry(BiEntry<K, V> entry) { - this.delegate = entry; - } - - @Override public K getKey() { - return delegate.key; - } - - @Override public V getValue() { - return delegate.value; - } - - @Override public V setValue(V value) { - V oldValue = delegate.value; - int valueHash = hash(value); - if (valueHash == delegate.valueHash && Objects.equal(value, oldValue)) { - return value; - } - checkArgument( - seekByValue(value, valueHash) == null, "value already present: %s", value); - delete(delegate); - BiEntry<K, V> newEntry = - new BiEntry<K, V>(delegate.key, delegate.keyHash, value, valueHash); - insert(newEntry); - expectedModCount = modCount; - if (toRemove == delegate) { - toRemove = newEntry; - } - delegate = newEntry; - return oldValue; - } - } - }; - } - } - - private transient BiMap<V, K> inverse; - - @Override - public BiMap<V, K> inverse() { - return (inverse == null) ? inverse = new Inverse() : inverse; - } - - private final class Inverse extends AbstractMap<V, K> implements BiMap<V, K>, Serializable { - BiMap<K, V> forward() { - return HashBiMap.this; - } - - @Override - public int size() { - return size; - } - - @Override - public void clear() { - forward().clear(); - } - - @Override - public boolean containsKey(@Nullable Object value) { - return forward().containsValue(value); - } - - @Override - public K get(@Nullable Object value) { - BiEntry<K, V> entry = seekByValue(value, hash(value)); - return (entry == null) ? null : entry.key; - } - - @Override - public K put(@Nullable V value, @Nullable K key) { - return putInverse(value, key, false); - } - - @Override - public K forcePut(@Nullable V value, @Nullable K key) { - return putInverse(value, key, true); - } - - @Override - public K remove(@Nullable Object value) { - BiEntry<K, V> entry = seekByValue(value, hash(value)); - if (entry == null) { - return null; - } else { - delete(entry); - return entry.key; - } - } - - @Override - public BiMap<K, V> inverse() { - return forward(); - } - - @Override - public Set<V> keySet() { - return new InverseKeySet(); - } - - private final class InverseKeySet extends Maps.KeySet<V, K> { - @Override - Map<V, K> map() { - return Inverse.this; - } - - @Override - public boolean remove(@Nullable Object o) { - BiEntry<K, V> entry = seekByValue(o, hash(o)); - if (entry == null) { - return false; - } else { - delete(entry); - return true; - } - } - - @Override - public Iterator<V> iterator() { - return new Itr<V>() { - @Override V output(BiEntry<K, V> entry) { - return entry.value; - } - }; - } - } - - @Override - public Set<K> values() { - return forward().keySet(); - } - - @Override - public Set<Entry<V, K>> entrySet() { - return new Maps.EntrySet<V, K>() { - - @Override - Map<V, K> map() { - return Inverse.this; - } - - @Override - public Iterator<Entry<V, K>> iterator() { - return new Itr<Entry<V, K>>() { - @Override - Entry<V, K> output(BiEntry<K, V> entry) { - return new InverseEntry(entry); - } - - class InverseEntry extends AbstractMapEntry<V, K> { - BiEntry<K, V> delegate; - - InverseEntry(BiEntry<K, V> entry) { - this.delegate = entry; - } - - @Override - public V getKey() { - return delegate.value; - } - - @Override - public K getValue() { - return delegate.key; - } - - @Override - public K setValue(K key) { - K oldKey = delegate.key; - int keyHash = hash(key); - if (keyHash == delegate.keyHash && Objects.equal(key, oldKey)) { - return key; - } - checkArgument(seekByKey(key, keyHash) == null, "value already present: %s", key); - delete(delegate); - BiEntry<K, V> newEntry = - new BiEntry<K, V>(key, keyHash, delegate.value, delegate.valueHash); - insert(newEntry); - expectedModCount = modCount; - // This is safe because entries can only get bumped up to earlier in the iteration, - // so they can't get revisited. - return oldKey; - } - } - }; - } - }; - } - - Object writeReplace() { - return new InverseSerializedForm<K, V>(HashBiMap.this); - } - } - - private static final class InverseSerializedForm<K, V> implements Serializable { - private final HashBiMap<K, V> bimap; - - InverseSerializedForm(HashBiMap<K, V> bimap) { - this.bimap = bimap; - } - - Object readResolve() { - return bimap.inverse(); - } + @Override public V forcePut(@Nullable K key, @Nullable V value) { + return super.forcePut(key, value); } /** - * @serialData the number of entries, first key, first value, second key, second value, and so on. + * @serialData the number of entries, first key, first value, second key, + * second value, and so on. */ @GwtIncompatible("java.io.ObjectOutputStream") private void writeObject(ObjectOutputStream stream) throws IOException { @@ -660,10 +100,12 @@ public final class HashBiMap<K, V> extends AbstractMap<K, V> implements BiMap<K, } @GwtIncompatible("java.io.ObjectInputStream") - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { stream.defaultReadObject(); int size = Serialization.readCount(stream); - init(size); + setDelegates(Maps.<K, V>newHashMapWithExpectedSize(size), + Maps.<V, K>newHashMapWithExpectedSize(size)); Serialization.populateMap(this, stream, size); } diff --git a/guava/src/com/google/common/collect/HashMultimap.java b/guava/src/com/google/common/collect/HashMultimap.java index bab2a05..d347ff1 100644 --- a/guava/src/com/google/common/collect/HashMultimap.java +++ b/guava/src/com/google/common/collect/HashMultimap.java @@ -48,7 +48,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/src/com/google/common/collect/Hashing.java b/guava/src/com/google/common/collect/Hashing.java index b13eb7c..9c5f6bc 100644 --- a/guava/src/com/google/common/collect/Hashing.java +++ b/guava/src/com/google/common/collect/Hashing.java @@ -17,50 +17,27 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.primitives.Ints; /** * Static methods for implementing hash-based collections. * * @author Kevin Bourrillion * @author Jesse Wilson - * @author Austin Appleby */ @GwtCompatible final class Hashing { private Hashing() {} - private static final int C1 = 0xcc9e2d51; - private static final int C2 = 0x1b873593; - /* - * This method was rewritten in Java from an intermediate step of the Murmur hash function in - * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the - * following header: - * - * MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author - * hereby disclaims copyright to this source code. + * This method was 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/licenses/publicdomain + * + * As of 2010/06/11, this method is identical to the (package private) hash + * method in OpenJDK 7's java.util.HashMap class. */ static int smear(int hashCode) { - return C2 * Integer.rotateLeft(hashCode * C1, 15); - } - - static int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; - - static int closedTableSize(int expectedEntries, double loadFactor) { - // Get the recommended table size. - // Round down to the nearest power of 2. - expectedEntries = Math.max(expectedEntries, 2); - int tableSize = Integer.highestOneBit(expectedEntries); - // Check to make sure that we will not exceed the maximum load factor. - if ((double) expectedEntries / tableSize > loadFactor) { - tableSize <<= 1; - return (tableSize > 0) ? tableSize : MAX_TABLE_SIZE; - } - return tableSize; - } - - static boolean needsResizing(int size, int tableSize, double loadFactor) { - return size > loadFactor * tableSize && tableSize < MAX_TABLE_SIZE; + hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12); + return hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4); } } diff --git a/guava/src/com/google/common/collect/ImmutableAsList.java b/guava/src/com/google/common/collect/ImmutableAsList.java index 249abee..9eb87de 100644 --- a/guava/src/com/google/common/collect/ImmutableAsList.java +++ b/guava/src/com/google/common/collect/ImmutableAsList.java @@ -17,49 +17,36 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; /** - * List returned by {@link ImmutableCollection#asList} that delegates {@code contains} checks - * to the backing collection. + * List returned by {@link ImmutableCollection#asList} when the collection isn't + * an {@link ImmutableList} or an {@link ImmutableSortedSet}. * * @author Jared Levy - * @author Louis Wasserman */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") -abstract class ImmutableAsList<E> extends ImmutableList<E> { - abstract ImmutableCollection<E> delegateCollection(); +final class ImmutableAsList<E> extends RegularImmutableList<E> { + private final transient ImmutableCollection<E> collection; - @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(); + ImmutableAsList(Object[] array, ImmutableCollection<E> collection) { + super(array, 0, array.length); + this.collection = collection; } - @Override - public boolean isEmpty() { - return delegateCollection().isEmpty(); - } - - @Override - boolean isPartialView() { - return delegateCollection().isPartialView(); + @Override public boolean contains(Object target) { + // The collection's contains() is at least as fast as RegularImmutableList's + // and is often faster. + return collection.contains(target); } /** * Serialized form that leads to the same performance as the original list. */ - @GwtIncompatible("serialization") static class SerializedForm implements Serializable { final ImmutableCollection<?> collection; SerializedForm(ImmutableCollection<?> collection) { @@ -71,14 +58,12 @@ abstract class ImmutableAsList<E> extends ImmutableList<E> { private static final long serialVersionUID = 0; } - @GwtIncompatible("serialization") private void readObject(ObjectInputStream stream) throws InvalidObjectException { throw new InvalidObjectException("Use SerializedForm"); } - @GwtIncompatible("serialization") @Override Object writeReplace() { - return new SerializedForm(delegateCollection()); + return new SerializedForm(collection); } } diff --git a/guava/src/com/google/common/collect/ImmutableBiMap.java b/guava/src/com/google/common/collect/ImmutableBiMap.java index d7968b2..9d8e144 100644 --- a/guava/src/com/google/common/collect/ImmutableBiMap.java +++ b/guava/src/com/google/common/collect/ImmutableBiMap.java @@ -16,13 +16,12 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.GwtCompatible; -import java.util.Collection; import java.util.Map; +import javax.annotation.Nullable; + /** * An immutable {@link BiMap} with reliable user-specified iteration order. Does * not permit null keys or values. An {@code ImmutableBiMap} and its inverse @@ -44,22 +43,23 @@ import java.util.Map; 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(); + /** * Returns the empty bimap. */ // 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; } /** * Returns an immutable bimap containing a single entry. */ public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1) { - checkNotNull(k1, "null key in entry: null=%s", v1); - checkNotNull(v1, "null value in entry: %s=null", k1); - return new SingletonImmutableBiMap<K, V>(k1, v1); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of(k1, v1)); } /** @@ -68,10 +68,7 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> * @throws IllegalArgumentException if duplicate keys or values are added */ public static <K, V> ImmutableBiMap<K, V> of(K k1, V v1, K k2, V v2) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of(k1, v1, k2, v2)); } /** @@ -81,11 +78,8 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> */ public static <K, V> ImmutableBiMap<K, V> of( K k1, V v1, K k2, V v2, K k3, V v3) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .put(k3, v3) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of( + k1, v1, k2, v2, k3, v3)); } /** @@ -95,12 +89,8 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> */ public static <K, V> ImmutableBiMap<K, V> of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .put(k3, v3) - .put(k4, v4) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of( + k1, v1, k2, v2, k3, v3, k4, v4)); } /** @@ -110,13 +100,8 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> */ public static <K, V> ImmutableBiMap<K, V> of( K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - return new Builder<K, V>() - .put(k1, v1) - .put(k2, v2) - .put(k3, v3) - .put(k4, v4) - .put(k5, v5) - .build(); + return new RegularImmutableBiMap<K, V>(ImmutableMap.of( + k1, v1, k2, v2, k3, v3, k4, v4, k5, v5)); } // looking for of() with > 5 entries? Use the builder instead. @@ -184,7 +169,11 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> * @throws IllegalArgumentException if duplicate keys or values were added */ @Override public ImmutableBiMap<K, V> build() { - return fromEntries(entries); + ImmutableMap<K, V> map = super.build(); + if (map.isEmpty()) { + return of(); + } + return new RegularImmutableBiMap<K, V>(map); } } @@ -213,25 +202,18 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> } } - return fromEntries(ImmutableList.copyOf(map.entrySet())); - } - - static <K, V> ImmutableBiMap<K, V> fromEntries( - Collection<? extends Entry<? extends K, ? extends V>> entries) { - switch (entries.size()) { - case 0: - return of(); - case 1: { - Entry<? extends K, ? extends V> entry = Iterables.getOnlyElement(entries); - return new SingletonImmutableBiMap<K, V>(entry.getKey(), entry.getValue()); - } - default: - return new RegularImmutableBiMap<K, V>(entries); + if (map.isEmpty()) { + return of(); } + + ImmutableMap<K, V> immutableMap = ImmutableMap.copyOf(map); + return new RegularImmutableBiMap<K, V>(immutableMap); } ImmutableBiMap() {} + abstract ImmutableMap<K, V> delegate(); + /** * {@inheritDoc} * @@ -241,6 +223,26 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> @Override public abstract ImmutableBiMap<V, K> inverse(); + @Override public boolean containsKey(@Nullable Object key) { + return delegate().containsKey(key); + } + + @Override public boolean containsValue(@Nullable Object value) { + return inverse().containsKey(value); + } + + @Override public ImmutableSet<Entry<K, V>> entrySet() { + return delegate().entrySet(); + } + + @Override public V get(@Nullable Object key) { + return delegate().get(key); + } + + @Override public ImmutableSet<K> keySet() { + return delegate().keySet(); + } + /** * Returns an immutable set of the values in this map. The values are in the * same order as the parameters used to build this map. @@ -253,14 +255,50 @@ public abstract class ImmutableBiMap<K, V> extends ImmutableMap<K, V> * Guaranteed to throw an exception and leave the bimap unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public V forcePut(K key, V value) { throw new UnsupportedOperationException(); } + @Override public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override public boolean equals(@Nullable Object object) { + return object == this || delegate().equals(object); + } + + @Override public int hashCode() { + return delegate().hashCode(); + } + + @Override public String toString() { + return delegate().toString(); + } + + /** Bimap with no mappings. */ + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + static class EmptyBiMap extends ImmutableBiMap<Object, Object> { + @Override ImmutableMap<Object, Object> delegate() { + return ImmutableMap.of(); + } + @Override public ImmutableBiMap<Object, Object> inverse() { + return this; + } + @Override boolean isPartialView() { + return false; + } + Object readResolve() { + return EMPTY_IMMUTABLE_BIMAP; // preserve singleton property + } + } + /** * Serialized type for all ImmutableBiMap instances. It captures the logical * contents and they are reconstructed using public factory methods. This diff --git a/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java b/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java index 50f93c3..1c596e2 100644 --- a/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java +++ b/guava/src/com/google/common/collect/ImmutableClassToInstanceMap.java @@ -16,14 +16,10 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.primitives.Primitives; import java.util.Map; -import javax.annotation.Nullable; - /** * A class-to-instance map backed by an {@link ImmutableMap}. See also {@link * MutableClassToInstanceMap}. @@ -142,18 +138,15 @@ public final class ImmutableClassToInstanceMap<B> extends @Override @SuppressWarnings("unchecked") // value could not get in if not a T - @Nullable public <T extends B> T getInstance(Class<T> type) { - return (T) delegate.get(checkNotNull(type)); + return (T) delegate.get(type); } /** * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public <T extends B> T putInstance(Class<T> type, T value) { throw new UnsupportedOperationException(); diff --git a/guava/src/com/google/common/collect/ImmutableCollection.java b/guava/src/com/google/common/collect/ImmutableCollection.java index 2aeca97..5fca2aa 100644 --- a/guava/src/com/google/common/collect/ImmutableCollection.java +++ b/guava/src/com/google/common/collect/ImmutableCollection.java @@ -17,7 +17,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.VisibleForTesting; import java.io.Serializable; import java.util.Collection; @@ -86,9 +85,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean add(E e) { throw new UnsupportedOperationException(); @@ -98,9 +95,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean remove(Object object) { throw new UnsupportedOperationException(); @@ -110,9 +105,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean addAll(Collection<? extends E> newElements) { throw new UnsupportedOperationException(); @@ -122,9 +115,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean removeAll(Collection<?> oldElements) { throw new UnsupportedOperationException(); @@ -134,9 +125,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean retainAll(Collection<?> elementsToKeep) { throw new UnsupportedOperationException(); @@ -146,9 +135,7 @@ public abstract class ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void clear() { throw new UnsupportedOperationException(); @@ -177,7 +164,7 @@ public abstract class ImmutableCollection<E> case 1: return ImmutableList.of(iterator().next()); default: - return new RegularImmutableAsList<E>(this, toArray()); + return new ImmutableAsList<E>(toArray(), this); } } @@ -199,7 +186,7 @@ public abstract class ImmutableCollection<E> } @Override public UnmodifiableIterator<Object> iterator() { - return Iterators.EMPTY_LIST_ITERATOR; + return Iterators.EMPTY_ITERATOR; } private static final Object[] EMPTY_ARRAY = new Object[0]; @@ -285,24 +272,6 @@ public abstract class ImmutableCollection<E> * @since 10.0 */ public abstract static class Builder<E> { - static final int DEFAULT_INITIAL_CAPACITY = 4; - - @VisibleForTesting - static int expandedCapacity(int oldCapacity, int minCapacity) { - if (minCapacity < 0) { - throw new AssertionError("cannot store more than MAX_VALUE elements"); - } - // careful of overflow! - int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; - if (newCapacity < minCapacity) { - newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; - } - if (newCapacity < 0) { - newCapacity = Integer.MAX_VALUE; - // guaranteed to be >= newCapacity - } - return newCapacity; - } Builder() { } diff --git a/guava/src/com/google/common/collect/ImmutableEnumMap.java b/guava/src/com/google/common/collect/ImmutableEnumMap.java deleted file mode 100644 index 6738685..0000000 --- a/guava/src/com/google/common/collect/ImmutableEnumMap.java +++ /dev/null @@ -1,151 +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 static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.EnumMap; -import java.util.Iterator; - -import javax.annotation.Nullable; - -/** - * Implementation of {@link ImmutableMap} backed by a non-empty {@link - * java.util.EnumMap}. - * - * @author Louis Wasserman - */ -@GwtCompatible(serializable = true, emulated = true) -@SuppressWarnings("serial") // we're overriding default serialization -final class ImmutableEnumMap<K extends Enum<K>, V> extends ImmutableMap<K, V> { - static <K extends Enum<K>, V> ImmutableMap<K, V> asImmutable(EnumMap<K, V> map) { - switch (map.size()) { - case 0: - return ImmutableMap.of(); - case 1: { - Entry<K, V> entry = Iterables.getOnlyElement(map.entrySet()); - return ImmutableMap.of(entry.getKey(), entry.getValue()); - } - default: - return new ImmutableEnumMap<K, V>(map); - } - } - - private transient final EnumMap<K, V> delegate; - - private ImmutableEnumMap(EnumMap<K, V> delegate) { - this.delegate = delegate; - checkArgument(!delegate.isEmpty()); - } - - @Override - ImmutableSet<K> createKeySet() { - return new ImmutableSet<K>() { - - @Override - public boolean contains(Object object) { - return delegate.containsKey(object); - } - - @Override - public int size() { - return ImmutableEnumMap.this.size(); - } - - @Override - public UnmodifiableIterator<K> iterator() { - return Iterators.unmodifiableIterator(delegate.keySet().iterator()); - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - - @Override - public int size() { - return delegate.size(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - return delegate.containsKey(key); - } - - @Override - public V get(Object key) { - return delegate.get(key); - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new ImmutableMapEntrySet<K, V>() { - - @Override - ImmutableMap<K, V> map() { - return ImmutableEnumMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return new UnmodifiableIterator<Entry<K, V>>() { - private final Iterator<Entry<K, V>> backingIterator = delegate.entrySet().iterator(); - - @Override - public boolean hasNext() { - return backingIterator.hasNext(); - } - - @Override - public Entry<K, V> next() { - Entry<K, V> entry = backingIterator.next(); - return Maps.immutableEntry(entry.getKey(), entry.getValue()); - } - }; - } - }; - } - - @Override - boolean isPartialView() { - return false; - } - - // All callers of the constructor are restricted to <K extends Enum<K>>. - @Override Object writeReplace() { - return new EnumSerializedForm<K, V>(delegate); - } - - /* - * This class is used to serialize ImmutableEnumSet instances. - */ - private static class EnumSerializedForm<K extends Enum<K>, V> - implements Serializable { - final EnumMap<K, V> delegate; - EnumSerializedForm(EnumMap<K, V> delegate) { - this.delegate = delegate; - } - Object readResolve() { - return new ImmutableEnumMap<K, V>(delegate); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableEnumSet.java b/guava/src/com/google/common/collect/ImmutableEnumSet.java index d187b5c..ac6dd0e 100644 --- a/guava/src/com/google/common/collect/ImmutableEnumSet.java +++ b/guava/src/com/google/common/collect/ImmutableEnumSet.java @@ -31,17 +31,6 @@ import java.util.EnumSet; @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization final class ImmutableEnumSet<E extends Enum<E>> extends ImmutableSet<E> { - static <E extends Enum<E>> ImmutableSet<E> asImmutable(EnumSet<E> set) { - switch (set.size()) { - case 0: - return ImmutableSet.of(); - case 1: - return ImmutableSet.of(Iterables.getOnlyElement(set)); - default: - return new ImmutableEnumSet<E>(set); - } - } - /* * Notes on EnumSet and <E extends Enum<E>>: * @@ -52,7 +41,7 @@ final class ImmutableEnumSet<E extends Enum<E>> extends ImmutableSet<E> { */ private final transient EnumSet<E> delegate; - private ImmutableEnumSet(EnumSet<E> delegate) { + ImmutableEnumSet(EnumSet<E> delegate) { this.delegate = delegate; } diff --git a/guava/src/com/google/common/collect/ImmutableList.java b/guava/src/com/google/common/collect/ImmutableList.java index a01f4bc..cd8235a 100644 --- a/guava/src/com/google/common/collect/ImmutableList.java +++ b/guava/src/com/google/common/collect/ImmutableList.java @@ -16,17 +16,15 @@ package com.google.common.collect; -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.collect.ObjectArrays.checkElementNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -50,10 +48,6 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this type 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>. - * * @see ImmutableMap * @see ImmutableSet * @author Kevin Bourrillion @@ -259,19 +253,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * @throws NullPointerException if any of {@code elements} is null */ public static <E> ImmutableList<E> copyOf(Iterator<? extends E> elements) { - // We special-case for 0 or 1 elements, but going further is madness. - if (!elements.hasNext()) { - return of(); - } - E first = elements.next(); - if (!elements.hasNext()) { - return of(first); - } else { - return new ImmutableList.Builder<E>() - .add(first) - .addAll(elements) - .build(); - } + return copyFromCollection(Lists.newArrayList(elements)); } /** @@ -291,12 +273,9 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } } - /** - * Views the array as an immutable list. The array must have only non-null {@code E} elements. - * - * <p>The array must be internally created. - */ - static <E> ImmutableList<E> asImmutableList(Object[] elements) { + private static <E> ImmutableList<E> copyFromCollection( + Collection<? extends E> collection) { + Object[] elements = collection.toArray(); switch (elements.length) { case 0: return of(); @@ -305,23 +284,29 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> ImmutableList<E> list = new SingletonImmutableList<E>((E) elements[0]); return list; default: + // safe to use the array without copying it + // as specified by Collection.toArray(). return construct(elements); } } - - private static <E> ImmutableList<E> copyFromCollection( - Collection<? extends E> collection) { - return asImmutableList(collection.toArray()); - } - + /** {@code elements} has to be internally created array. */ private static <E> ImmutableList<E> construct(Object... elements) { for (int i = 0; i < elements.length; i++) { - ObjectArrays.checkElementNotNull(elements[i], i); + checkElementNotNull(elements[i], i); } return new RegularImmutableList<E>(elements); } + // We do this instead of Preconditions.checkNotNull to save boxing and array + // creation cost. + private static Object checkElementNotNull(Object element, int index) { + if (element == null) { + throw new NullPointerException("at index " + index); + } + return element; + } + ImmutableList() {} // This declaration is needed to make List.iterator() and @@ -334,29 +319,15 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> return listIterator(0); } - @Override public UnmodifiableListIterator<E> listIterator(int index) { - return new AbstractIndexedListIterator<E>(size(), index) { - @Override - protected E get(int index) { - return ImmutableList.this.get(index); - } - }; - } + @Override public abstract UnmodifiableListIterator<E> listIterator(int index); - @Override - public int indexOf(@Nullable Object object) { - return (object == null) ? -1 : Lists.indexOfImpl(this, object); - } + // Mark these two methods with @Nullable @Override - public int lastIndexOf(@Nullable Object object) { - return (object == null) ? -1 : Lists.lastIndexOfImpl(this, object); - } + public abstract int indexOf(@Nullable Object object); @Override - public boolean contains(@Nullable Object object) { - return indexOf(object) >= 0; - } + public abstract int lastIndexOf(@Nullable Object object); // constrain the return type to ImmutableList<E> @@ -367,67 +338,13 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * returned.) */ @Override - public ImmutableList<E> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size()); - int length = toIndex - fromIndex; - switch (length) { - case 0: - return of(); - case 1: - return of(get(fromIndex)); - default: - return subListUnchecked(fromIndex, toIndex); - } - } - - /** - * Called by the default implementation of {@link #subList} when {@code - * toIndex - fromIndex > 1}, after index validation has already been - * performed. - */ - ImmutableList<E> subListUnchecked(int fromIndex, int toIndex) { - return new SubList(fromIndex, toIndex - fromIndex); - } - - class SubList extends ImmutableList<E> { - transient final int offset; - transient final int length; - - SubList(int offset, int length) { - this.offset = offset; - this.length = length; - } - - @Override - public int size() { - return length; - } - - @Override - public E get(int index) { - checkElementIndex(index, length); - return ImmutableList.this.get(index + offset); - } - - @Override - public ImmutableList<E> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, length); - return ImmutableList.this.subList(fromIndex + offset, toIndex + offset); - } - - @Override - boolean isPartialView() { - return true; - } - } + public abstract ImmutableList<E> subList(int fromIndex, int toIndex); /** * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean addAll(int index, Collection<? extends E> newElements) { throw new UnsupportedOperationException(); @@ -437,9 +354,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final E set(int index, E element) { throw new UnsupportedOperationException(); @@ -449,9 +364,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void add(int index, E element) { throw new UnsupportedOperationException(); @@ -461,9 +374,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the list unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final E remove(int index) { throw new UnsupportedOperationException(); @@ -491,8 +402,8 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } private static class ReverseImmutableList<E> extends ImmutableList<E> { - private final transient ImmutableList<E> forwardList; - private final transient int size; + private transient final ImmutableList<E> forwardList; + private transient final int size; ReverseImmutableList(ImmutableList<E> backingList) { this.forwardList = backingList; @@ -530,18 +441,18 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> } @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); return forwardList.subList( reversePosition(toIndex), reversePosition(fromIndex)).reverse(); } @Override public E get(int index) { - checkElementIndex(index, size); + Preconditions.checkElementIndex(index, size); return forwardList.get(reverseIndex(index)); } @Override public UnmodifiableListIterator<E> listIterator(int index) { - checkPositionIndex(index, size); + Preconditions.checkPositionIndex(index, size); final UnmodifiableListIterator<E> forward = forwardList.listIterator(reversePosition(index)); return new UnmodifiableListIterator<E>() { @@ -583,7 +494,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> return forwardList.isPartialView(); } } - + @Override public boolean equals(Object obj) { return Lists.equalsImpl(this, obj); } @@ -641,34 +552,13 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * @since 2.0 (imported from Google Collections Library) */ public static final class Builder<E> extends ImmutableCollection.Builder<E> { - private Object[] contents; - private int size; + private final ArrayList<E> contents = Lists.newArrayList(); /** * Creates a new builder. The returned builder is equivalent to the builder * generated by {@link ImmutableList#builder}. */ - public Builder() { - this(DEFAULT_INITIAL_CAPACITY); - } - - // TODO(user): consider exposing this - Builder(int capacity) { - this.contents = new Object[capacity]; - this.size = 0; - } - - /** - * Expand the absolute capacity of the builder so it can accept at least - * the specified number of elements without being resized. - */ - Builder<E> ensureCapacity(int minCapacity) { - if (contents.length < minCapacity) { - this.contents = ObjectArrays.arraysCopyOf( - this.contents, expandedCapacity(contents.length, minCapacity)); - } - return this; - } + public Builder() {} /** * Adds {@code element} to the {@code ImmutableList}. @@ -678,9 +568,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * @throws NullPointerException if {@code element} is null */ @Override public Builder<E> add(E element) { - checkNotNull(element); - ensureCapacity(size + 1); - contents[size++] = element; + contents.add(checkNotNull(element)); return this; } @@ -695,7 +583,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> @Override public Builder<E> addAll(Iterable<? extends E> elements) { if (elements instanceof Collection) { Collection<?> collection = (Collection<?>) elements; - ensureCapacity(size + collection.size()); + contents.ensureCapacity(contents.size() + collection.size()); } super.addAll(elements); return this; @@ -710,12 +598,8 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * null element */ @Override public Builder<E> add(E... elements) { - for (int i = 0; i < elements.length; i++) { - checkElementNotNull(elements[i], i); - } - ensureCapacity(size + elements.length); - System.arraycopy(elements, 0, contents, size, elements.length); - size += elements.length; + contents.ensureCapacity(contents.size() + elements.length); + super.add(elements); return this; } @@ -737,21 +621,7 @@ public abstract class ImmutableList<E> extends ImmutableCollection<E> * the {@code Builder}. */ @Override public ImmutableList<E> build() { - switch (size) { - case 0: - return of(); - case 1: - @SuppressWarnings("unchecked") // guaranteed to be an E - E singleElement = (E) contents[0]; - return of(singleElement); - default: - if (size == contents.length) { - // no need to copy; any further add operations on the builder will copy the buffer - return new RegularImmutableList<E>(contents); - } else { - return new RegularImmutableList<E>(ObjectArrays.arraysCopyOf(contents, size)); - } - } + return copyOf(contents); } } } diff --git a/guava/src/com/google/common/collect/ImmutableListMultimap.java b/guava/src/com/google/common/collect/ImmutableListMultimap.java index 865fa6f..3071075 100644 --- a/guava/src/com/google/common/collect/ImmutableListMultimap.java +++ b/guava/src/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 com.google.common.annotations.GwtIncompatible; @@ -45,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 Jared Levy * @since 2.0 (imported from Google Collections Library) */ @@ -200,7 +197,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; @@ -211,7 +208,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; @@ -291,14 +288,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; @@ -318,9 +314,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(); } @@ -328,9 +323,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/src/com/google/common/collect/ImmutableMap.java b/guava/src/com/google/common/collect/ImmutableMap.java index c1d7933..0a2ef77 100644 --- a/guava/src/com/google/common/collect/ImmutableMap.java +++ b/guava/src/com/google/common/collect/ImmutableMap.java @@ -19,15 +19,12 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.getOnlyElement; -import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; -import java.util.EnumMap; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -50,10 +47,6 @@ import javax.annotation.Nullable; * having your element type cache its own hash codes, and by making use of the * cached values to short-circuit a slow {@code equals} algorithm. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @author Jesse Wilson * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) @@ -66,8 +59,10 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * {@link Collections#emptyMap}, and is preferable mainly for consistency * and maintainability of your code. */ + // 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; } /** @@ -77,7 +72,8 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * maintainability of your code. */ 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)); } /** @@ -140,9 +136,9 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * throw {@link UnsupportedOperationException}. */ static <K, V> Entry<K, V> entryOf(K key, V value) { - checkNotNull(key, "null key in entry: null=%s", value); - checkNotNull(value, "null value in entry: %s=null", key); - return Maps.immutableEntry(key, value); + return Maps.immutableEntry( + checkNotNull(key, "null key"), + checkNotNull(value, "null value")); } /** @@ -193,7 +189,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { public Builder<K, V> put(Entry<? extends K, ? extends V> entry) { K key = entry.getKey(); V value = entry.getValue(); - if (entry instanceof ImmutableEntry) { + if (entry instanceof ImmutableEntry<?, ?>) { checkNotNull(key); checkNotNull(value); @SuppressWarnings("unchecked") // all supported methods are covariant @@ -242,7 +238,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { case 0: return of(); case 1: - return new SingletonImmutableBiMap<K, V>(getOnlyElement(entries)); + return new SingletonImmutableMap<K, V>(getOnlyElement(entries)); default: Entry<?, ?>[] entryArray = entries.toArray(new Entry<?, ?>[entries.size()]); @@ -274,16 +270,6 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { if (!kvMap.isPartialView()) { 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; } @SuppressWarnings("unchecked") // we won't write to this array @@ -292,7 +278,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { case 0: return of(); case 1: - return new SingletonImmutableBiMap<K, V>(entryOf( + return new SingletonImmutableMap<K, V>(entryOf( entries[0].getKey(), entries[0].getValue())); default: for (int i = 0; i < entries.length; i++) { @@ -310,9 +296,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V put(K k, V v) { throw new UnsupportedOperationException(); @@ -322,9 +306,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V remove(Object o) { throw new UnsupportedOperationException(); @@ -334,9 +316,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void putAll(Map<? extends K, ? extends V> map) { throw new UnsupportedOperationException(); @@ -346,9 +326,7 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { * Guaranteed to throw an exception and leave the map unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void clear() { throw new UnsupportedOperationException(); @@ -364,132 +342,44 @@ public abstract class ImmutableMap<K, V> implements Map<K, V>, Serializable { return get(key) != null; } + // Overriding to mark it Nullable @Override - public boolean containsValue(@Nullable Object value) { - return value != null && Maps.containsValueImpl(this, value); - } + public abstract boolean containsValue(@Nullable Object value); // Overriding to mark it Nullable @Override public abstract V get(@Nullable Object key); - private transient ImmutableSet<Entry<K, V>> entrySet; - /** * Returns an immutable set of the mappings in this map. The entries are in * the same order as the parameters used to build this map. */ @Override - public ImmutableSet<Entry<K, V>> entrySet() { - ImmutableSet<Entry<K, V>> result = entrySet; - return (result == null) ? entrySet = createEntrySet() : result; - } - - abstract ImmutableSet<Entry<K, V>> createEntrySet(); - - private transient ImmutableSet<K> keySet; + public abstract ImmutableSet<Entry<K, V>> entrySet(); /** * Returns an immutable set of the keys in this map. These keys are in * the same order as the parameters used to build this map. */ @Override - public ImmutableSet<K> keySet() { - ImmutableSet<K> result = keySet; - return (result == null) ? keySet = createKeySet() : result; - } - - ImmutableSet<K> createKeySet() { - return new ImmutableMapKeySet<K, V>(this); - } - - private transient ImmutableCollection<V> values; + public abstract ImmutableSet<K> keySet(); /** * Returns an immutable collection of the values in this map. The values are * in the same order as the parameters used to build this map. */ @Override - public ImmutableCollection<V> values() { - ImmutableCollection<V> result = values; - return (result == null) ? values = new ImmutableMapValues<K, V>(this) : result; - } - - // cached so that this.multimapView().inverse() only computes inverse once - private transient ImmutableSetMultimap<K, V> multimapView; - - /** - * Returns a multimap view of the map. - * - * @since 14.0 - */ - @Beta - 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 = viewMapValuesAsSingletonSets(); - return new ImmutableSetMultimap<K, V>(map, map.size(), null); - } - - private ImmutableMap<K, ImmutableSet<V>> viewMapValuesAsSingletonSets() { - class MapViewOfValuesAsSingletonSets extends ImmutableMap<K, ImmutableSet<V>> { - @Override public int size() { - return ImmutableMap.this.size(); - } - - @Override public boolean containsKey(@Nullable Object key) { - return ImmutableMap.this.containsKey(key); - } - - @Override public ImmutableSet<V> get(@Nullable Object key) { - V outerValue = ImmutableMap.this.get(key); - return (outerValue == null) ? null : ImmutableSet.of(outerValue); - } - - @Override boolean isPartialView() { - return false; - } - - @Override ImmutableSet<Entry<K, ImmutableSet<V>>> createEntrySet() { - return new ImmutableMapEntrySet<K, ImmutableSet<V>>() { - @Override ImmutableMap<K, ImmutableSet<V>> map() { - return MapViewOfValuesAsSingletonSets.this; - } - - @Override - public UnmodifiableIterator<Entry<K, ImmutableSet<V>>> iterator() { - final Iterator<Entry<K,V>> backingIterator = ImmutableMap.this - .entrySet().iterator(); - return new UnmodifiableIterator<Entry<K, ImmutableSet<V>>>() { - @Override public boolean hasNext() { - return backingIterator.hasNext(); - } - - @Override public Entry<K, ImmutableSet<V>> next() { - final Entry<K, V> backingEntry = backingIterator.next(); - return new AbstractMapEntry<K, ImmutableSet<V>>() { - @Override public K getKey() { - return backingEntry.getKey(); - } - - @Override public ImmutableSet<V> getValue() { - return ImmutableSet.of(backingEntry.getValue()); - } - }; - } - }; - } - }; - } - } - return new MapViewOfValuesAsSingletonSets(); - } + public abstract ImmutableCollection<V> values(); @Override public boolean equals(@Nullable Object object) { - return Maps.equalsImpl(this, object); + if (object == this) { + return true; + } + if (object instanceof Map) { + Map<?, ?> that = (Map<?, ?>) object; + return this.entrySet().equals(that.entrySet()); + } + return false; } abstract boolean isPartialView(); diff --git a/guava/src/com/google/common/collect/ImmutableMapEntrySet.java b/guava/src/com/google/common/collect/ImmutableMapEntrySet.java deleted file mode 100644 index a6aa6e0..0000000 --- a/guava/src/com/google/common/collect/ImmutableMapEntrySet.java +++ /dev/null @@ -1,76 +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 com.google.common.annotations.GwtIncompatible; - -import java.io.Serializable; -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(); - } - - @GwtIncompatible("serialization") - @Override - Object writeReplace() { - return new EntrySetSerializedForm<K, V>(map()); - } - - @GwtIncompatible("serialization") - private static class EntrySetSerializedForm<K, V> implements Serializable { - final ImmutableMap<K, V> map; - EntrySetSerializedForm(ImmutableMap<K, V> map) { - this.map = map; - } - Object readResolve() { - return map.entrySet(); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableMapKeySet.java b/guava/src/com/google/common/collect/ImmutableMapKeySet.java deleted file mode 100644 index fbb59d8..0000000 --- a/guava/src/com/google/common/collect/ImmutableMapKeySet.java +++ /dev/null @@ -1,95 +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 com.google.common.annotations.GwtIncompatible; - -import java.io.Serializable; -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; - } - - @GwtIncompatible("serialization") - @Override Object writeReplace() { - return new KeySetSerializedForm<K>(map); - } - - @GwtIncompatible("serialization") - private static class KeySetSerializedForm<K> implements Serializable { - final ImmutableMap<K, ?> map; - KeySetSerializedForm(ImmutableMap<K, ?> map) { - this.map = map; - } - Object readResolve() { - return map.keySet(); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableMapValues.java b/guava/src/com/google/common/collect/ImmutableMapValues.java deleted file mode 100644 index 6ec7464..0000000 --- a/guava/src/com/google/common/collect/ImmutableMapValues.java +++ /dev/null @@ -1,91 +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 com.google.common.annotations.GwtIncompatible; - -import java.io.Serializable; -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; - } - }; - } - - @GwtIncompatible("serialization") - @Override Object writeReplace() { - return new SerializedForm<V>(map); - } - - @GwtIncompatible("serialization") - private static class SerializedForm<V> implements Serializable { - final ImmutableMap<?, V> map; - SerializedForm(ImmutableMap<?, V> map) { - this.map = map; - } - Object readResolve() { - return map.values(); - } - private static final long serialVersionUID = 0; - } -} diff --git a/guava/src/com/google/common/collect/ImmutableMultimap.java b/guava/src/com/google/common/collect/ImmutableMultimap.java index e29dbc1..13e213e 100644 --- a/guava/src/com/google/common/collect/ImmutableMultimap.java +++ b/guava/src/com/google/common/collect/ImmutableMultimap.java @@ -18,9 +18,9 @@ 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.annotations.GwtIncompatible; -import com.google.common.base.Function; import java.io.Serializable; import java.util.Arrays; @@ -30,9 +30,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; @@ -54,16 +53,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() { @@ -123,7 +119,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>>()); } @@ -134,6 +130,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 @@ -153,7 +166,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; /** @@ -228,8 +240,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; } @@ -238,6 +252,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; @@ -253,23 +268,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); } } @@ -327,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> removeAll(Object key) { throw new UnsupportedOperationException(); @@ -339,9 +335,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) { @@ -352,9 +346,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(); @@ -374,17 +366,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(); @@ -394,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(K key, Iterable<? extends V> values) { throw new UnsupportedOperationException(); @@ -406,9 +395,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(); @@ -418,30 +405,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 /** @@ -463,11 +485,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 @@ -476,12 +495,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> @@ -493,7 +509,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() { @@ -515,34 +554,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 @@ -552,78 +565,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 @@ -631,12 +587,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> { @@ -647,7 +599,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 diff --git a/guava/src/com/google/common/collect/ImmutableMultiset.java b/guava/src/com/google/common/collect/ImmutableMultiset.java index 6680a2d..bd07423 100644 --- a/guava/src/com/google/common/collect/ImmutableMultiset.java +++ b/guava/src/com/google/common/collect/ImmutableMultiset.java @@ -19,6 +19,7 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.Multiset.Entry; import com.google.common.primitives.Ints; import java.io.Serializable; @@ -28,6 +29,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Set; import javax.annotation.Nullable; @@ -39,10 +41,6 @@ import javax.annotation.Nullable; * multiset contains multiple instances of an element, those instances are * consecutive in the iteration order. * - * <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 * @author Louis Wasserman * @since 2.0 (imported from Google Collections Library) @@ -136,6 +134,23 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Returns an immutable multiset containing the given elements. * * <p>The multiset is ordered by the first occurrence of each element. For + * example, {@code ImmutableMultiset.of(2, 3, 1, 3)} yields a multiset with + * elements in the order {@code 2, 3, 3, 1}. + * + * @throws NullPointerException if any of {@code elements} is null + * @deprecated use {@link #copyOf(Object[])}. <b>This method is scheduled for + * deletion in January 2012.</b> + * @since 2.0 (changed from varargs in 6.0) + */ + @Deprecated + public static <E> ImmutableMultiset<E> of(E[] elements) { + return copyOf(Arrays.asList(elements)); + } + + /** + * Returns an immutable multiset containing the given elements. + * + * <p>The multiset is ordered by the first occurrence of each element. For * example, {@code ImmutableMultiset.copyOf([2, 3, 1, 3])} yields a multiset * with elements in the order {@code 2, 3, 3, 1}. * @@ -206,8 +221,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> if (size == 0) { return of(); } - return new RegularImmutableMultiset<E>( - builder.build(), Ints.saturatedCast(size)); + return new RegularImmutableMultiset<E>(builder.build(), Ints.saturatedCast(size)); } /** @@ -230,7 +244,8 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> ImmutableMultiset() {} @Override public UnmodifiableIterator<E> iterator() { - final Iterator<Entry<E>> entryIterator = entrySet().iterator(); + final Iterator<Entry<E>> entryIterator = entryIterator(); + return new UnmodifiableIterator<E>() { int remaining; E element; @@ -267,9 +282,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final int add(E element, int occurrences) { throw new UnsupportedOperationException(); @@ -279,9 +292,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final int remove(Object element, int occurrences) { throw new UnsupportedOperationException(); @@ -291,9 +302,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final int setCount(E element, int count) { throw new UnsupportedOperationException(); @@ -303,9 +312,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> * Guaranteed to throw an exception and leave the collection unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final boolean setCount(E element, int oldCount, int newCount) { throw new UnsupportedOperationException(); @@ -341,17 +348,39 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> private transient ImmutableSet<Entry<E>> entrySet; @Override - public ImmutableSet<Entry<E>> entrySet() { + public Set<Entry<E>> entrySet() { ImmutableSet<Entry<E>> es = entrySet; return (es == null) ? (entrySet = createEntrySet()) : es; } - abstract ImmutableSet<Entry<E>> createEntrySet(); + abstract UnmodifiableIterator<Entry<E>> entryIterator(); + + abstract int distinctElements(); + + ImmutableSet<Entry<E>> createEntrySet() { + return new EntrySet<E>(this); + } + + static class EntrySet<E> extends ImmutableSet<Entry<E>> { + transient final ImmutableMultiset<E> multiset; + + public EntrySet(ImmutableMultiset<E> multiset) { + this.multiset = multiset; + } + + @Override + public UnmodifiableIterator<Entry<E>> iterator() { + return multiset.entryIterator(); + } + + @Override + public int size() { + return multiset.distinctElements(); + } - abstract class EntrySet extends ImmutableSet<Entry<E>> { @Override boolean isPartialView() { - return ImmutableMultiset.this.isPartialView(); + return multiset.isPartialView(); } @Override @@ -361,7 +390,7 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> if (entry.getCount() <= 0) { return false; } - int count = count(entry.getElement()); + int count = multiset.count(entry.getElement()); return count == entry.getCount(); } return false; @@ -401,29 +430,28 @@ public abstract class ImmutableMultiset<E> extends ImmutableCollection<E> @Override public int hashCode() { - return ImmutableMultiset.this.hashCode(); + return multiset.hashCode(); } // We can't label this with @Override, because it doesn't override anything // in the GWT emulated version. - // TODO(cpovirk): try making all copies of this method @GwtIncompatible instead Object writeReplace() { - return new EntrySetSerializedForm<E>(ImmutableMultiset.this); + return new EntrySetSerializedForm<E>(multiset); } - private static final long serialVersionUID = 0; - } + static class EntrySetSerializedForm<E> implements Serializable { + final ImmutableMultiset<E> multiset; - static class EntrySetSerializedForm<E> implements Serializable { - final ImmutableMultiset<E> multiset; + EntrySetSerializedForm(ImmutableMultiset<E> multiset) { + this.multiset = multiset; + } - EntrySetSerializedForm(ImmutableMultiset<E> multiset) { - this.multiset = multiset; + Object readResolve() { + return multiset.entrySet(); + } } - Object readResolve() { - return multiset.entrySet(); - } + private static final long serialVersionUID = 0; } private static class SerializedForm implements Serializable { diff --git a/guava/src/com/google/common/collect/ImmutableRangeMap.java b/guava/src/com/google/common/collect/ImmutableRangeMap.java deleted file mode 100644 index 9545f1d..0000000 --- a/guava/src/com/google/common/collect/ImmutableRangeMap.java +++ /dev/null @@ -1,299 +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 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.GwtIncompatible; -import com.google.common.collect.SortedLists.KeyAbsentBehavior; -import com.google.common.collect.SortedLists.KeyPresentBehavior; - -import java.util.Map; -import java.util.Map.Entry; -import java.util.NoSuchElementException; - -import javax.annotation.Nullable; - -/** - * An immutable implementation of {@code RangeMap}, supporting all query operations efficiently. - * - * <p>Like all {@code RangeMap} implementations, this supports neither null keys nor null values. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -@GwtIncompatible("NavigableMap") -public class ImmutableRangeMap<K extends Comparable<?>, V> implements RangeMap<K, V> { - - @SuppressWarnings("unchecked") - private static final ImmutableRangeMap EMPTY = - new ImmutableRangeMap(ImmutableList.of(), ImmutableList.of()); - - /** - * Returns an empty immutable range map. - */ - @SuppressWarnings("unchecked") - public static final <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of() { - return EMPTY; - } - - /** - * Returns an immutable range map mapping a single range to a single value. - */ - public static final <K extends Comparable<?>, V> ImmutableRangeMap<K, V> of( - Range<K> range, V value) { - return new ImmutableRangeMap<K, V>(ImmutableList.of(range), ImmutableList.of(value)); - } - - @SuppressWarnings("unchecked") - public static final <K extends Comparable<?>, V> ImmutableRangeMap<K, V> copyOf( - RangeMap<K, ? extends V> rangeMap) { - if (rangeMap instanceof ImmutableRangeMap) { - return (ImmutableRangeMap<K, V>) rangeMap; - } - Map<Range<K>, ? extends V> map = rangeMap.asMapOfRanges(); - ImmutableList.Builder<Range<K>> rangesBuilder = new ImmutableList.Builder<Range<K>>(map.size()); - ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(map.size()); - for (Entry<Range<K>, ? extends V> entry : map.entrySet()) { - rangesBuilder.add(entry.getKey()); - valuesBuilder.add(entry.getValue()); - } - return new ImmutableRangeMap<K, V>(rangesBuilder.build(), valuesBuilder.build()); - } - - /** - * Returns a new builder for an immutable range map. - */ - public static <K extends Comparable<?>, V> Builder<K, V> builder() { - return new Builder<K, V>(); - } - - /** - * A builder for immutable range maps. Overlapping ranges are prohibited. - */ - public static final class Builder<K extends Comparable<?>, V> { - private final RangeSet<K> keyRanges; - private final RangeMap<K, V> rangeMap; - - public Builder() { - this.keyRanges = TreeRangeSet.create(); - this.rangeMap = TreeRangeMap.create(); - } - - /** - * Associates the specified range with the specified value. - * - * @throws IllegalArgumentException if {@code range} overlaps with any other ranges inserted - * into this builder, or if {@code range} is empty - */ - public Builder<K, V> put(Range<K> range, V value) { - checkNotNull(range); - checkNotNull(value); - checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range); - if (!keyRanges.complement().encloses(range)) { - // it's an error case; we can afford an expensive lookup - for (Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) { - Range<K> key = entry.getKey(); - if (key.isConnected(range) && !key.intersection(range).isEmpty()) { - throw new IllegalArgumentException( - "Overlapping ranges: range " + range + " overlaps with entry " + entry); - } - } - } - keyRanges.add(range); - rangeMap.put(range, value); - return this; - } - - /** - * Copies all associations from the specified range map into this builder. - * - * @throws IllegalArgumentException if any of the ranges in {@code rangeMap} overlap with ranges - * already in this builder - */ - public Builder<K, V> putAll(RangeMap<K, ? extends V> rangeMap) { - for (Entry<Range<K>, ? extends V> entry : rangeMap.asMapOfRanges().entrySet()) { - put(entry.getKey(), entry.getValue()); - } - return this; - } - - /** - * Returns an {@code ImmutableRangeMap} containing the associations previously added to this - * builder. - */ - public ImmutableRangeMap<K, V> build() { - Map<Range<K>, V> map = rangeMap.asMapOfRanges(); - ImmutableList.Builder<Range<K>> rangesBuilder = - new ImmutableList.Builder<Range<K>>(map.size()); - ImmutableList.Builder<V> valuesBuilder = new ImmutableList.Builder<V>(map.size()); - for (Entry<Range<K>, V> entry : map.entrySet()) { - rangesBuilder.add(entry.getKey()); - valuesBuilder.add(entry.getValue()); - } - return new ImmutableRangeMap<K, V>(rangesBuilder.build(), valuesBuilder.build()); - } - } - - private final ImmutableList<Range<K>> ranges; - private final ImmutableList<V> values; - - ImmutableRangeMap(ImmutableList<Range<K>> ranges, ImmutableList<V> values) { - this.ranges = ranges; - this.values = values; - } - - @Override - @Nullable - public V get(K key) { - int index = SortedLists.binarySearch(ranges, Range.<K>lowerBoundFn(), - Cut.belowValue(key), KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); - if (index == -1) { - return null; - } else { - Range<K> range = ranges.get(index); - return range.contains(key) ? values.get(index) : null; - } - } - - @Override - @Nullable - public Map.Entry<Range<K>, V> getEntry(K key) { - int index = SortedLists.binarySearch(ranges, Range.<K>lowerBoundFn(), - Cut.belowValue(key), KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_LOWER); - if (index == -1) { - return null; - } else { - Range<K> range = ranges.get(index); - return range.contains(key) ? Maps.immutableEntry(range, values.get(index)) : null; - } - } - - @Override - public Range<K> span() { - if (ranges.isEmpty()) { - throw new NoSuchElementException(); - } - Range<K> firstRange = ranges.get(0); - Range<K> lastRange = ranges.get(ranges.size() - 1); - return Range.create(firstRange.lowerBound, lastRange.upperBound); - } - - @Override - public void put(Range<K> range, V value) { - throw new UnsupportedOperationException(); - } - - @Override - public void putAll(RangeMap<K, V> rangeMap) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(Range<K> range) { - throw new UnsupportedOperationException(); - } - - @Override - public ImmutableMap<Range<K>, V> asMapOfRanges() { - if (ranges.isEmpty()) { - return ImmutableMap.of(); - } - RegularImmutableSortedSet<Range<K>> rangeSet = - new RegularImmutableSortedSet<Range<K>>(ranges, Range.RANGE_LEX_ORDERING); - return new RegularImmutableSortedMap<Range<K>, V>(rangeSet, values); - } - - @Override - public ImmutableRangeMap<K, V> subRangeMap(final Range<K> range) { - if (checkNotNull(range).isEmpty()) { - return ImmutableRangeMap.of(); - } else if (ranges.isEmpty() || range.encloses(span())) { - return this; - } - int lowerIndex = SortedLists.binarySearch( - ranges, Range.<K>upperBoundFn(), range.lowerBound, - KeyPresentBehavior.FIRST_AFTER, KeyAbsentBehavior.NEXT_HIGHER); - int upperIndex = SortedLists.binarySearch(ranges, - Range.<K>lowerBoundFn(), range.upperBound, - KeyPresentBehavior.ANY_PRESENT, KeyAbsentBehavior.NEXT_HIGHER); - if (lowerIndex >= upperIndex) { - return ImmutableRangeMap.of(); - } - final int off = lowerIndex; - final int len = upperIndex - lowerIndex; - ImmutableList<Range<K>> subRanges = new ImmutableList<Range<K>>() { - @Override - public int size() { - return len; - } - - @Override - public Range<K> get(int index) { - checkElementIndex(index, len); - if (index == 0 || index == len - 1) { - return ranges.get(index + off).intersection(range); - } else { - return ranges.get(index + off); - } - } - - @Override - boolean isPartialView() { - return true; - } - }; - final ImmutableRangeMap<K, V> outer = this; - return new ImmutableRangeMap<K, V>( - subRanges, values.subList(lowerIndex, upperIndex)) { - @Override - public ImmutableRangeMap<K, V> subRangeMap(Range<K> subRange) { - if (range.isConnected(subRange)) { - return outer.subRangeMap(subRange.intersection(range)); - } else { - return ImmutableRangeMap.of(); - } - } - }; - } - - @Override - public int hashCode() { - return asMapOfRanges().hashCode(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof RangeMap) { - RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; - return asMapOfRanges().equals(rangeMap.asMapOfRanges()); - } - return false; - } - - @Override - public String toString() { - return asMapOfRanges().toString(); - } -} diff --git a/guava/src/com/google/common/collect/ImmutableRangeSet.java b/guava/src/com/google/common/collect/ImmutableRangeSet.java deleted file mode 100644 index bd21bbe..0000000 --- a/guava/src/com/google/common/collect/ImmutableRangeSet.java +++ /dev/null @@ -1,608 +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 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.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; -import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.collect.SortedLists.KeyAbsentBehavior; -import com.google.common.collect.SortedLists.KeyPresentBehavior; -import com.google.common.primitives.Ints; - -import java.io.Serializable; -import java.util.Collections; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * An efficient immutable implementation of a {@link RangeSet}. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -public final class ImmutableRangeSet<C extends Comparable> extends AbstractRangeSet<C> - implements Serializable { - - @SuppressWarnings("unchecked") - private static final ImmutableRangeSet EMPTY = new ImmutableRangeSet(ImmutableList.of()); - - @SuppressWarnings("unchecked") - private static final ImmutableRangeSet ALL = new ImmutableRangeSet(ImmutableList.of(Range.all())); - - /** - * Returns an empty immutable range set. - */ - @SuppressWarnings("unchecked") - public static <C extends Comparable> ImmutableRangeSet<C> of() { - return EMPTY; - } - - /** - * Returns an immutable range set containing the single range {@link Range#all()}. - */ - @SuppressWarnings("unchecked") - static <C extends Comparable> ImmutableRangeSet<C> all() { - return ALL; - } - - /** - * Returns an immutable range set containing the specified single range. If {@link Range#isEmpty() - * range.isEmpty()}, this is equivalent to {@link ImmutableRangeSet#of()}. - */ - public static <C extends Comparable> ImmutableRangeSet<C> of(Range<C> range) { - checkNotNull(range); - if (range.isEmpty()) { - return of(); - } else if (range.equals(Range.all())) { - return all(); - } else { - return new ImmutableRangeSet<C>(ImmutableList.of(range)); - } - } - - /** - * Returns an immutable copy of the specified {@code RangeSet}. - */ - public static <C extends Comparable> ImmutableRangeSet<C> copyOf(RangeSet<C> rangeSet) { - checkNotNull(rangeSet); - if (rangeSet.isEmpty()) { - return of(); - } else if (rangeSet.encloses(Range.<C>all())) { - return all(); - } - - if (rangeSet instanceof ImmutableRangeSet) { - ImmutableRangeSet<C> immutableRangeSet = (ImmutableRangeSet<C>) rangeSet; - if (!immutableRangeSet.isPartialView()) { - return immutableRangeSet; - } - } - return new ImmutableRangeSet<C>(ImmutableList.copyOf(rangeSet.asRanges())); - } - - ImmutableRangeSet(ImmutableList<Range<C>> ranges) { - this.ranges = ranges; - } - - private ImmutableRangeSet(ImmutableList<Range<C>> ranges, ImmutableRangeSet<C> complement) { - this.ranges = ranges; - this.complement = complement; - } - - private transient final ImmutableList<Range<C>> ranges; - - @Override - public boolean encloses(Range<C> otherRange) { - int index = SortedLists.binarySearch(ranges, - Range.<C>lowerBoundFn(), - otherRange.lowerBound, - Ordering.natural(), - ANY_PRESENT, - NEXT_LOWER); - return index != -1 && ranges.get(index).encloses(otherRange); - } - - @Override - public Range<C> rangeContaining(C value) { - int index = SortedLists.binarySearch(ranges, - Range.<C>lowerBoundFn(), - Cut.belowValue(value), - Ordering.natural(), - ANY_PRESENT, - NEXT_LOWER); - if (index != -1) { - Range<C> range = ranges.get(index); - return range.contains(value) ? range : null; - } - return null; - } - - @Override - public Range<C> span() { - if (ranges.isEmpty()) { - throw new NoSuchElementException(); - } - return Range.create( - ranges.get(0).lowerBound, - ranges.get(ranges.size() - 1).upperBound); - } - - @Override - public boolean isEmpty() { - return ranges.isEmpty(); - } - - @Override - public void add(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void addAll(RangeSet<C> other) { - throw new UnsupportedOperationException(); - } - - @Override - public void remove(Range<C> range) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeAll(RangeSet<C> other) { - throw new UnsupportedOperationException(); - } - - @Override - public ImmutableSet<Range<C>> asRanges() { - if (ranges.isEmpty()) { - return ImmutableSet.of(); - } - return new RegularImmutableSortedSet<Range<C>>(ranges, Range.RANGE_LEX_ORDERING); - } - - private transient ImmutableRangeSet<C> complement; - - private final class ComplementRanges extends ImmutableList<Range<C>> { - // True if the "positive" range set is empty or bounded below. - private final boolean positiveBoundedBelow; - - // True if the "positive" range set is empty or bounded above. - private final boolean positiveBoundedAbove; - - private final int size; - - ComplementRanges() { - this.positiveBoundedBelow = ranges.get(0).hasLowerBound(); - this.positiveBoundedAbove = Iterables.getLast(ranges).hasUpperBound(); - - int size = ranges.size() - 1; - if (positiveBoundedBelow) { - size++; - } - if (positiveBoundedAbove) { - size++; - } - this.size = size; - } - - @Override - public int size() { - return size; - } - - @Override - public Range<C> get(int index) { - checkElementIndex(index, size); - - Cut<C> lowerBound; - if (positiveBoundedBelow) { - lowerBound = (index == 0) ? Cut.<C>belowAll() : ranges.get(index - 1).upperBound; - } else { - lowerBound = ranges.get(index).upperBound; - } - - Cut<C> upperBound; - if (positiveBoundedAbove && index == size - 1) { - upperBound = Cut.<C>aboveAll(); - } else { - upperBound = ranges.get(index + (positiveBoundedBelow ? 0 : 1)).lowerBound; - } - - return Range.create(lowerBound, upperBound); - } - - @Override - boolean isPartialView() { - return true; - } - } - - @Override - public ImmutableRangeSet<C> complement() { - ImmutableRangeSet<C> result = complement; - if (result != null) { - return result; - } else if (ranges.isEmpty()) { - return complement = all(); - } else if (ranges.size() == 1 && ranges.get(0).equals(Range.all())) { - return complement = of(); - } else { - ImmutableList<Range<C>> complementRanges = new ComplementRanges(); - result = complement = new ImmutableRangeSet<C>(complementRanges, this); - } - return result; - } - - /** - * Returns a list containing the nonempty intersections of {@code range} - * with the ranges in this range set. - */ - private ImmutableList<Range<C>> intersectRanges(final Range<C> range) { - if (ranges.isEmpty() || range.isEmpty()) { - return ImmutableList.of(); - } else if (range.encloses(span())) { - return ranges; - } - - final int fromIndex; - if (range.hasLowerBound()) { - fromIndex = SortedLists.binarySearch( - ranges, Range.<C>upperBoundFn(), range.lowerBound, KeyPresentBehavior.FIRST_AFTER, - KeyAbsentBehavior.NEXT_HIGHER); - } else { - fromIndex = 0; - } - - int toIndex; - if (range.hasUpperBound()) { - toIndex = SortedLists.binarySearch( - ranges, Range.<C>lowerBoundFn(), range.upperBound, KeyPresentBehavior.FIRST_PRESENT, - KeyAbsentBehavior.NEXT_HIGHER); - } else { - toIndex = ranges.size(); - } - final int length = toIndex - fromIndex; - if (length == 0) { - return ImmutableList.of(); - } else { - return new ImmutableList<Range<C>>() { - @Override - public int size() { - return length; - } - - @Override - public Range<C> get(int index) { - checkElementIndex(index, length); - if (index == 0 || index == length - 1) { - return ranges.get(index + fromIndex).intersection(range); - } else { - return ranges.get(index + fromIndex); - } - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - } - - /** - * Returns a view of the intersection of this range set with the given range. - */ - @Override - public ImmutableRangeSet<C> subRangeSet(Range<C> range) { - if (!isEmpty()) { - Range<C> span = span(); - if (range.encloses(span)) { - return this; - } else if (range.isConnected(span)) { - return new ImmutableRangeSet<C>(intersectRanges(range)); - } - } - return of(); - } - - /** - * Returns an {@link ImmutableSortedSet} containing the same values in the given domain - * {@linkplain RangeSet#contains contained} by this range set. - * - * <p><b>Note:</b> {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For - * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty - * ranges {@code [3..3)} and {@code [4..4)}. - * - * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} view of a large - * range set (such as {@code ImmutableRangeSet.of(Range.greaterThan(0))}). 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. - * - * <p>The returned set's {@link Object#toString} method returns a short-hand form of the set's - * contents, such as {@code "[1..100]}"}. - * - * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if - * neither has an upper bound - */ - public ImmutableSortedSet<C> asSet(DiscreteDomain<C> domain) { - checkNotNull(domain); - if (isEmpty()) { - return ImmutableSortedSet.of(); - } - Range<C> span = span().canonical(domain); - if (!span.hasLowerBound()) { - // according to the spec of canonical, neither this ImmutableRangeSet nor - // the range have a lower bound - throw new IllegalArgumentException( - "Neither the DiscreteDomain nor this range set are bounded below"); - } else if (!span.hasUpperBound()) { - try { - domain.maxValue(); - } catch (NoSuchElementException e) { - throw new IllegalArgumentException( - "Neither the DiscreteDomain nor this range set are bounded above"); - } - } - - return new AsSet(domain); - } - - private final class AsSet extends ImmutableSortedSet<C> { - private final DiscreteDomain<C> domain; - - AsSet(DiscreteDomain<C> domain) { - super(Ordering.natural()); - this.domain = domain; - } - - private transient Integer size; - - @Override - public int size() { - // racy single-check idiom - Integer result = size; - if (result == null) { - long total = 0; - for (Range<C> range : ranges) { - total += range.asSet(domain).size(); - if (total >= Integer.MAX_VALUE) { - break; - } - } - result = size = Ints.saturatedCast(total); - } - return result.intValue(); - } - - @Override - public UnmodifiableIterator<C> iterator() { - return new AbstractIterator<C>() { - final Iterator<Range<C>> rangeItr = ranges.iterator(); - Iterator<C> elemItr = Iterators.emptyIterator(); - - @Override - protected C computeNext() { - while (!elemItr.hasNext()) { - if (rangeItr.hasNext()) { - elemItr = rangeItr.next().asSet(domain).iterator(); - } else { - return endOfData(); - } - } - return elemItr.next(); - } - }; - } - - @Override - @GwtIncompatible("NavigableSet") - public UnmodifiableIterator<C> descendingIterator() { - return new AbstractIterator<C>() { - final Iterator<Range<C>> rangeItr = ranges.reverse().iterator(); - Iterator<C> elemItr = Iterators.emptyIterator(); - - @Override - protected C computeNext() { - while (!elemItr.hasNext()) { - if (rangeItr.hasNext()) { - elemItr = rangeItr.next().asSet(domain).descendingIterator(); - } else { - return endOfData(); - } - } - return elemItr.next(); - } - }; - } - - ImmutableSortedSet<C> subSet(Range<C> range) { - return subRangeSet(range).asSet(domain); - } - - @Override - ImmutableSortedSet<C> headSetImpl(C toElement, boolean inclusive) { - return subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive))); - } - - @Override - ImmutableSortedSet<C> subSetImpl( - C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { - if (!fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0) { - return ImmutableSortedSet.of(); - } - return subSet(Range.range( - fromElement, BoundType.forBoolean(fromInclusive), - toElement, BoundType.forBoolean(toInclusive))); - } - - @Override - ImmutableSortedSet<C> tailSetImpl(C fromElement, boolean inclusive) { - return subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); - } - - @Override - public boolean contains(@Nullable Object o) { - if (o == null) { - return false; - } - try { - @SuppressWarnings("unchecked") // we catch CCE's - C c = (C) o; - return ImmutableRangeSet.this.contains(c); - } catch (ClassCastException e) { - return false; - } - } - - @Override - int indexOf(Object target) { - if (contains(target)) { - @SuppressWarnings("unchecked") // if it's contained, it's definitely a C - C c = (C) target; - long total = 0; - for (Range<C> range : ranges) { - if (range.contains(c)) { - return Ints.saturatedCast(total + range.asSet(domain).indexOf(c)); - } else { - total += range.asSet(domain).size(); - } - } - throw new AssertionError("impossible"); - } - return -1; - } - - @Override - boolean isPartialView() { - return ranges.isPartialView(); - } - - @Override - public String toString() { - return ranges.toString(); - } - - @Override - Object writeReplace() { - return new AsSetSerializedForm<C>(ranges, domain); - } - } - - private static class AsSetSerializedForm<C extends Comparable> implements Serializable { - private final ImmutableList<Range<C>> ranges; - private final DiscreteDomain<C> domain; - - AsSetSerializedForm(ImmutableList<Range<C>> ranges, DiscreteDomain<C> domain) { - this.ranges = ranges; - this.domain = domain; - } - - Object readResolve() { - return new ImmutableRangeSet<C>(ranges).asSet(domain); - } - } - - boolean isPartialView() { - return ranges.isPartialView(); - } - - /** - * Returns a new builder for an immutable range set. - */ - public static <C extends Comparable<?>> Builder<C> builder() { - return new Builder<C>(); - } - - /** - * A builder for immutable range sets. - */ - public static class Builder<C extends Comparable<?>> { - private final RangeSet<C> rangeSet; - - public Builder() { - this.rangeSet = TreeRangeSet.create(); - } - - /** - * Add the specified range to this builder. Adjacent/abutting ranges are permitted, but - * empty ranges, or ranges with nonempty overlap, are forbidden. - * - * @throws IllegalArgumentException if {@code range} is empty or has nonempty intersection with - * any ranges already added to the builder - */ - public Builder<C> add(Range<C> range) { - if (range.isEmpty()) { - throw new IllegalArgumentException("range must not be empty, but was " + range); - } else if (!rangeSet.complement().encloses(range)) { - for (Range<C> currentRange : rangeSet.asRanges()) { - checkArgument( - !currentRange.isConnected(range) || currentRange.intersection(range).isEmpty(), - "Ranges may not overlap, but received %s and %s", currentRange, range); - } - throw new AssertionError("should have thrown an IAE above"); - } - rangeSet.add(range); - return this; - } - - /** - * Add all ranges from the specified range set to this builder. Duplicate or connected ranges - * are permitted, and will be merged in the resulting immutable range set. - */ - public Builder<C> addAll(RangeSet<C> ranges) { - for (Range<C> range : ranges.asRanges()) { - add(range); - } - return this; - } - - /** - * Returns an {@code ImmutableRangeSet} containing the ranges added to this builder. - */ - public ImmutableRangeSet<C> build() { - return copyOf(rangeSet); - } - } - - private static final class SerializedForm<C extends Comparable> implements Serializable { - private final ImmutableList<Range<C>> ranges; - - SerializedForm(ImmutableList<Range<C>> ranges) { - this.ranges = ranges; - } - - Object readResolve() { - if (ranges.isEmpty()) { - return of(); - } else if (ranges.equals(ImmutableList.of(Range.all()))) { - return all(); - } else { - return new ImmutableRangeSet<C>(ranges); - } - } - } - - Object writeReplace() { - return new SerializedForm<C>(ranges); - } -} diff --git a/guava/src/com/google/common/collect/ImmutableSet.java b/guava/src/com/google/common/collect/ImmutableSet.java index b96829c..fb60ce6 100644 --- a/guava/src/com/google/common/collect/ImmutableSet.java +++ b/guava/src/com/google/common/collect/ImmutableSet.java @@ -20,14 +20,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Ints; import java.io.Serializable; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -59,10 +57,6 @@ import javax.annotation.Nullable; * outside its package as it has no public or protected constructors. Thus, * instances of this type 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>. - * * @see ImmutableList * @see ImmutableMap * @author Kevin Bourrillion @@ -102,7 +96,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2) { - return construct(2, e1, e2); + return construct(e1, e2); } /** @@ -113,7 +107,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3) { - return construct(3, e1, e2, e3); + return construct(e1, e2, e3); } /** @@ -124,7 +118,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4) { - return construct(4, e1, e2, e3, e4); + return construct(e1, e2, e3, e4); } /** @@ -135,7 +129,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4, E e5) { - return construct(5, e1, e2, e3, e4, e5); + return construct(e1, e2, e3, e4, e5); } /** @@ -156,72 +150,59 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> elements[3] = e4; elements[4] = e5; elements[5] = e6; - System.arraycopy(others, 0, elements, paramCount, others.length); - return construct(elements.length, elements); + for (int i = paramCount; i < elements.length; i++) { + elements[i] = others[i - paramCount]; + } + return construct(elements); } - /** - * Constructs an {@code ImmutableSet} from the first {@code n} elements of the specified array. - * If {@code k} is the size of the returned {@code ImmutableSet}, then the unique elements of - * {@code elements} will be in the first {@code k} positions, and {@code elements[i] == null} for - * {@code k <= i < n}. - * - * <p>This may modify {@code elements}. Additionally, if {@code n == elements.length} and - * {@code elements} contains no duplicates, {@code elements} may be used without copying in the - * returned {@code ImmutableSet}, in which case it may no longer be modified. - * - * <p>{@code elements} may contain only values of type {@code E}. - * - * @throws NullPointerException if any of the first {@code n} elements of {@code elements} is - * null - */ - private static <E> ImmutableSet<E> construct(int n, Object... elements) { - switch (n) { - case 0: - return of(); - case 1: - @SuppressWarnings("unchecked") // safe; elements contains only E's - E elem = (E) elements[0]; - return of(elem); - default: - // continue below to handle the general case - } - int tableSize = chooseTableSize(n); + /** {@code elements} has to be internally created array. */ + private static <E> ImmutableSet<E> construct(Object... elements) { + int tableSize = chooseTableSize(elements.length); Object[] table = new Object[tableSize]; int mask = tableSize - 1; + ArrayList<Object> uniqueElementsList = null; int hashCode = 0; - int uniques = 0; - for (int i = 0; i < n; i++) { - Object element = ObjectArrays.checkElementNotNull(elements[i], i); + for (int i = 0; i < elements.length; i++) { + Object element = elements[i]; int hash = element.hashCode(); for (int j = Hashing.smear(hash); ; j++) { int index = j & mask; Object value = table[index]; if (value == null) { + if (uniqueElementsList != null) { + uniqueElementsList.add(element); + } // Came to an empty slot. Put the element here. - elements[uniques++] = element; table[index] = element; hashCode += hash; break; } else if (value.equals(element)) { + if (uniqueElementsList == null) { + // first dup + uniqueElementsList = new ArrayList<Object>(elements.length); + for (int k = 0; k < i; k++) { + Object previous = elements[k]; + uniqueElementsList.add(previous); + } + } break; } } } - Arrays.fill(elements, uniques, n, null); - if (uniques == 1) { + Object[] uniqueElements = uniqueElementsList == null + ? elements + : uniqueElementsList.toArray(); + if (uniqueElements.length == 1) { // There is only one element or elements are all duplicates @SuppressWarnings("unchecked") // we are careful to only pass in E - E element = (E) elements[0]; + E element = (E) uniqueElements[0]; return new SingletonImmutableSet<E>(element, hashCode); - } else if (tableSize != chooseTableSize(uniques)) { + } else if (tableSize > 2 * chooseTableSize(uniqueElements.length)) { // Resize the table when the array includes too many duplicates. // when this happens, we have already made a copy - return construct(uniques, elements); + return construct(uniqueElements); } else { - Object[] uniqueElements = (uniques < elements.length) - ? ObjectArrays.arraysCopyOf(elements, uniques) - : elements; return new RegularImmutableSet<E>(uniqueElements, hashCode, table, mask); } } @@ -229,30 +210,18 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> // We use power-of-2 tables, and this is the highest int that's a power of 2 static final int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; - // Represents how tightly we can pack things, as a maximum. - private static final double DESIRED_LOAD_FACTOR = 0.7; - // If the set has this many elements, it will "max out" the table size - private static final int CUTOFF = - (int) Math.floor(MAX_TABLE_SIZE * DESIRED_LOAD_FACTOR); + static final int CUTOFF = 1 << 29; /** * Returns an array size suitable for the backing array of a hash table that - * uses open addressing with linear probing in its implementation. The - * returned size is the smallest power of two that can hold setSize elements - * with the desired load factor. - * - * <p>Do not call this method with setSize < 2. + * uses linear probing in its implementation. The returned size is the + * smallest power of two that can hold setSize elements while being at most + * 50% full, if possible. */ - @VisibleForTesting static int chooseTableSize(int setSize) { - // Correct the size for open addressing to match desired load factor. + static int chooseTableSize(int setSize) { if (setSize < CUTOFF) { - // Round up to the next highest power of 2. - int tableSize = Integer.highestOneBit(setSize - 1) << 1; - while (tableSize * DESIRED_LOAD_FACTOR < setSize) { - tableSize <<= 1; - } - return tableSize; + return Integer.highestOneBit(setSize) << 2; } // The table can't be completely full or we'll get infinite reprobes @@ -277,7 +246,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> case 1: return of(elements[0]); default: - return construct(elements.length, elements.clone()); + return construct(elements.clone()); } } @@ -312,19 +281,9 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any of {@code elements} is null */ public static <E> ImmutableSet<E> copyOf(Iterator<? extends E> elements) { - // We special-case for 0 or 1 elements, but anything further is madness. - if (!elements.hasNext()) { - return of(); - } - E first = elements.next(); - if (!elements.hasNext()) { - return of(first); - } else { - return new ImmutableSet.Builder<E>() - .add(first) - .addAll(elements) - .build(); - } + // TODO(benyu): here we could avoid toArray() for 0 or 1-element list, + // worth it? + return copyFromCollection(Lists.newArrayList(elements)); } /** @@ -366,12 +325,6 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> if (!set.isPartialView()) { return set; } - } else if (elements instanceof EnumSet) { - EnumSet<?> enumSet = EnumSet.copyOf((EnumSet<?>) elements); - @SuppressWarnings("unchecked") - // immutable collections are safe for covariant casts - ImmutableSet<E> result = (ImmutableSet<E>) ImmutableEnumSet.asImmutable(enumSet); - return result; } return copyFromCollection(elements); } @@ -389,7 +342,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> default: // safe to use the array without copying it // as specified by Collection.toArray(). - return construct(elements.length, elements); + return construct(elements); } } @@ -438,16 +391,30 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> return false; } + /* + * The cast is safe because the only way to create an instance is via the + * create() method above, which only permits elements of type E. + */ + @SuppressWarnings("unchecked") @Override public UnmodifiableIterator<E> iterator() { - return asList().iterator(); + return (UnmodifiableIterator<E>) Iterators.forArray(elements); } @Override public Object[] toArray() { - return asList().toArray(); + Object[] array = new Object[size()]; + System.arraycopy(elements, 0, array, 0, size()); + return array; } @Override public <T> T[] toArray(T[] array) { - return asList().toArray(array); + int size = size(); + if (array.length < size) { + array = ObjectArrays.newArray(array, size); + } else if (array.length > size) { + array[size] = null; + } + System.arraycopy(elements, 0, array, 0, size); + return array; } @Override public boolean containsAll(Collection<?> targets) { @@ -473,7 +440,65 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> } @Override ImmutableList<E> createAsList() { - return new RegularImmutableAsList<E>(this, elements); + return new ImmutableAsList<E>(elements, this); + } + } + + /** such as ImmutableMap.keySet() */ + abstract static class TransformedImmutableSet<D, E> extends ImmutableSet<E> { + final D[] source; + final int hashCode; + + TransformedImmutableSet(D[] source, int hashCode) { + this.source = source; + this.hashCode = hashCode; + } + + abstract E transform(D element); + + @Override + public int size() { + return source.length; + } + + @Override public boolean isEmpty() { + return false; + } + + @Override public UnmodifiableIterator<E> iterator() { + return new AbstractIndexedListIterator<E>(source.length) { + @Override protected E get(int index) { + return transform(source[index]); + } + }; + } + + @Override public Object[] toArray() { + return toArray(new Object[size()]); + } + + @Override public <T> T[] toArray(T[] array) { + int size = size(); + if (array.length < size) { + array = ObjectArrays.newArray(array, size); + } else if (array.length > size) { + array[size] = null; + } + + // Writes will produce ArrayStoreException when the toArray() doc requires + Object[] objectArray = array; + for (int i = 0; i < source.length; i++) { + objectArray[i] = transform(source[i]); + } + return array; + } + + @Override public final int hashCode() { + return hashCode; + } + + @Override boolean isHashCodeFast() { + return true; } } @@ -524,34 +549,14 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @since 2.0 (imported from Google Collections Library) */ public static class Builder<E> extends ImmutableCollection.Builder<E> { - Object[] contents; - int size; + // accessed directly by ImmutableSortedSet + final ArrayList<E> contents = Lists.newArrayList(); /** * Creates a new builder. The returned builder is equivalent to the builder * generated by {@link ImmutableSet#builder}. */ - public Builder() { - this(DEFAULT_INITIAL_CAPACITY); - } - - Builder(int capacity) { - checkArgument(capacity >= 0, "capacity must be >= 0 but was %s", capacity); - this.contents = new Object[capacity]; - this.size = 0; - } - - /** - * Expand the absolute capacity of the builder so it can accept at least - * the specified number of elements without being resized. - */ - Builder<E> ensureCapacity(int minCapacity) { - if (contents.length < minCapacity) { - contents = ObjectArrays.arraysCopyOf( - contents, expandedCapacity(contents.length, minCapacity)); - } - return this; - } + public Builder() {} /** * Adds {@code element} to the {@code ImmutableSet}. If the {@code @@ -563,8 +568,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if {@code element} is null */ @Override public Builder<E> add(E element) { - ensureCapacity(size + 1); - contents[size++] = checkNotNull(element); + contents.add(checkNotNull(element)); return this; } @@ -578,12 +582,8 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * null element */ @Override public Builder<E> add(E... elements) { - for (int i = 0; i < elements.length; i++) { - ObjectArrays.checkElementNotNull(elements[i], i); - } - ensureCapacity(size + elements.length); - System.arraycopy(elements, 0, contents, size, elements.length); - size += elements.length; + contents.ensureCapacity(contents.size() + elements.length); + super.add(elements); return this; } @@ -599,7 +599,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> @Override public Builder<E> addAll(Iterable<? extends E> elements) { if (elements instanceof Collection) { Collection<?> collection = (Collection<?>) elements; - ensureCapacity(size + collection.size()); + contents.ensureCapacity(contents.size() + collection.size()); } super.addAll(elements); return this; @@ -624,11 +624,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * the {@code Builder}. */ @Override public ImmutableSet<E> build() { - ImmutableSet<E> result = construct(size, contents); - // construct has the side effect of deduping contents, so we update size - // accordingly. - size = result.size(); - return result; + return copyOf(contents); } } } diff --git a/guava/src/com/google/common/collect/ImmutableSetMultimap.java b/guava/src/com/google/common/collect/ImmutableSetMultimap.java index 6eedf1a..04a6978 100644 --- a/guava/src/com/google/common/collect/ImmutableSetMultimap.java +++ b/guava/src/com/google/common/collect/ImmutableSetMultimap.java @@ -18,9 +18,9 @@ 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.annotations.GwtIncompatible; -import com.google.common.base.Function; import java.io.IOException; import java.io.InvalidObjectException; @@ -28,12 +28,10 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; 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; @@ -53,10 +51,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) */ @@ -151,7 +145,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>>()); } @@ -162,6 +156,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 @@ -186,7 +197,7 @@ public class ImmutableSetMultimap<K, V> * generated by {@link ImmutableSetMultimap#builder}. */ public Builder() { - builderMultimap = new BuilderMultimap<K, V>(); + builderMultimap = new BuilderMultimap<K, V>(); } /** @@ -235,25 +246,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; @@ -263,23 +275,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); } } @@ -302,7 +297,7 @@ public class ImmutableSetMultimap<K, V> Multimap<? extends K, ? extends V> multimap) { return copyOf(multimap, null); } - + private static <K, V> ImmutableSetMultimap<K, V> copyOf( Multimap<? extends K, ? extends V> multimap, Comparator<? super V> valueComparator) { @@ -328,7 +323,7 @@ public class ImmutableSetMultimap<K, V> K key = entry.getKey(); Collection<? extends V> values = entry.getValue(); ImmutableSet<V> set = (valueComparator == null) - ? ImmutableSet.copyOf(values) + ? ImmutableSet.copyOf(values) : ImmutableSortedSet.copyOf(valueComparator, values); if (!set.isEmpty()) { builder.put(key, set); @@ -343,10 +338,10 @@ public class ImmutableSetMultimap<K, V> // Returned by get() when values are sorted and a missing key is provided. private final transient ImmutableSortedSet<V> emptySet; - ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size, + ImmutableSetMultimap(ImmutableMap<K, ImmutableSet<V>> map, int size, @Nullable Comparator<? super V> valueComparator) { super(map, size); - this.emptySet = (valueComparator == null) + this.emptySet = (valueComparator == null) ? null : ImmutableSortedSet.<V>emptySet(valueComparator); } @@ -375,13 +370,13 @@ public class ImmutableSetMultimap<K, V> /** * {@inheritDoc} * - * <p>Because an inverse of a set multimap cannot contain multiple pairs with - * the same key and value, this method returns an {@code ImmutableSetMultimap} - * rather than the {@code ImmutableMultimap} specified in the {@code - * ImmutableMultimap} class. + * <p>Because an inverse of a set multimap cannot contain multiple pairs with the same key and + * value, this method returns an {@code ImmutableSetMultimap} rather than the + * {@code ImmutableMultimap} specified in the {@code ImmutableMultimap} class. * - * @since 11.0 + * @since 11 */ + @Beta public ImmutableSetMultimap<V, K> inverse() { ImmutableSetMultimap<V, K> result = inverse; return (result == null) ? (inverse = invert()) : result; @@ -401,9 +396,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(); } @@ -411,9 +405,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/src/com/google/common/collect/ImmutableSortedAsList.java b/guava/src/com/google/common/collect/ImmutableSortedAsList.java index 98aab41..e557570 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedAsList.java +++ b/guava/src/com/google/common/collect/ImmutableSortedAsList.java @@ -14,8 +14,7 @@ package com.google.common.collect; -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; import java.util.Comparator; @@ -27,60 +26,80 @@ import javax.annotation.Nullable; * @author Jared Levy * @author Louis Wasserman */ -@GwtCompatible(emulated = true) @SuppressWarnings("serial") -final class ImmutableSortedAsList<E> extends RegularImmutableAsList<E> - implements SortedIterable<E> { +final class ImmutableSortedAsList<E> extends ImmutableList<E> implements SortedIterable<E> { + private final transient ImmutableSortedSet<E> backingSet; + private final transient ImmutableList<E> backingList; + ImmutableSortedAsList( ImmutableSortedSet<E> backingSet, ImmutableList<E> backingList) { - super(backingSet, backingList); - } - - @Override - ImmutableSortedSet<E> delegateCollection() { - return (ImmutableSortedSet<E>) super.delegateCollection(); + this.backingSet = backingSet; + this.backingList = backingList; } @Override public Comparator<? super E> comparator() { - return delegateCollection().comparator(); + return backingSet.comparator(); } - // Override indexOf() and lastIndexOf() to be O(log N) instead of O(N). + // Override contains(), indexOf(), and lastIndexOf() to be O(log N) instead of O(N). + + @Override public boolean contains(@Nullable Object target) { + // TODO: why not contains(target)? + return backingSet.indexOf(target) >= 0; + } - @GwtIncompatible("ImmutableSortedSet.indexOf") - // TODO(cpovirk): consider manual binary search under GWT to preserve O(log N) lookup @Override public int indexOf(@Nullable Object target) { - int index = delegateCollection().indexOf(target); + return backingSet.indexOf(target); + } - // TODO(kevinb): reconsider if it's really worth making feeble attempts at - // sanity for inconsistent comparators. + @Override public int lastIndexOf(@Nullable Object target) { + return backingSet.indexOf(target); + } - // The equals() check is needed when the comparator isn't compatible with - // equals(). - return (index >= 0 && get(index).equals(target)) ? index : -1; + // The returned ImmutableSortedAsList maintains the contains(), indexOf(), and + // lastIndexOf() performance benefits. + @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, size()); + return (fromIndex == toIndex) ? ImmutableList.<E>of() + : new RegularImmutableSortedSet<E>( + backingList.subList(fromIndex, toIndex), backingSet.comparator()) + .asList(); } - @GwtIncompatible("ImmutableSortedSet.indexOf") - @Override public int lastIndexOf(@Nullable Object target) { - return indexOf(target); + // The ImmutableAsList serialized form has the correct behavior. + @Override Object writeReplace() { + return new ImmutableAsList.SerializedForm(backingSet); + } + + @Override public UnmodifiableIterator<E> iterator() { + return backingList.iterator(); + } + + @Override public E get(int index) { + return backingList.get(index); + } + + @Override public UnmodifiableListIterator<E> listIterator() { + return backingList.listIterator(); + } + + @Override public UnmodifiableListIterator<E> listIterator(int index) { + return backingList.listIterator(index); + } + + @Override public int size() { + return backingList.size(); + } + + @Override public boolean equals(@Nullable Object obj) { + return backingList.equals(obj); } - @Override - public boolean contains(Object target) { - // Necessary for ISS's with comparators inconsistent with equals. - return indexOf(target) >= 0; + @Override public int hashCode() { + return backingList.hashCode(); } - @GwtIncompatible("super.subListUnchecked does not exist; inherited subList is valid if slow") - /* - * TODO(cpovirk): if we start to override indexOf/lastIndexOf under GWT, we'll want some way to - * override subList to return an ImmutableSortedAsList for better performance. Right now, I'm not - * sure there's any performance hit from our failure to override subListUnchecked under GWT - */ - @Override - ImmutableList<E> subListUnchecked(int fromIndex, int toIndex) { - return new RegularImmutableSortedSet<E>( - super.subListUnchecked(fromIndex, toIndex), comparator()) - .asList(); + @Override boolean isPartialView() { + return backingList.isPartialView(); } } diff --git a/guava/src/com/google/common/collect/ImmutableSortedMap.java b/guava/src/com/google/common/collect/ImmutableSortedMap.java index d8420fe..c700f7f 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedMap.java +++ b/guava/src/com/google/common/collect/ImmutableSortedMap.java @@ -18,17 +18,22 @@ 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.keyOrNull; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.INVERTED_INSERTION_INDEX; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; +import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.SortedLists.KeyAbsentBehavior; +import com.google.common.collect.SortedLists.KeyPresentBehavior; +import java.io.Serializable; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.NavigableMap; +import java.util.NoSuchElementException; import java.util.SortedMap; import java.util.TreeMap; @@ -48,64 +53,28 @@ 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 * @author Louis Wasserman - * @since 2.0 (imported from Google Collections Library; implements {@code - * NavigableMap} since 12.0) + * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible(serializable = true, emulated = true) -public abstract class ImmutableSortedMap<K, V> - extends ImmutableSortedMapFauxverideShim<K, V> implements NavigableMap<K, V> { +public class ImmutableSortedMap<K, V> + extends ImmutableSortedMapFauxverideShim<K, V> implements SortedMap<K, V> { /* * TODO(kevinb): Confirm that ImmutableSortedMap is faster to construct and * uses less memory than TreeMap; then say so in the class Javadoc. + * + * TODO(kevinb): Create separate subclasses for empty, single-entry, and + * multiple-entry instances, if it's deemed beneficial. */ - private static final Comparator<Comparable> NATURAL_ORDER = Ordering.natural(); - - private static final ImmutableSortedMap<Comparable, Object> NATURAL_EMPTY_MAP = - new EmptyImmutableSortedMap<Comparable, Object>(NATURAL_ORDER); - - static <K, V> ImmutableSortedMap<K, V> emptyMap(Comparator<? super K> comparator) { - if (Ordering.natural().equals(comparator)) { - return of(); - } else { - return new EmptyImmutableSortedMap<K, V>(comparator); - } - } - - static <K, V> ImmutableSortedMap<K, V> fromSortedEntries( - Comparator<? super K> comparator, - Collection<? extends Entry<? extends K, ? extends V>> entries) { - if (entries.isEmpty()) { - return emptyMap(comparator); - } - ImmutableList.Builder<K> keyBuilder = ImmutableList.builder(); - ImmutableList.Builder<V> valueBuilder = ImmutableList.builder(); - for (Entry<? extends K, ? extends V> entry : entries) { - keyBuilder.add(entry.getKey()); - valueBuilder.add(entry.getValue()); - } + private static final Comparator<Comparable> NATURAL_ORDER = + Ordering.natural(); - return new RegularImmutableSortedMap<K, V>( - new RegularImmutableSortedSet<K>(keyBuilder.build(), comparator), - valueBuilder.build()); - } - - static <K, V> ImmutableSortedMap<K, V> from( - ImmutableSortedSet<K> keySet, ImmutableList<V> valueList) { - if (keySet.isEmpty()) { - return emptyMap(keySet.comparator()); - } else { - return new RegularImmutableSortedMap<K, V>( - (RegularImmutableSortedSet<K>) keySet, - valueList); - } - } + private static final ImmutableSortedMap<Comparable, Object> + NATURAL_EMPTY_MAP = + new ImmutableSortedMap<Comparable, Object>( + ImmutableList.<Entry<Comparable, Object>>of(), NATURAL_ORDER); /** * Returns the empty sorted map. @@ -117,12 +86,24 @@ public abstract class ImmutableSortedMap<K, V> return (ImmutableSortedMap<K, V>) NATURAL_EMPTY_MAP; } + @SuppressWarnings("unchecked") + private static <K, V> ImmutableSortedMap<K, V> emptyMap( + Comparator<? super K> comparator) { + if (NATURAL_ORDER.equals(comparator)) { + return (ImmutableSortedMap<K, V>) NATURAL_EMPTY_MAP; + } else { + return new ImmutableSortedMap<K, V>( + ImmutableList.<Entry<K, V>>of(), comparator); + } + } + /** * Returns an immutable map containing a single entry. */ public static <K extends Comparable<? super K>, V> ImmutableSortedMap<K, V> of(K k1, V v1) { - return from(ImmutableSortedSet.of(k1), ImmutableList.of(v1)); + return new ImmutableSortedMap<K, V>( + ImmutableList.of(entryOf(k1, v1)), Ordering.natural()); } /** @@ -249,7 +230,7 @@ public abstract class ImmutableSortedMap<K, V> SortedMap<?, ?> sortedMap = (SortedMap<?, ?>) map; Comparator<?> comparator2 = sortedMap.comparator(); sameComparator = (comparator2 == null) - ? comparator == NATURAL_ORDER + ? comparator == NATURAL_ORDER : comparator.equals(comparator2); } @@ -264,7 +245,7 @@ public abstract class ImmutableSortedMap<K, V> } // "adding" type params to an array of a raw type should be safe as - // long as no one can ever cast that same array instance back to a + // long as no one can ever cast that same array instance back to a // raw type. @SuppressWarnings("unchecked") Entry<K, V>[] entries = map.entrySet().toArray(new Entry[0]); @@ -281,9 +262,9 @@ public abstract class ImmutableSortedMap<K, V> validateEntries(list, comparator); } - return fromSortedEntries(comparator, list); + return new ImmutableSortedMap<K, V>(ImmutableList.copyOf(list), comparator); } - + private static <K, V> void sortEntries( List<Entry<K, V>> entries, final Comparator<? super K> comparator) { Comparator<Entry<K, V>> entryComparator = new Comparator<Entry<K, V>>() { @@ -292,7 +273,7 @@ public abstract class ImmutableSortedMap<K, V> return comparator.compare(entry1.getKey(), entry2.getKey()); } }; - + Collections.sort(entries, entryComparator); } @@ -312,8 +293,13 @@ public abstract class ImmutableSortedMap<K, V> * Returns a builder that creates immutable sorted maps whose keys are * ordered by their natural ordering. The sorted maps use {@link * Ordering#natural()} as the comparator. + * + * <p>Note: the type parameter {@code K} extends {@code Comparable<K>} rather + * than {@code Comparable<? super K>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - 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()); } @@ -332,8 +318,13 @@ public abstract class ImmutableSortedMap<K, V> /** * Returns a builder that creates immutable sorted maps whose keys are * ordered by the reverse of their natural ordering. + * + * <p>Note: the type parameter {@code K} extends {@code Comparable<K>} rather + * than {@code Comparable<? super K>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - 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()); } @@ -414,48 +405,209 @@ public abstract class ImmutableSortedMap<K, V> @Override public ImmutableSortedMap<K, V> build() { sortEntries(entries, comparator); validateEntries(entries, comparator); - return fromSortedEntries(comparator, entries); + return new ImmutableSortedMap<K, V>( + ImmutableList.copyOf(entries), comparator); } } - ImmutableSortedMap() { - } + final transient ImmutableList<Entry<K, V>> entries; + private final transient Comparator<? super K> comparator; - ImmutableSortedMap(ImmutableSortedMap<K, V> descendingMap) { - this.descendingMap = descendingMap; + ImmutableSortedMap( + ImmutableList<Entry<K, V>> entries, Comparator<? super K> comparator) { + this.entries = entries; + this.comparator = comparator; } @Override public int size() { - return values().size(); + return entries.size(); + } + + // Pretend the comparator can compare anything. If it turns out it can't + // compare two elements, it'll throw a CCE. Only methods that are specified to + // throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator<Object> unsafeComparator() { + return (Comparator<Object>) comparator; + } + + @Override public V get(@Nullable Object key) { + if (key == null) { + return null; + } + int i; + try { + i = index(key, ANY_PRESENT, INVERTED_INSERTION_INDEX); + } catch (ClassCastException e) { + return null; + } + return i >= 0 ? entries.get(i).getValue() : null; } @Override public boolean containsValue(@Nullable Object value) { - return values().contains(value); + if (value == null) { + return false; + } + return Iterators.contains(valueIterator(), value); } @Override boolean isPartialView() { - return keySet().isPartialView() || values().isPartialView(); + return entries.isPartialView(); } + private transient ImmutableSet<Entry<K, V>> entrySet; + /** * Returns an immutable set of the mappings in this map, sorted by the key * ordering. */ @Override public ImmutableSet<Entry<K, V>> entrySet() { - return super.entrySet(); + ImmutableSet<Entry<K, V>> es = entrySet; + return (es == null) ? (entrySet = createEntrySet()) : es; } + private ImmutableSet<Entry<K, V>> createEntrySet() { + return isEmpty() ? ImmutableSet.<Entry<K, V>>of() + : new EntrySet<K, V>(this); + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class EntrySet<K, V> extends ImmutableSet<Entry<K, V>> { + final transient ImmutableSortedMap<K, V> map; + + EntrySet(ImmutableSortedMap<K, V> map) { + this.map = map; + } + + @Override boolean isPartialView() { + return map.isPartialView(); + } + + @Override + public int size() { + return map.size(); + } + + @Override public UnmodifiableIterator<Entry<K, V>> iterator() { + return map.entries.iterator(); + } + + @Override public boolean contains(Object target) { + if (target instanceof Entry) { + Entry<?, ?> entry = (Entry<?, ?>) target; + V mappedValue = map.get(entry.getKey()); + return mappedValue != null && mappedValue.equals(entry.getValue()); + } + return false; + } + + @Override Object writeReplace() { + return new EntrySetSerializedForm<K, V>(map); + } + } + + private static class EntrySetSerializedForm<K, V> implements Serializable { + final ImmutableSortedMap<K, V> map; + EntrySetSerializedForm(ImmutableSortedMap<K, V> map) { + this.map = map; + } + Object readResolve() { + return map.entrySet(); + } + private static final long serialVersionUID = 0; + } + + private transient ImmutableSortedSet<K> keySet; + /** * Returns an immutable sorted set of the keys in this map. */ - @Override public abstract ImmutableSortedSet<K> keySet(); + @Override public ImmutableSortedSet<K> keySet() { + ImmutableSortedSet<K> ks = keySet; + return (ks == null) ? (keySet = createKeySet()) : ks; + } + + @SuppressWarnings("serial") // does not use default serialization + private ImmutableSortedSet<K> createKeySet() { + if (isEmpty()) { + return ImmutableSortedSet.emptySet(comparator); + } + + return new RegularImmutableSortedSet<K>( + new TransformedImmutableList<Entry<K, V>, K>(entries) { + + @Override K transform(Entry<K, V> entry) { + return entry.getKey(); + } + }, comparator); + } + + private transient ImmutableCollection<V> values; /** * Returns an immutable collection of the values in this map, sorted by the * ordering of the corresponding keys. */ - @Override public abstract ImmutableCollection<V> values(); + @Override public ImmutableCollection<V> values() { + ImmutableCollection<V> v = values; + return (v == null) ? (values = new Values<V>(this)) : v; + } + + UnmodifiableIterator<V> valueIterator(){ + final UnmodifiableIterator<Entry<K, V>> entryIterator = entries.iterator(); + return new UnmodifiableIterator<V>() { + + @Override public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override public V next() { + return entryIterator.next().getValue(); + } + }; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class Values<V> extends ImmutableCollection<V> { + private final ImmutableSortedMap<?, V> map; + + Values(ImmutableSortedMap<?, V> map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override public UnmodifiableIterator<V> iterator() { + return map.valueIterator(); + } + + @Override public boolean contains(Object target) { + return map.containsValue(target); + } + + @Override boolean isPartialView() { + return true; + } + + @Override Object writeReplace() { + return new ValuesSerializedForm<V>(map); + } + } + + private static class ValuesSerializedForm<V> implements Serializable { + final ImmutableSortedMap<?, V> map; + ValuesSerializedForm(ImmutableSortedMap<?, V> map) { + this.map = map; + } + Object readResolve() { + return map.values(); + } + private static final long serialVersionUID = 0; + } /** * Returns the comparator that orders the keys, which is @@ -465,17 +617,23 @@ public abstract class ImmutableSortedMap<K, V> */ @Override public Comparator<? super K> comparator() { - return keySet().comparator(); + return comparator; } @Override public K firstKey() { - return keySet().first(); + if (isEmpty()) { + throw new NoSuchElementException(); + } + return entries.get(0).getKey(); } @Override public K lastKey() { - return keySet().last(); + if (isEmpty()) { + throw new NoSuchElementException(); + } + return entries.get(size() - 1).getKey(); } /** @@ -493,20 +651,15 @@ public abstract class ImmutableSortedMap<K, V> return headMap(toKey, false); } - /** - * This method returns a {@code ImmutableSortedMap}, consisting of the entries - * whose keys are less than (or equal to, if {@code inclusive}) {@code toKey}. - * - * <p>The {@link SortedMap#headMap} documentation states that a submap of a - * submap throws an {@link IllegalArgumentException} if passed a {@code toKey} - * greater than an earlier {@code toKey}. However, this method doesn't throw - * an exception in that situation, but instead keeps the original {@code - * toKey}. - * - * @since 12.0 - */ - @Override - public abstract ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive); + ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive){ + int index; + if (inclusive) { + index = index(toKey, ANY_PRESENT, NEXT_LOWER) + 1; + } else { + index = index(toKey, ANY_PRESENT, NEXT_HIGHER); + } + return createSubmap(0, index); + } /** * This method returns a {@code ImmutableSortedMap}, consisting of the entries @@ -526,29 +679,12 @@ public abstract class ImmutableSortedMap<K, V> return subMap(fromKey, true, toKey, false); } - /** - * This method returns a {@code ImmutableSortedMap}, consisting of the entries - * whose keys ranges from {@code fromKey} to {@code toKey}, inclusive or - * exclusive as indicated by the boolean flags. - * - * <p>The {@link SortedMap#subMap} documentation states that a submap of a - * submap throws an {@link IllegalArgumentException} if passed a {@code - * fromKey} less than an earlier {@code fromKey}. However, this method doesn't - * throw an exception in that situation, but instead keeps the original {@code - * fromKey}. Similarly, this method keeps the original {@code toKey}, instead - * of throwing an exception, if passed a {@code toKey} greater than an earlier - * {@code toKey}. - * - * @since 12.0 - */ - @Override - public ImmutableSortedMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, + ImmutableSortedMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { checkNotNull(fromKey); checkNotNull(toKey); - checkArgument(comparator().compare(fromKey, toKey) <= 0, - "expected fromKey <= toKey but %s > %s", fromKey, toKey); - return headMap(toKey, toInclusive).tailMap(fromKey, fromInclusive); + checkArgument(comparator.compare(fromKey, toKey) <= 0); + return tailMap(fromKey, fromInclusive).headMap(toKey, toInclusive); } /** @@ -566,117 +702,39 @@ public abstract class ImmutableSortedMap<K, V> return tailMap(fromKey, true); } - /** - * This method returns a {@code ImmutableSortedMap}, consisting of the entries - * whose keys are greater than (or equal to, if {@code inclusive}) - * {@code fromKey}. - * - * <p>The {@link SortedMap#tailMap} documentation states that a submap of a - * submap throws an {@link IllegalArgumentException} if passed a {@code - * fromKey} less than an earlier {@code fromKey}. However, this method doesn't - * throw an exception in that situation, but instead keeps the original {@code - * fromKey}. - * - * @since 12.0 - */ - @Override - public abstract ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive); - - @Override - public Entry<K, V> lowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - public K lowerKey(K key) { - return keyOrNull(lowerEntry(key)); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - public K floorKey(K key) { - return keyOrNull(floorEntry(key)); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - public K ceilingKey(K key) { - return keyOrNull(ceilingEntry(key)); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K higherKey(K key) { - return keyOrNull(higherEntry(key)); - } - - @Override - public Entry<K, V> firstEntry() { - return isEmpty() ? null : entrySet().asList().get(0); - } - - @Override - public Entry<K, V> lastEntry() { - return isEmpty() ? null : entrySet().asList().get(size() - 1); + ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) { + int index; + if (inclusive) { + index = index(fromKey, ANY_PRESENT, NEXT_HIGHER); + } else { + index = index(fromKey, ANY_PRESENT, NEXT_LOWER) + 1; + } + return createSubmap(index, size()); } - /** - * Guaranteed to throw an exception and leave the map unmodified. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - public final Entry<K, V> pollFirstEntry() { - throw new UnsupportedOperationException(); + private ImmutableList<K> keyList() { + return new TransformedImmutableList<Entry<K, V>, K>(entries) { + @Override + K transform(Entry<K, V> entry) { + return entry.getKey(); + } + }; } - /** - * Guaranteed to throw an exception and leave the map unmodified. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @Override - public final Entry<K, V> pollLastEntry() { - throw new UnsupportedOperationException(); + private int index( + Object key, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { + return SortedLists.binarySearch( + keyList(), checkNotNull(key), unsafeComparator(), presentBehavior, absentBehavior); } - private transient ImmutableSortedMap<K, V> descendingMap; - - @Override - public ImmutableSortedMap<K, V> descendingMap() { - ImmutableSortedMap<K, V> result = descendingMap; - if (result == null) { - result = descendingMap = createDescendingMap(); + private ImmutableSortedMap<K, V> createSubmap( + int newFromIndex, int newToIndex) { + if (newFromIndex < newToIndex) { + return new ImmutableSortedMap<K, V>( + entries.subList(newFromIndex, newToIndex), comparator); + } else { + return emptyMap(comparator); } - return result; - } - - abstract ImmutableSortedMap<K, V> createDescendingMap(); - - @Override - public ImmutableSortedSet<K> navigableKeySet() { - return keySet(); - } - - @Override - public ImmutableSortedSet<K> descendingKeySet() { - return keySet().descendingSet(); } /** diff --git a/guava/src/com/google/common/collect/ImmutableSortedMultiset.java b/guava/src/com/google/common/collect/ImmutableSortedMultiset.java index dadcfb5..82f4abe 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/ImmutableSortedMultiset.java @@ -14,13 +14,12 @@ 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.GwtIncompatible; import java.io.Serializable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -62,7 +61,7 @@ import java.util.List; * * {(x, y) | x.compareTo(y) == 0}}</pre> * - * <b>Warning:</b> Like most multisets, an {@code ImmutableSortedMultiset} will not function + * <b>Warning:</b> Like most multisets, an {@code ImmutableSortedMultiset} will not function * correctly if an element is modified after being placed in the multiset. For this reason, and to * avoid general confusion, it is strongly recommended to place only immutable objects into this * collection. @@ -70,16 +69,10 @@ import java.util.List; * <p><b>Note:</b> Although this class is not final, it cannot be subclassed as it has no public or * protected constructors. Thus, instances of this type 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 Louis Wasserman - * @since 12.0 */ -@Beta @GwtIncompatible("hasn't been tested yet") -public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultisetFauxverideShim<E> +abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultisetFauxverideShim<E> implements SortedMultiset<E> { // TODO(user): GWT compatibility @@ -100,11 +93,8 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * Returns an immutable sorted multiset containing a single element. */ public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E element) { - RegularImmutableSortedSet<E> elementSet = - (RegularImmutableSortedSet<E>) ImmutableSortedSet.of(element); - int[] counts = {1}; - long[] cumulativeCounts = {0, 1}; - return new RegularImmutableSortedMultiset<E>(elementSet, counts, cumulativeCounts, 0, 1); + return RegularImmutableSortedMultiset.createFromSorted( + NATURAL_ORDER, ImmutableList.of(Multisets.immutableEntry(checkNotNull(element), 1))); } /** @@ -161,9 +151,15 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset */ @SuppressWarnings("unchecked") public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( - E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + E e1, + E e2, + E e3, + E e4, + E e5, + E e6, + E... remaining) { int size = remaining.length + 6; - List<E> all = Lists.newArrayListWithCapacity(size); + List<E> all = new ArrayList<E>(size); Collections.addAll(all, e1, e2, e3, e4, e5, e6); Collections.addAll(all, remaining); return copyOf(Ordering.natural(), all); @@ -224,7 +220,7 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset // Unsafe, see ImmutableSortedMultisetFauxverideShim. @SuppressWarnings("unchecked") Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); - return copyOf(naturalOrder, elements); + return copyOfInternal(naturalOrder, elements); } /** @@ -236,7 +232,7 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset public static <E> ImmutableSortedMultiset<E> copyOf( Comparator<? super E> comparator, Iterator<? extends E> elements) { checkNotNull(comparator); - return new Builder<E>(comparator).addAll(elements).build(); + return copyOfInternal(comparator, elements); } /** @@ -251,21 +247,8 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset */ public static <E> ImmutableSortedMultiset<E> copyOf( Comparator<? super E> comparator, Iterable<? extends E> elements) { - if (elements instanceof ImmutableSortedMultiset) { - @SuppressWarnings("unchecked") // immutable collections are always safe for covariant casts - ImmutableSortedMultiset<E> multiset = (ImmutableSortedMultiset<E>) elements; - if (comparator.equals(multiset.comparator())) { - if (multiset.isPartialView()) { - return copyOfSortedEntries(comparator, multiset.entrySet().asList()); - } else { - return multiset; - } - } - } - elements = Lists.newArrayList(elements); // defensive copy - TreeMultiset<E> sortedCopy = TreeMultiset.create(checkNotNull(comparator)); - Iterables.addAll(sortedCopy, elements); - return copyOfSortedEntries(comparator, sortedCopy.entrySet()); + checkNotNull(comparator); + return copyOfInternal(comparator, elements); } /** @@ -282,29 +265,50 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * * @throws NullPointerException if {@code sortedMultiset} or any of its elements is null */ + @SuppressWarnings("unchecked") public static <E> ImmutableSortedMultiset<E> copyOfSorted(SortedMultiset<E> sortedMultiset) { - return copyOfSortedEntries(sortedMultiset.comparator(), - Lists.newArrayList(sortedMultiset.entrySet())); + Comparator<? super E> comparator = sortedMultiset.comparator(); + if (comparator == null) { + comparator = (Comparator<? super E>) NATURAL_ORDER; + } + return copyOfInternal(comparator, sortedMultiset); + } + + @SuppressWarnings("unchecked") + private static <E> ImmutableSortedMultiset<E> copyOfInternal( + Comparator<? super E> comparator, Iterable<? extends E> iterable) { + if (SortedIterables.hasSameComparator(comparator, iterable) + && iterable instanceof ImmutableSortedMultiset<?>) { + ImmutableSortedMultiset<E> multiset = (ImmutableSortedMultiset<E>) iterable; + if (!multiset.isPartialView()) { + return (ImmutableSortedMultiset<E>) iterable; + } + } + ImmutableList<Entry<E>> entries = + (ImmutableList) ImmutableList.copyOf(SortedIterables.sortedCounts(comparator, iterable)); + if (entries.isEmpty()) { + return emptyMultiset(comparator); + } + verifyEntries(entries); + return RegularImmutableSortedMultiset.createFromSorted(comparator, entries); } - private static <E> ImmutableSortedMultiset<E> copyOfSortedEntries( - Comparator<? super E> comparator, Collection<Entry<E>> entries) { + private static <E> ImmutableSortedMultiset<E> copyOfInternal( + Comparator<? super E> comparator, Iterator<? extends E> iterator) { + @SuppressWarnings("unchecked") // We can safely cast from IL<Entry<? extends E>> to IL<Entry<E>> + ImmutableList<Entry<E>> entries = + (ImmutableList) ImmutableList.copyOf(SortedIterables.sortedCounts(comparator, iterator)); if (entries.isEmpty()) { return emptyMultiset(comparator); } - ImmutableList.Builder<E> elementsBuilder = new ImmutableList.Builder<E>(entries.size()); - int[] counts = new int[entries.size()]; - long[] cumulativeCounts = new long[entries.size() + 1]; - int i = 0; + verifyEntries(entries); + return RegularImmutableSortedMultiset.createFromSorted(comparator, entries); + } + + private static <E> void verifyEntries(Collection<Entry<E>> entries) { for (Entry<E> entry : entries) { - elementsBuilder.add(entry.getElement()); - counts[i] = entry.getCount(); - cumulativeCounts[i + 1] = cumulativeCounts[i] + counts[i]; - i++; + checkNotNull(entry.getElement()); } - return new RegularImmutableSortedMultiset<E>( - new RegularImmutableSortedSet<E>(elementsBuilder.build(), comparator), - counts, cumulativeCounts, 0, entries.size()); } @SuppressWarnings("unchecked") @@ -315,15 +319,49 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset return new EmptyImmutableSortedMultiset<E>(comparator); } - ImmutableSortedMultiset() {} + private final transient Comparator<? super E> comparator; + + ImmutableSortedMultiset(Comparator<? super E> comparator) { + this.comparator = checkNotNull(comparator); + } @Override - public final Comparator<? super E> comparator() { - return elementSet().comparator(); + public Comparator<? super E> comparator() { + return comparator; } + // Pretend the comparator can compare anything. If it turns out it can't + // compare two elements, it'll throw a CCE. Only methods that are specified to + // throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator<Object> unsafeComparator() { + return (Comparator<Object>) comparator; + } + + private transient Comparator<? super E> reverseComparator; + + Comparator<? super E> reverseComparator() { + Comparator<? super E> result = reverseComparator; + if (result == null) { + return reverseComparator = Ordering.from(comparator).<E>reverse(); + } + return result; + } + + private transient ImmutableSortedSet<E> elementSet; + @Override - public abstract ImmutableSortedSet<E> elementSet(); + public ImmutableSortedSet<E> elementSet() { + ImmutableSortedSet<E> result = elementSet; + if (result == null) { + return elementSet = createElementSet(); + } + return result; + } + + abstract ImmutableSortedSet<E> createElementSet(); + + abstract ImmutableSortedSet<E> createDescendingElementSet(); transient ImmutableSortedMultiset<E> descendingMultiset; @@ -336,15 +374,13 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset return result; } + abstract UnmodifiableIterator<Entry<E>> descendingEntryIterator(); + /** * {@inheritDoc} * * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final Entry<E> pollFirstEntry() { throw new UnsupportedOperationException(); @@ -354,13 +390,9 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * {@inheritDoc} * * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override - public final Entry<E> pollLastEntry() { + public Entry<E> pollLastEntry() { throw new UnsupportedOperationException(); } @@ -370,8 +402,6 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset @Override public ImmutableSortedMultiset<E> subMultiset( E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { - checkArgument(comparator().compare(lowerBound, upperBound) <= 0, - "Expected lowerBound <= upperBound but %s > %s", lowerBound, upperBound); return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); } @@ -432,8 +462,6 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset * * Builder instances can be reused; it is safe to call {@link #build} multiple times to build * multiple multisets in series. - * - * @since 12.0 */ public static class Builder<E> extends ImmutableMultiset.Builder<E> { private final Comparator<? super E> comparator; @@ -538,7 +566,7 @@ public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultiset */ @Override public ImmutableSortedMultiset<E> build() { - return copyOfSorted((SortedMultiset<E>) contents); + return copyOf(comparator, contents); } } diff --git a/guava/src/com/google/common/collect/ImmutableSortedSet.java b/guava/src/com/google/common/collect/ImmutableSortedSet.java index cfeb8a5..b2d871f 100644 --- a/guava/src/com/google/common/collect/ImmutableSortedSet.java +++ b/guava/src/com/google/common/collect/ImmutableSortedSet.java @@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.io.InvalidObjectException; import java.io.ObjectInputStream; @@ -32,7 +31,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import java.util.NavigableSet; import java.util.SortedSet; import javax.annotation.Nullable; @@ -80,20 +78,16 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this type 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>. - * * @see ImmutableSet * @author Jared Levy * @author Louis Wasserman - * @since 2.0 (imported from Google Collections Library; implements {@code NavigableSet} since 12.0) + * @since 2.0 (imported from Google Collections Library) */ // TODO(benyu): benchmark and optimize all creation paths, which are a mess now @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // we're overriding default serialization public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxverideShim<E> - implements NavigableSet<E>, SortedIterable<E> { + implements SortedSet<E>, SortedIterable<E> { private static final Comparator<Comparable> NATURAL_ORDER = Ordering.natural(); @@ -182,7 +176,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride E e1, E e2, E e3, E e4, E e5) { return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4, e5)); } - + /** * Returns an immutable sorted set containing the given elements sorted by * their natural ordering. When multiple elements are equivalent according to @@ -306,7 +300,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride // Unsafe, see ImmutableSortedSetFauxverideShim. @SuppressWarnings("unchecked") Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); - return copyOf(naturalOrder, elements); + return copyOfInternal(naturalOrder, elements); } /** @@ -320,7 +314,8 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride */ public static <E> ImmutableSortedSet<E> copyOf( Comparator<? super E> comparator, Iterator<? extends E> elements) { - return copyOf(comparator, Lists.newArrayList(elements)); + checkNotNull(comparator); + return copyOfInternal(comparator, elements); } /** @@ -339,19 +334,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride public static <E> ImmutableSortedSet<E> copyOf( Comparator<? super E> comparator, Iterable<? extends E> elements) { checkNotNull(comparator); - boolean hasSameComparator = - SortedIterables.hasSameComparator(comparator, elements); - - if (hasSameComparator && (elements instanceof ImmutableSortedSet)) { - @SuppressWarnings("unchecked") - ImmutableSortedSet<E> original = (ImmutableSortedSet<E>) elements; - if (!original.isPartialView()) { - return original; - } - } - @SuppressWarnings("unchecked") // elements only contains E's; it's safe. - E[] array = (E[]) Iterables.toArray(elements); - return construct(comparator, array.length, array); + return copyOfInternal(comparator, elements); } /** @@ -374,7 +357,8 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride */ public static <E> ImmutableSortedSet<E> copyOf( Comparator<? super E> comparator, Collection<? extends E> elements) { - return copyOf(comparator, (Iterable<? extends E>) elements); + checkNotNull(comparator); + return copyOfInternal(comparator, elements); } /** @@ -394,69 +378,41 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride * @throws NullPointerException if {@code sortedSet} or any of its elements * is null */ + @SuppressWarnings("unchecked") public static <E> ImmutableSortedSet<E> copyOfSorted(SortedSet<E> sortedSet) { - Comparator<? super E> comparator = SortedIterables.comparator(sortedSet); - Object[] elements = sortedSet.toArray(); - if (elements.length == 0) { - return emptySet(comparator); - } else { - return new RegularImmutableSortedSet<E>( - ImmutableList.<E>asImmutableList(elements), comparator); + Comparator<? super E> comparator = sortedSet.comparator(); + if (comparator == null) { + comparator = (Comparator<? super E>) NATURAL_ORDER; } + return copyOfInternal(comparator, sortedSet); } - /** - * Sorts and eliminates duplicates from the first {@code n} positions in {@code contents}. - * Returns the number of unique elements. If this returns {@code k}, then the first {@code k} - * elements of {@code contents} will be the sorted, unique elements, and {@code - * contents[i] == null} for {@code k <= i < n}. - * - * @throws NullPointerException if any of the first {@code n} elements of {@code contents} is - * null - */ - static <E> int sortAndUnique( - Comparator<? super E> comparator, int n, E... contents) { - if (n == 0) { - return 0; - } - for (int i = 0; i < n; i++) { - ObjectArrays.checkElementNotNull(contents[i], i); - } - Arrays.sort(contents, 0, n, comparator); - int uniques = 1; - for (int i = 1; i < n; i++) { - E cur = contents[i]; - E prev = contents[uniques - 1]; - if (comparator.compare(cur, prev) != 0) { - contents[uniques++] = cur; + private static <E> ImmutableSortedSet<E> copyOfInternal( + Comparator<? super E> comparator, Iterable<? extends E> elements) { + boolean hasSameComparator = + SortedIterables.hasSameComparator(comparator, elements); + + if (hasSameComparator && (elements instanceof ImmutableSortedSet)) { + @SuppressWarnings("unchecked") + ImmutableSortedSet<E> original = (ImmutableSortedSet<E>) elements; + if (!original.isPartialView()) { + return original; } } - Arrays.fill(contents, uniques, n, null); - return uniques; + ImmutableList<E> list = ImmutableList.copyOf( + SortedIterables.sortedUnique(comparator, elements)); + return list.isEmpty() + ? ImmutableSortedSet.<E>emptySet(comparator) + : new RegularImmutableSortedSet<E>(list, comparator); } - /** - * Constructs an {@code ImmutableSortedSet} from the first {@code n} elements of - * {@code contents}. If {@code k} is the size of the returned {@code ImmutableSortedSet}, then - * the sorted unique elements are in the first {@code k} positions of {@code contents}, and - * {@code contents[i] == null} for {@code k <= i < n}. - * - * <p>If {@code k == contents.length}, then {@code contents} may no longer be safe for - * modification. - * - * @throws NullPointerException if any of the first {@code n} elements of {@code contents} is - * null - */ - static <E> ImmutableSortedSet<E> construct( - Comparator<? super E> comparator, int n, E... contents) { - int uniques = sortAndUnique(comparator, n, contents); - if (uniques == 0) { - return emptySet(comparator); - } else if (uniques < contents.length) { - contents = ObjectArrays.arraysCopyOf(contents, uniques); - } - return new RegularImmutableSortedSet<E>( - ImmutableList.<E>asImmutableList(contents), comparator); + private static <E> ImmutableSortedSet<E> copyOfInternal( + Comparator<? super E> comparator, Iterator<? extends E> elements) { + ImmutableList<E> list = + ImmutableList.copyOf(SortedIterables.sortedUnique(comparator, elements)); + return list.isEmpty() + ? ImmutableSortedSet.<E>emptySet(comparator) + : new RegularImmutableSortedSet<E>(list, comparator); } /** @@ -474,8 +430,13 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride /** * Returns a builder that creates immutable sorted sets whose elements are * ordered by the reverse of their natural ordering. + * + * <p>Note: the type parameter {@code E} extends {@code Comparable<E>} rather + * than {@code Comparable<? super E>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - public static <E extends Comparable<?>> Builder<E> reverseOrder() { + public static <E extends Comparable<E>> Builder<E> reverseOrder() { return new Builder<E>(Ordering.natural().reverse()); } @@ -485,8 +446,13 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride * Ordering#natural()} as the comparator. This method provides more * type-safety than {@link #builder}, as it can be called only for classes * that implement {@link Comparable}. + * + * <p>Note: the type parameter {@code E} extends {@code Comparable<E>} rather + * than {@code Comparable<? super E>} as a workaround for javac <a + * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug + * 6468354</a>. */ - public static <E extends Comparable<?>> Builder<E> naturalOrder() { + public static <E extends Comparable<E>> Builder<E> naturalOrder() { return new Builder<E>(Ordering.natural()); } @@ -577,11 +543,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride * of the {@code Builder} and its comparator. */ @Override public ImmutableSortedSet<E> build() { - @SuppressWarnings("unchecked") // we're careful to put only E's in here - E[] contentsArray = (E[]) contents; - ImmutableSortedSet<E> result = construct(comparator, size, contentsArray); - this.size = result.size(); // we eliminated duplicates in-place in contentsArray - return result; + return copyOfInternal(comparator, contents.iterator()); } } @@ -636,12 +598,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride return headSet(toElement, false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> headSet(E toElement, boolean inclusive) { + ImmutableSortedSet<E> headSet(E toElement, boolean inclusive) { return headSetImpl(checkNotNull(toElement), inclusive); } @@ -663,12 +620,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride return subSet(fromElement, true, toElement, false); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> subSet( + ImmutableSortedSet<E> subSet( E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { checkNotNull(fromElement); checkNotNull(toElement); @@ -692,12 +644,7 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride return tailSet(fromElement, true); } - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> tailSet(E fromElement, boolean inclusive) { + ImmutableSortedSet<E> tailSet(E fromElement, boolean inclusive) { return tailSetImpl(checkNotNull(fromElement), inclusive); } @@ -713,110 +660,6 @@ public abstract class ImmutableSortedSet<E> extends ImmutableSortedSetFauxveride abstract ImmutableSortedSet<E> tailSetImpl(E fromElement, boolean inclusive); /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E lower(E e) { - return Iterators.getNext(headSet(e, false).descendingIterator(), null); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E floor(E e) { - return Iterators.getNext(headSet(e, true).descendingIterator(), null); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E ceiling(E e) { - return Iterables.getFirst(tailSet(e, true), null); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public E higher(E e) { - return Iterables.getFirst(tailSet(e, false), null); - } - - @Override - public E first() { - return iterator().next(); - } - - @Override - public E last() { - return descendingIterator().next(); - } - - /** - * Guaranteed to throw an exception and leave the set unmodified. - * - * @since 12.0 - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @GwtIncompatible("NavigableSet") - @Override - public final E pollFirst() { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the set unmodified. - * - * @since 12.0 - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ - @Deprecated - @GwtIncompatible("NavigableSet") - @Override - public final E pollLast() { - throw new UnsupportedOperationException(); - } - - @GwtIncompatible("NavigableSet") - transient ImmutableSortedSet<E> descendingSet; - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public ImmutableSortedSet<E> descendingSet() { - // racy single-check idiom - ImmutableSortedSet<E> result = descendingSet; - if (result == null) { - result = descendingSet = createDescendingSet(); - result.descendingSet = this; - } - return result; - } - - @GwtIncompatible("NavigableSet") - ImmutableSortedSet<E> createDescendingSet() { - return new DescendingImmutableSortedSet<E>(this); - } - - /** - * @since 12.0 - */ - @GwtIncompatible("NavigableSet") - @Override - public abstract UnmodifiableIterator<E> descendingIterator(); - - /** * Returns the position of an element within the set, or -1 if not present. */ abstract int indexOf(@Nullable Object target); diff --git a/guava/src/com/google/common/collect/ImmutableTable.java b/guava/src/com/google/common/collect/ImmutableTable.java index 8296a83..debd49b 100644 --- a/guava/src/com/google/common/collect/ImmutableTable.java +++ b/guava/src/com/google/common/collect/ImmutableTable.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. @@ -18,6 +18,7 @@ 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 java.util.Comparator; @@ -34,13 +35,10 @@ import javax.annotation.Nullable; * it has no public or protected constructors. Thus, instances of this class are * guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) * @since 11.0 */ +@Beta @GwtCompatible // TODO(gak): make serializable public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { @@ -72,7 +70,7 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { */ public static final <R, C, V> ImmutableTable<R, C, V> copyOf( Table<? extends R, ? extends C, ? extends V> table) { - if (table instanceof ImmutableTable) { + if (table instanceof ImmutableTable<?, ?, ?>) { @SuppressWarnings("unchecked") ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table; @@ -106,7 +104,7 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { /** * Returns a new builder. The generated builder is equivalent to the builder - * created by the {@link Builder#ImmutableTable.Builder()} constructor. + * created by the {@link Builder#Builder()} constructor. */ public static final <R, C, V> Builder<R, C, V> builder() { return new Builder<R, C, V>(); @@ -256,8 +254,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { /** * {@inheritDoc} * - * <p>The value {@code Map<R, V>} instances in the returned map are - * {@link ImmutableMap} instances as well. + * <p>The value {@code Map<R, V>}s in the returned map are + * {@link ImmutableMap}s as well. */ @Override public abstract ImmutableMap<C, Map<R, V>> columnMap(); @@ -273,8 +271,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { /** * {@inheritDoc} * - * <p>The value {@code Map<C, V>} instances in the returned map are - * {@link ImmutableMap} instances as well. + * <p>The value {@code Map<C, V>}s in the returned map are + * {@link ImmutableMap}s as well. */ @Override public abstract ImmutableMap<R, Map<C, V>> rowMap(); @@ -282,9 +280,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void clear() { + @Override public final void clear() { throw new UnsupportedOperationException(); } @@ -292,9 +289,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V put(R rowKey, C columnKey, V value) { + @Override public final V put(R rowKey, C columnKey, V value) { throw new UnsupportedOperationException(); } @@ -302,9 +298,8 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void putAll( + @Override public final void putAll( Table<? extends R, ? extends C, ? extends V> table) { throw new UnsupportedOperationException(); } @@ -313,16 +308,15 @@ public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> { * Guaranteed to throw an exception and leave the table unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final V remove(Object rowKey, Object columnKey) { + @Override public final V remove(Object rowKey, Object columnKey) { throw new UnsupportedOperationException(); } @Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; - } else if (obj instanceof Table) { + } else if (obj instanceof Table<?, ?, ?>) { Table<?, ?, ?> that = (Table<?, ?, ?>) obj; return this.cellSet().equals(that.cellSet()); } else { diff --git a/guava/src/com/google/common/collect/Interners.java b/guava/src/com/google/common/collect/Interners.java index bd592d6..f5a6c85 100644 --- a/guava/src/com/google/common/collect/Interners.java +++ b/guava/src/com/google/common/collect/Interners.java @@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Function; import com.google.common.collect.MapMakerInternalMap.ReferenceEntry; @@ -51,24 +51,16 @@ public final class Interners { }; } - /** - * Returns a new thread-safe interner which retains a weak reference to each instance it has - * interned, and so does not prevent these instances from being garbage-collected. This most - * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative - * when the memory usage of that implementation is unacceptable. Note that unlike {@link - * String#intern}, using this interner does not consume memory in the permanent generation. - */ - @GwtIncompatible("java.lang.ref.WeakReference") - public static <E> Interner<E> newWeakInterner() { - return new WeakInterner<E>(); - } - - private static class WeakInterner<E> implements Interner<E> { + private static class CustomInterner<E> implements Interner<E> { // MapMaker is our friend, we know about this type - private final MapMakerInternalMap<E, Dummy> map = new MapMaker() - .weakKeys() - .keyEquivalence(Equivalence.equals()) + private final MapMakerInternalMap<E, Dummy> map; + + CustomInterner(GenericMapMaker<? super E, Object> mm) { + this.map = mm + .strongValues() + .keyEquivalence(Equivalences.equals()) .makeCustomMap(); + } @Override public E intern(E sample) { while (true) { @@ -100,6 +92,18 @@ public final class Interners { } /** + * Returns a new thread-safe interner which retains a weak reference to each instance it has + * interned, and so does not prevent these instances from being garbage-collected. This most + * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative + * when the memory usage of that implementation is unacceptable. Note that unlike {@link + * String#intern}, using this interner does not consume memory in the permanent generation. + */ + @GwtIncompatible("java.lang.ref.WeakReference") + public static <E> Interner<E> newWeakInterner() { + return new CustomInterner<E>(new MapMaker().weakKeys()); + } + + /** * Returns a function that delegates to the {@link Interner#intern} method of the given interner. * * @since 8.0 @@ -125,7 +129,7 @@ public final class Interners { } @Override public boolean equals(Object other) { - if (other instanceof InternerFunction) { + if (other instanceof InternerFunction<?>) { InternerFunction<?> that = (InternerFunction<?>) other; return interner.equals(that.interner); } diff --git a/guava/src/com/google/common/collect/Iterables.java b/guava/src/com/google/common/collect/Iterables.java index 39ad2f2..a92c2b2 100644 --- a/guava/src/com/google/common/collect/Iterables.java +++ b/guava/src/com/google/common/collect/Iterables.java @@ -23,6 +23,7 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; 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; @@ -31,6 +32,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -50,10 +52,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) @@ -84,7 +82,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) { @@ -113,14 +111,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); } @@ -242,13 +246,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()); } @@ -278,9 +275,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); } @@ -372,7 +368,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); @@ -487,7 +483,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)); @@ -538,7 +534,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); @@ -567,7 +563,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); @@ -583,7 +579,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); @@ -607,7 +603,7 @@ public final class Iterables { final Iterable<?> unfiltered, final Class<T> type) { checkNotNull(unfiltered); checkNotNull(type); - return new FluentIterable<T>() { + return new IterableWithToString<T>() { @Override public Iterator<T> iterator() { return Iterators.filter(unfiltered.iterator(), type); @@ -616,7 +612,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) { @@ -635,8 +632,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 @@ -654,8 +651,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); } @@ -707,7 +703,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); @@ -760,8 +756,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); @@ -777,16 +773,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); } @@ -827,17 +818,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); } @@ -847,7 +837,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(); } @@ -885,7 +875,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? @@ -896,12 +886,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 @@ -957,7 +947,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); @@ -986,7 +976,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); @@ -996,7 +986,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()); @@ -1023,6 +1013,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 @@ -1038,6 +1052,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. @@ -1056,7 +1107,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/src/com/google/common/collect/Iterators.java b/guava/src/com/google/common/collect/Iterators.java index 39e13c3..847d1dd 100644 --- a/guava/src/com/google/common/collect/Iterators.java +++ b/guava/src/com/google/common/collect/Iterators.java @@ -24,7 +24,6 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; 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; @@ -38,7 +37,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; @@ -54,10 +52,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) @@ -66,8 +60,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; @@ -76,22 +70,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; - } }; /** @@ -100,20 +78,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 = @@ -307,11 +275,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(); } /** @@ -347,8 +319,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; } @@ -459,8 +431,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. @@ -480,11 +452,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, @@ -502,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, @@ -525,11 +487,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, @@ -550,11 +507,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) { @@ -569,11 +521,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) { @@ -764,8 +711,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 @@ -785,10 +732,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; } @@ -853,12 +799,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(); + } }; } @@ -910,8 +866,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 { @@ -930,8 +886,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; } @@ -958,24 +913,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; @@ -1051,21 +1006,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 /** @@ -1103,14 +1043,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; @@ -1122,7 +1069,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]; } @@ -1379,19 +1326,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/src/com/google/common/collect/LinkedHashMultimap.java b/guava/src/com/google/common/collect/LinkedHashMultimap.java index 47bbe6f..39c0bfb 100644 --- a/guava/src/com/google/common/collect/LinkedHashMultimap.java +++ b/guava/src/com/google/common/collect/LinkedHashMultimap.java @@ -16,24 +16,20 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkArgument; - import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; 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.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -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; @@ -69,23 +65,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>(); } /** @@ -99,9 +101,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); } /** @@ -115,158 +115,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()); + } + }; } /** @@ -277,8 +320,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); } @@ -309,265 +352,20 @@ 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. /** - * @serialData the expected values per key, the number of distinct keys, - * the number of entries, and the entries in order + * @serialData the number of distinct keys, and then for each distinct key: + * the first key, the number of values for that key, and the key's values, + * followed by successive keys and values from the entries() ordering */ @GwtIncompatible("java.io.ObjectOutputStream") private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); - stream.writeInt(valueSetCapacity); - stream.writeInt(keySet().size()); - for (K key : keySet()) { - stream.writeObject(key); - } - stream.writeInt(size()); - for (Map.Entry<K, V> entry : entries()) { + stream.writeInt(expectedValuesPerKey); + Serialization.writeMultimap(this, stream); + for (Map.Entry<K, V> entry : linkedEntries) { stream.writeObject(entry.getKey()); stream.writeObject(entry.getValue()); } @@ -577,28 +375,22 @@ public final class LinkedHashMultimap<K, V> extends AbstractSetMultimap<K, V> { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - multimapHeaderEntry = new ValueEntry<K, V>(null, null, 0, null); - succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); - valueSetCapacity = stream.readInt(); - int distinctKeys = stream.readInt(); - Map<K, Collection<V>> map = - new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys)); - for (int i = 0; i < distinctKeys; i++) { - @SuppressWarnings("unchecked") - K key = (K) stream.readObject(); - map.put(key, createCollection(key)); - } - int entries = stream.readInt(); - for (int i = 0; i < entries; i++) { - @SuppressWarnings("unchecked") + expectedValuesPerKey = stream.readInt(); + int distinctKeys = Serialization.readCount(stream); + setMap(new LinkedHashMap<K, Collection<V>>(Maps.capacity(distinctKeys))); + linkedEntries = new LinkedHashSet<Map.Entry<K, V>>( + distinctKeys * expectedValuesPerKey); + Serialization.populateMultimap(this, stream, distinctKeys); + linkedEntries.clear(); // will clear and repopulate entries + for (int i = 0; i < size(); i++) { + @SuppressWarnings("unchecked") // reading data stored by writeObject K key = (K) stream.readObject(); - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") // reading data stored by writeObject V value = (V) stream.readObject(); - map.get(key).add(value); + linkedEntries.add(Maps.immutableEntry(key, value)); } - setMap(map); } @GwtIncompatible("java serialization not supported") - private static final long serialVersionUID = 1; + private static final long serialVersionUID = 0; } diff --git a/guava/src/com/google/common/collect/LinkedHashMultiset.java b/guava/src/com/google/common/collect/LinkedHashMultiset.java index 9b8c45b..c8db8e2 100644 --- a/guava/src/com/google/common/collect/LinkedHashMultiset.java +++ b/guava/src/com/google/common/collect/LinkedHashMultiset.java @@ -31,10 +31,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/src/com/google/common/collect/LinkedListMultimap.java b/guava/src/com/google/common/collect/LinkedListMultimap.java index 0a7b100..336ca72 100644 --- a/guava/src/com/google/common/collect/LinkedListMultimap.java +++ b/guava/src/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; @@ -29,10 +31,11 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; 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; @@ -92,10 +95,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) */ @@ -126,32 +125,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 @@ -185,11 +164,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) { @@ -208,32 +191,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; } @@ -245,7 +223,7 @@ public class LinkedListMultimap<K, V> nextSibling.previous = node; nextSibling.previousSibling = node; } - size++; + keyCount.add(key); return node; } @@ -265,27 +243,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. */ @@ -309,7 +281,6 @@ public class LinkedListMultimap<K, V> Node<K, V> next; Node<K, V> current; Node<K, V> previous; - int expectedModCount = modCount; NodeIterator() { next = head; @@ -331,19 +302,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; @@ -352,7 +316,6 @@ public class LinkedListMultimap<K, V> } @Override public void remove() { - checkForConcurrentModification(); checkState(current != null); if (current != next) { // after call to next() previous = current.previous; @@ -362,16 +325,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; @@ -405,21 +365,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); @@ -430,11 +382,9 @@ public class LinkedListMultimap<K, V> } @Override public void remove() { - checkForConcurrentModification(); checkState(current != null); removeAllNodes(current.key); current = null; - expectedModCount = modCount; } } @@ -449,8 +399,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); } /** @@ -463,17 +412,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(); } @@ -552,7 +500,7 @@ public class LinkedListMultimap<K, V> @Override public int size() { - return size; + return keyCount.size(); } @Override @@ -562,7 +510,7 @@ public class LinkedListMultimap<K, V> @Override public boolean containsKey(@Nullable Object key) { - return keyToKeyList.containsKey(key); + return keyToKeyHead.containsKey(key); } @Override @@ -689,9 +637,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 @@ -709,8 +657,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); @@ -730,19 +677,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); } }; } @@ -760,50 +707,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 @@ -819,9 +755,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; @@ -841,20 +845,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(); } }; } @@ -905,14 +936,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(); } }; } @@ -921,39 +993,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; } }; } @@ -1020,7 +1128,9 @@ public class LinkedListMultimap<K, V> private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - keyToKeyList = Maps.newLinkedHashMap(); + keyCount = LinkedHashMultiset.create(); + keyToKeyHead = Maps.newHashMap(); + keyToKeyTail = Maps.newHashMap(); int size = stream.readInt(); for (int i = 0; i < size; i++) { @SuppressWarnings("unchecked") // reading data stored by writeObject diff --git a/guava/src/com/google/common/collect/ListMultimap.java b/guava/src/com/google/common/collect/ListMultimap.java index a83b81a..cf4cbaa 100644 --- a/guava/src/com/google/common/collect/ListMultimap.java +++ b/guava/src/com/google/common/collect/ListMultimap.java @@ -26,16 +26,11 @@ import javax.annotation.Nullable; /** * A {@code Multimap} that can hold duplicate key-value pairs and that maintains - * the insertion ordering of values for a given key. See the {@link Multimap} - * documentation for information common to all multimaps. + * the insertion ordering of values for a given key. * * <p>The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods * each return a {@link List} of values. Though the method signature doesn't say * so explicitly, the map returned by {@link #asMap} has {@code List} values. - * - * <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) diff --git a/guava/src/com/google/common/collect/Lists.java b/guava/src/com/google/common/collect/Lists.java index 271da37..850d87e 100644 --- a/guava/src/com/google/common/collect/Lists.java +++ b/guava/src/com/google/common/collect/Lists.java @@ -25,7 +25,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.annotations.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Objects; @@ -44,24 +43,19 @@ import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.RandomAccess; -import java.util.concurrent.CopyOnWriteArrayList; 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>. + * class's counterparts {@link Sets} and {@link Maps}. * * @author Kevin Bourrillion * @author Mike Bostock * @author Louis Wasserman * @since 2.0 (imported from Google Collections Library) */ -@GwtCompatible(emulated = true) +@GwtCompatible public final class Lists { private Lists() {} @@ -227,38 +221,6 @@ public final class Lists { } /** - * Creates an empty {@code CopyOnWriteArrayList} instance. - * - * <p><b>Note:</b> if you need an immutable empty {@link List}, use - * {@link Collections#emptyList} instead. - * - * @return a new, empty {@code CopyOnWriteArrayList} - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArrayList") - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() { - return new CopyOnWriteArrayList<E>(); - } - - /** - * Creates a {@code CopyOnWriteArrayList} instance containing the given elements. - * - * @param elements the elements that the list should contain, in order - * @return a new {@code CopyOnWriteArrayList} containing those elements - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArrayList") - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList( - Iterable<? extends E> elements) { - // We copy elements to an ArrayList first, rather than incurring the - // quadratic cost of adding them to the COWAL directly. - Collection<? extends E> elementsCollection = (elements instanceof Collection) - ? Collections2.cast(elements) - : newArrayList(elements); - return new CopyOnWriteArrayList<E>(elementsCollection); - } - - /** * 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 @@ -352,127 +314,6 @@ public final class 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 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 @@ -491,19 +332,13 @@ public final class Lists { * 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. + * returned list. The returned list always implements {@link Serializable}, + * but serialization will succeed only when {@code fromList} and + * {@code function} are serializable. 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) { @@ -539,10 +374,51 @@ public final class Lists { return fromList.size(); } @Override public ListIterator<T> listIterator(final int index) { - return new TransformedListIterator<F, T>(fromList.listIterator(index)) { + final ListIterator<F> delegate = fromList.listIterator(index); + return new ListIterator<T>() { + @Override + public void add(T e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override + public boolean hasPrevious() { + return delegate.hasPrevious(); + } + + @Override + public T next() { + return function.apply(delegate.next()); + } + + @Override + public int nextIndex() { + return delegate.nextIndex(); + } + + @Override + public T previous() { + return function.apply(delegate.previous()); + } + + @Override + public int previousIndex() { + return delegate.previousIndex(); + } + @Override - T transform(F from) { - return function.apply(from); + public void remove() { + delegate.remove(); + } + + @Override + public void set(T e) { + throw new UnsupportedOperationException("not supported"); } }; } @@ -670,6 +546,10 @@ public final class Lists { this.string = string; } + @Override public boolean contains(@Nullable Object object) { + return indexOf(object) >= 0; + } + @Override public int indexOf(@Nullable Object object) { return (object instanceof Character) ? string.indexOf((Character) object) : -1; @@ -680,9 +560,17 @@ public final class Lists { ? string.lastIndexOf((Character) object) : -1; } + @Override public UnmodifiableListIterator<Character> listIterator( + int index) { + return new AbstractIndexedListIterator<Character>(size(), index) { + @Override protected Character get(int index) { + return string.charAt(index); + } + }; + } + @Override public ImmutableList<Character> subList( int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size()); // for GWT return charactersOf(string.substring(fromIndex, toIndex)); } @@ -691,7 +579,6 @@ public final class Lists { } @Override public Character get(int index) { - checkElementIndex(index, size()); // for GWT return string.charAt(index); } @@ -758,7 +645,6 @@ public final class Lists { } @Override public Character get(int index) { - checkElementIndex(index, size()); // for GWT return sequence.charAt(index); } @@ -795,7 +681,6 @@ public final class Lists { } @Override public List<Character> subList(int fromIndex, int toIndex) { - checkPositionIndexes(fromIndex, toIndex, size()); // for GWT return charactersOf(sequence.subSequence(fromIndex, toIndex)); } @@ -1003,13 +888,10 @@ public final class Lists { /** * An implementation of {@link List#hashCode()}. */ - static int hashCodeImpl(List<?> list) { + 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; } @@ -1146,11 +1028,4 @@ public final class Lists { 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/src/com/google/common/collect/MapConstraints.java b/guava/src/com/google/common/collect/MapConstraints.java index 090625d..11351bb 100644 --- a/guava/src/com/google/common/collect/MapConstraints.java +++ b/guava/src/com/google/common/collect/MapConstraints.java @@ -21,7 +21,6 @@ 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 java.util.Collection; import java.util.Comparator; import java.util.Iterator; @@ -396,7 +395,7 @@ public final class MapConstraints { /** @see MapConstraints#constrainedMultimap */ private static class ConstrainedMultimap<K, V> - extends ForwardingMultimap<K, V> implements Serializable { + extends ForwardingMultimap<K, V> { final MapConstraint<? super K, ? super V> constraint; final Multimap<K, V> delegate; transient Collection<Entry<K, V>> entries; diff --git a/guava/src/com/google/common/collect/MapMaker.java b/guava/src/com/google/common/collect/MapMaker.java index a5a035e..330018e 100644 --- a/guava/src/com/google/common/collect/MapMaker.java +++ b/guava/src/com/google/common/collect/MapMaker.java @@ -18,12 +18,12 @@ 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 static com.google.common.collect.MapMakerInternalMap.Strength.SOFT; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Ascii; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.base.Throwables; @@ -51,6 +51,8 @@ import javax.annotation.Nullable; * <ul> * <li>keys or values automatically wrapped in {@linkplain WeakReference weak} or {@linkplain * SoftReference soft} references + * <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>notification of evicted (or otherwise removed) entries * <li>on-demand computation of values for keys not already present * </ul> @@ -60,6 +62,8 @@ import javax.annotation.Nullable; * ConcurrentMap<Key, Graph> graphs = new MapMaker() * .concurrencyLevel(4) * .weakKeys() + * .maximumSize(10000) + * .expireAfterWrite(10, TimeUnit.MINUTES) * .makeComputingMap( * new Function<Key, Graph>() { * public Graph apply(Key key) { @@ -75,9 +79,10 @@ import javax.annotation.Nullable; * interface. It does not permit null keys or values. * * <p><b>Note:</b> by default, the returned map uses equality comparisons (the {@link Object#equals - * equals} method) to determine equality for keys or values. However, if {@link #weakKeys} was - * specified, the map uses identity ({@code ==}) comparisons instead for keys. Likewise, if {@link - * #weakValues} or {@link #softValues} was specified, the map uses identity comparisons for values. + * equals} method) to determine equality for keys or values. However, if {@link #weakKeys} or {@link + * #softKeys} was specified, the map uses identity ({@code ==}) comparisons instead for keys. + * Likewise, if {@link #weakValues} or {@link #softValues} was specified, the map uses identity + * comparisons for values. * * <p>The view collections of the returned map have <i>weakly consistent iterators</i>. This means * that they are safe for concurrent use, but if other threads modify the map after the iterator is @@ -129,6 +134,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { RemovalCause nullRemovalCause; Equivalence<Object> keyEquivalence; + Equivalence<Object> valueEquivalence; Ticker ticker; @@ -138,12 +144,16 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { */ public MapMaker() {} + private boolean useNullMap() { + return (nullRemovalCause == null); + } + /** * Sets a custom {@code Equivalence} strategy for comparing keys. * - * <p>By default, the map uses {@link Equivalence#identity} to determine key equality when {@link - * #weakKeys} is specified, and {@link Equivalence#equals()} otherwise. The only place this is - * used is in {@link Interners.WeakInterner}. + * <p>By default, the map uses {@link Equivalences#identity} to determine key equality when + * {@link #weakKeys} or {@link #softKeys} is specified, and {@link Equivalences#equals()} + * otherwise. */ @GwtIncompatible("To be supported") @Override @@ -159,6 +169,27 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Sets a custom {@code Equivalence} strategy for comparing values. + * + * <p>By default, the map uses {@link Equivalences#identity} to determine value equality when + * {@link #weakValues} or {@link #softValues} is specified, and {@link Equivalences#equals()} + * otherwise. + */ + @GwtIncompatible("To be supported") + @Override + MapMaker valueEquivalence(Equivalence<Object> equivalence) { + checkState(valueEquivalence == null, + "value equivalence was already set to %s", valueEquivalence); + this.valueEquivalence = checkNotNull(equivalence); + this.useCustomMap = true; + return this; + } + + 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 @@ -200,9 +231,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if a maximum size was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #maximumSize} being - * replaced by {@link com.google.common.cache.CacheBuilder#maximumSize}. Note that {@code - * CacheBuilder} is simply an enhanced API for an implementation which was branched from - * {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#maximumSize}. */ @Deprecated @Override @@ -252,6 +281,16 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Specifies that each key (not value) stored in the map should be strongly referenced. + * + * @throws IllegalStateException if the key strength was already set + */ + @Override + MapMaker strongKeys() { + return setKeyStrength(Strength.STRONG); + } + + /** * Specifies that each key (not value) stored in the map should be wrapped in a {@link * WeakReference} (by default, strong references are used). * @@ -268,10 +307,36 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { return setKeyStrength(Strength.WEAK); } + /** + * <b>This method is broken.</b> Maps with soft keys offer no functional advantage over maps with + * weak keys, and they waste memory by keeping unreachable elements in the map. If your goal is to + * create a memory-sensitive map, then consider using soft values instead. + * + * <p>Specifies that each key (not value) stored in the map should be wrapped in a + * {@link SoftReference} (by default, strong references are used). Softly-referenced objects will + * be garbage-collected in a <i>globally</i> least-recently-used manner, in response to memory + * demand. + * + * <p><b>Warning:</b> when this method is used, the resulting map will use identity ({@code ==}) + * comparison to determine equality of keys, which is a technical violation of the {@link Map} + * specification, and may not be what you expect. + * + * @throws IllegalStateException if the key strength was already set + * @see SoftReference + * @deprecated use {@link #softValues} to create a memory-sensitive map, or {@link #weakKeys} to + * create a map that doesn't hold strong references to the keys. + * <b>This method is scheduled for deletion in January 2013.</b> + */ + @Deprecated + @GwtIncompatible("java.lang.ref.SoftReference") + @Override + public MapMaker softKeys() { + return setKeyStrength(Strength.SOFT); + } + MapMaker setKeyStrength(Strength strength) { checkState(keyStrength == null, "Key strength was already set to %s", keyStrength); keyStrength = checkNotNull(strength); - checkArgument(keyStrength != SOFT, "Soft keys are not supported"); if (strength != Strength.STRONG) { // STRONG could be used during deserialization. useCustomMap = true; @@ -284,6 +349,16 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Specifies that each value (not key) stored in the map should be strongly referenced. + * + * @throws IllegalStateException if the value strength was already set + */ + @Override + MapMaker strongValues() { + return setValueStrength(Strength.STRONG); + } + + /** * Specifies that each value (not key) stored in the map should be wrapped in a * {@link WeakReference} (by default, strong references are used). * @@ -347,6 +422,22 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { } /** + * Old name of {@link #expireAfterWrite}. + * + * @deprecated Caching functionality in {@code MapMaker} is being moved to + * {@link com.google.common.cache.CacheBuilder}. Functionality equivalent to + * {@link MapMaker#expiration} is provided by + * {@link com.google.common.cache.CacheBuilder#expireAfterWrite}. + * <b>This method is scheduled for deletion in July 2012.</b> + */ + @Deprecated + @Override + public + MapMaker expiration(long duration, TimeUnit unit) { + return expireAfterWrite(duration, unit); + } + + /** * Specifies that each entry should be automatically removed from the map once a fixed duration * has elapsed after the entry's creation, or the most recent replacement of its value. * @@ -367,9 +458,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if the time to live or time to idle was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #expireAfterWrite} being - * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterWrite}. Note that {@code - * CacheBuilder} is simply an enhanced API for an implementation which was branched from - * {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterWrite}. */ @Deprecated @Override @@ -417,9 +506,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if the time to idle or time to live was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #expireAfterAccess} being - * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterAccess}. Note that - * {@code CacheBuilder} is simply an enhanced API for an implementation which was branched - * from {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#expireAfterAccess}. */ @Deprecated @GwtIncompatible("To be supported") @@ -469,9 +556,7 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @throws IllegalStateException if a removal listener was already set * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #removalListener} being - * replaced by {@link com.google.common.cache.CacheBuilder#removalListener}. Note that {@code - * CacheBuilder} is simply an enhanced API for an implementation which was branched from - * {@code MapMaker}. + * replaced by {@link com.google.common.cache.CacheBuilder#removalListener}. */ @Deprecated @GwtIncompatible("To be supported") @@ -571,16 +656,17 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * @return a serializable concurrent map having the requested features * @deprecated Caching functionality in {@code MapMaker} is being moved to * {@link com.google.common.cache.CacheBuilder}, with {@link #makeComputingMap} being replaced - * by {@link com.google.common.cache.CacheBuilder#build}. See the - * <a href="http://code.google.com/p/guava-libraries/wiki/MapMakerMigration">MapMaker - * Migration Guide</a> for more details. + * by {@link com.google.common.cache.CacheBuilder#build}. Note that uses of + * {@link #makeComputingMap} with {@code AtomicLong} values can often be migrated to + * {@link AtomicLongMap}. * <b>This method is scheduled for deletion in February 2013.</b> + * */ @Deprecated @Override public <K, V> ConcurrentMap<K, V> makeComputingMap( Function<? super K, ? extends V> computingFunction) { - return (nullRemovalCause == null) + return useNullMap() ? new MapMaker.ComputingMapAdapter<K, V>(this, computingFunction) : new NullComputingConcurrentMap<K, V>(this, computingFunction); } @@ -616,6 +702,9 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { if (keyEquivalence != null) { s.addValue("keyEquivalence"); } + if (valueEquivalence != null) { + s.addValue("valueEquivalence"); + } if (removalListener != null) { s.addValue("removalListener"); } @@ -706,8 +795,9 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { }, /** - * The entry was removed automatically because its key or value was garbage-collected. This can - * occur when using {@link #softValues}, {@link #weakKeys}, or {@link #weakValues}. + * The entry was removed automatically because its key or value was garbage-collected. This + * can occur when using {@link #softKeys}, {@link #softValues}, {@link #weakKeys}, or {@link + * #weakValues}. */ COLLECTED { @Override @@ -862,10 +952,6 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { * Overrides get() to compute on demand. Also throws an exception when {@code null} is returned * from a computation. */ - /* - * This might make more sense in ComputingConcurrentHashMap, but it causes a javac crash in some - * cases there: http://code.google.com/p/guava-libraries/issues/detail?id=950 - */ static final class ComputingMapAdapter<K, V> extends ComputingConcurrentHashMap<K, V> implements Serializable { private static final long serialVersionUID = 0; @@ -893,4 +979,5 @@ public final class MapMaker extends GenericMapMaker<Object, Object> { return value; } } + } diff --git a/guava/src/com/google/common/collect/MapMakerInternalMap.java b/guava/src/com/google/common/collect/MapMakerInternalMap.java index b2d05bd..a5a6be8 100644 --- a/guava/src/com/google/common/collect/MapMakerInternalMap.java +++ b/guava/src/com/google/common/collect/MapMakerInternalMap.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Ticker; import com.google.common.collect.GenericMapMaker.NullListener; import com.google.common.collect.MapMaker.RemovalCause; @@ -199,7 +200,7 @@ class MapMakerInternalMap<K, V> valueStrength = builder.getValueStrength(); keyEquivalence = builder.getKeyEquivalence(); - valueEquivalence = valueStrength.defaultEquivalence(); + valueEquivalence = builder.getValueEquivalence(); maximumSize = builder.maximumSize; expireAfterAccessNanos = builder.getExpireAfterAccessNanos(); @@ -301,7 +302,7 @@ class MapMakerInternalMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.equals(); + return Equivalences.equals(); } }, @@ -314,7 +315,7 @@ class MapMakerInternalMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.identity(); + return Equivalences.identity(); } }, @@ -327,7 +328,7 @@ class MapMakerInternalMap<K, V> @Override Equivalence<Object> defaultEquivalence() { - return Equivalence.identity(); + return Equivalences.identity(); } }; @@ -403,6 +404,60 @@ class MapMakerInternalMap<K, V> } }, + SOFT { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + }, + SOFT_EXPIRABLE { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftExpirableEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + <K, V> ReferenceEntry<K, V> copyEntry( + Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { + ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + return newEntry; + } + }, + SOFT_EVICTABLE { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftEvictableEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + <K, V> ReferenceEntry<K, V> copyEntry( + Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { + ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + SOFT_EXPIRABLE_EVICTABLE { + @Override + <K, V> ReferenceEntry<K, V> newEntry( + Segment<K, V> segment, K key, int hash, @Nullable ReferenceEntry<K, V> next) { + return new SoftExpirableEvictableEntry<K, V>(segment.keyReferenceQueue, key, hash, next); + } + + @Override + <K, V> ReferenceEntry<K, V> copyEntry( + Segment<K, V> segment, ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { + ReferenceEntry<K, V> newEntry = super.copyEntry(segment, original, newNext); + copyExpirableEntry(original, newEntry); + copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + WEAK { @Override <K, V> ReferenceEntry<K, V> newEntry( @@ -469,7 +524,7 @@ class MapMakerInternalMap<K, V> */ static final EntryFactory[][] factories = { { STRONG, STRONG_EXPIRABLE, STRONG_EVICTABLE, STRONG_EXPIRABLE_EVICTABLE }, - {}, // no support for SOFT keys + { SOFT, SOFT_EXPIRABLE, SOFT_EVICTABLE, SOFT_EXPIRABLE_EVICTABLE }, { WEAK, WEAK_EXPIRABLE, WEAK_EVICTABLE, WEAK_EXPIRABLE_EVICTABLE } }; @@ -550,11 +605,8 @@ class MapMakerInternalMap<K, V> /** * Creates a copy of this reference for the given entry. - * - * <p>{@code value} may be null only for a loading reference. */ - ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, @Nullable V value, ReferenceEntry<K, V> entry); + ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry); /** * Clears this reference object. @@ -587,8 +639,8 @@ class MapMakerInternalMap<K, V> } @Override - public ValueReference<Object, Object> copyFor(ReferenceQueue<Object> queue, - @Nullable Object value, ReferenceEntry<Object, Object> entry) { + public ValueReference<Object, Object> copyFor( + ReferenceQueue<Object> queue, ReferenceEntry<Object, Object> entry) { return this; } @@ -783,7 +835,7 @@ class MapMakerInternalMap<K, V> public void setPreviousEvictable(ReferenceEntry<Object, Object> previous) {} } - abstract static class AbstractReferenceEntry<K, V> implements ReferenceEntry<K, V> { + static abstract class AbstractReferenceEntry<K, V> implements ReferenceEntry<K, V> { @Override public ValueReference<K, V> getValueReference() { throw new UnsupportedOperationException(); @@ -1704,8 +1756,8 @@ class MapMakerInternalMap<K, V> @Override public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new WeakValueReference<K, V>(queue, value, entry); + ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new WeakValueReference<K, V>(queue, get(), entry); } @Override @@ -1742,9 +1794,8 @@ class MapMakerInternalMap<K, V> } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { - return new SoftValueReference<K, V>(queue, value, entry); + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { + return new SoftValueReference<K, V>(queue, get(), entry); } @Override @@ -1779,8 +1830,7 @@ class MapMakerInternalMap<K, V> } @Override - public ValueReference<K, V> copyFor( - ReferenceQueue<V> queue, V value, ReferenceEntry<K, V> entry) { + public ValueReference<K, V> copyFor(ReferenceQueue<V> queue, ReferenceEntry<K, V> entry) { return this; } @@ -2129,26 +2179,11 @@ class MapMakerInternalMap<K, V> return map.entryFactory.newEntry(this, key, hash, next); } - /** - * Copies {@code original} into a new entry chained to {@code newNext}. Returns the new entry, - * or {@code null} if {@code original} was already garbage collected. - */ @GuardedBy("Segment.this") ReferenceEntry<K, V> copyEntry(ReferenceEntry<K, V> original, ReferenceEntry<K, V> newNext) { - if (original.getKey() == null) { - // key collected - return null; - } - ValueReference<K, V> valueReference = original.getValueReference(); - V value = valueReference.get(); - if ((value == null) && !valueReference.isComputingReference()) { - // value collected - return null; - } - ReferenceEntry<K, V> newEntry = map.entryFactory.copyEntry(this, original, newNext); - newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, newEntry)); return newEntry; } @@ -2617,14 +2652,14 @@ class MapMakerInternalMap<K, V> // Clone nodes leading up to the tail. for (ReferenceEntry<K, V> e = head; e != tail; e = e.getNext()) { - int newIndex = e.getHash() & newMask; - ReferenceEntry<K, V> newNext = newTable.get(newIndex); - ReferenceEntry<K, V> newFirst = copyEntry(e, newNext); - if (newFirst != null) { - newTable.set(newIndex, newFirst); - } else { + if (isCollected(e)) { removeCollectedEntry(e); newCount--; + } else { + int newIndex = e.getHash() & newMask; + ReferenceEntry<K, V> newNext = newTable.get(newIndex); + ReferenceEntry<K, V> newFirst = copyEntry(e, newNext); + newTable.set(newIndex, newFirst); } } } @@ -2867,12 +2902,11 @@ class MapMakerInternalMap<K, V> int newCount = count; ReferenceEntry<K, V> newFirst = entry.getNext(); for (ReferenceEntry<K, V> e = first; e != entry; e = e.getNext()) { - ReferenceEntry<K, V> next = copyEntry(e, newFirst); - if (next != null) { - newFirst = next; - } else { + if (isCollected(e)) { removeCollectedEntry(e); newCount--; + } else { + newFirst = copyEntry(e, newFirst); } } this.count = newCount; @@ -3008,6 +3042,17 @@ class MapMakerInternalMap<K, V> } /** + * Returns {@code true} if the entry has been partially collected, meaning that either the key + * is null, or the value is null and it is not computing. + */ + boolean isCollected(ReferenceEntry<K, V> entry) { + if (entry.getKey() == null) { + return true; + } + return isCollected(entry.getValueReference()); + } + + /** * Returns {@code true} if the value has been partially collected, meaning that the value is * null and it is not computing. */ @@ -3215,7 +3260,7 @@ class MapMakerInternalMap<K, V> @Override public Iterator<ReferenceEntry<K, V>> iterator() { - return new AbstractSequentialIterator<ReferenceEntry<K, V>>(peek()) { + return new AbstractLinkedIterator<ReferenceEntry<K, V>>(peek()) { @Override protected ReferenceEntry<K, V> computeNext(ReferenceEntry<K, V> previous) { ReferenceEntry<K, V> next = previous.getNextEvictable(); @@ -3351,7 +3396,7 @@ class MapMakerInternalMap<K, V> @Override public Iterator<ReferenceEntry<K, V>> iterator() { - return new AbstractSequentialIterator<ReferenceEntry<K, V>>(peek()) { + return new AbstractLinkedIterator<ReferenceEntry<K, V>>(peek()) { @Override protected ReferenceEntry<K, V> computeNext(ReferenceEntry<K, V> previous) { ReferenceEntry<K, V> next = previous.getNextExpirable(); @@ -3446,6 +3491,17 @@ class MapMakerInternalMap<K, V> return segmentFor(hash).getEntry(key, hash); } + /** + * Returns the live internal entry for the specified key. Does not impact recency ordering. + */ + ReferenceEntry<K, V> getLiveEntry(@Nullable Object key) { + if (key == null) { + return null; + } + int hash = hash(key); + return segmentFor(hash).getLiveEntry(key, hash); + } + @Override public boolean containsKey(@Nullable Object key) { if (key == null) { @@ -3466,7 +3522,7 @@ class MapMakerInternalMap<K, V> // such that none of the subsequent iterations observed it, despite the fact that at every point // in time it was present somewhere int the map. This becomes increasingly unlikely as // CONTAINS_VALUE_RETRIES increases, though without locking it is theoretically possible. - final Segment<K, V>[] segments = this.segments; + final Segment<K,V>[] segments = this.segments; long last = -1L; for (int i = 0; i < CONTAINS_VALUE_RETRIES; i++) { long sum = 0L; @@ -3476,7 +3532,7 @@ class MapMakerInternalMap<K, V> int c = segment.count; // read-volatile AtomicReferenceArray<ReferenceEntry<K, V>> table = segment.table; - for (int j = 0; j < table.length(); j++) { + for (int j = 0 ; j < table.length(); j++) { for (ReferenceEntry<K, V> e = table.get(j); e != null; e = e.getNext()) { V v = segment.getLiveValue(e); if (v != null && valueEquivalence.equivalent(value, v)) { @@ -3561,7 +3617,7 @@ class MapMakerInternalMap<K, V> } } - transient Set<K> keySet; + Set<K> keySet; @Override public Set<K> keySet() { @@ -3569,7 +3625,7 @@ class MapMakerInternalMap<K, V> return (ks != null) ? ks : (keySet = new KeySet()); } - transient Collection<V> values; + Collection<V> values; @Override public Collection<V> values() { @@ -3577,7 +3633,7 @@ class MapMakerInternalMap<K, V> return (vs != null) ? vs : (values = new Values()); } - transient Set<Entry<K, V>> entrySet; + Set<Entry<K, V>> entrySet; @Override public Set<Entry<K, V>> entrySet() { @@ -3587,7 +3643,7 @@ class MapMakerInternalMap<K, V> // Iterator Support - abstract class HashIterator<E> implements Iterator<E> { + abstract class HashIterator { int nextSegmentIndex; int nextTableIndex; @@ -3603,8 +3659,6 @@ class MapMakerInternalMap<K, V> advance(); } - public abstract E next(); - final void advance() { nextExternal = null; @@ -3696,7 +3750,7 @@ class MapMakerInternalMap<K, V> } } - final class KeyIterator extends HashIterator<K> { + final class KeyIterator extends HashIterator implements Iterator<K> { @Override public K next() { @@ -3704,7 +3758,7 @@ class MapMakerInternalMap<K, V> } } - final class ValueIterator extends HashIterator<V> { + final class ValueIterator extends HashIterator implements Iterator<V> { @Override public V next() { @@ -3759,7 +3813,7 @@ class MapMakerInternalMap<K, V> } } - final class EntryIterator extends HashIterator<Entry<K, V>> { + final class EntryIterator extends HashIterator implements Iterator<Entry<K, V>> { @Override public Entry<K, V> next() { @@ -3945,6 +3999,7 @@ class MapMakerInternalMap<K, V> .setKeyStrength(keyStrength) .setValueStrength(valueStrength) .keyEquivalence(keyEquivalence) + .valueEquivalence(valueEquivalence) .concurrencyLevel(concurrencyLevel); mapMaker.removalListener(removalListener); if (expireAfterWriteNanos > 0) { diff --git a/guava/src/com/google/common/collect/Maps.java b/guava/src/com/google/common/collect/Maps.java index 9569a52..4215869 100644 --- a/guava/src/com/google/common/collect/Maps.java +++ b/guava/src/com/google/common/collect/Maps.java @@ -23,6 +23,7 @@ import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Equivalence; +import com.google.common.base.Equivalences; import com.google.common.base.Function; import com.google.common.base.Joiner.MapJoiner; import com.google.common.base.Objects; @@ -35,6 +36,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,25 +48,17 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NavigableSet; import java.util.Properties; 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 @@ -76,60 +70,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. * @@ -285,9 +225,9 @@ public final class Maps { * @param comparator the comparator to sort the keys with * @return a new, empty {@code TreeMap} */ - public static <C, K extends C, V> TreeMap<K, V> newTreeMap( - @Nullable Comparator<C> comparator) { - // Ideally, the extra type parameter "C" shouldn't be necessary. It is a + public static <K, V> TreeMap<K, V> newTreeMap( + @Nullable Comparator<? super K> comparator) { + // Ideally, the "? super" shouldn't be necessary. It is a // work-around of a compiler type inference quirk that prevents the // following code from being compiled: // Comparator<Class<?>> comparator = null; @@ -329,6 +269,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. @@ -352,7 +324,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()); } /** @@ -524,7 +496,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()) @@ -561,6 +533,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); @@ -645,484 +618,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); - } - - /** - * Returns a view of the navigable 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 - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> asMap( - NavigableSet<K> set, Function<? super K, V> function) { - return new NavigableAsMapView<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(); - } - } - - @GwtIncompatible("NavigableMap") - private static final class NavigableAsMapView<K, V> - extends AbstractNavigableMap<K, V> { - /* - * Using AbstractNavigableMap is simpler than extending SortedAsMapView and rewriting all the - * NavigableMap methods. - */ - - private final NavigableSet<K> set; - private final Function<? super K, V> function; - - NavigableAsMapView(NavigableSet<K> ks, Function<? super K, V> vFunction) { - this.set = checkNotNull(ks); - this.function = checkNotNull(vFunction); - } - - @Override - public NavigableMap<K, V> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return asMap(set.subSet(fromKey, fromInclusive, toKey, toInclusive), function); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return asMap(set.headSet(toKey, inclusive), function); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return asMap(set.tailSet(fromKey, inclusive), function); - } - - @Override - public Comparator<? super K> comparator() { - return set.comparator(); - } - - @Override - @Nullable - public V get(@Nullable Object key) { - if (set.contains(key)) { - @SuppressWarnings("unchecked") // unsafe, but Javadoc warns about it - K k = (K) key; - return function.apply(k); - } else { - return null; - } - } - - @Override - public void clear() { - set.clear(); - } - - @Override - Iterator<Entry<K, V>> entryIterator() { - return asSetEntryIterator(set, function); - } - - @Override - Iterator<Entry<K, V>> descendingEntryIterator() { - return descendingMap().entrySet().iterator(); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return removeOnlyNavigableSet(set); - } - - @Override - public int size() { - return set.size(); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return asMap(set.descendingSet(), function); - } - } - - 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)); - } - }; - } - - @GwtIncompatible("NavigableSet") - private static <E> NavigableSet<E> removeOnlyNavigableSet(final NavigableSet<E> set) { - return new ForwardingNavigableSet<E>() { - @Override - protected NavigableSet<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)); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return removeOnlyNavigableSet(super.headSet(toElement, inclusive)); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return removeOnlyNavigableSet(super.tailSet(fromElement, inclusive)); - } - - @Override - public NavigableSet<E> subSet(E fromElement, boolean fromInclusive, - E toElement, boolean toInclusive) { - return removeOnlyNavigableSet(super.subSet( - fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet<E> descendingSet() { - return removeOnlyNavigableSet(super.descendingSet()); - } - }; - } - - /** - * 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 @@ -1143,6 +638,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. @@ -1330,38 +843,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 @@ -1384,7 +865,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, @@ -1458,8 +939,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); } /** @@ -1501,67 +990,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)); - } - - /** - * Returns a view of a navigable map where each value is transformed by a - * function. All other properties of the map, such as iteration order, are - * left intact. For example, the code: <pre> {@code - * - * NavigableMap<String, Integer> map = Maps.newTreeMap(); - * map.put("a", 4); - * map.put("b", 9); - * Function<Integer, Double> sqrt = - * new Function<Integer, Double>() { - * public Double apply(Integer in) { - * return Math.sqrt((int) in); - * } - * }; - * NavigableMap<String, Double> transformed = - * Maps.transformNavigableValues(map, sqrt); - * System.out.println(transformed);}</pre> - * - * ... prints {@code {a=2.0, b=3.0}}. - * - * Changes in the underlying map are reflected in this view. - * Conversely, this view supports removal operations, and these are reflected - * in the underlying map. - * - * <p>It's acceptable for the underlying map to contain null keys, and even - * null values provided that the function is capable of accepting null input. - * The transformed map might contain null values, if the function sometimes - * gives a null result. - * - * <p>The returned map is not thread-safe or serializable, even if the - * underlying map is. - * - * <p>The function is applied lazily, invoked when needed. This is necessary - * for the returned map to be a view, but it means that the function will be - * applied many times for bulk operations like {@link Map#containsValue} and - * {@code Map.toString()}. For this to perform well, {@code function} should - * be fast. To avoid lazy evaluation when the returned map doesn't need to be - * a view, copy the returned map into a new map of your choosing. - * - * @since 13.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V1, V2> NavigableMap<K, V2> transformValues( - NavigableMap<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); } /** @@ -1676,74 +1116,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); - } - - /** - * Returns a view of a navigable map whose values are derived from the - * original navigable map's entries. In contrast to {@link - * #transformValues}, this method's entry-transformation logic may - * depend on the key as well as the value. - * - * <p>All other properties of the transformed map, such as iteration order, - * are left intact. For example, the code: <pre> {@code - * - * NavigableMap<String, Boolean> options = Maps.newTreeMap(); - * options.put("verbose", false); - * options.put("sort", true); - * EntryTransformer<String, Boolean, String> flagPrefixer = - * new EntryTransformer<String, Boolean, String>() { - * public String transformEntry(String key, Boolean value) { - * return value ? key : ("yes" + key); - * } - * }; - * NavigableMap<String, String> transformed = - * LabsMaps.transformNavigableEntries(options, flagPrefixer); - * System.out.println(transformed);}</pre> - * - * ... prints {@code {sort=yessort, verbose=verbose}}. - * - * <p>Changes in the underlying map are reflected in this view. - * Conversely, this view supports removal operations, and these are reflected - * in the underlying map. - * - * <p>It's acceptable for the underlying map to contain null keys and null - * values provided that the transformer is capable of accepting null inputs. - * The transformed map might contain null values if the transformer sometimes - * gives a null result. - * - * <p>The returned map is not thread-safe or serializable, even if the - * underlying map is. - * - * <p>The transformer is applied lazily, invoked when needed. This is - * necessary for the returned map to be a view, but it means that the - * transformer will be applied many times for bulk operations like {@link - * Map#containsValue} and {@link Object#toString}. For this to perform well, - * {@code transformer} should be fast. To avoid lazy evaluation when the - * returned map doesn't need to be a view, copy the returned map into a new - * map of your choosing. - * - * <p><b>Warning:</b> This method assumes that for any instance {@code k} of - * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies - * that {@code k2} is also of type {@code K}. Using an {@code - * EntryTransformer} key type for which this may not hold, such as {@code - * ArrayList}, may risk a {@code ClassCastException} when calling methods on - * the transformed map. - * - * @since 13.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V1, V2> NavigableMap<K, V2> transformEntries( - final NavigableMap<K, V1> fromMap, - EntryTransformer<? super K, ? super V1, V2> transformer) { - return new TransformedEntriesNavigableMap<K, V1, V2>(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); } @@ -1835,23 +1210,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()); - } - }; - } - }; + }); } }; } @@ -1909,144 +1278,7 @@ public final class Maps { @Override public SortedMap<K, V2> tailMap(K fromKey) { return transformEntries(fromMap().tailMap(fromKey), transformer); } - } - - @GwtIncompatible("NavigableMap") - private static class TransformedEntriesNavigableMap<K, V1, V2> - extends TransformedEntriesSortedMap<K, V1, V2> - implements NavigableMap<K, V2> { - - TransformedEntriesNavigableMap(NavigableMap<K, V1> fromMap, - EntryTransformer<? super K, ? super V1, V2> transformer) { - super(fromMap, transformer); - } - - @Override public Entry<K, V2> ceilingEntry(K key) { - return transformEntry(fromMap().ceilingEntry(key)); - } - - @Override public K ceilingKey(K key) { - return fromMap().ceilingKey(key); - } - - @Override public NavigableSet<K> descendingKeySet() { - return fromMap().descendingKeySet(); - } - - @Override public NavigableMap<K, V2> descendingMap() { - return transformEntries(fromMap().descendingMap(), transformer); - } - - @Override public Entry<K, V2> firstEntry() { - return transformEntry(fromMap().firstEntry()); - } - @Override public Entry<K, V2> floorEntry(K key) { - return transformEntry(fromMap().floorEntry(key)); - } - - @Override public K floorKey(K key) { - return fromMap().floorKey(key); - } - - @Override public NavigableMap<K, V2> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override public NavigableMap<K, V2> headMap(K toKey, boolean inclusive) { - return transformEntries( - fromMap().headMap(toKey, inclusive), transformer); - } - - @Override public Entry<K, V2> higherEntry(K key) { - return transformEntry(fromMap().higherEntry(key)); - } - - @Override public K higherKey(K key) { - return fromMap().higherKey(key); - } - - @Override public Entry<K, V2> lastEntry() { - return transformEntry(fromMap().lastEntry()); - } - - @Override public Entry<K, V2> lowerEntry(K key) { - return transformEntry(fromMap().lowerEntry(key)); - } - @Override public K lowerKey(K key) { - return fromMap().lowerKey(key); - } - - @Override public NavigableSet<K> navigableKeySet() { - return fromMap().navigableKeySet(); - } - - @Override public Entry<K, V2> pollFirstEntry() { - return transformEntry(fromMap().pollFirstEntry()); - } - - @Override public Entry<K, V2> pollLastEntry() { - return transformEntry(fromMap().pollLastEntry()); - } - - @Override public NavigableMap<K, V2> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return transformEntries( - fromMap().subMap(fromKey, fromInclusive, toKey, toInclusive), - transformer); - } - - @Override public NavigableMap<K, V2> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override public NavigableMap<K, V2> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override public NavigableMap<K, V2> tailMap(K fromKey, boolean inclusive) { - return transformEntries( - fromMap().tailMap(fromKey, inclusive), transformer); - } - - private Entry<K, V2> transformEntry(Entry<K, V1> entry) { - if (entry == null) { - return null; - } - K key = entry.getKey(); - V2 v2 = transformer.transformEntry(key, entry.getValue()); - return Maps.immutableEntry(key, v2); - } - - @Override protected NavigableMap<K, V1> fromMap() { - return (NavigableMap<K, V1>) super.fromMap(); - } - } - - 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()); - } } /** @@ -2081,11 +1313,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>( @@ -2122,80 +1358,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 - // performance. - return filterEntries(unfiltered, new KeyPredicate<K, V>(keyPredicate)); - } - - /** - * Returns a navigable map containing the mappings in {@code unfiltered} whose - * keys satisfy a predicate. The returned map is a live view of {@code - * unfiltered}; changes to one affect the other. - * - * <p>The resulting map'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 map and its views. When given a key that - * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} - * methods throw an {@link IllegalArgumentException}. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called - * on the filtered map or its views, only mappings whose keys satisfy the - * filter will be removed from the underlying map. - * - * <p>The returned map isn't threadsafe or serializable, even if {@code - * unfiltered} is. - * - * <p>Many of the filtered map's methods, such as {@code size()}, - * iterate across every key/value mapping in the underlying map and determine - * which satisfy the filter. When a live view is <i>not</i> needed, it may be - * faster to copy the filtered map and use the copy. - * - * <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with - * equals</i>, as documented at {@link Predicate#apply}. Do not provide a - * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is - * inconsistent with equals. - * - * @since 14.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> filterKeys( - NavigableMap<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); } /** @@ -2231,10 +1406,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); } /** @@ -2268,79 +1449,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 navigable map containing the mappings in {@code unfiltered} whose - * values satisfy a predicate. The returned map is a live view of {@code - * unfiltered}; changes to one affect the other. - * - * <p>The resulting map'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 map and its views. When given a value - * that doesn't satisfy the predicate, the map's {@code put()}, {@code - * putAll()}, and {@link Entry#setValue} methods throw an {@link - * IllegalArgumentException}. - * - * <p>When methods such as {@code removeAll()} and {@code clear()} are called - * on the filtered map or its views, only mappings whose values satisfy the - * filter will be removed from the underlying map. - * - * <p>The returned map isn't threadsafe or serializable, even if {@code - * unfiltered} is. - * - * <p>Many of the filtered map's methods, such as {@code size()}, - * iterate across every key/value mapping in the underlying map and determine - * which satisfy the filter. When a live view is <i>not</i> needed, it may be - * faster to copy the filtered map and use the copy. - * - * <p><b>Warning:</b> {@code valuePredicate} must be <i>consistent with - * equals</i>, as documented at {@link Predicate#apply}. Do not provide a - * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is - * inconsistent with equals. - * - * @since 14.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> filterValues( - NavigableMap<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); } /** @@ -2376,8 +1496,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) @@ -2416,15 +1534,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) @@ -2432,83 +1545,6 @@ public final class Maps { } /** - * Returns a sorted map containing the mappings in {@code unfiltered} that - * satisfy a predicate. The returned map is a live view of {@code unfiltered}; - * changes to one affect the other. - * - * <p>The resulting map'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 map and its views. When given a - * key/value pair that doesn't satisfy the predicate, the map's {@code put()} - * 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 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 map or its views, only mappings that satisfy the filter - * will be removed from the underlying map. - * - * <p>The returned map isn't threadsafe or serializable, even if {@code - * unfiltered} is. - * - * <p>Many of the filtered map's methods, such as {@code size()}, - * iterate across every key/value mapping in the underlying map and determine - * which satisfy the filter. When a live view is <i>not</i> needed, it may be - * faster to copy the filtered map 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 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> filterEntries( - NavigableMap<K, V> unfiltered, - Predicate<? super Entry<K, V>> entryPredicate) { - checkNotNull(entryPredicate); - return (unfiltered instanceof FilteredEntryNavigableMap) - ? filterFiltered((FilteredEntryNavigableMap<K, V>) unfiltered, entryPredicate) - : new FilteredEntryNavigableMap<K, V>(checkNotNull(unfiltered), entryPredicate); - } - - /** - * 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. */ @@ -2653,7 +1689,6 @@ public final class Maps { } } } - /** * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when * filtering a filtered sorted map. @@ -2714,251 +1749,6 @@ public final class Maps { } } - /** - * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when - * filtering a filtered navigable map. - */ - @GwtIncompatible("NavigableMap") - private static <K, V> NavigableMap<K, V> filterFiltered( - FilteredEntryNavigableMap<K, V> map, - Predicate<? super Entry<K, V>> entryPredicate) { - Predicate<Entry<K, V>> predicate - = Predicates.and(map.predicate, entryPredicate); - return new FilteredEntryNavigableMap<K, V>(map.sortedMap(), predicate); - } - - @GwtIncompatible("NavigableMap") - private static class FilteredEntryNavigableMap<K, V> extends FilteredEntrySortedMap<K, V> - implements NavigableMap<K, V> { - - FilteredEntryNavigableMap( - NavigableMap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { - super(unfiltered, entryPredicate); - } - - @Override - NavigableMap<K, V> sortedMap() { - return (NavigableMap<K, V>) super.sortedMap(); - } - - @Override - public Entry<K, V> lowerEntry(K key) { - return headMap(key, false).lastEntry(); - } - - @Override - public K lowerKey(K key) { - return keyOrNull(lowerEntry(key)); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return headMap(key, true).lastEntry(); - } - - @Override - public K floorKey(K key) { - return keyOrNull(floorEntry(key)); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return tailMap(key, true).firstEntry(); - } - - @Override - public K ceilingKey(K key) { - return keyOrNull(ceilingEntry(key)); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return tailMap(key, false).firstEntry(); - } - - @Override - public K higherKey(K key) { - return keyOrNull(higherEntry(key)); - } - - @Override - public Entry<K, V> firstEntry() { - return Iterables.getFirst(entrySet(), null); - } - - @Override - public Entry<K, V> lastEntry() { - return Iterables.getFirst(descendingMap().entrySet(), null); - } - - @Override - public Entry<K, V> pollFirstEntry() { - return pollFirstSatisfyingEntry(sortedMap().entrySet().iterator()); - } - - @Override - public Entry<K, V> pollLastEntry() { - return pollFirstSatisfyingEntry(sortedMap().descendingMap().entrySet().iterator()); - } - - @Nullable - Entry<K, V> pollFirstSatisfyingEntry(Iterator<Entry<K, V>> entryIterator) { - while (entryIterator.hasNext()) { - Entry<K, V> entry = entryIterator.next(); - if (predicate.apply(entry)) { - entryIterator.remove(); - return entry; - } - } - return null; - } - - @Override - public NavigableMap<K, V> descendingMap() { - return filterEntries(sortedMap().descendingMap(), predicate); - } - - @Override - public NavigableSet<K> keySet() { - return (NavigableSet<K>) super.keySet(); - } - - @Override - NavigableSet<K> createKeySet() { - return new NavigableKeySet<K, V>(this) { - @Override - public boolean removeAll(Collection<?> c) { - boolean changed = false; - Iterator<Entry<K, V>> entryIterator = sortedMap().entrySet().iterator(); - while (entryIterator.hasNext()) { - Entry<K, V> entry = entryIterator.next(); - if (c.contains(entry.getKey()) && predicate.apply(entry)) { - entryIterator.remove(); - changed = true; - } - } - return changed; - } - - @Override - public boolean retainAll(Collection<?> c) { - boolean changed = false; - Iterator<Entry<K, V>> entryIterator = sortedMap().entrySet().iterator(); - while (entryIterator.hasNext()) { - Entry<K, V> entry = entryIterator.next(); - if (!c.contains(entry.getKey()) && predicate.apply(entry)) { - entryIterator.remove(); - changed = true; - } - } - return changed; - } - }; - } - - @Override - public NavigableSet<K> navigableKeySet() { - return keySet(); - } - - @Override - public NavigableSet<K> descendingKeySet() { - return descendingMap().navigableKeySet(); - } - - @Override - public NavigableMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public NavigableMap<K, V> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return filterEntries( - sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive), predicate); - } - - @Override - public NavigableMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return filterEntries(sortedMap().headMap(toKey, inclusive), predicate); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return filterEntries(sortedMap().tailMap(fromKey, inclusive), predicate); - } - } - - /** - * 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; @@ -3050,14 +1840,10 @@ public final class Maps { @Override public Set<K> keySet() { Set<K> result = keySet; - return (result == null) ? keySet = createKeySet() : result; + return (result == null) ? keySet = new KeySet() : result; } - Set<K> createKeySet() { - return new KeySet(); - } - - 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>() { @@ -3093,13 +1879,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; } @@ -3119,224 +1914,6 @@ public final class Maps { } /** - * Returns an unmodifiable view of the specified navigable map. Query operations on the returned - * map read through to the specified map, and attempts to modify the returned map, whether direct - * or via its views, result in an {@code UnsupportedOperationException}. - * - * <p>The returned navigable map will be serializable if the specified navigable map is - * serializable. - * - * @param map the navigable map for which an unmodifiable view is to be returned - * @return an unmodifiable view of the specified navigable map - * @since 12.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> unmodifiableNavigableMap(NavigableMap<K, V> map) { - checkNotNull(map); - if (map instanceof UnmodifiableNavigableMap) { - return map; - } else { - return new UnmodifiableNavigableMap<K, V>(map); - } - } - - @Nullable private static <K, V> Entry<K, V> unmodifiableOrNull(@Nullable Entry<K, V> entry) { - return (entry == null) ? null : Maps.unmodifiableEntry(entry); - } - - @GwtIncompatible("NavigableMap") - static class UnmodifiableNavigableMap<K, V> - extends ForwardingSortedMap<K, V> implements NavigableMap<K, V>, Serializable { - private final NavigableMap<K, V> delegate; - - UnmodifiableNavigableMap(NavigableMap<K, V> delegate) { - this.delegate = delegate; - } - - @Override - protected SortedMap<K, V> delegate() { - return Collections.unmodifiableSortedMap(delegate); - } - - @Override - public Entry<K, V> lowerEntry(K key) { - return unmodifiableOrNull(delegate.lowerEntry(key)); - } - - @Override - public K lowerKey(K key) { - return delegate.lowerKey(key); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return unmodifiableOrNull(delegate.floorEntry(key)); - } - - @Override - public K floorKey(K key) { - return delegate.floorKey(key); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return unmodifiableOrNull(delegate.ceilingEntry(key)); - } - - @Override - public K ceilingKey(K key) { - return delegate.ceilingKey(key); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return unmodifiableOrNull(delegate.higherEntry(key)); - } - - @Override - public K higherKey(K key) { - return delegate.higherKey(key); - } - - @Override - public Entry<K, V> firstEntry() { - return unmodifiableOrNull(delegate.firstEntry()); - } - - @Override - public Entry<K, V> lastEntry() { - return unmodifiableOrNull(delegate.lastEntry()); - } - - @Override - public final Entry<K, V> pollFirstEntry() { - throw new UnsupportedOperationException(); - } - - @Override - public final Entry<K, V> pollLastEntry() { - throw new UnsupportedOperationException(); - } - - private transient UnmodifiableNavigableMap<K, V> descendingMap; - - @Override - public NavigableMap<K, V> descendingMap() { - UnmodifiableNavigableMap<K, V> result = descendingMap; - if (result == null) { - descendingMap = result = new UnmodifiableNavigableMap<K, V>(delegate.descendingMap()); - result.descendingMap = this; - } - return result; - } - - @Override - public Set<K> keySet() { - return navigableKeySet(); - } - - @Override - public NavigableSet<K> navigableKeySet() { - return Sets.unmodifiableNavigableSet(delegate.navigableKeySet()); - } - - @Override - public NavigableSet<K> descendingKeySet() { - return Sets.unmodifiableNavigableSet(delegate.descendingKeySet()); - } - - @Override - public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public - NavigableMap<K, V> - subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return Maps.unmodifiableNavigableMap(delegate.subMap( - fromKey, - fromInclusive, - toKey, - toInclusive)); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return Maps.unmodifiableNavigableMap(delegate.headMap(toKey, inclusive)); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return Maps.unmodifiableNavigableMap(delegate.tailMap(fromKey, inclusive)); - } - } - - /** - * Returns a synchronized (thread-safe) navigable map backed by the specified - * navigable map. In order to guarantee serial access, it is critical that - * <b>all</b> access to the backing navigable map is accomplished - * through the returned navigable map (or its views). - * - * <p>It is imperative that the user manually synchronize on the returned - * navigable map when iterating over any of its collection views, or the - * collections views of any of its {@code descendingMap}, {@code subMap}, - * {@code headMap} or {@code tailMap} views. <pre> {@code - * - * NavigableMap<K, V> map = synchronizedNavigableMap(new TreeMap<K, V>()); - * - * // Needn't be in synchronized block - * NavigableSet<K> set = map.navigableKeySet(); - * - * synchronized (map) { // Synchronizing on map, not set! - * Iterator<K> it = set.iterator(); // Must be in synchronized block - * while (it.hasNext()){ - * foo(it.next()); - * } - * }}</pre> - * - * or: <pre> {@code - * - * NavigableMap<K, V> map = synchronizedNavigableMap(new TreeMap<K, V>()); - * NavigableMap<K, V> map2 = map.subMap(foo, false, bar, true); - * - * // Needn't be in synchronized block - * NavigableSet<K> set2 = map2.descendingKeySet(); - * - * synchronized (map) { // Synchronizing on map, not map2 or set2! - * Iterator<K> it = set2.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 navigable map will be serializable if the specified - * navigable map is serializable. - * - * @param navigableMap the navigable map to be "wrapped" in a synchronized - * navigable map. - * @return a synchronized view of the specified navigable map. - * @since 13.0 - */ - @GwtIncompatible("NavigableMap") - public static <K, V> NavigableMap<K, V> synchronizedNavigableMap( - NavigableMap<K, V> navigableMap) { - return Synchronized.navigableMap(navigableMap); - } - - /** * {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code * entrySet().isEmpty()} instead of {@code size() == 0} to speed up * implementations where {@code size()} is O(n), and it delegates the {@code @@ -3344,7 +1921,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} @@ -3381,7 +1958,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; } @@ -3389,6 +1966,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 = @@ -3396,46 +1984,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; } } @@ -3494,6 +2061,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) { @@ -3517,30 +2091,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() { @@ -3563,153 +2143,27 @@ public final class Maps { return false; } - @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(); - } - - @GwtIncompatible("NavigableMap") - static class NavigableKeySet<K, V> extends KeySet<K, V> implements NavigableSet<K> { - private final NavigableMap<K, V> map; - - NavigableKeySet(NavigableMap<K, V> map) { - this.map = checkNotNull(map); - } - - @Override - NavigableMap<K, V> map() { - return map; - } - - @Override - public Comparator<? super K> comparator() { - return map().comparator(); - } - - @Override - public K first() { - return map().firstKey(); - } - - @Override - public K last() { - return map().lastKey(); - } - - @Override - public K lower(K e) { - return map().lowerKey(e); - } - - @Override - public K floor(K e) { - return map().floorKey(e); - } - - @Override - public K ceiling(K e) { - return map().ceilingKey(e); - } - - @Override - public K higher(K e) { - return map().higherKey(e); - } - - @Override - public K pollFirst() { - return keyOrNull(map().pollFirstEntry()); - } - - @Override - public K pollLast() { - return keyOrNull(map().pollLastEntry()); - } - - @Override - public NavigableSet<K> descendingSet() { - return map().descendingKeySet(); - } - - @Override - public Iterator<K> descendingIterator() { - return descendingSet().iterator(); - } - - @Override - public NavigableSet<K> subSet( - K fromElement, - boolean fromInclusive, - K toElement, - boolean toInclusive) { - return map().subMap(fromElement, fromInclusive, toElement, toInclusive).navigableKeySet(); - } - - @Override - public NavigableSet<K> headSet(K toElement, boolean inclusive) { - return map().headMap(toElement, inclusive).navigableKeySet(); - } - - @Override - public NavigableSet<K> tailSet(K fromElement, boolean inclusive) { - return map().tailMap(fromElement, inclusive).navigableKeySet(); - } - - @Override - public SortedSet<K> subSet(K fromElement, K toElement) { - return subSet(fromElement, true, toElement, false); - } - @Override - public SortedSet<K> headSet(K toElement) { - return headSet(toElement, false); + 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 SortedSet<K> tailSet(K fromElement) { - return tailSet(fromElement, true); + @Override public void clear() { + map().clear(); } } - 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) { @@ -3771,8 +2225,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() { @@ -3835,197 +2288,4 @@ public final class Maps { } } } - - @GwtIncompatible("NavigableMap") - abstract static class DescendingMap<K, V> extends ForwardingMap<K, V> - implements NavigableMap<K, V> { - - abstract NavigableMap<K, V> forward(); - - @Override - protected final Map<K, V> delegate() { - return forward(); - } - - private transient Comparator<? super K> comparator; - - @SuppressWarnings("unchecked") - @Override - public Comparator<? super K> comparator() { - Comparator<? super K> result = comparator; - if (result == null) { - Comparator<? super K> forwardCmp = forward().comparator(); - if (forwardCmp == null) { - forwardCmp = (Comparator) Ordering.natural(); - } - result = comparator = reverse(forwardCmp); - } - return result; - } - - // If we inline this, we get a javac error. - private static <T> Ordering<T> reverse(Comparator<T> forward) { - return Ordering.from(forward).reverse(); - } - - @Override - public K firstKey() { - return forward().lastKey(); - } - - @Override - public K lastKey() { - return forward().firstKey(); - } - - @Override - public Entry<K, V> lowerEntry(K key) { - return forward().higherEntry(key); - } - - @Override - public K lowerKey(K key) { - return forward().higherKey(key); - } - - @Override - public Entry<K, V> floorEntry(K key) { - return forward().ceilingEntry(key); - } - - @Override - public K floorKey(K key) { - return forward().ceilingKey(key); - } - - @Override - public Entry<K, V> ceilingEntry(K key) { - return forward().floorEntry(key); - } - - @Override - public K ceilingKey(K key) { - return forward().floorKey(key); - } - - @Override - public Entry<K, V> higherEntry(K key) { - return forward().lowerEntry(key); - } - - @Override - public K higherKey(K key) { - return forward().lowerKey(key); - } - - @Override - public Entry<K, V> firstEntry() { - return forward().lastEntry(); - } - - @Override - public Entry<K, V> lastEntry() { - return forward().firstEntry(); - } - - @Override - public Entry<K, V> pollFirstEntry() { - return forward().pollLastEntry(); - } - - @Override - public Entry<K, V> pollLastEntry() { - return forward().pollFirstEntry(); - } - - @Override - public NavigableMap<K, V> descendingMap() { - return forward(); - } - - private transient Set<Entry<K, V>> entrySet; - - @Override - public Set<Entry<K, V>> entrySet() { - Set<Entry<K, V>> result = entrySet; - return (result == null) ? entrySet = createEntrySet() : result; - } - - abstract Iterator<Entry<K, V>> entryIterator(); - - Set<Entry<K, V>> createEntrySet() { - return new EntrySet<K, V>() { - - @Override - Map<K, V> map() { - return DescendingMap.this; - } - - @Override - public Iterator<Entry<K, V>> iterator() { - return entryIterator(); - } - }; - } - - @Override - public Set<K> keySet() { - return navigableKeySet(); - } - - private transient NavigableSet<K> navigableKeySet; - - @Override - public NavigableSet<K> navigableKeySet() { - NavigableSet<K> result = navigableKeySet; - return (result == null) ? navigableKeySet = new NavigableKeySet<K, V>(this) : result; - } - - @Override - public NavigableSet<K> descendingKeySet() { - return forward().navigableKeySet(); - } - - @Override - public - NavigableMap<K, V> - subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - return forward().subMap(toKey, toInclusive, fromKey, fromInclusive).descendingMap(); - } - - @Override - public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - return forward().tailMap(toKey, inclusive).descendingMap(); - } - - @Override - public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - return forward().headMap(fromKey, inclusive).descendingMap(); - } - - @Override - public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public Collection<V> values() { - return new Values<K, V>() { - @Override - Map<K, V> map() { - return DescendingMap.this; - } - }; - } - } } diff --git a/guava/src/com/google/common/collect/MinMaxPriorityQueue.java b/guava/src/com/google/common/collect/MinMaxPriorityQueue.java index f9c2d92..4429c34 100644 --- a/guava/src/com/google/common/collect/MinMaxPriorityQueue.java +++ b/guava/src/com/google/common/collect/MinMaxPriorityQueue.java @@ -26,13 +26,13 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.math.IntMath; import java.util.AbstractQueue; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.PriorityQueue; @@ -85,7 +85,7 @@ import java.util.Queue; * @author Torbjorn Gannholm * @since 8.0 */ -// TODO(kevinb): GWT compatibility +// TODO(kevinb): @GwtCompatible @Beta public final class MinMaxPriorityQueue<E> extends AbstractQueue<E> { @@ -747,6 +747,7 @@ public final class MinMaxPriorityQueue<E> extends AbstractQueue<E> { private class QueueIterator implements Iterator<E> { private int cursor = -1; private int expectedModCount = modCount; + // TODO(user): Switch to ArrayDeque once Guava supports it. private Queue<E> forgetMeNot; private List<E> skipMe; private E lastFromForgetMeNot; @@ -787,7 +788,7 @@ public final class MinMaxPriorityQueue<E> extends AbstractQueue<E> { MoveDesc<E> moved = removeAt(cursor); if (moved != null) { if (forgetMeNot == null) { - forgetMeNot = new ArrayDeque<E>(); + forgetMeNot = new LinkedList<E>(); skipMe = new ArrayList<E>(3); } forgetMeNot.add(moved.toTrickle); diff --git a/guava/src/com/google/common/collect/Multimap.java b/guava/src/com/google/common/collect/Multimap.java index ed5afb3..900f820 100644 --- a/guava/src/com/google/common/collect/Multimap.java +++ b/guava/src/com/google/common/collect/Multimap.java @@ -19,139 +19,40 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** - * A collection that maps keys to values, similar to {@link Map}, but in which - * each key may be associated with <i>multiple</i> values. You can visualize the - * contents of a multimap either as a map from keys to <i>nonempty</i> - * collections of values: + * A collection similar to a {@code Map}, but which may associate multiple + * values with a single key. If you call {@link #put} twice, with the same key + * but different values, the multimap contains mappings from the key to both + * values. * - * <ul> - * <li>a → 1, 2 - * <li>b → 3 - * </ul> + * <p>The methods {@link #get}, {@link #keySet}, {@link #keys}, {@link #values}, + * {@link #entries}, and {@link #asMap} return collections that are views of the + * multimap. If the multimap is modifiable, updating it can change the contents + * of those collections, and updating the collections will change the multimap. + * In contrast, {@link #replaceValues} and {@link #removeAll} return collections + * that are independent of subsequent multimap changes. * - * ... or as a single "flattened" collection of key-value pairs: + * <p>Depending on the implementation, a multimap may or may not allow duplicate + * key-value pairs. In other words, the multimap contents after adding the same + * key and value twice varies between implementations. In multimaps allowing + * duplicates, the multimap will contain two mappings, and {@code get} will + * return a collection that includes the value twice. In multimaps not + * supporting duplicates, the multimap will contain a single mapping from the + * key to the value, and {@code get} will return a collection that includes the + * value once. * - * <ul> - * <li>a → 1 - * <li>a → 2 - * <li>b → 3 - * </ul> - * - * <p><b>Important:</b> although the first interpretation resembles how most - * multimaps are <i>implemented</i>, the design of the {@code Multimap} API is - * based on the <i>second</i> form. So, using the multimap shown above as an - * example, the {@link #size} is {@code 3}, not {@code 2}, and the {@link - * #values} collection is {@code [1, 2, 3]}, not {@code [[1, 2], [3]]}. For - * those times when the first style is more useful, use the multimap's {@link - * #asMap} view (or create a {@code Map<K, Collection<V>>} in the first place). - * - * <h3>Example</h3> - * - * <p>The following code: <pre> {@code - * - * ListMultimap<String, String> multimap = ArrayListMultimap.create(); - * for (President pres : US_PRESIDENTS_IN_ORDER) { - * multimap.put(pres.firstName(), pres.lastName()); - * } - * for (String firstName : multimap.keySet()) { - * List<String> lastNames = multimap.get(firstName); - * out.println(firstName + ": " + lastNames); - * }}</pre> - * - * ... produces output such as: <pre> {@code - * - * Zachary: [Taylor] - * John: [Adams, Adams, Tyler, Kennedy] - * George: [Washington, Bush, Bush] - * Grover: [Cleveland] - * ...}</pre> - * - * <h3>Views</h3> - * - * <p>Much of the power of the multimap API comes from the <i>view - * collections</i> it provides. These always reflect the latest state of the - * multimap itself. When they support modification, the changes are - * <i>write-through</i> (they automatically update the backing multimap). These - * view collections are: - * - * <ul> - * <li>{@link #asMap}, mentioned above</li> - * <li>{@link #keys}, {@link #keySet}, {@link #values}, {@link #entries}, which - * are similar to the corresponding view collections of {@link Map} - * <li>and, notably, even the collection returned by {@link #get get(key)} is an - * active view of the values corresponding to {@code key} - * </ul> - * - * <p>The collections returned by the {@link #replaceValues replaceValues} and - * {@link #removeAll removeAll} methods, which contain values that have just - * been removed from the multimap, are naturally <i>not</i> views. - * - * <h3>Subinterfaces</h3> - * - * <p>Instead of using the {@code Multimap} interface directly, prefer the - * subinterfaces {@link ListMultimap} and {@link SetMultimap}. These take their - * names from the fact that the collections they return from {@code get} behave - * like (and, of course, implement) {@link List} and {@link Set}, respectively. - * - * <p>For example, the "presidents" code snippet above used a {@code - * ListMultimap}; if it had used a {@code SetMultimap} instead, two presidents - * would have vanished, and last names might or might not appear in - * chronological order. - * - * <p><b>Warning:</b> instances of type {@code Multimap} may not implement - * {@link Object#equals} in the way you expect (multimaps containing the same - * key-value pairs, even in the same order, may or may not be equal). The - * recommended subinterfaces provide a much stronger guarantee. - * - * <h3>Comparison to a map of collections</h3> - * - * <p>Multimaps are commonly used in places where a {@code Map<K, - * Collection<V>>} would otherwise have appeared. The differences include: - * - * <ul> - * <li>There is no need to populate an empty collection before adding an entry - * with {@link #put put}. - * <li>{@code get} never returns {@code null}, only an empty collection. - * <li>A key contained in the multimap always maps to at least one value. Any - * operation that causes a key to have zero associated values has the effect - * of <i>removing</i> that key from the multimap. - * <li>The total entry count is available as {@link #size}. - * <li>Many complex operations become easier; for example, {@code - * Collections.min(multimap.values())} finds the smallest value across all - * keys. - * </ul> - * - * <h3>Implementations</h3> - * - * <p>As always, prefer the immutable implementations, {@link - * ImmutableListMultimap} and {@link ImmutableSetMultimap}. General-purpose - * mutable implementations are listed above under "All Known Implementing - * Classes". You can also create a <i>custom</i> multimap, backed by any {@code - * Map} and {@link Collection} types, using the {@link Multimaps#newMultimap - * Multimaps.newMultimap} family of methods. Finally, another popular way to - * obtain a multimap is using {@link Multimaps#index Multimaps.index}. See - * the {@link Multimaps} class for these and other static utilities related - * to multimaps. - * - * <h3>Other Notes</h3> - * - * <p>All methods that modify the multimap are optional. The view collections - * returned by the multimap may or may not be modifiable. Any modification - * method that is not supported will throw {@link - * UnsupportedOperationException}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap"> - * {@code Multimap}</a>. + * <p>All methods that alter the multimap are optional, and the views returned + * by the multimap may or may not be modifiable. When modification isn't + * supported, those methods will throw an {@link UnsupportedOperationException}. * * @author Jared Levy + * @param <K> the type of keys maintained by this multimap + * @param <V> the type of mapped values * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible @@ -207,7 +108,7 @@ public interface Multimap<K, V> { boolean put(@Nullable K key, @Nullable V value); /** - * Removes a single key-value pair from the multimap. + * Removes a key-value pair from the multimap. * * @param key key of entry to remove from the multimap * @param value value of entry to remove the multimap @@ -268,17 +169,15 @@ public interface Multimap<K, V> { // Views /** - * Returns a collection view containing the values associated with {@code key} - * in this multimap, if any. Note that even when ({@code containsKey(key)} is - * false, {@code get(key)} still returns an empty collection, not {@code - * null}. + * Returns a collection view of all values associated with a key. If no + * mappings in the multimap have the provided key, an empty collection is + * returned. * * <p>Changes to the returned collection will update the underlying multimap, * and vice versa. * * @param key key to search for in multimap - * @return a view collection containing the zero or more values that the key - * maps to + * @return the collection of values that the key maps to */ Collection<V> get(@Nullable K key); diff --git a/guava/src/com/google/common/collect/Multimaps.java b/guava/src/com/google/common/collect/Multimaps.java index 92e5d06..e2f593e 100644 --- a/guava/src/com/google/common/collect/Multimaps.java +++ b/guava/src/com/google/common/collect/Multimaps.java @@ -20,14 +20,17 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; 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.IOException; @@ -35,6 +38,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.AbstractCollection; +import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -52,10 +56,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 @@ -67,14 +67,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 @@ -114,7 +109,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, @@ -423,13 +418,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()); * } @@ -630,7 +625,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(); @@ -639,6 +634,10 @@ public final class Multimaps { public Collection<V> next() { return unmodifiableValueCollection(iterator.next()); } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } }; } @Override public Object[] toArray() { @@ -1061,7 +1060,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; @@ -1142,7 +1141,7 @@ public final class Multimaps { @Override public Multiset<K> keys() { - return new Multimaps.Keys<K, V>(this); + return Multisets.forSet(map.keySet()); } @Override @@ -1193,27 +1192,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(); + } }; } @@ -1315,6 +1322,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); @@ -1383,29 +1391,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; @@ -1415,25 +1409,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() { @@ -1454,25 +1453,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) { @@ -1522,16 +1554,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(); } } @@ -1576,6 +1631,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) { @@ -1642,6 +1698,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) { @@ -1728,6 +1785,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, @@ -1783,36 +1858,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() { @@ -1833,22 +1911,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; @@ -1859,16 +1937,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) { @@ -1877,7 +1969,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; @@ -1897,35 +1996,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(); } } @@ -1941,7 +2050,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()); } @@ -1949,7 +2058,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()); } @@ -2047,8 +2156,8 @@ public final class Multimaps { * <p>The resulting multimap's views have iterators that don't support * {@code remove()}, but all other methods are supported by the multimap and * its views. When adding a key that doesn't satisfy the predicate, the - * multimap's {@code put()}, {@code putAll()}, and {@code replaceValues()} - * methods throw an {@link IllegalArgumentException}. + * multimap's {@code put()}, {@code putAll()}, and {@replaceValues()} methods + * throw an {@link IllegalArgumentException}. * * <p>When methods such as {@code removeAll()} and {@code clear()} are called on * the filtered multimap or its views, only mappings whose keys satisfy the @@ -2069,21 +2178,19 @@ public final class Multimaps { * * @since 11.0 */ + @Beta @GwtIncompatible(value = "untested") public static <K, V> Multimap<K, V> filterKeys( - Multimap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { - if (unfiltered instanceof FilteredKeyMultimap) { - FilteredKeyMultimap<K, V> prev = (FilteredKeyMultimap<K, V>) unfiltered; - return new FilteredKeyMultimap<K, V>(prev.unfiltered, - Predicates.and(prev.keyPredicate, keyPredicate)); - } else if (unfiltered instanceof FilteredMultimap) { - FilteredMultimap<K, V> prev = (FilteredMultimap<K, V>) unfiltered; - return new FilteredEntryMultimap<K, V>(prev.unfiltered, - Predicates.<Entry<K, V>>and(prev.entryPredicate(), - Predicates.compose(keyPredicate, Maps.<K>keyFunction()))); - } else { - return new FilteredKeyMultimap<K, V>(unfiltered, keyPredicate); - } + Multimap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { + checkNotNull(keyPredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return keyPredicate.apply(input.getKey()); + } + }; + return filterEntries(unfiltered, entryPredicate); } /** @@ -2094,8 +2201,8 @@ public final class Multimaps { * <p>The resulting multimap's views have iterators that don't support * {@code remove()}, but all other methods are supported by the multimap and * its views. When adding a value that doesn't satisfy the predicate, the - * multimap's {@code put()}, {@code putAll()}, and {@code replaceValues()} - * methods throw an {@link IllegalArgumentException}. + * multimap's {@code put()}, {@code putAll()}, and {@replaceValues()} methods + * throw an {@link IllegalArgumentException}. * * <p>When methods such as {@code removeAll()} and {@code clear()} are called on * the filtered multimap or its views, only mappings whose value satisfy the @@ -2116,10 +2223,19 @@ public final class Multimaps { * * @since 11.0 */ + @Beta @GwtIncompatible(value = "untested") public static <K, V> Multimap<K, V> filterValues( - Multimap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { - return filterEntries(unfiltered, Predicates.compose(valuePredicate, Maps.<V>valueFunction())); + Multimap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { + checkNotNull(valuePredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return valuePredicate.apply(input.getValue()); + } + }; + return filterEntries(unfiltered, entryPredicate); } /** @@ -2130,8 +2246,8 @@ public final class Multimaps { * <p>The resulting multimap's views have iterators that don't support * {@code remove()}, but all other methods are supported by the multimap and * its views. When adding a key/value pair that doesn't satisfy the predicate, - * multimap's {@code put()}, {@code putAll()}, and {@code replaceValues()} - * methods throw an {@link IllegalArgumentException}. + * multimap's {@code put()}, {@code putAll()}, and {@replaceValues()} methods + * throw an {@link IllegalArgumentException}. * * <p>When methods such as {@code removeAll()} and {@code clear()} are called on * the filtered multimap or its views, only mappings whose keys satisfy the @@ -2150,27 +2266,489 @@ public final class Multimaps { * * @since 11.0 */ + @Beta @GwtIncompatible(value = "untested") public static <K, V> Multimap<K, V> filterEntries( - Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { + Multimap<K, V> unfiltered, Predicate<? super Entry<K, V>> entryPredicate) { checkNotNull(entryPredicate); return (unfiltered instanceof FilteredMultimap) ? filterFiltered((FilteredMultimap<K, V>) unfiltered, entryPredicate) - : new FilteredEntryMultimap<K, V>(checkNotNull(unfiltered), entryPredicate); + : new FilteredMultimap<K, V>(checkNotNull(unfiltered), entryPredicate); } /** * 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/src/com/google/common/collect/Multiset.java b/guava/src/com/google/common/collect/Multiset.java index bb254c9..823750e 100644 --- a/guava/src/com/google/common/collect/Multiset.java +++ b/guava/src/com/google/common/collect/Multiset.java @@ -31,13 +31,13 @@ import javax.annotation.Nullable; * may have duplicate elements. A multiset is also sometimes called a * <i>bag</i>. * - * <p>Elements of a multiset that are equal to one another are referred to as - * <i>occurrences</i> of the same single element. The total number of - * occurrences of an element in a multiset is called the <i>count</i> of that - * element (the terms "frequency" and "multiplicity" are equivalent, but not - * used in this API). Since the count of an element is represented as an {@code - * int}, a multiset may never contain more than {@link Integer#MAX_VALUE} - * occurrences of any one element. + * <p>Elements of a multiset that are equal to one another (see "Note on + * element equivalence", below) are referred to as <i>occurrences</i> of the + * same single element. The total number of occurrences of an element in a + * multiset is called the <i>count</i> of that element (the terms "frequency" + * and "multiplicity" are equivalent, but not used in this API). Since the count + * of an element is represented as an {@code int}, a multiset may never contain + * more than {@link Integer#MAX_VALUE} occurrences of any one element. * * <p>{@code Multiset} refines the specifications of several methods from * {@code Collection}. It also defines an additional query operation, {@link @@ -77,10 +77,6 @@ import javax.annotation.Nullable; * may wish to use {@link com.google.common.util.concurrent.AtomicLongMap} * instead. Note, however, that unlike {@code Multiset}, {@code AtomicLongMap} * does not automatically remove zeros. - * - * <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 * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/Multisets.java b/guava/src/com/google/common/collect/Multisets.java index d0ab028..dbd54c3 100644 --- a/guava/src/com/google/common/collect/Multisets.java +++ b/guava/src/com/google/common/collect/Multisets.java @@ -18,33 +18,32 @@ 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.Objects; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Multiset.Entry; -import com.google.common.primitives.Ints; +import static com.google.common.base.Preconditions.checkState; import java.io.Serializable; +import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; +import java.util.SortedSet; import javax.annotation.Nullable; +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.collect.Multiset.Entry; +import com.google.common.primitives.Ints; + /** * Provides static utility methods for creating and working with {@link * Multiset} instances. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Multisets"> - * {@code Multisets}</a>. - * * @author Kevin Bourrillion * @author Mike Bostock * @author Louis Wasserman @@ -194,10 +193,92 @@ public final class Multisets { @Beta public static <E> SortedMultiset<E> unmodifiableSortedMultiset( SortedMultiset<E> sortedMultiset) { - // it's in its own file so it can be emulated for GWT return new UnmodifiableSortedMultiset<E>(checkNotNull(sortedMultiset)); } + private static final class UnmodifiableSortedMultiset<E> + extends UnmodifiableMultiset<E> implements SortedMultiset<E> { + private 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 unmodifiableSortedMultiset( + delegate().headMultiset(upperBound, boundType)); + } + + @Override + public SortedMultiset<E> subMultiset( + E lowerBound, BoundType lowerBoundType, + E upperBound, BoundType upperBoundType) { + return unmodifiableSortedMultiset(delegate().subMultiset( + lowerBound, lowerBoundType, upperBound, upperBoundType)); + } + + @Override + public SortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { + return unmodifiableSortedMultiset( + delegate().tailMultiset(lowerBound, boundType)); + } + + private static final long serialVersionUID = 0; + } + /** * Returns an immutable multiset entry with the specified element and count. * The entry will be serializable if {@code e} is. @@ -235,125 +316,152 @@ public final class Multisets { } /** - * Returns a view of the elements of {@code unfiltered} that satisfy a predicate. The returned - * multiset is a live view of {@code unfiltered}; changes to one affect the other. - * - * <p>The resulting multiset's iterators, and those of its {@code entrySet()} and - * {@code elementSet()}, do not support {@code remove()}. However, all other multiset methods - * supported by {@code unfiltered} are supported by the returned multiset. When given an element - * that doesn't satisfy the predicate, the multiset's {@code add()} and {@code addAll()} methods - * throw an {@link IllegalArgumentException}. When methods such as {@code removeAll()} and - * {@code clear()} are called on the filtered multiset, only elements that satisfy the filter - * will be removed from the underlying multiset. + * Returns a multiset view of the specified set. The multiset is backed by the + * set, so changes to the set are reflected in the multiset, and vice versa. + * If the set is modified while an iteration over the multiset is in progress + * (except through the iterator's own {@code remove} operation) the results of + * the iteration are undefined. * - * <p>The returned multiset isn't threadsafe or serializable, even if {@code unfiltered} is. + * <p>The multiset supports element removal, which removes the corresponding + * element from the set. It does not support the {@code add} or {@code addAll} + * operations, nor does it support the use of {@code setCount} to add + * elements. * - * <p>Many of the filtered multiset's methods, such as {@code size()}, iterate across every - * element in the underlying multiset and determine which elements satisfy the filter. When a - * live view is <i>not</i> needed, it may be faster to copy the returned multiset and use the - * copy. + * <p>The returned multiset will be serializable if the specified set is + * serializable. The multiset is threadsafe if the set is threadsafe. * - * <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, as documented at - * {@link Predicate#apply}. Do not provide a predicate such as - * {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See - * {@link Iterables#filter(Iterable, Class)} for related functionality.) - * - * @since 14.0 + * @param set the backing set for the returned multiset view */ - @Beta - public static <E> Multiset<E> filter(Multiset<E> unfiltered, Predicate<? super E> predicate) { - if (unfiltered instanceof FilteredMultiset) { - // Support clear(), removeAll(), and retainAll() when filtering a filtered - // collection. - FilteredMultiset<E> filtered = (FilteredMultiset<E>) unfiltered; - Predicate<E> combinedPredicate - = Predicates.<E>and(filtered.predicate, predicate); - return new FilteredMultiset<E>(filtered.unfiltered, combinedPredicate); - } - return new FilteredMultiset<E>(unfiltered, predicate); + static <E> Multiset<E> forSet(Set<E> set) { + return new SetMultiset<E>(set); } - private static final class FilteredMultiset<E> extends AbstractMultiset<E> { - final Multiset<E> unfiltered; - final Predicate<? super E> predicate; + /** @see Multisets#forSet */ + private static class SetMultiset<E> extends ForwardingCollection<E> + implements Multiset<E>, Serializable { + final Set<E> delegate; - FilteredMultiset(Multiset<E> unfiltered, Predicate<? super E> predicate) { - this.unfiltered = checkNotNull(unfiltered); - this.predicate = checkNotNull(predicate); + SetMultiset(Set<E> set) { + delegate = checkNotNull(set); } - @Override - Set<E> createElementSet() { - return Sets.filter(unfiltered.elementSet(), predicate); + @Override protected Set<E> delegate() { + return delegate; } @Override - Set<Entry<E>> createEntrySet() { - return Sets.filter(unfiltered.entrySet(), new Predicate<Entry<E>>() { - @Override - public boolean apply(Entry<E> entry) { - return predicate.apply(entry.getElement()); - } - }); + public int count(Object element) { + return delegate.contains(element) ? 1 : 0; } @Override - Iterator<Entry<E>> entryIterator() { - throw new AssertionError("should never be called"); + public int add(E element, int occurrences) { + throw new UnsupportedOperationException(); } @Override - int distinctElements() { - return elementSet().size(); + public int remove(Object element, int occurrences) { + if (occurrences == 0) { + return count(element); + } + checkArgument(occurrences > 0); + return delegate.remove(element) ? 1 : 0; } + transient Set<E> elementSet; + @Override - public boolean contains(@Nullable Object element) { - return count(element) > 0; + public Set<E> elementSet() { + Set<E> es = elementSet; + return (es == null) ? elementSet = new ElementSet() : es; } - @Override - public int count(@Nullable Object element) { - int count = unfiltered.count(element); - if (count > 0) { - @SuppressWarnings("unchecked") // element is equal to an E - E e = (E) element; - return predicate.apply(e) ? count : 0; + transient Set<Entry<E>> entrySet; + + @Override public Set<Entry<E>> entrySet() { + Set<Entry<E>> es = entrySet; + if (es == null) { + es = entrySet = new EntrySet<E>() { + @Override Multiset<E> multiset() { + return SetMultiset.this; + } + + @Override public Iterator<Entry<E>> iterator() { + return Iterators.transform(delegate.iterator(), + new Function<E, Entry<E>>() { + @Override public Entry<E> apply(E elem) { + return immutableEntry(elem, 1); + } + }); + } + + @Override public int size() { + return delegate.size(); + } + }; } - return 0; + return es; } - @Override - public int add(@Nullable E element, int occurrences) { - checkArgument(predicate.apply(element), - "Element %s does not match predicate %s", element, predicate); - return unfiltered.add(element, occurrences); + @Override public boolean add(E o) { + throw new UnsupportedOperationException(); + } + + @Override public boolean addAll(Collection<? extends E> c) { + throw new UnsupportedOperationException(); } @Override - public int remove(@Nullable Object element, int occurrences) { - Multisets.checkNonnegative(occurrences, "occurrences"); - if (occurrences == 0) { - return count(element); + public int setCount(E element, int count) { + checkNonnegative(count, "count"); + + if (count == count(element)) { + return count; + } else if (count == 0) { + remove(element); + return 1; } else { - return contains(element) ? unfiltered.remove(element, occurrences) : 0; + throw new UnsupportedOperationException(); } } @Override - public boolean removeAll(Collection<?> c) { - return elementSet().removeAll(c); + public boolean setCount(E element, int oldCount, int newCount) { + return setCountImpl(this, element, oldCount, newCount); } - @Override - public boolean retainAll(Collection<?> c) { - return elementSet().retainAll(c); + @Override public boolean equals(@Nullable Object object) { + if (object instanceof Multiset) { + Multiset<?> that = (Multiset<?>) object; + return this.size() == that.size() && delegate.equals(that.elementSet()); + } + return false; } - @Override - public void clear() { - elementSet().clear(); + @Override public int hashCode() { + int sum = 0; + for (E e : this) { + sum += ((e == null) ? 0 : e.hashCode()) ^ 1; + } + return sum; + } + + /** @see SetMultiset#elementSet */ + class ElementSet extends ForwardingSet<E> { + @Override protected Set<E> delegate() { + return delegate; + } + + @Override public boolean add(E o) { + throw new UnsupportedOperationException(); + } + + @Override public boolean addAll(Collection<? extends E> c) { + throw new UnsupportedOperationException(); + } } + + private static final long serialVersionUID = 0; } /** @@ -370,93 +478,16 @@ public final class Multisets { } /** - * Returns an unmodifiable view of the union of two multisets. - * In the returned multiset, the count of each element is the <i>maximum</i> - * of its counts in the two backing multisets. The iteration order of the - * returned multiset matches that of the element set of {@code multiset1} - * followed by the members of the element set of {@code multiset2} that are - * not contained in {@code multiset1}, with repeated occurrences of the same + * Returns an unmodifiable <b>view</b> of the intersection of two multisets. + * An element's count in the multiset is the smaller of its counts in the two + * backing multisets. The iteration order of the returned multiset matches the + * element set of {@code multiset1}, with repeated occurrences of the same * element appearing consecutively. * * <p>Results are undefined if {@code multiset1} and {@code multiset2} are * based on different equivalence relations (as {@code HashMultiset} and * {@code TreeMultiset} are). * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> union( - final Multiset<? extends E> multiset1, final Multiset<? extends E> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public boolean contains(@Nullable Object element) { - return multiset1.contains(element) || multiset2.contains(element); - } - - @Override - public boolean isEmpty() { - return multiset1.isEmpty() && multiset2.isEmpty(); - } - - @Override - public int count(Object element) { - return Math.max(multiset1.count(element), multiset2.count(element)); - } - - @Override - Set<E> createElementSet() { - return Sets.union(multiset1.elementSet(), multiset2.elementSet()); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<? extends Entry<? extends E>> iterator1 - = multiset1.entrySet().iterator(); - final Iterator<? extends Entry<? extends E>> iterator2 - = multiset2.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - if (iterator1.hasNext()) { - Entry<? extends E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = Math.max(entry1.getCount(), multiset2.count(element)); - return immutableEntry(element, count); - } - while (iterator2.hasNext()) { - Entry<? extends E> entry2 = iterator2.next(); - E element = entry2.getElement(); - if (!multiset1.contains(element)) { - return immutableEntry(element, entry2.getCount()); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return elementSet().size(); - } - }; - } - - /** - * Returns an unmodifiable view of the intersection of two multisets. - * In the returned multiset, the count of each element is the <i>minimum</i> - * of its counts in the two backing multisets, with elements that would have - * a count of 0 not included. The iteration order of the returned multiset - * matches that of the element set of {@code multiset1}, with repeated - * occurrences of the same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * * @since 2.0 */ public static <E> Multiset<E> intersection( @@ -488,88 +519,7 @@ public final class Multisets { E element = entry1.getElement(); int count = Math.min(entry1.getCount(), multiset2.count(element)); if (count > 0) { - return immutableEntry(element, count); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return elementSet().size(); - } - }; - } - - /** - * Returns an unmodifiable view of the sum of two multisets. - * In the returned multiset, the count of each element is the <i>sum</i> of - * its counts in the two backing multisets. The iteration order of the - * returned multiset matches that of the element set of {@code multiset1} - * followed by the members of the element set of {@code multiset2} that that - * are not contained in {@code multiset1}, with repeated occurrences of the - * same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> sum( - final Multiset<? extends E> multiset1, final Multiset<? extends E> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public boolean contains(@Nullable Object element) { - return multiset1.contains(element) || multiset2.contains(element); - } - - @Override - public boolean isEmpty() { - return multiset1.isEmpty() && multiset2.isEmpty(); - } - - @Override - public int size() { - return multiset1.size() + multiset2.size(); - } - - @Override - public int count(Object element) { - return multiset1.count(element) + multiset2.count(element); - } - - @Override - Set<E> createElementSet() { - return Sets.union(multiset1.elementSet(), multiset2.elementSet()); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<? extends Entry<? extends E>> iterator1 - = multiset1.entrySet().iterator(); - final Iterator<? extends Entry<? extends E>> iterator2 - = multiset2.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - if (iterator1.hasNext()) { - Entry<? extends E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = entry1.getCount() + multiset2.count(element); - return immutableEntry(element, count); - } - while (iterator2.hasNext()) { - Entry<? extends E> entry2 = iterator2.next(); - E element = entry2.getElement(); - if (!multiset1.contains(element)) { - return immutableEntry(element, entry2.getCount()); + return Multisets.immutableEntry(element, count); } } return endOfData(); @@ -585,66 +535,12 @@ public final class Multisets { } /** - * Returns an unmodifiable view of the difference of two multisets. - * In the returned multiset, the count of each element is the result of the - * <i>zero-truncated subtraction</i> of its count in the second multiset from - * its count in the first multiset, with elements that would have a count of - * 0 not included. The iteration order of the returned multiset matches that - * of the element set of {@code multiset1}, with repeated occurrences of the - * same element appearing consecutively. - * - * <p>Results are undefined if {@code multiset1} and {@code multiset2} are - * based on different equivalence relations (as {@code HashMultiset} and - * {@code TreeMultiset} are). - * - * @since 14.0 - */ - @Beta - public static <E> Multiset<E> difference( - final Multiset<E> multiset1, final Multiset<?> multiset2) { - checkNotNull(multiset1); - checkNotNull(multiset2); - - return new AbstractMultiset<E>() { - @Override - public int count(@Nullable Object element) { - int count1 = multiset1.count(element); - return (count1 == 0) ? 0 : - Math.max(0, count1 - multiset2.count(element)); - } - - @Override - Iterator<Entry<E>> entryIterator() { - final Iterator<Entry<E>> iterator1 = multiset1.entrySet().iterator(); - return new AbstractIterator<Entry<E>>() { - @Override - protected Entry<E> computeNext() { - while (iterator1.hasNext()) { - Entry<E> entry1 = iterator1.next(); - E element = entry1.getElement(); - int count = entry1.getCount() - multiset2.count(element); - if (count > 0) { - return immutableEntry(element, count); - } - } - return endOfData(); - } - }; - } - - @Override - int distinctElements() { - return Iterators.size(entryIterator()); - } - }; - } - - /** * Returns {@code true} if {@code subMultiset.count(o) <= * superMultiset.count(o)} for all {@code o}. * * @since 10.0 */ + @Beta public static boolean containsOccurrences( Multiset<?> superMultiset, Multiset<?> subMultiset) { checkNotNull(superMultiset); @@ -677,7 +573,7 @@ public final class Multisets { * of this operation * @since 10.0 */ - public static boolean retainOccurrences(Multiset<?> multisetToModify, + @Beta public static boolean retainOccurrences(Multiset<?> multisetToModify, Multiset<?> multisetToRetain) { return retainOccurrencesImpl(multisetToModify, multisetToRetain); } @@ -729,7 +625,7 @@ public final class Multisets { * this operation * @since 10.0 */ - public static boolean removeOccurrences( + @Beta public static boolean removeOccurrences( Multiset<?> multisetToModify, Multiset<?> occurrencesToRemove) { return removeOccurrencesImpl(multisetToModify, occurrencesToRemove); } @@ -864,7 +760,6 @@ public final class Multisets { */ static boolean retainAllImpl( Multiset<?> self, Collection<?> elementsToRetain) { - checkNotNull(elementsToRetain); Collection<?> collection = (elementsToRetain instanceof Multiset) ? ((Multiset<?>) elementsToRetain).elementSet() : elementsToRetain; @@ -905,7 +800,7 @@ public final class Multisets { } } - abstract static class ElementSet<E> extends Sets.ImprovedAbstractSet<E> { + static abstract class ElementSet<E> extends AbstractSet<E> { abstract Multiset<E> multiset(); @Override public void clear() { @@ -925,12 +820,12 @@ public final class Multisets { } @Override public Iterator<E> iterator() { - return new TransformedIterator<Entry<E>, E>(multiset().entrySet().iterator()) { - @Override - E transform(Entry<E> entry) { - return entry.getElement(); - } - }; + return Iterators.transform(multiset().entrySet().iterator(), + new Function<Entry<E>, E>() { + @Override public E apply(Entry<E> entry) { + return entry.getElement(); + } + }); } @Override @@ -948,14 +843,11 @@ public final class Multisets { } } - abstract static class EntrySet<E> extends Sets.ImprovedAbstractSet<Entry<E>> { + static abstract class EntrySet<E> extends AbstractSet<Entry<E>>{ abstract Multiset<E> multiset(); @Override public boolean contains(@Nullable Object o) { if (o instanceof Entry) { - /* - * The GWT compiler wrongly issues a warning here. - */ @SuppressWarnings("cast") Entry<?> entry = (Entry<?>) o; if (entry.getCount() <= 0) { @@ -968,21 +860,10 @@ public final class Multisets { return false; } - // GWT compiler warning; see contains(). @SuppressWarnings("cast") - @Override public boolean remove(Object object) { - if (object instanceof Multiset.Entry) { - Entry<?> entry = (Entry<?>) object; - Object element = entry.getElement(); - int entryCount = entry.getCount(); - if (entryCount != 0) { - // Safe as long as we never add a new entry, which we won't. - @SuppressWarnings("unchecked") - Multiset<Object> multiset = (Multiset) multiset(); - return multiset.setCount(element, entryCount, 0); - } - } - return false; + @Override public boolean remove(Object o) { + return contains(o) + && multiset().elementSet().remove(((Entry<?>) o).getElement()); } @Override public void clear() { @@ -1035,7 +916,8 @@ public final class Multisets { @Override public void remove() { - Iterators.checkRemove(canRemove); + checkState( + canRemove, "no calls to next() since the last call to remove()"); if (totalCount == 1) { entryIterator.remove(); } else { diff --git a/guava/src/com/google/common/collect/MutableClassToInstanceMap.java b/guava/src/com/google/common/collect/MutableClassToInstanceMap.java index c723cea..b40e801 100644 --- a/guava/src/com/google/common/collect/MutableClassToInstanceMap.java +++ b/guava/src/com/google/common/collect/MutableClassToInstanceMap.java @@ -25,10 +25,6 @@ import java.util.Map; /** * A mutable class-to-instance map backed by an arbitrary user-provided map. * See also {@link ImmutableClassToInstanceMap}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#ClassToInstanceMap"> - * {@code ClassToInstanceMap}</a>. * * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/ObjectArrays.java b/guava/src/com/google/common/collect/ObjectArrays.java index dfd1fe9..954a30e 100644 --- a/guava/src/com/google/common/collect/ObjectArrays.java +++ b/guava/src/com/google/common/collect/ObjectArrays.java @@ -19,7 +19,6 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; -import java.lang.reflect.Array; import java.util.Collection; import javax.annotation.Nullable; @@ -32,8 +31,6 @@ import javax.annotation.Nullable; */ @GwtCompatible(emulated = true) public final class ObjectArrays { - static final Object[] EMPTY_ARRAY = new Object[0]; - private ObjectArrays() {} /** @@ -43,9 +40,8 @@ public final class ObjectArrays { * @param length the length of the new array */ @GwtIncompatible("Array.newInstance(Class, int)") - @SuppressWarnings("unchecked") public static <T> T[] newArray(Class<T> type, int length) { - return (T[]) Array.newInstance(type, length); + return Platform.newArray(type, length); } /** @@ -69,8 +65,8 @@ public final class ObjectArrays { @GwtIncompatible("Array.newInstance(Class, int)") public static <T> T[] concat(T[] first, T[] second, Class<T> type) { T[] result = newArray(type, first.length + second.length); - System.arraycopy(first, 0, result, 0, first.length); - System.arraycopy(second, 0, result, first.length, second.length); + Platform.unsafeArrayCopy(first, 0, result, 0, first.length); + Platform.unsafeArrayCopy(second, 0, result, first.length, second.length); return result; } @@ -86,7 +82,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; } @@ -108,7 +104,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; } @@ -183,13 +179,4 @@ 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/src/com/google/common/collect/Ordering.java b/guava/src/com/google/common/collect/Ordering.java index 9ee9c48..1f0c6e3 100644 --- a/guava/src/com/google/common/collect/Ordering.java +++ b/guava/src/com/google/common/collect/Ordering.java @@ -19,13 +19,12 @@ 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.annotations.VisibleForTesting; import com.google.common.base.Function; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -35,15 +34,13 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.SortedMap; import java.util.SortedSet; -import java.util.TreeSet; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; /** - * A comparator, with additional methods to support common operations. This is - * an "enriched" version of {@code Comparator}, in the same sense that {@link - * FluentIterable} is an enriched {@link Iterable}). For example: <pre> {@code + * A comparator with added methods to support common functions. For example: + * <pre> {@code * * if (Ordering.from(comparator).reverse().isOrdered(list)) { ... }}</pre> * @@ -62,17 +59,13 @@ import javax.annotation.Nullable; * are. For example, if {@code ordering} and {@code function} can themselves be * serialized, then {@code ordering.onResultOf(function)} can as well. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/OrderingExplained"> - * {@code Ordering}</a>. - * * @author Jesse Wilson * @author Kevin Bourrillion * @since 2.0 (imported from Google Collections Library) */ @GwtCompatible public abstract class Ordering<T> implements Comparator<T> { - // Natural order + // Static factories /** * Returns a serializable ordering that uses the natural order of the values. @@ -89,17 +82,13 @@ public abstract class Ordering<T> implements Comparator<T> { return (Ordering<C>) NaturalOrdering.INSTANCE; } - // Static factories - /** - * Returns an ordering based on an <i>existing</i> comparator instance. Note - * that there's no need to create a <i>new</i> comparator just to pass it in - * here; simply subclass {@code Ordering} and implement its {@code compareTo} - * method directly instead. + * Returns an ordering for a pre-existing {@code comparator}. Note + * that if the comparator is not pre-existing, and you don't require + * serialization, you can subclass {@code Ordering} and implement its + * {@link #compare(Object, Object) compare} method instead. * * @param comparator the comparator that defines the order - * @return comparator itself if it is already an {@code Ordering}; otherwise - * an ordering that wraps that comparator */ @GwtCompatible(serializable = true) public static <T> Ordering<T> from(Comparator<T> comparator) { @@ -173,48 +162,23 @@ public abstract class Ordering<T> implements Comparator<T> { return explicit(Lists.asList(leastValue, remainingValuesInOrder)); } - // Ordering<Object> singletons - /** - * Returns an ordering which treats all values as equal, indicating "no - * ordering." Passing this ordering to any <i>stable</i> sort algorithm - * results in no change to the order of elements. Note especially that {@link - * #sortedCopy} and {@link #immutableSortedCopy} are stable, and in the - * returned instance these are implemented by simply copying the source list. - * - * <p>Example: <pre> {@code - * - * Ordering.allEqual().nullsLast().sortedCopy( - * asList(t, null, e, s, null, t, null))}</pre> - * - * Assuming {@code t}, {@code e} and {@code s} are non-null, this returns - * {@code [t, e, s, t, null, null, null]} regardlesss of the true comparison - * order of those three values (which might not even implement {@link - * Comparable} at all). - * - * <p><b>Warning:</b> by definition, this comparator is not <i>consistent with - * equals</i> (as defined {@linkplain Comparator here}). Avoid its use in - * APIs, such as {@link TreeSet#TreeSet(Comparator)}, where such consistency - * is expected. - * - * <p>The returned comparator is serializable. + * Exception thrown by a {@link Ordering#explicit(List)} or {@link + * Ordering#explicit(Object, Object[])} comparator when comparing a value + * outside the set of values it can compare. Extending {@link + * ClassCastException} may seem odd, but it is required. */ - @GwtCompatible(serializable = true) - @SuppressWarnings("unchecked") - public static Ordering<Object> allEqual() { - return AllEqualOrdering.INSTANCE; - } + // TODO(kevinb): make this public, document it right + @VisibleForTesting + static class IncomparableValueException extends ClassCastException { + final Object value; - /** - * Returns an ordering that compares objects by the natural ordering of their - * string representations as returned by {@code toString()}. It does not - * support null values. - * - * <p>The comparator is serializable. - */ - @GwtCompatible(serializable = true) - public static Ordering<Object> usingToString() { - return UsingToStringOrdering.INSTANCE; + IncomparableValueException(Object value) { + super("Cannot compare value: " + value); + this.value = value; + } + + private static final long serialVersionUID = 0; } /** @@ -256,10 +220,6 @@ public abstract class Ordering<T> implements Comparator<T> { @Override public int compare(Object left, Object right) { if (left == right) { return 0; - } else if (left == null) { - return -1; - } else if (right == null) { - return 1; } int leftCode = identityHashCode(left); int rightCode = identityHashCode(right); @@ -292,62 +252,46 @@ public abstract class Ordering<T> implements Comparator<T> { } } - // Constructor - /** - * Constructs a new instance of this class (only invokable by the subclass - * constructor, typically implicit). - */ - protected Ordering() {} - - // Instance-based factories (and any static equivalents) - - /** - * Returns the reverse of this ordering; the {@code Ordering} equivalent to - * {@link Collections#reverseOrder(Comparator)}. + * Returns an ordering that compares objects by the natural ordering of their + * string representations as returned by {@code toString()}. It does not + * support null values. + * + * <p>The comparator is serializable. */ - // type parameter <S> lets us avoid the extra <String> in statements like: - // Ordering<String> o = Ordering.<String>natural().reverse(); @GwtCompatible(serializable = true) - public <S extends T> Ordering<S> reverse() { - return new ReverseOrdering<S>(this); + public static Ordering<Object> usingToString() { + return UsingToStringOrdering.INSTANCE; } /** - * Returns an ordering that treats {@code null} as less than all other values - * and uses {@code this} to compare non-null values. + * Returns an ordering which tries each given comparator in order until a + * non-zero result is found, returning that result, and returning zero only if + * all comparators return zero. The returned ordering is based on the state of + * the {@code comparators} iterable at the time it was provided to this + * method. + * + * <p>The returned ordering is equivalent to that produced using {@code + * Ordering.from(comp1).compound(comp2).compound(comp3) . . .}. + * + * <p><b>Warning:</b> Supplying an argument with undefined iteration order, + * such as a {@link HashSet}, will produce non-deterministic results. + * + * @param comparators the comparators to try in order */ - // type parameter <S> lets us avoid the extra <String> in statements like: - // Ordering<String> o = Ordering.<String>natural().nullsFirst(); @GwtCompatible(serializable = true) - public <S extends T> Ordering<S> nullsFirst() { - return new NullsFirstOrdering<S>(this); + public static <T> Ordering<T> compound( + Iterable<? extends Comparator<? super T>> comparators) { + return new CompoundOrdering<T>(comparators); } /** - * Returns an ordering that treats {@code null} as greater than all other - * values and uses this ordering to compare non-null values. + * Constructs a new instance of this class (only invokable by the subclass + * constructor, typically implicit). */ - // type parameter <S> lets us avoid the extra <String> in statements like: - // Ordering<String> o = Ordering.<String>natural().nullsLast(); - @GwtCompatible(serializable = true) - public <S extends T> Ordering<S> nullsLast() { - return new NullsLastOrdering<S>(this); - } + protected Ordering() {} - /** - * Returns a new ordering on {@code F} which orders elements by first applying - * a function to them, then comparing those results using {@code this}. For - * example, to compare objects by their string forms, in a case-insensitive - * manner, use: <pre> {@code - * - * Ordering.from(String.CASE_INSENSITIVE_ORDER) - * .onResultOf(Functions.toStringFunction())}</pre> - */ - @GwtCompatible(serializable = true) - public <F> Ordering<F> onResultOf(Function<F, ? extends T> function) { - return new ByFunctionOrdering<F, T>(function, this); - } + // Non-static factories /** * Returns an ordering which first uses the ordering {@code this}, but which @@ -367,24 +311,28 @@ public abstract class Ordering<T> implements Comparator<T> { } /** - * Returns an ordering which tries each given comparator in order until a - * non-zero result is found, returning that result, and returning zero only if - * all comparators return zero. The returned ordering is based on the state of - * the {@code comparators} iterable at the time it was provided to this - * method. - * - * <p>The returned ordering is equivalent to that produced using {@code - * Ordering.from(comp1).compound(comp2).compound(comp3) . . .}. - * - * <p><b>Warning:</b> Supplying an argument with undefined iteration order, - * such as a {@link HashSet}, will produce non-deterministic results. + * Returns the reverse of this ordering; the {@code Ordering} equivalent to + * {@link Collections#reverseOrder(Comparator)}. + */ + // type parameter <S> lets us avoid the extra <String> in statements like: + // Ordering<String> o = Ordering.<String>natural().reverse(); + @GwtCompatible(serializable = true) + public <S extends T> Ordering<S> reverse() { + return new ReverseOrdering<S>(this); + } + + /** + * Returns a new ordering on {@code F} which orders elements by first applying + * a function to them, then comparing those results using {@code this}. For + * example, to compare objects by their string forms, in a case-insensitive + * manner, use: <pre> {@code * - * @param comparators the comparators to try in order + * Ordering.from(String.CASE_INSENSITIVE_ORDER) + * .onResultOf(Functions.toStringFunction())}</pre> */ @GwtCompatible(serializable = true) - public static <T> Ordering<T> compound( - Iterable<? extends Comparator<? super T>> comparators) { - return new CompoundOrdering<T>(comparators); + public <F> Ordering<F> onResultOf(Function<F, ? extends T> function) { + return new ByFunctionOrdering<F, T>(function, this); } /** @@ -416,162 +364,32 @@ public abstract class Ordering<T> implements Comparator<T> { return new LexicographicalOrdering<S>(this); } - // Regular instance methods - - // Override to add @Nullable - @Override public abstract int compare(@Nullable T left, @Nullable T right); - - /** - * Returns the least of the specified values according to this ordering. If - * there are multiple least values, the first of those is returned. The - * iterator will be left exhausted: its {@code hasNext()} method will return - * {@code false}. - * - * @param iterator the iterator whose minimum element is to be determined - * @throws NoSuchElementException if {@code iterator} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - * - * @since 11.0 - */ - public <E extends T> E min(Iterator<E> iterator) { - // let this throw NoSuchElementException as necessary - E minSoFar = iterator.next(); - - while (iterator.hasNext()) { - minSoFar = min(minSoFar, iterator.next()); - } - - return minSoFar; - } - - /** - * Returns the least of the specified values according to this ordering. If - * there are multiple least values, the first of those is returned. - * - * @param iterable the iterable whose minimum element is to be determined - * @throws NoSuchElementException if {@code iterable} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E min(Iterable<E> iterable) { - return min(iterable.iterator()); - } - - /** - * Returns the lesser of the two values according to this ordering. If the - * values compare as 0, the first is returned. - * - * <p><b>Implementation note:</b> this method is invoked by the default - * implementations of the other {@code min} overloads, so overriding it will - * affect their behavior. - * - * @param a value to compare, returned if less than or equal to b. - * @param b value to compare. - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E min(@Nullable E a, @Nullable E b) { - return (compare(a, b) <= 0) ? a : b; - } - - /** - * Returns the least of the specified values according to this ordering. If - * there are multiple least values, the first of those is returned. - * - * @param a value to compare, returned if less than or equal to the rest. - * @param b value to compare - * @param c value to compare - * @param rest values to compare - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E min( - @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { - E minSoFar = min(min(a, b), c); - - for (E r : rest) { - minSoFar = min(minSoFar, r); - } - - return minSoFar; - } - /** - * Returns the greatest of the specified values according to this ordering. If - * there are multiple greatest values, the first of those is returned. The - * iterator will be left exhausted: its {@code hasNext()} method will return - * {@code false}. - * - * @param iterator the iterator whose maximum element is to be determined - * @throws NoSuchElementException if {@code iterator} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - * - * @since 11.0 - */ - public <E extends T> E max(Iterator<E> iterator) { - // let this throw NoSuchElementException as necessary - E maxSoFar = iterator.next(); - - while (iterator.hasNext()) { - maxSoFar = max(maxSoFar, iterator.next()); - } - - return maxSoFar; - } - - /** - * Returns the greatest of the specified values according to this ordering. If - * there are multiple greatest values, the first of those is returned. - * - * @param iterable the iterable whose maximum element is to be determined - * @throws NoSuchElementException if {@code iterable} is empty - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. + * Returns an ordering that treats {@code null} as less than all other values + * and uses {@code this} to compare non-null values. */ - public <E extends T> E max(Iterable<E> iterable) { - return max(iterable.iterator()); + // type parameter <S> lets us avoid the extra <String> in statements like: + // Ordering<String> o = Ordering.<String>natural().nullsFirst(); + @GwtCompatible(serializable = true) + public <S extends T> Ordering<S> nullsFirst() { + return new NullsFirstOrdering<S>(this); } /** - * Returns the greater of the two values according to this ordering. If the - * values compare as 0, the first is returned. - * - * <p><b>Implementation note:</b> this method is invoked by the default - * implementations of the other {@code max} overloads, so overriding it will - * affect their behavior. - * - * @param a value to compare, returned if greater than or equal to b. - * @param b value to compare. - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. + * Returns an ordering that treats {@code null} as greater than all other + * values and uses this ordering to compare non-null values. */ - public <E extends T> E max(@Nullable E a, @Nullable E b) { - return (compare(a, b) >= 0) ? a : b; + // type parameter <S> lets us avoid the extra <String> in statements like: + // Ordering<String> o = Ordering.<String>natural().nullsLast(); + @GwtCompatible(serializable = true) + public <S extends T> Ordering<S> nullsLast() { + return new NullsLastOrdering<S>(this); } - /** - * Returns the greatest of the specified values according to this ordering. If - * there are multiple greatest values, the first of those is returned. - * - * @param a value to compare, returned if greater than or equal to the rest. - * @param b value to compare - * @param c value to compare - * @param rest values to compare - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> under this ordering. - */ - public <E extends T> E max( - @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { - E maxSoFar = max(max(a, b), c); - - for (E r : rest) { - maxSoFar = max(maxSoFar, r); - } + // Regular instance methods - return maxSoFar; - } + // Override to add @Nullable + @Override public abstract int compare(@Nullable T left, @Nullable T right); /** * Returns the {@code k} least elements of the given iterable according to @@ -587,130 +405,64 @@ public abstract class Ordering<T> implements Comparator<T> { * @throws IllegalArgumentException if {@code k} is negative * @since 8.0 */ + @Beta public <E extends T> List<E> leastOf(Iterable<E> iterable, int k) { - if (iterable instanceof Collection) { - Collection<E> collection = (Collection<E>) iterable; - if (collection.size() <= 2L * k) { - // In this case, just dumping the collection to an array and sorting is - // faster than using the implementation for Iterator, which is - // specialized for k much smaller than n. - - @SuppressWarnings("unchecked") // c only contains E's and doesn't escape - E[] array = (E[]) collection.toArray(); - Arrays.sort(array, this); - if (array.length > k) { - array = ObjectArrays.arraysCopyOf(array, k); - } - return Collections.unmodifiableList(Arrays.asList(array)); - } + checkArgument(k >= 0, "%d is negative", k); + + // values is not an E[], but we use it as such for readability. Hack. + @SuppressWarnings("unchecked") + E[] values = (E[]) Iterables.toArray(iterable); + + // TODO(nshupe): also sort whole list if k is *near* values.length? + // TODO(kevinb): benchmark this impl against hand-coded heap + E[] resultArray; + if (values.length <= k) { + Arrays.sort(values, this); + resultArray = values; + } else { + quicksortLeastK(values, 0, values.length - 1, k); + + // this is not an E[], but we use it as such for readability. Hack. + @SuppressWarnings("unchecked") + E[] tmp = (E[]) new Object[k]; + resultArray = tmp; + System.arraycopy(values, 0, resultArray, 0, k); } - return leastOf(iterable.iterator(), k); + + return Collections.unmodifiableList(Arrays.asList(resultArray)); } /** - * Returns the {@code k} least elements from the given iterator according to - * this ordering, in order from least to greatest. If there are fewer than + * Returns the {@code k} greatest elements of the given iterable according to + * this ordering, in order from greatest to least. If there are fewer than * {@code k} elements present, all will be included. * * <p>The implementation does not necessarily use a <i>stable</i> sorting * algorithm; when multiple elements are equivalent, it is undefined which * will come first. * - * @return an immutable {@code RandomAccess} list of the {@code k} least - * elements in ascending order + * @return an immutable {@code RandomAccess} list of the {@code k} greatest + * elements in <i>descending order</i> * @throws IllegalArgumentException if {@code k} is negative - * @since 14.0 + * @since 8.0 */ - public <E extends T> List<E> leastOf(Iterator<E> elements, int k) { - checkNotNull(elements); - checkArgument(k >= 0, "k (%s) must be nonnegative", k); - - if (k == 0 || !elements.hasNext()) { - return ImmutableList.of(); - } else if (k >= Integer.MAX_VALUE / 2) { - // k is really large; just do a straightforward sorted-copy-and-sublist - ArrayList<E> list = Lists.newArrayList(elements); - Collections.sort(list, this); - if (list.size() > k) { - list.subList(k, list.size()).clear(); - } - list.trimToSize(); - return Collections.unmodifiableList(list); - } - - /* - * Our goal is an O(n) algorithm using only one pass and O(k) additional - * memory. - * - * We use the following algorithm: maintain a buffer of size 2*k. Every time - * the buffer gets full, find the median and partition around it, keeping - * only the lowest k elements. This requires n/k find-median-and-partition - * steps, each of which take O(k) time with a traditional quickselect. - * - * After sorting the output, the whole algorithm is O(n + k log k). It - * degrades gracefully for worst-case input (descending order), performs - * competitively or wins outright for randomly ordered input, and doesn't - * require the whole collection to fit into memory. - */ - int bufferCap = k * 2; - @SuppressWarnings("unchecked") // we'll only put E's in - E[] buffer = (E[]) new Object[bufferCap]; - E threshold = elements.next(); - buffer[0] = threshold; - int bufferSize = 1; - // threshold is the kth smallest element seen so far. Once bufferSize >= k, - // anything larger than threshold can be ignored immediately. - - while (bufferSize < k && elements.hasNext()) { - E e = elements.next(); - buffer[bufferSize++] = e; - threshold = max(threshold, e); - } - - while (elements.hasNext()) { - E e = elements.next(); - if (compare(e, threshold) >= 0) { - continue; - } - - buffer[bufferSize++] = e; - if (bufferSize == bufferCap) { - // We apply the quickselect algorithm to partition about the median, - // and then ignore the last k elements. - int left = 0; - int right = bufferCap - 1; - - int minThresholdPosition = 0; - // The leftmost position at which the greatest of the k lower elements - // -- the new value of threshold -- might be found. - - while (left < right) { - int pivotIndex = (left + right + 1) >>> 1; - int pivotNewIndex = partition(buffer, left, right, pivotIndex); - if (pivotNewIndex > k) { - right = pivotNewIndex - 1; - } else if (pivotNewIndex < k) { - left = Math.max(pivotNewIndex, left + 1); - minThresholdPosition = pivotNewIndex; - } else { - break; - } - } - bufferSize = k; + @Beta + public <E extends T> List<E> greatestOf(Iterable<E> iterable, int k) { + // TODO(kevinb): see if delegation is hurting performance noticeably + // TODO(kevinb): if we change this implementation, add full unit tests. + return reverse().leastOf(iterable, k); + } - threshold = buffer[minThresholdPosition]; - for (int i = minThresholdPosition + 1; i < bufferSize; i++) { - threshold = max(threshold, buffer[i]); - } + private <E extends T> void quicksortLeastK( + E[] values, int left, int right, int k) { + if (right > left) { + int pivotIndex = (left + right) >>> 1; // left + ((right - left) / 2) + int pivotNewIndex = partition(values, left, right, pivotIndex); + quicksortLeastK(values, left, pivotNewIndex - 1, k); + if (pivotNewIndex < k) { + quicksortLeastK(values, pivotNewIndex + 1, right, k); } } - - Arrays.sort(buffer, 0, bufferSize, this); - - bufferSize = Math.min(bufferSize, k); - return Collections.unmodifiableList( - Arrays.asList(ObjectArrays.arraysCopyOf(buffer, bufferSize))); - // We can't use ImmutableList; we have to be null-friendly! } private <E extends T> int partition( @@ -732,41 +484,15 @@ public abstract class Ordering<T> implements Comparator<T> { } /** - * Returns the {@code k} greatest elements of the given iterable according to - * this ordering, in order from greatest to least. If there are fewer than - * {@code k} elements present, all will be included. - * - * <p>The implementation does not necessarily use a <i>stable</i> sorting - * algorithm; when multiple elements are equivalent, it is undefined which - * will come first. - * - * @return an immutable {@code RandomAccess} list of the {@code k} greatest - * elements in <i>descending order</i> - * @throws IllegalArgumentException if {@code k} is negative - * @since 8.0 - */ - public <E extends T> List<E> greatestOf(Iterable<E> iterable, int k) { - // TODO(kevinb): see if delegation is hurting performance noticeably - // TODO(kevinb): if we change this implementation, add full unit tests. - return reverse().leastOf(iterable, k); - } - - /** - * Returns the {@code k} greatest elements from the given iterator according to - * this ordering, in order from greatest to least. If there are fewer than - * {@code k} elements present, all will be included. - * - * <p>The implementation does not necessarily use a <i>stable</i> sorting - * algorithm; when multiple elements are equivalent, it is undefined which - * will come first. + * {@link Collections#binarySearch(List, Object, Comparator) Searches} + * {@code sortedList} for {@code key} using the binary search algorithm. The + * list must be sorted using this ordering. * - * @return an immutable {@code RandomAccess} list of the {@code k} greatest - * elements in <i>descending order</i> - * @throws IllegalArgumentException if {@code k} is negative - * @since 14.0 + * @param sortedList the list to be searched + * @param key the key to be searched for */ - public <E extends T> List<E> greatestOf(Iterator<E> iterator, int k) { - return reverse().leastOf(iterator, k); + public int binarySearch(List<? extends T> sortedList, @Nullable T key) { + return Collections.binarySearch(sortedList, key, this); } /** @@ -783,10 +509,9 @@ public abstract class Ordering<T> implements Comparator<T> { * @return a new list containing the given elements in sorted order */ public <E extends T> List<E> sortedCopy(Iterable<E> iterable) { - @SuppressWarnings("unchecked") // does not escape, and contains only E's - E[] array = (E[]) Iterables.toArray(iterable); - Arrays.sort(array, this); - return Lists.newArrayList(Arrays.asList(array)); + List<E> list = Lists.newArrayList(iterable); + Collections.sort(list, this); + return list; } /** @@ -806,13 +531,7 @@ public abstract class Ordering<T> implements Comparator<T> { */ public <E extends T> ImmutableList<E> immutableSortedCopy( Iterable<E> iterable) { - @SuppressWarnings("unchecked") // we'll only ever have E's in here - E[] elements = (E[]) Iterables.toArray(iterable); - for (E e : elements) { - checkNotNull(e); - } - Arrays.sort(elements, this); - return ImmutableList.asImmutableList(elements); + return ImmutableList.copyOf(sortedCopy(iterable)); } /** @@ -858,34 +577,157 @@ public abstract class Ordering<T> implements Comparator<T> { } /** - * {@link Collections#binarySearch(List, Object, Comparator) Searches} - * {@code sortedList} for {@code key} using the binary search algorithm. The - * list must be sorted using this ordering. + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. The + * iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. * - * @param sortedList the list to be searched - * @param key the key to be searched for + * @param iterator the iterator whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + * + * @since 11.0 */ - public int binarySearch(List<? extends T> sortedList, @Nullable T key) { - return Collections.binarySearch(sortedList, key, this); + @Beta + public <E extends T> E max(Iterator<E> iterator) { + // let this throw NoSuchElementException as necessary + E maxSoFar = iterator.next(); + + while (iterator.hasNext()) { + maxSoFar = max(maxSoFar, iterator.next()); + } + + return maxSoFar; } /** - * Exception thrown by a {@link Ordering#explicit(List)} or {@link - * Ordering#explicit(Object, Object[])} comparator when comparing a value - * outside the set of values it can compare. Extending {@link - * ClassCastException} may seem odd, but it is required. + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param iterable the iterable whose maximum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. */ - // TODO(kevinb): make this public, document it right - @VisibleForTesting - static class IncomparableValueException extends ClassCastException { - final Object value; + public <E extends T> E max(Iterable<E> iterable) { + return max(iterable.iterator()); + } - IncomparableValueException(Object value) { - super("Cannot compare value: " + value); - this.value = value; + /** + * Returns the greatest of the specified values according to this ordering. If + * there are multiple greatest values, the first of those is returned. + * + * @param a value to compare, returned if greater than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E max( + @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E maxSoFar = max(max(a, b), c); + + for (E r : rest) { + maxSoFar = max(maxSoFar, r); } - private static final long serialVersionUID = 0; + return maxSoFar; + } + + /** + * Returns the greater of the two values according to this ordering. If the + * values compare as 0, the first is returned. + * + * <p><b>Implementation note:</b> this method is invoked by the default + * implementations of the other {@code max} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if greater than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E max(@Nullable E a, @Nullable E b) { + return compare(a, b) >= 0 ? a : b; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. The + * iterator will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @param iterator the iterator whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterator} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + * + * @since 11.0 + */ + @Beta + public <E extends T> E min(Iterator<E> iterator) { + // let this throw NoSuchElementException as necessary + E minSoFar = iterator.next(); + + while (iterator.hasNext()) { + minSoFar = min(minSoFar, iterator.next()); + } + + return minSoFar; + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param iterable the iterable whose minimum element is to be determined + * @throws NoSuchElementException if {@code iterable} is empty + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E min(Iterable<E> iterable) { + return min(iterable.iterator()); + } + + /** + * Returns the least of the specified values according to this ordering. If + * there are multiple least values, the first of those is returned. + * + * @param a value to compare, returned if less than or equal to the rest. + * @param b value to compare + * @param c value to compare + * @param rest values to compare + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E min( + @Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E minSoFar = min(min(a, b), c); + + for (E r : rest) { + minSoFar = min(minSoFar, r); + } + + return minSoFar; + } + + /** + * Returns the lesser of the two values according to this ordering. If the + * values compare as 0, the first is returned. + * + * <p><b>Implementation note:</b> this method is invoked by the default + * implementations of the other {@code min} overloads, so overriding it will + * affect their behavior. + * + * @param a value to compare, returned if less than or equal to b. + * @param b value to compare. + * @throws ClassCastException if the parameters are not <i>mutually + * comparable</i> under this ordering. + */ + public <E extends T> E min(@Nullable E a, @Nullable E b) { + return compare(a, b) <= 0 ? a : b; } // Never make these public diff --git a/guava/src/com/google/common/collect/PeekingIterator.java b/guava/src/com/google/common/collect/PeekingIterator.java index 294b2e6..be8989d 100644 --- a/guava/src/com/google/common/collect/PeekingIterator.java +++ b/guava/src/com/google/common/collect/PeekingIterator.java @@ -23,10 +23,6 @@ import java.util.NoSuchElementException; /** * An iterator that supports a one-element lookahead while iterating. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionHelpersExplained#PeekingIterator"> - * {@code PeekingIterator}</a>. * * @author Mick Killianey * @since 2.0 (imported from Google Collections Library) diff --git a/guava/src/com/google/common/collect/Platform.java b/guava/src/com/google/common/collect/Platform.java index cd321e8..408563b 100644 --- a/guava/src/com/google/common/collect/Platform.java +++ b/guava/src/com/google/common/collect/Platform.java @@ -17,16 +17,9 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Maps.EntryTransformer; +import com.google.common.annotations.GwtIncompatible; import java.lang.reflect.Array; -import java.util.Map; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.SortedMap; -import java.util.SortedSet; /** * Methods factored out so that they can be emulated differently in GWT. @@ -44,6 +37,35 @@ class Platform { } /** + * Wrapper around {@link System#arraycopy} so that it can be emulated + * correctly in GWT. + * + * <p>It is only intended for the case {@code src} and {@code dest} are + * different. It also doesn't validate the types and indices. + * + * <p>As of GWT 2.0, The built-in {@link System#arraycopy} doesn't work + * in general case. See + * http://code.google.com/p/google-web-toolkit/issues/detail?id=3621 + * for more details. + */ + static void unsafeArrayCopy( + Object[] src, int srcPos, Object[] dest, int destPos, int length) { + System.arraycopy(src, srcPos, dest, destPos, length); + } + + /** + * Returns a new array of the given length with the specified component type. + * + * @param type the component type + * @param length the length of the new array + */ + @GwtIncompatible("Array.newInstance(Class, int)") + @SuppressWarnings("unchecked") + static <T> T[] newArray(Class<T> type, int length) { + return (T[]) Array.newInstance(type, length); + } + + /** * Returns a new array of the given length with the same type as a reference * array. * @@ -70,34 +92,5 @@ class Platform { return mapMaker.weakKeys(); } - static <K, V1, V2> SortedMap<K, V2> mapsTransformEntriesSortedMap( - SortedMap<K, V1> fromMap, - EntryTransformer<? super K, ? super V1, V2> transformer) { - return (fromMap instanceof NavigableMap) - ? Maps.transformEntries((NavigableMap<K, V1>) fromMap, transformer) - : Maps.transformEntriesIgnoreNavigable(fromMap, transformer); - } - - static <K, V> SortedMap<K, V> mapsAsMapSortedSet(SortedSet<K> set, - Function<? super K, V> function) { - return (set instanceof NavigableSet) - ? Maps.asMap((NavigableSet<K>) set, function) - : Maps.asMapSortedIgnoreNavigable(set, function); - } - - static <E> SortedSet<E> setsFilterSortedSet(SortedSet<E> set, - Predicate<? super E> predicate) { - return (set instanceof NavigableSet) - ? Sets.filter((NavigableSet<E>) set, predicate) - : Sets.filterSortedIgnoreNavigable(set, predicate); - } - - static <K, V> SortedMap<K, V> mapsFilterSortedMap(SortedMap<K, V> map, - Predicate<? super Map.Entry<K, V>> predicate) { - return (map instanceof NavigableMap) - ? Maps.filterEntries((NavigableMap<K, V>) map, predicate) - : Maps.filterSortedIgnoreNavigable(map, predicate); - } - private Platform() {} } diff --git a/guava/src/com/google/common/collect/Queues.java b/guava/src/com/google/common/collect/Queues.java index d2bf4ad..d68146a 100644 --- a/guava/src/com/google/common/collect/Queues.java +++ b/guava/src/com/google/common/collect/Queues.java @@ -19,7 +19,6 @@ import com.google.common.base.Preconditions; import java.util.ArrayDeque; import java.util.Collection; -import java.util.Deque; import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; @@ -32,12 +31,14 @@ import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; /** - * Static utility methods pertaining to {@link Queue} and {@link Deque} instances. - * Also see this class's counterparts {@link Lists}, {@link Sets}, and {@link Maps}. + * Static utility methods pertaining to {@link Queue} + * instances. Also see this class's counterparts + * {@link Lists}, {@link Sets}, and {@link Maps}. * * @author Kurt Alfred Kluever * @since 11.0 */ +@Beta public final class Queues { private Queues() {} @@ -54,32 +55,6 @@ public final class Queues { // ArrayDeque - /** - * Creates an empty {@code ArrayDeque} instance. - * - * @return a new, empty {@code ArrayDeque} - * @since 12.0 - */ - public static <E> ArrayDeque<E> newArrayDeque() { - return new ArrayDeque<E>(); - } - - /** - * Creates an {@code ArrayDeque} instance containing the given elements. - * - * @param elements the elements that the queue should contain, in order - * @return a new {@code ArrayDeque} containing those elements - * @since 12.0 - */ - public static <E> ArrayDeque<E> newArrayDeque(Iterable<? extends E> elements) { - if (elements instanceof Collection) { - return new ArrayDeque<E>(Collections2.cast(elements)); - } - ArrayDeque<E> deque = new ArrayDeque<E>(); - Iterables.addAll(deque, elements); - return deque; - } - // ConcurrentLinkedQueue /** @@ -109,44 +84,6 @@ public final class Queues { // LinkedBlockingDeque - /** - * Creates an empty {@code LinkedBlockingDeque} instance. - * - * @return a new, empty {@code LinkedBlockingDeque} - * @since 12.0 - */ - public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque() { - return new LinkedBlockingDeque<E>(); - } - - /** - * Creates a {@code LinkedBlockingDeque} with the given (fixed) capacity. - * - * @param capacity the capacity of this deque - * @return a new, empty {@code LinkedBlockingDeque} - * @throws IllegalArgumentException if {@code capacity} is less than 1 - * @since 12.0 - */ - public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(int capacity) { - return new LinkedBlockingDeque<E>(capacity); - } - - /** - * Creates an {@code LinkedBlockingDeque} instance containing the given elements. - * - * @param elements the elements that the queue should contain, in order - * @return a new {@code LinkedBlockingDeque} containing those elements - * @since 12.0 - */ - public static <E> LinkedBlockingDeque<E> newLinkedBlockingDeque(Iterable<? extends E> elements) { - if (elements instanceof Collection) { - return new LinkedBlockingDeque<E>(Collections2.cast(elements)); - } - LinkedBlockingDeque<E> deque = new LinkedBlockingDeque<E>(); - Iterables.addAll(deque, elements); - return deque; - } - // LinkedBlockingQueue /** @@ -249,12 +186,12 @@ public final class Queues { public static <E> SynchronousQueue<E> newSynchronousQueue() { return new SynchronousQueue<E>(); } - + /** - * Drains the queue as {@link BlockingQueue#drainTo(Collection, int)}, but if the requested + * Drains the queue as {@link BlockingQueue#drainTo(Collection, int)}, but if the requested * {@code numElements} elements are not available, it will wait for them up to the specified * timeout. - * + * * @param q the blocking queue to be drained * @param buffer where to add the transferred elements * @param numElements the number of elements to be waited for @@ -263,7 +200,6 @@ public final class Queues { * @return the number of elements transferred * @throws InterruptedException if interrupted while waiting */ - @Beta public static <E> int drain(BlockingQueue<E> q, Collection<? super E> buffer, int numElements, long timeout, TimeUnit unit) throws InterruptedException { Preconditions.checkNotNull(buffer); @@ -289,13 +225,13 @@ public final class Queues { } return added; } - + /** - * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, - * but with a different behavior in case it is interrupted while waiting. In that case, the - * operation will continue as usual, and in the end the thread's interruption status will be set - * (no {@code InterruptedException} is thrown). - * + * Drains the queue as {@linkplain #drain(BlockingQueue, Collection, int, long, TimeUnit)}, + * but with a different behavior in case it is interrupted while waiting. In that case, the + * operation will continue as usual, and in the end the thread's interruption status will be set + * (no {@code InterruptedException} is thrown). + * * @param q the blocking queue to be drained * @param buffer where to add the transferred elements * @param numElements the number of elements to be waited for @@ -303,8 +239,7 @@ public final class Queues { * @param unit a {@code TimeUnit} determining how to interpret the timeout parameter * @return the number of elements transferred */ - @Beta - public static <E> int drainUninterruptibly(BlockingQueue<E> q, Collection<? super E> buffer, + public static <E> int drainUninterruptibly(BlockingQueue<E> q, Collection<? super E> buffer, int numElements, long timeout, TimeUnit unit) { Preconditions.checkNotNull(buffer); long deadline = System.nanoTime() + unit.toNanos(timeout); @@ -312,7 +247,7 @@ public final class Queues { boolean interrupted = false; try { while (added < numElements) { - // we could rely solely on #poll, but #drainTo might be more efficient when there are + // we could rely solely on #poll, but #drainTo might be more efficient when there are // multiple elements already available (e.g. LinkedBlockingQueue#drainTo locks only once) added += q.drainTo(buffer, numElements - added); if (added < numElements) { // not enough elements immediately available; will have to poll @@ -339,36 +274,4 @@ public final class Queues { } return added; } - - /** - * Returns a synchronized (thread-safe) queue backed by the specified queue. In order to - * guarantee serial access, it is critical that <b>all</b> access to the backing queue is - * accomplished through the returned queue. - * - * <p>It is imperative that the user manually synchronize on the returned queue when accessing - * the queue's iterator: <pre> {@code - * - * Queue<E> queue = Queues.synchronizedQueue(MinMaxPriorityQueue<E>.create()); - * ... - * queue.add(element); // Needn't be in synchronized block - * ... - * synchronized (queue) { // Must synchronize on queue! - * Iterator<E> i = queue.iterator(); // Must be in synchronized block - * while (i.hasNext()) { - * foo(i.next()); - * } - * }}</pre> - * - * Failure to follow this advice may result in non-deterministic behavior. - * - * <p>The returned queue will be serializable if the specified queue is serializable. - * - * @param queue the queue to be wrapped in a synchronized view - * @return a synchronized view of the specified queue - * @since 14.0 - */ - @Beta - public static <E> Queue<E> synchronizedQueue(Queue<E> queue) { - return Synchronized.queue(queue, null); - } } diff --git a/guava/src/com/google/common/collect/Range.java b/guava/src/com/google/common/collect/Range.java index e70c34f..c6a9189 100644 --- a/guava/src/com/google/common/collect/Range.java +++ b/guava/src/com/google/common/collect/Range.java @@ -17,17 +17,15 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Ranges.create; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.base.Equivalence; -import com.google.common.base.Function; import com.google.common.base.Predicate; import java.io.Serializable; import java.util.Collections; import java.util.Comparator; -import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedSet; @@ -35,338 +33,94 @@ import java.util.SortedSet; import javax.annotation.Nullable; /** - * A range (or "interval") defines the <i>boundaries</i> around a contiguous span of values of some - * {@code Comparable} type; for example, "integers from 1 to 100 inclusive." Note that it is not - * possible to <i>iterate</i> over these contained values. To do so, pass this range instance and - * an appropriate {@link DiscreteDomain} to {@link ContiguousSet#create}. + * A range, sometimes known as an <i>interval</i>, is a <i>convex</i> + * (informally, "contiguous" or "unbroken") portion of a particular domain. + * Formally, convexity means that for any {@code a <= b <= c}, + * {@code range.contains(a) && range.contains(c)} implies that {@code + * range.contains(b)}. * - * <h3>Types of ranges</h3> + * <p>A range is characterized by its lower and upper <i>bounds</i> (extremes), + * each of which can <i>open</i> (exclusive of its endpoint), <i>closed</i> + * (inclusive of its endpoint), or <i>unbounded</i>. This yields nine basic + * types of ranges: * - * <p>Each end of the range may be bounded or unbounded. If bounded, there is an associated - * <i>endpoint</i> value, and the range is considered to be either <i>open</i> (does not include the - * endpoint) or <i>closed</i> (includes the endpoint) on that side. With three possibilities on each - * side, this yields nine basic types of ranges, enumerated below. (Notation: a square bracket - * ({@code [ ]}) indicates that the range is closed on that side; a parenthesis ({@code ( )}) means - * it is either open or unbounded. The construct {@code {x | statement}} is read "the set of all - * <i>x</i> such that <i>statement</i>.") + * <ul> + * <li>{@code (a..b) = {x | a < x < b}} + * <li>{@code [a..b] = {x | a <= x <= b}} + * <li>{@code [a..b) = {x | a <= x < b}} + * <li>{@code (a..b] = {x | a < x <= b}} + * <li>{@code (a..+∞) = {x | x > a}} + * <li>{@code [a..+∞) = {x | x >= a}} + * <li>{@code (-∞..b) = {x | x < b}} + * <li>{@code (-∞..b] = {x | x <= b}} + * <li>{@code (-∞..+∞) = all values} + * </ul> + * + * (The notation {@code {x | statement}} is read "the set of all <i>x</i> such + * that <i>statement</i>.") * - * <blockquote><table> - * <tr><td><b>Notation</b> <td><b>Definition</b> <td><b>Factory method</b> - * <tr><td>{@code (a..b)} <td>{@code {x | a < x < b}} <td>{@link Range#open open} - * <tr><td>{@code [a..b]} <td>{@code {x | a <= x <= b}}<td>{@link Range#closed closed} - * <tr><td>{@code (a..b]} <td>{@code {x | a < x <= b}} <td>{@link Range#openClosed openClosed} - * <tr><td>{@code [a..b)} <td>{@code {x | a <= x < b}} <td>{@link Range#closedOpen closedOpen} - * <tr><td>{@code (a..+∞)} <td>{@code {x | x > a}} <td>{@link Range#greaterThan greaterThan} - * <tr><td>{@code [a..+∞)} <td>{@code {x | x >= a}} <td>{@link Range#atLeast atLeast} - * <tr><td>{@code (-∞..b)} <td>{@code {x | x < b}} <td>{@link Range#lessThan lessThan} - * <tr><td>{@code (-∞..b]} <td>{@code {x | x <= b}} <td>{@link Range#atMost atMost} - * <tr><td>{@code (-∞..+∞)}<td>{@code {x}} <td>{@link Range#all all} - * </table></blockquote> + * <p>Notice that we use a square bracket ({@code [ ]}) to denote that an range + * is closed on that end, and a parenthesis ({@code ( )}) when it is open or + * unbounded. * - * <p>When both endpoints exist, the upper endpoint may not be less than the lower. The endpoints - * may be equal only if at least one of the bounds is closed: + * <p>The values {@code a} and {@code b} used above are called <i>endpoints</i>. + * The upper endpoint may not be less than the lower endpoint. The endpoints may + * be equal only if at least one of the bounds is closed: * * <ul> - * <li>{@code [a..a]} : a singleton range - * <li>{@code [a..a); (a..a]} : {@linkplain #isEmpty empty} ranges; also valid - * <li>{@code (a..a)} : <b>invalid</b>; an exception will be thrown + * <li>{@code [a..a]} : singleton range + * <li>{@code [a..a); (a..a]} : {@linkplain #isEmpty empty}, but valid + * <li>{@code (a..a)} : <b>invalid</b> * </ul> * - * <h3>Warnings</h3> + * <p>Instances of this type can be obtained using the static factory methods in + * the {@link Ranges} class. * - * <ul> - * <li>Use immutable value types only, if at all possible. If you must use a mutable type, <b>do - * not</b> allow the endpoint instances to mutate after the range is created! - * <li>Your value type's comparison method should be {@linkplain Comparable consistent with equals} - * if at all possible. Otherwise, be aware that concepts used throughout this documentation such - * as "equal", "same", "unique" and so on actually refer to whether {@link Comparable#compareTo - * compareTo} returns zero, not whether {@link Object#equals equals} returns {@code true}. - * <li>A class which implements {@code Comparable<UnrelatedType>} is very broken, and will cause - * undefined horrible things to happen in {@code Range}. For now, the Range API does not prevent - * its use, because this would also rule out all ungenerified (pre-JDK1.5) data types. <b>This - * may change in the future.</b> - * </ul> + * <p>Instances of {@code Range} are immutable. It is strongly encouraged to + * use this class only with immutable data types. When creating a range over a + * mutable type, take great care not to allow the value objects to mutate after + * the range is created. * - * <h3>Other notes</h3> + * <p>In this and other range-related specifications, concepts like "equal", + * "same", "unique" and so on are based on {@link Comparable#compareTo} + * returning zero, not on {@link Object#equals} returning {@code true}. Of + * course, when these methods are kept <i>consistent</i> (as defined in {@link + * Comparable}), this is not an issue. * - * <ul> - * <li>Instances of this type are obtained using the static factory methods in this class. - * <li>Ranges are <i>convex</i>: whenever two values are contained, all values in between them must - * also be contained. More formally, for any {@code c1 <= c2 <= c3} of type {@code C}, {@code - * r.contains(c1) && r.contains(c3)} implies {@code r.contains(c2)}). This means that a {@code - * Range<Integer>} can never be used to represent, say, "all <i>prime</i> numbers from 1 to - * 100." - * <li>When evaluated as a {@link Predicate}, a range yields the same result as invoking {@link - * #contains}. - * <li>Terminology note: a range {@code a} is said to be the <i>maximal</i> range having property - * <i>P</i> if, for all ranges {@code b} also having property <i>P</i>, {@code a.encloses(b)}. - * Likewise, {@code a} is <i>minimal</i> when {@code b.encloses(a)} for all {@code b} having - * property <i>P</i>. See, for example, the definition of {@link #intersection intersection}. - * </ul> + * <p>A range {@code a} is said to be the <i>maximal</i> range having property + * <i>P</i> if, for all ranges {@code b} also having property <i>P</i>, {@code + * a.encloses(b)}. Likewise, {@code a} is <i>minimal</i> when {@code + * b.encloses(a)} for all {@code b} having property <i>P</i>. See, for example, + * the definition of {@link #intersection}. * - * <h3>Further reading</h3> + * <p>This class can be used with any type which implements {@code Comparable}; + * it does not require {@code Comparable<? super C>} because this would be + * incompatible with pre-Java 5 types. If this class is used with a perverse + * {@code Comparable} type ({@code Foo implements Comparable<Bar>} where {@code + * Bar} is not a supertype of {@code Foo}), any of its methods may throw {@link + * ClassCastException}. (There is no good reason for such a type to exist.) * - * <p>See the Guava User Guide article on - * <a href="http://code.google.com/p/guava-libraries/wiki/RangesExplained">{@code Range}</a>. + * <p>When evaluated as a {@link Predicate}, a range yields the same result as + * invoking {@link #contains}. * * @author Kevin Bourrillion * @author Gregory Kick * @since 10.0 */ @GwtCompatible -@SuppressWarnings("rawtypes") -public final class Range<C extends Comparable> implements Predicate<C>, Serializable { - - private static final Function<Range, Cut> LOWER_BOUND_FN = new Function<Range, Cut>() { - @Override - public Cut apply(Range range) { - return range.lowerBound; - } - }; - - @SuppressWarnings("unchecked") - static <C extends Comparable<?>> Function<Range<C>, Cut<C>> lowerBoundFn() { - return (Function) LOWER_BOUND_FN; - } - - private static final Function<Range, Cut> UPPER_BOUND_FN = new Function<Range, Cut>() { - @Override - public Cut apply(Range range) { - return range.upperBound; - } - }; - - @SuppressWarnings("unchecked") - static <C extends Comparable<?>> Function<Range<C>, Cut<C>> upperBoundFn() { - return (Function) UPPER_BOUND_FN; - } - - static final Ordering<Range<?>> RANGE_LEX_ORDERING = new Ordering<Range<?>>() { - @Override - public int compare(Range<?> left, Range<?> right) { - return ComparisonChain.start() - .compare(left.lowerBound, right.lowerBound) - .compare(left.upperBound, right.upperBound) - .result(); - } - }; - - static <C extends Comparable<?>> Range<C> create( - Cut<C> lowerBound, Cut<C> upperBound) { - return new Range<C>(lowerBound, upperBound); - } - - /** - * Returns a range that contains all values strictly greater than {@code - * lower} and strictly less than {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than <i>or - * equal to</i> {@code upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> open(C lower, C upper) { - return create(Cut.aboveValue(lower), Cut.belowValue(upper)); - } - - /** - * Returns a range that contains all values greater than or equal to - * {@code lower} and less than or equal to {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> closed(C lower, C upper) { - return create(Cut.belowValue(lower), Cut.aboveValue(upper)); - } - - /** - * Returns a range that contains all values greater than or equal to - * {@code lower} and strictly less than {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> closedOpen( - C lower, C upper) { - return create(Cut.belowValue(lower), Cut.belowValue(upper)); - } - - /** - * Returns a range that contains all values strictly greater than {@code - * lower} and less than or equal to {@code upper}. - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> openClosed( - C lower, C upper) { - return create(Cut.aboveValue(lower), Cut.aboveValue(upper)); - } - - /** - * Returns a range that contains any value from {@code lower} to {@code - * upper}, where each endpoint may be either inclusive (closed) or exclusive - * (open). - * - * @throws IllegalArgumentException if {@code lower} is greater than {@code - * upper} - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> range( - C lower, BoundType lowerType, C upper, BoundType upperType) { - checkNotNull(lowerType); - checkNotNull(upperType); - - Cut<C> lowerBound = (lowerType == BoundType.OPEN) - ? Cut.aboveValue(lower) - : Cut.belowValue(lower); - Cut<C> upperBound = (upperType == BoundType.OPEN) - ? Cut.belowValue(upper) - : Cut.aboveValue(upper); - return create(lowerBound, upperBound); - } - - /** - * Returns a range that contains all values strictly less than {@code - * endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> lessThan(C endpoint) { - return create(Cut.<C>belowAll(), Cut.belowValue(endpoint)); - } - - /** - * Returns a range that contains all values less than or equal to - * {@code endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> atMost(C endpoint) { - return create(Cut.<C>belowAll(), Cut.aboveValue(endpoint)); - } - - /** - * Returns a range with no lower bound up to the given endpoint, which may be - * either inclusive (closed) or exclusive (open). - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> upTo( - C endpoint, BoundType boundType) { - switch (boundType) { - case OPEN: - return lessThan(endpoint); - case CLOSED: - return atMost(endpoint); - default: - throw new AssertionError(); - } - } - - /** - * Returns a range that contains all values strictly greater than {@code - * endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> greaterThan(C endpoint) { - return create(Cut.aboveValue(endpoint), Cut.<C>aboveAll()); - } - - /** - * Returns a range that contains all values greater than or equal to - * {@code endpoint}. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> atLeast(C endpoint) { - return create(Cut.belowValue(endpoint), Cut.<C>aboveAll()); - } - - /** - * Returns a range from the given endpoint, which may be either inclusive - * (closed) or exclusive (open), with no upper bound. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> downTo( - C endpoint, BoundType boundType) { - switch (boundType) { - case OPEN: - return greaterThan(endpoint); - case CLOSED: - return atLeast(endpoint); - default: - throw new AssertionError(); - } - } - - private static final Range<Comparable> ALL = - new Range<Comparable>(Cut.belowAll(), Cut.aboveAll()); - - /** - * Returns a range that contains every value of type {@code C}. - * - * @since 14.0 - */ - @SuppressWarnings("unchecked") - public static <C extends Comparable<?>> Range<C> all() { - return (Range) ALL; - } - - /** - * Returns a range that {@linkplain Range#contains(Comparable) contains} only - * the given value. The returned range is {@linkplain BoundType#CLOSED closed} - * on both ends. - * - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> singleton(C value) { - return closed(value, value); - } - - /** - * Returns the minimal range that - * {@linkplain Range#contains(Comparable) contains} all of the given values. - * The returned range is {@linkplain BoundType#CLOSED closed} on both ends. - * - * @throws ClassCastException if the parameters are not <i>mutually - * comparable</i> - * @throws NoSuchElementException if {@code values} is empty - * @throws NullPointerException if any of {@code values} is null - * @since 14.0 - */ - public static <C extends Comparable<?>> Range<C> encloseAll( - Iterable<C> values) { - checkNotNull(values); - if (values instanceof ContiguousSet) { - return ((ContiguousSet<C>) values).range(); - } - Iterator<C> valueIterator = values.iterator(); - C min = checkNotNull(valueIterator.next()); - C max = min; - while (valueIterator.hasNext()) { - C value = checkNotNull(valueIterator.next()); - min = Ordering.natural().min(min, value); - max = Ordering.natural().max(max, value); - } - return closed(min, max); - } - +@Beta +public final class Range<C extends Comparable> + implements Predicate<C>, Serializable { final Cut<C> lowerBound; final Cut<C> upperBound; - private Range(Cut<C> lowerBound, Cut<C> upperBound) { - if (lowerBound.compareTo(upperBound) > 0 || lowerBound == Cut.<C>aboveAll() - || upperBound == Cut.<C>belowAll()) { - throw new IllegalArgumentException("Invalid range: " + toString(lowerBound, upperBound)); + Range(Cut<C> lowerBound, Cut<C> upperBound) { + if (lowerBound.compareTo(upperBound) > 0) { + throw new IllegalArgumentException( + "Invalid range: " + toString(lowerBound, upperBound)); } - this.lowerBound = checkNotNull(lowerBound); - this.upperBound = checkNotNull(upperBound); + this.lowerBound = lowerBound; + this.upperBound = upperBound; } /** @@ -379,19 +133,20 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ /** * Returns the lower endpoint of this range. * - * @throws IllegalStateException if this range is unbounded below (that is, {@link - * #hasLowerBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded below (that is, + * {@link #hasLowerBound()} returns {@code false}) */ public C lowerEndpoint() { return lowerBound.endpoint(); } /** - * Returns the type of this range's lower bound: {@link BoundType#CLOSED} if the range includes - * its lower endpoint, {@link BoundType#OPEN} if it does not. + * Returns the type of this range's lower bound: {@link BoundType#CLOSED} if + * the range includes its lower endpoint, {@link BoundType#OPEN} if it does + * not. * - * @throws IllegalStateException if this range is unbounded below (that is, {@link - * #hasLowerBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded below (that is, + * {@link #hasLowerBound()} returns {@code false}) */ public BoundType lowerBoundType() { return lowerBound.typeAsLowerBound(); @@ -407,41 +162,42 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ /** * Returns the upper endpoint of this range. * - * @throws IllegalStateException if this range is unbounded above (that is, {@link - * #hasUpperBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded above (that is, + * {@link #hasUpperBound()} returns {@code false}) */ public C upperEndpoint() { return upperBound.endpoint(); } /** - * Returns the type of this range's upper bound: {@link BoundType#CLOSED} if the range includes - * its upper endpoint, {@link BoundType#OPEN} if it does not. + * Returns the type of this range's upper bound: {@link BoundType#CLOSED} if + * the range includes its upper endpoint, {@link BoundType#OPEN} if it does + * not. * - * @throws IllegalStateException if this range is unbounded above (that is, {@link - * #hasUpperBound()} returns {@code false}) + * @throws IllegalStateException if this range is unbounded above (that is, + * {@link #hasUpperBound()} returns {@code false}) */ public BoundType upperBoundType() { return upperBound.typeAsUpperBound(); } /** - * Returns {@code true} if this range is of the form {@code [v..v)} or {@code (v..v]}. (This does - * not encompass ranges of the form {@code (v..v)}, because such ranges are <i>invalid</i> and - * can't be constructed at all.) + * Returns {@code true} if this range is of the form {@code [v..v)} or {@code + * (v..v]}. (This does not encompass ranges of the form {@code (v..v)}, + * because such ranges are <i>invalid</i> and can't be constructed at all.) * - * <p>Note that certain discrete ranges such as the integer range {@code (3..4)} are <b>not</b> - * considered empty, even though they contain no actual values. In these cases, it may be - * helpful to preprocess ranges with {@link #canonical(DiscreteDomain)}. + * <p>Note that certain discrete ranges such as the integer range {@code + * (3..4)} are <b>not</b> considered empty, even though they contain no actual + * values. */ public boolean isEmpty() { return lowerBound.equals(upperBound); } /** - * Returns {@code true} if {@code value} is within the bounds of this range. For example, on the - * range {@code [0..2)}, {@code contains(1)} returns {@code true}, while {@code contains(2)} - * returns {@code false}. + * Returns {@code true} if {@code value} is within the bounds of this + * range. For example, on the range {@code [0..2)}, {@code contains(1)} + * returns {@code true}, while {@code contains(2)} returns {@code false}. */ public boolean contains(C value) { checkNotNull(value); @@ -450,16 +206,17 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Equivalent to {@link #contains}; provided only to satisfy the {@link Predicate} interface. When - * using a reference of type {@code Range}, always invoke {@link #contains} directly instead. + * Equivalent to {@link #contains}; provided only to satisfy the {@link + * Predicate} interface. When using a reference of type {@code Range}, always + * invoke {@link #contains} directly instead. */ @Override public boolean apply(C input) { return contains(input); } /** - * Returns {@code true} if every element in {@code values} is {@linkplain #contains contained} in - * this range. + * Returns {@code true} if every element in {@code values} is {@linkplain + * #contains contained} in this range. */ public boolean containsAll(Iterable<? extends C> values) { if (Iterables.isEmpty(values)) { @@ -484,27 +241,42 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Returns {@code true} if the bounds of {@code other} do not extend outside the bounds of this - * range. Examples: + * Returns {@code true} if the bounds of {@code other} do not extend outside + * the bounds of this range. Examples: * * <ul> * <li>{@code [3..6]} encloses {@code [4..5]} * <li>{@code (3..6)} encloses {@code (3..6)} - * <li>{@code [3..6]} encloses {@code [4..4)} (even though the latter is empty) + * <li>{@code [3..6]} encloses {@code [4..4)} (even though the latter is + * empty) * <li>{@code (3..6]} does not enclose {@code [3..6]} - * <li>{@code [4..5]} does not enclose {@code (3..6)} (even though it contains every value - * contained by the latter range) - * <li>{@code [3..6]} does not enclose {@code (1..1]} (even though it contains every value - * contained by the latter range) + * <li>{@code [4..5]} does not enclose {@code (3..6)} (even though it contains + * every value contained by the latter range) + * <li>{@code [3..6]} does not enclose {@code (1..1]} (even though it contains + * every value contained by the latter range) * </ul> * - * Note that if {@code a.encloses(b)}, then {@code b.contains(v)} implies {@code a.contains(v)}, - * but as the last two examples illustrate, the converse is not always true. + * Note that if {@code a.encloses(b)}, then {@code b.contains(v)} implies + * {@code a.contains(v)}, but as the last two examples illustrate, the + * converse is not always true. * - * <p>Being reflexive, antisymmetric and transitive, the {@code encloses} relation defines a - * <i>partial order</i> over ranges. There exists a unique {@linkplain Range#all maximal} range - * according to this relation, and also numerous {@linkplain #isEmpty minimal} ranges. Enclosure - * also implies {@linkplain #isConnected connectedness}. + * <p>The encloses relation has the following properties: + * + * <ul> + * <li>reflexive: {@code a.encloses(a)} is always true + * <li>antisymmetric: {@code a.encloses(b) && b.encloses(a)} implies {@code + * a.equals(b)} + * <li>transitive: {@code a.encloses(b) && b.encloses(c)} implies {@code + * a.encloses(c)} + * <li>not a total ordering: {@code !a.encloses(b)} does not imply {@code + * b.encloses(a)} + * <li>there exists a {@linkplain Ranges#all maximal} range, for which + * {@code encloses} is always true + * <li>there also exist {@linkplain #isEmpty minimal} ranges, for + * which {@code encloses(b)} is always false when {@code !equals(b)} + * <li>if {@code a.encloses(b)}, then {@link #isConnected a.isConnected(b)} + * is {@code true}. + * </ul> */ public boolean encloses(Range<C> other) { return lowerBound.compareTo(other.lowerBound) <= 0 @@ -512,133 +284,160 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Returns {@code true} if there exists a (possibly empty) range which is {@linkplain #encloses - * enclosed} by both this range and {@code other}. + * Returns the maximal range {@linkplain #encloses enclosed} by both this + * range and {@code other}, if such a range exists. + * + * <p>For example, the intersection of {@code [1..5]} and {@code (3..7)} is + * {@code (3..5]}. The resulting range may be empty; for example, + * {@code [1..5)} intersected with {@code [5..7)} yields the empty range + * {@code [5..5)}. + * + * <p>Generally, the intersection exists if and only if this range and + * {@code other} are {@linkplain #isConnected connected}. + * + * <p>The intersection operation has the following properties: * - * <p>For example, * <ul> - * <li>{@code [2, 4)} and {@code [5, 7)} are not connected - * <li>{@code [2, 4)} and {@code [3, 5)} are connected, because both enclose {@code [3, 4)} - * <li>{@code [2, 4)} and {@code [4, 6)} are connected, because both enclose the empty range - * {@code [4, 4)} + * <li>commutative: {@code a.intersection(b)} produces the same result as + * {@code b.intersection(a)} + * <li>associative: {@code a.intersection(b).intersection(c)} produces the + * same result as {@code a.intersection(b.intersection(c))} + * <li>idempotent: {@code a.intersection(a)} equals {@code a} + * <li>identity ({@link Ranges#all}): {@code a.intersection(Ranges.all())} + * equals {@code a} * </ul> * - * <p>Note that this range and {@code other} have a well-defined {@linkplain #span union} and - * {@linkplain #intersection intersection} (as a single, possibly-empty range) if and only if this - * method returns {@code true}. - * - * <p>The connectedness relation is both reflexive and symmetric, but does not form an {@linkplain - * Equivalence equivalence relation} as it is not transitive. - * - * <p>Note that certain discrete ranges are not considered connected, even though there are no - * elements "between them." For example, {@code [3, 5]} is not considered connected to {@code - * [6, 10]}. In these cases, it may be desirable for both input ranges to be preprocessed with - * {@link #canonical(DiscreteDomain)} before testing for connectedness. + * @throws IllegalArgumentException if no range exists that is enclosed by + * both these ranges */ - public boolean isConnected(Range<C> other) { - return lowerBound.compareTo(other.upperBound) <= 0 - && other.lowerBound.compareTo(upperBound) <= 0; + public Range<C> intersection(Range<C> other) { + Cut<C> newLower = Ordering.natural().max(lowerBound, other.lowerBound); + Cut<C> newUpper = Ordering.natural().min(upperBound, other.upperBound); + return create(newLower, newUpper); } /** - * Returns the maximal range {@linkplain #encloses enclosed} by both this range and {@code - * connectedRange}, if such a range exists. - * - * <p>For example, the intersection of {@code [1..5]} and {@code (3..7)} is {@code (3..5]}. The - * resulting range may be empty; for example, {@code [1..5)} intersected with {@code [5..7)} - * yields the empty range {@code [5..5)}. - * - * <p>The intersection exists if and only if the two ranges are {@linkplain #isConnected - * connected}. - * - * <p>The intersection operation is commutative, associative and idempotent, and its identity - * element is {@link Range#all}). + * Returns {@code true} if there exists a (possibly empty) range which is + * {@linkplain #encloses enclosed} by both this range and {@code other}. + * + * <p>For example, + * <ul> + * <li>{@code [2, 4)} and {@code [5, 7)} are not connected + * <li>{@code [2, 4)} and {@code [3, 5)} are connected, because both enclose + * {@code [3, 4)} + * <li>{@code [2, 4)} and {@code [4, 6)} are connected, because both enclose + * the empty range {@code [4, 4)} + * </ul> + * + * <p>Note that this range and {@code other} have a well-defined {@linkplain + * #span union} and {@linkplain #intersection intersection} (as a single, + * possibly-empty range) if and only if this method returns {@code true}. + * + * <p>The connectedness relation has the following properties: * - * @throws IllegalArgumentException if {@code isConnected(connectedRange)} is {@code false} + * <ul> + * <li>symmetric: {@code a.isConnected(b)} produces the same result as + * {@code b.isConnected(a)} + * <li>reflexive: {@code a.isConnected(a)} returns {@code true} + * </ul> */ - public Range<C> intersection(Range<C> connectedRange) { - int lowerCmp = lowerBound.compareTo(connectedRange.lowerBound); - int upperCmp = upperBound.compareTo(connectedRange.upperBound); - if (lowerCmp >= 0 && upperCmp <= 0) { - return this; - } else if (lowerCmp <= 0 && upperCmp >= 0) { - return connectedRange; - } else { - Cut<C> newLower = (lowerCmp >= 0) ? lowerBound : connectedRange.lowerBound; - Cut<C> newUpper = (upperCmp <= 0) ? upperBound : connectedRange.upperBound; - return create(newLower, newUpper); - } + public boolean isConnected(Range<C> other) { + return lowerBound.compareTo(other.upperBound) <= 0 + && other.lowerBound.compareTo(upperBound) <= 0; } /** - * Returns the minimal range that {@linkplain #encloses encloses} both this range and {@code - * other}. For example, the span of {@code [1..3]} and {@code (5..7)} is {@code [1..7)}. + * Returns the minimal range that {@linkplain #encloses encloses} both this + * range and {@code other}. For example, the span of {@code [1..3]} and + * {@code (5..7)} is {@code [1..7)}. Note that the span may contain values + * that are not contained by either original range. * - * <p><i>If</i> the input ranges are {@linkplain #isConnected connected}, the returned range can - * also be called their <i>union</i>. If they are not, note that the span might contain values - * that are not contained in either input range. + * <p>The span operation has the following properties: * - * <p>Like {@link #intersection(Range) intersection}, this operation is commutative, associative - * and idempotent. Unlike it, it is always well-defined for any two input ranges. + * <ul> + * <li>closed: the range {@code a.span(b)} exists for all ranges {@code a} and + * {@code b} + * <li>commutative: {@code a.span(b)} equals {@code b.span(a)} + * <li>associative: {@code a.span(b).span(c)} equals {@code a.span(b.span(c))} + * <li>idempotent: {@code a.span(a)} equals {@code a} + * </ul> + * + * <p>Note that the returned range is also called the <i>union</i> of this + * range and {@code other} if and only if the ranges are + * {@linkplain #isConnected connected}. */ public Range<C> span(Range<C> other) { - int lowerCmp = lowerBound.compareTo(other.lowerBound); - int upperCmp = upperBound.compareTo(other.upperBound); - if (lowerCmp <= 0 && upperCmp >= 0) { - return this; - } else if (lowerCmp >= 0 && upperCmp <= 0) { - return other; - } else { - Cut<C> newLower = (lowerCmp <= 0) ? lowerBound : other.lowerBound; - Cut<C> newUpper = (upperCmp >= 0) ? upperBound : other.upperBound; - return create(newLower, newUpper); - } + Cut<C> newLower = Ordering.natural().min(lowerBound, other.lowerBound); + Cut<C> newUpper = Ordering.natural().max(upperBound, other.upperBound); + return create(newLower, newUpper); } /** - * Returns an {@link ContiguousSet} containing the same values in the given domain - * {@linkplain Range#contains contained} by this range. + * Returns an {@link ImmutableSortedSet} containing the same values in the + * given domain {@linkplain Range#contains contained} by this range. * - * <p><b>Note:</b> {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For - * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty - * ranges {@code [3..3)} and {@code [4..4)}. + * <p><b>Note:</b> {@code a.asSet().equals(b.asSet())} does not imply {@code + * a.equals(b)}! For example, {@code a} and {@code b} could be {@code [2..4]} + * and {@code (1..5)}, or the empty ranges {@code [3..3)} and {@code [4..4)}. * - * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} view of a large - * range (such as {@code Range.greaterThan(0)}). 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. + * <p><b>Warning:</b> Be extremely careful what you do with the {@code asSet} + * view of a large range (such as {@code Ranges.greaterThan(0)}). 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. * - * <p>The returned set's {@link Object#toString} method returns a short-hand form of the set's - * contents, such as {@code "[1..100]}"}. + * <p>The returned set's {@link Object#toString} method returns a short-hand + * form of set's contents such as {@code "[1..100]}"}. * - * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if - * neither has an upper bound - * @deprecated Use {@code ContiguousSet.create(range, domain)} instead. + * @throws IllegalArgumentException if neither this range nor the domain has a + * lower bound, or if neither has an upper bound */ // TODO(kevinb): commit in spec to which methods are efficient? - @Beta @GwtCompatible(serializable = false) - @Deprecated public ContiguousSet<C> asSet(DiscreteDomain<C> domain) { - return ContiguousSet.create(this, domain); + checkNotNull(domain); + Range<C> effectiveRange = this; + try { + if (!hasLowerBound()) { + effectiveRange = effectiveRange.intersection( + Ranges.atLeast(domain.minValue())); + } + if (!hasUpperBound()) { + effectiveRange = effectiveRange.intersection( + Ranges.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() + || compareOrThrow( + lowerBound.leastValueAbove(domain), + upperBound.greatestValueBelow(domain)) > 0; + + return empty + ? new EmptyContiguousSet<C>(domain) + : new RegularContiguousSet<C>(effectiveRange, domain); } /** - * Returns the canonical form of this range in the given domain. The canonical form has the - * following properties: + * Returns the canonical form of this range in the given domain. The canonical + * form has the following properties: * * <ul> - * <li>equivalence: {@code a.canonical().contains(v) == a.contains(v)} for all {@code v} (in other - * words, {@code ContiguousSet.create(a.canonical(domain), domain).equals( - * ContiguousSet.create(a, domain))} + * <li>equivalence: {@code a.canonical().contains(v) == a.contains(v)} for + * all {@code v} (in other words, {@code + * a.canonical(domain).asSet(domain).equals(a.asSet(domain))} * <li>uniqueness: unless {@code a.isEmpty()}, - * {@code ContiguousSet.create(a, domain).equals(ContiguousSet.create(b, domain))} implies + * {@code a.asSet(domain).equals(b.asSet(domain))} implies * {@code a.canonical(domain).equals(b.canonical(domain))} - * <li>idempotence: {@code a.canonical(domain).canonical(domain).equals(a.canonical(domain))} + * <li>idempotence: {@code + * a.canonical(domain).canonical(domain).equals(a.canonical(domain))} * </ul> * - * Furthermore, this method guarantees that the range returned will be one of the following - * canonical forms: + * Furthermore, this method guarantees that the range returned will be one + * of the following canonical forms: * * <ul> * <li>[start..end) @@ -651,15 +450,18 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ checkNotNull(domain); Cut<C> lower = lowerBound.canonical(domain); Cut<C> upper = upperBound.canonical(domain); - return (lower == lowerBound && upper == upperBound) ? this : create(lower, upper); + return (lower == lowerBound && upper == upperBound) + ? this : create(lower, upper); } /** - * Returns {@code true} if {@code object} is a range having the same endpoints and bound types as - * this range. Note that discrete ranges such as {@code (1..4)} and {@code [2..3]} are <b>not</b> - * equal to one another, despite the fact that they each contain precisely the same set of values. - * Similarly, empty ranges are not equal unless they have exactly the same representation, so - * {@code [3..3)}, {@code (3..3]}, {@code (4..4]} are all unequal. + * Returns {@code true} if {@code object} is a range having the same + * endpoints and bound types as this range. Note that discrete ranges + * such as {@code (1..4)} and {@code [2..3]} are <b>not</b> equal to one + * another, despite the fact that they each contain precisely the same set of + * values. Similarly, empty ranges are not equal unless they have exactly + * the same representation, so {@code [3..3)}, {@code (3..3]}, {@code (4..4]} + * are all unequal. */ @Override public boolean equals(@Nullable Object object) { if (object instanceof Range) { @@ -676,8 +478,8 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ } /** - * Returns a string representation of this range, such as {@code "[3..5)"} (other examples are - * listed in the class documentation). + * Returns a string representation of this range, such as {@code "[3..5)"} + * (other examples are listed in the class documentation). */ @Override public String toString() { return toString(lowerBound, upperBound); @@ -698,14 +500,6 @@ public final class Range<C extends Comparable> implements Predicate<C>, Serializ return (SortedSet<T>) iterable; } - Object readResolve() { - if (this.equals(ALL)) { - return all(); - } else { - return this; - } - } - @SuppressWarnings("unchecked") // this method may throw CCE static int compareOrThrow(Comparable left, Comparable right) { return left.compareTo(right); diff --git a/guava/src/com/google/common/collect/RangeMap.java b/guava/src/com/google/common/collect/RangeMap.java deleted file mode 100644 index ba42985..0000000 --- a/guava/src/com/google/common/collect/RangeMap.java +++ /dev/null @@ -1,134 +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.Beta; - -import java.util.Map; - -import javax.annotation.Nullable; - -/** - * A mapping from disjoint nonempty ranges to non-null values. Queries look up the value - * associated with the range (if any) that contains a specified key. - * - * <p>In contrast to {@link RangeSet}, no "coalescing" is done of {@linkplain - * Range#isConnected(Range) connected} ranges, even if they are mapped to the same value. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -public interface RangeMap<K extends Comparable, V> { - /** - * Returns the value associated with the specified key, or {@code null} if there is no - * such value. - * - * <p>Specifically, if any range in this range map contains the specified key, the value - * associated with that range is returned. - */ - @Nullable - V get(K key); - - /** - * Returns the range containing this key and its associated value, if such a range is present - * in the range map, or {@code null} otherwise. - */ - @Nullable - Map.Entry<Range<K>, V> getEntry(K key); - - /** - * Returns the minimal range {@linkplain Range#encloses(Range) enclosing} the ranges - * in this {@code RangeMap}. - * - * @throws NoSuchElementException if this range map is empty - */ - Range<K> span(); - - /** - * Maps a range to a specified value (optional operation). - * - * <p>Specifically, after a call to {@code put(range, value)}, if - * {@link Range#contains(Comparable) range.contains(k)}, then {@link #get(Comparable) get(k)} - * will return {@code value}. - * - * <p>If {@code range} {@linkplain Range#isEmpty() is empty}, then this is a no-op. - */ - void put(Range<K> range, V value); - - /** - * Puts all the associations from {@code rangeMap} into this range map (optional operation). - */ - void putAll(RangeMap<K, V> rangeMap); - - /** - * Removes all associations from this range map (optional operation). - */ - void clear(); - - /** - * Removes all associations from this range map in the specified range (optional operation). - * - * <p>If {@code !range.contains(k)}, {@link #get(Comparable) get(k)} will return the same result - * before and after a call to {@code remove(range)}. If {@code range.contains(k)}, then - * after a call to {@code remove(range)}, {@code get(k)} will return {@code null}. - */ - void remove(Range<K> range); - - /** - * Returns a view of this range map as an unmodifiable {@code Map<Range<K>, V>}. - * Modifications to this range map are guaranteed to read through to the returned {@code Map}. - * - * <p>It is guaranteed that no empty ranges will be in the returned {@code Map}. - */ - Map<Range<K>, V> asMapOfRanges(); - - /** - * Returns a view of the part of this range map that intersects with {@code range}. - * - * <p>For example, if {@code rangeMap} had the entries - * {@code [1, 5] => "foo", (6, 8) => "bar", (10, \u2025) => "baz"} - * then {@code rangeMap.subRangeMap(Range.open(3, 12))} would return a range map - * with the entries {@code (3, 5) => "foo", (6, 8) => "bar", (10, 12) => "baz"}. - * - * <p>The returned range map supports all optional operations that this range map supports, - * except for {@code asMapOfRanges().iterator().remove()}. - * - * <p>The returned range map will throw an {@link IllegalArgumentException} on an attempt to - * insert a range not {@linkplain Range#encloses(Range) enclosed} by {@code range}. - */ - RangeMap<K, V> subRangeMap(Range<K> range); - - /** - * Returns {@code true} if {@code obj} is another {@code RangeMap} that has an equivalent - * {@link #asMapOfRanges()}. - */ - @Override - boolean equals(@Nullable Object o); - - /** - * Returns {@code asMapOfRanges().hashCode()}. - */ - @Override - int hashCode(); - - /** - * Returns a readable string representation of this range map. - */ - @Override - String toString(); -} diff --git a/guava/src/com/google/common/collect/RangeSet.java b/guava/src/com/google/common/collect/RangeSet.java deleted file mode 100644 index f07c1fc..0000000 --- a/guava/src/com/google/common/collect/RangeSet.java +++ /dev/null @@ -1,206 +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.NoSuchElementException; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * A set comprising zero or more {@linkplain Range#isEmpty nonempty}, - * {@linkplain Range#isConnected(Range) disconnected} ranges of type {@code C}. - * - * <p>Implementations that choose to support the {@link #add(Range)} operation are required to - * ignore empty ranges and coalesce connected ranges. For example: <pre> {@code - * - * RangeSet<Integer> rangeSet = TreeRangeSet.create(); - * rangeSet.add(Range.closed(1, 10)); // {[1, 10]} - * rangeSet.add(Range.closedOpen(11, 15)); // {[1, 10], [11, 15)} - * rangeSet.add(Range.open(15, 20)); // disconnected range; {[1, 10], [11, 20)} - * rangeSet.add(Range.openClosed(0, 0)); // empty range; {[1, 10], [11, 20)} - * rangeSet.remove(Range.open(5, 10)); // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}}</pre> - * - * <p>Note that the behavior of {@link Range#isEmpty()} and {@link Range#isConnected(Range)} may - * not be as expected on discrete ranges. See the Javadoc of those methods for details. - * - * <p>For a {@link Set} whose contents are specified by a {@link Range}, see {@link ContiguousSet}. - * - * @author Kevin Bourrillion - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -public interface RangeSet<C extends Comparable> { - - // Query methods - - /** - * Determines whether any of this range set's member ranges contains {@code value}. - */ - boolean contains(C value); - - /** - * Returns the unique range from this range set that {@linkplain Range#contains contains} - * {@code value}, or {@code null} if this range set does not contain {@code value}. - */ - Range<C> rangeContaining(C value); - - /** - * Returns {@code true} if there exists a member range in this range set which - * {@linkplain Range#encloses encloses} the specified range. - */ - boolean encloses(Range<C> otherRange); - - /** - * Returns {@code true} if for each member range in {@code other} there exists a member range in - * this range set which {@linkplain Range#encloses encloses} it. It follows that - * {@code this.contains(value)} whenever {@code other.contains(value)}. Returns {@code true} if - * {@code other} is empty. - * - * <p>This is equivalent to checking if this range set {@link #encloses} each of the ranges in - * {@code other}. - */ - boolean enclosesAll(RangeSet<C> other); - - /** - * Returns {@code true} if this range set contains no ranges. - */ - boolean isEmpty(); - - /** - * Returns the minimal range which {@linkplain Range#encloses(Range) encloses} all ranges - * in this range set. - * - * @throws NoSuchElementException if this range set is {@linkplain #isEmpty() empty} - */ - Range<C> span(); - - // Views - - /** - * Returns a view of the {@linkplain Range#isConnected disconnected} ranges that make up this - * range set. The returned set may be empty. The iterators returned by its - * {@link Iterable#iterator} method return the ranges in increasing order of lower bound - * (equivalently, of upper bound). - */ - Set<Range<C>> asRanges(); - - /** - * Returns a view of the complement of this {@code RangeSet}. - * - * <p>The returned view supports the {@link #add} operation if this {@code RangeSet} supports - * {@link #remove}, and vice versa. - */ - RangeSet<C> complement(); - - /** - * Returns a view of the intersection of this {@code RangeSet} with the specified range. - * - * <p>The returned view supports all optional operations supported by this {@code RangeSet}, with - * the caveat that an {@link IllegalArgumentException} is thrown on an attempt to - * {@linkplain #add(Range) add} any range not {@linkplain Range#encloses(Range) enclosed} by - * {@code view}. - */ - RangeSet<C> subRangeSet(Range<C> view); - - // Modification - - /** - * Adds the specified range to this {@code RangeSet} (optional operation). That is, for equal - * range sets a and b, the result of {@code a.add(range)} is that {@code a} will be the minimal - * range set for which both {@code a.enclosesAll(b)} and {@code a.encloses(range)}. - * - * <p>Note that {@code range} will be {@linkplain Range#span(Range) coalesced} with any ranges in - * the range set that are {@linkplain Range#isConnected(Range) connected} with it. Moreover, - * if {@code range} is empty, this is a no-op. - * - * @throws UnsupportedOperationException if this range set does not support the {@code add} - * operation - */ - void add(Range<C> range); - - /** - * Removes the specified range from this {@code RangeSet} (optional operation). After this - * operation, if {@code range.contains(c)}, {@code this.contains(c)} will return {@code false}. - * - * <p>If {@code range} is empty, this is a no-op. - * - * @throws UnsupportedOperationException if this range set does not support the {@code remove} - * operation - */ - void remove(Range<C> range); - - /** - * Removes all ranges from this {@code RangeSet} (optional operation). After this operation, - * {@code this.contains(c)} will return false for all {@code c}. - * - * <p>This is equivalent to {@code remove(Range.all())}. - * - * @throws UnsupportedOperationException if this range set does not support the {@code clear} - * operation - */ - void clear(); - - /** - * Adds all of the ranges from the specified range set to this range set (optional operation). - * After this operation, this range set is the minimal range set that - * {@linkplain #enclosesAll(RangeSet) encloses} both the original range set and {@code other}. - * - * <p>This is equivalent to calling {@link #add} on each of the ranges in {@code other} in turn. - * - * @throws UnsupportedOperationException if this range set does not support the {@code addAll} - * operation - */ - void addAll(RangeSet<C> other); - - /** - * Removes all of the ranges from the specified range set from this range set (optional - * operation). After this operation, if {@code other.contains(c)}, {@code this.contains(c)} will - * return {@code false}. - * - * <p>This is equivalent to calling {@link #remove} on each of the ranges in {@code other} in - * turn. - * - * @throws UnsupportedOperationException if this range set does not support the {@code removeAll} - * operation - */ - void removeAll(RangeSet<C> other); - - // Object methods - - /** - * Returns {@code true} if {@code obj} is another {@code RangeSet} that contains the same ranges - * according to {@link Range#equals(Object)}. - */ - @Override - boolean equals(@Nullable Object obj); - - /** - * Returns {@code asRanges().hashCode()}. - */ - @Override - int hashCode(); - - /** - * Returns a readable string representation of this range set. For example, if this - * {@code RangeSet} consisted of {@code Ranges.closed(1, 3)} and {@code Ranges.greaterThan(4)}, - * this might return {@code " [1‥3](4‥+∞)}"}. - */ - @Override - String toString(); -} diff --git a/guava/src/com/google/common/collect/Ranges.java b/guava/src/com/google/common/collect/Ranges.java index 75cbc1e..131bf3b 100644 --- a/guava/src/com/google/common/collect/Ranges.java +++ b/guava/src/com/google/common/collect/Ranges.java @@ -16,9 +16,12 @@ 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 java.util.Iterator; import java.util.NoSuchElementException; /** @@ -59,21 +62,20 @@ import java.util.NoSuchElementException; * <dd>{@link #upTo} * </dl> * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/RangesExplained"> - * {@code Range}</a>. - * * @author Kevin Bourrillion * @author Gregory Kick * @since 10.0 - * @deprecated Use the corresponding method in {@link Range}. */ -@Deprecated @GwtCompatible @Beta public final class Ranges { private Ranges() {} + static <C extends Comparable<?>> Range<C> create( + Cut<C> lowerBound, Cut<C> upperBound) { + return new Range<C>(lowerBound, upperBound); + } + /** * Returns a range that contains all values strictly greater than {@code * lower} and strictly less than {@code upper}. @@ -82,7 +84,7 @@ public final class Ranges { * equal to</i> {@code upper} */ public static <C extends Comparable<?>> Range<C> open(C lower, C upper) { - return Range.open(lower, upper); + return create(Cut.aboveValue(lower), Cut.belowValue(upper)); } /** @@ -93,7 +95,7 @@ public final class Ranges { * upper} */ public static <C extends Comparable<?>> Range<C> closed(C lower, C upper) { - return Range.closed(lower, upper); + return create(Cut.belowValue(lower), Cut.aboveValue(upper)); } /** @@ -105,7 +107,7 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> closedOpen( C lower, C upper) { - return Range.closedOpen(lower, upper); + return create(Cut.belowValue(lower), Cut.belowValue(upper)); } /** @@ -117,7 +119,7 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> openClosed( C lower, C upper) { - return Range.openClosed(lower, upper); + return create(Cut.aboveValue(lower), Cut.aboveValue(upper)); } /** @@ -130,7 +132,16 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> range( C lower, BoundType lowerType, C upper, BoundType upperType) { - return Range.range(lower, lowerType, upper, upperType); + checkNotNull(lowerType); + checkNotNull(upperType); + + Cut<C> lowerBound = (lowerType == BoundType.OPEN) + ? Cut.aboveValue(lower) + : Cut.belowValue(lower); + Cut<C> upperBound = (upperType == BoundType.OPEN) + ? Cut.belowValue(upper) + : Cut.aboveValue(upper); + return create(lowerBound, upperBound); } /** @@ -138,7 +149,7 @@ public final class Ranges { * endpoint}. */ public static <C extends Comparable<?>> Range<C> lessThan(C endpoint) { - return Range.lessThan(endpoint); + return create(Cut.<C>belowAll(), Cut.belowValue(endpoint)); } /** @@ -146,7 +157,7 @@ public final class Ranges { * {@code endpoint}. */ public static <C extends Comparable<?>> Range<C> atMost(C endpoint) { - return Range.atMost(endpoint); + return create(Cut.<C>belowAll(), Cut.aboveValue(endpoint)); } /** @@ -155,7 +166,14 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> upTo( C endpoint, BoundType boundType) { - return Range.upTo(endpoint, boundType); + switch (boundType) { + case OPEN: + return lessThan(endpoint); + case CLOSED: + return atMost(endpoint); + default: + throw new AssertionError(); + } } /** @@ -163,7 +181,7 @@ public final class Ranges { * endpoint}. */ public static <C extends Comparable<?>> Range<C> greaterThan(C endpoint) { - return Range.greaterThan(endpoint); + return create(Cut.aboveValue(endpoint), Cut.<C>aboveAll()); } /** @@ -171,7 +189,7 @@ public final class Ranges { * {@code endpoint}. */ public static <C extends Comparable<?>> Range<C> atLeast(C endpoint) { - return Range.atLeast(endpoint); + return create(Cut.belowValue(endpoint), Cut.<C>aboveAll()); } /** @@ -180,12 +198,19 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> downTo( C endpoint, BoundType boundType) { - return Range.downTo(endpoint, boundType); + switch (boundType) { + case OPEN: + return greaterThan(endpoint); + case CLOSED: + return atLeast(endpoint); + default: + throw new AssertionError(); + } } /** Returns a range that contains every value of type {@code C}. */ public static <C extends Comparable<?>> Range<C> all() { - return Range.all(); + return create(Cut.<C>belowAll(), Cut.<C>aboveAll()); } /** @@ -194,7 +219,7 @@ public final class Ranges { * on both ends. */ public static <C extends Comparable<?>> Range<C> singleton(C value) { - return Range.singleton(value); + return closed(value, value); } /** @@ -209,6 +234,18 @@ public final class Ranges { */ public static <C extends Comparable<?>> Range<C> encloseAll( Iterable<C> values) { - return Range.encloseAll(values); + checkNotNull(values); + if (values instanceof ContiguousSet) { + return ((ContiguousSet<C>) values).range(); + } + Iterator<C> valueIterator = values.iterator(); + C min = checkNotNull(valueIterator.next()); + C max = min; + while (valueIterator.hasNext()) { + C value = checkNotNull(valueIterator.next()); + min = Ordering.natural().min(min, value); + max = Ordering.natural().max(max, value); + } + return closed(min, max); } } diff --git a/guava/src/com/google/common/collect/RegularContiguousSet.java b/guava/src/com/google/common/collect/RegularContiguousSet.java index 7948fe8..10b26f9 100644 --- a/guava/src/com/google/common/collect/RegularContiguousSet.java +++ b/guava/src/com/google/common/collect/RegularContiguousSet.java @@ -41,38 +41,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))); - } - - @GwtIncompatible("not used by GWT emulation") - @Override int indexOf(Object target) { - return contains(target) ? (int) domain.distance(first(), (C) target) : -1; + // 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 @@ -82,18 +76,6 @@ final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> }; } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<C> descendingIterator() { - return new AbstractSequentialIterator<C>(last()) { - final C first = first(); - - @Override - protected C computeNext(C previous) { - return equalsOrThrow(previous, first) ? null : domain.previous(previous); - } - }; - } - private static boolean equalsOrThrow(Comparable<?> left, @Nullable Comparable<?> right) { return right != null && Range.compareOrThrow(left, right) == 0; } @@ -115,10 +97,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) { @@ -127,7 +106,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() { @@ -153,7 +136,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); } } @@ -163,14 +146,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/src/com/google/common/collect/RegularImmutableAsList.java b/guava/src/com/google/common/collect/RegularImmutableAsList.java deleted file mode 100644 index c137d6b..0000000 --- a/guava/src/com/google/common/collect/RegularImmutableAsList.java +++ /dev/null @@ -1,91 +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; - -/** - * An {@link ImmutableAsList} implementation specialized for when the delegate collection is - * already backed by an {@code ImmutableList} or array. - * - * @author Louis Wasserman - */ -@GwtCompatible -@SuppressWarnings("serial") // uses writeReplace, not default serialization -class RegularImmutableAsList<E> extends ImmutableAsList<E> { - private final ImmutableCollection<E> delegate; - private final ImmutableList<? extends E> delegateList; - - RegularImmutableAsList(ImmutableCollection<E> delegate, ImmutableList<? extends E> delegateList) { - this.delegate = delegate; - this.delegateList = delegateList; - } - - RegularImmutableAsList(ImmutableCollection<E> delegate, Object[] array) { - this(delegate, ImmutableList.<E>asImmutableList(array)); - } - - @Override - ImmutableCollection<E> delegateCollection() { - return delegate; - } - - ImmutableList<? extends E> delegateList() { - return delegateList; - } - - @SuppressWarnings("unchecked") // safe covariant cast! - @Override - public UnmodifiableListIterator<E> listIterator(int index) { - return (UnmodifiableListIterator<E>) delegateList.listIterator(index); - } - - @Override - public Object[] toArray() { - return delegateList.toArray(); - } - - @Override - public <T> T[] toArray(T[] other) { - return delegateList.toArray(other); - } - - @Override - public int indexOf(Object object) { - return delegateList.indexOf(object); - } - - @Override - public int lastIndexOf(Object object) { - return delegateList.lastIndexOf(object); - } - - @Override - public boolean equals(Object obj) { - return delegateList.equals(obj); - } - - @Override - public int hashCode() { - return delegateList.hashCode(); - } - - @Override - public E get(int index) { - return delegateList.get(index); - } -} diff --git a/guava/src/com/google/common/collect/RegularImmutableBiMap.java b/guava/src/com/google/common/collect/RegularImmutableBiMap.java index b67aa76..dca1f05 100644 --- a/guava/src/com/google/common/collect/RegularImmutableBiMap.java +++ b/guava/src/com/google/common/collect/RegularImmutableBiMap.java @@ -16,283 +16,45 @@ package com.google.common.collect; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.GwtCompatible; -import java.io.Serializable; -import java.util.Collection; - -import javax.annotation.Nullable; - /** - * Bimap with two or more mappings. - * - * @author Louis Wasserman + * Bimap with one or more mappings. + * + * @author Jared Levy */ @GwtCompatible(serializable = true, emulated = true) @SuppressWarnings("serial") // uses writeReplace(), not default serialization class RegularImmutableBiMap<K, V> extends ImmutableBiMap<K, V> { - private static class BiMapEntry<K, V> extends ImmutableEntry<K, V> { - BiMapEntry(K key, V value) { - super(key, value); - } - - @Nullable - BiMapEntry<K, V> getNextInKToVBucket() { - return null; - } - - @Nullable - BiMapEntry<K, V> getNextInVToKBucket() { - return null; - } - } - - private static class NonTerminalBiMapEntry<K, V> extends BiMapEntry<K, V> { - @Nullable private final BiMapEntry<K, V> nextInKToVBucket; - @Nullable private final BiMapEntry<K, V> nextInVToKBucket; - - NonTerminalBiMapEntry(K key, V value, @Nullable BiMapEntry<K, V> nextInKToVBucket, - @Nullable BiMapEntry<K, V> nextInVToKBucket) { - super(key, value); - this.nextInKToVBucket = nextInKToVBucket; - this.nextInVToKBucket = nextInVToKBucket; - } + final transient ImmutableMap<K, V> delegate; + final transient ImmutableBiMap<V, K> inverse; - @Override - @Nullable - BiMapEntry<K, V> getNextInKToVBucket() { - return nextInKToVBucket; - } - - @Override - @Nullable - BiMapEntry<K, V> getNextInVToKBucket() { - return nextInVToKBucket; - } - } - - static final double MAX_LOAD_FACTOR = 1.2; - - private transient final BiMapEntry<K, V>[] kToVTable; - private transient final BiMapEntry<K, V>[] vToKTable; - private transient final BiMapEntry<K, V>[] entries; - private transient final int mask; - private transient final int hashCode; - - RegularImmutableBiMap(Collection<? extends Entry<? extends K, ? extends V>> entriesToAdd) { - int n = entriesToAdd.size(); - int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); - this.mask = tableSize - 1; - BiMapEntry<K, V>[] kToVTable = createEntryArray(tableSize); - BiMapEntry<K, V>[] vToKTable = createEntryArray(tableSize); - BiMapEntry<K, V>[] entries = createEntryArray(n); - int i = 0; - int hashCode = 0; - - for (Entry<? extends K, ? extends V> entry : entriesToAdd) { - K key = checkNotNull(entry.getKey()); - V value = checkNotNull(entry.getValue()); - - int keyHash = key.hashCode(); - int valueHash = value.hashCode(); - int keyBucket = Hashing.smear(keyHash) & mask; - int valueBucket = Hashing.smear(valueHash) & mask; - - BiMapEntry<K, V> nextInKToVBucket = kToVTable[keyBucket]; - for (BiMapEntry<K, V> kToVEntry = nextInKToVBucket; kToVEntry != null; - kToVEntry = kToVEntry.getNextInKToVBucket()) { - if (key.equals(kToVEntry.getKey())) { - throw new IllegalArgumentException("Multiple entries with same key: " + - entry + " and " + kToVEntry); - } - } - BiMapEntry<K, V> nextInVToKBucket = vToKTable[valueBucket]; - for (BiMapEntry<K, V> vToKEntry = nextInVToKBucket; vToKEntry != null; - vToKEntry = vToKEntry.getNextInVToKBucket()) { - if (value.equals(vToKEntry.getValue())) { - throw new IllegalArgumentException("Multiple entries with same value: " - + entry + " and " + vToKEntry); - } - } - BiMapEntry<K, V> newEntry = - (nextInKToVBucket == null && nextInVToKBucket == null) - ? new BiMapEntry<K, V>(key, value) - : new NonTerminalBiMapEntry<K, V>(key, value, nextInKToVBucket, nextInVToKBucket); - kToVTable[keyBucket] = newEntry; - vToKTable[valueBucket] = newEntry; - entries[i++] = newEntry; - hashCode += keyHash ^ valueHash; - } - - this.kToVTable = kToVTable; - this.vToKTable = vToKTable; - this.entries = entries; - this.hashCode = hashCode; - } - - @SuppressWarnings("unchecked") - private static <K, V> BiMapEntry<K, V>[] createEntryArray(int length) { - return new BiMapEntry[length]; - } + RegularImmutableBiMap(ImmutableMap<K, V> delegate) { + this.delegate = delegate; - @Override - @Nullable - public V get(@Nullable Object key) { - if (key == null) { - return null; - } - int bucket = Hashing.smear(key.hashCode()) & mask; - for (BiMapEntry<K, V> entry = kToVTable[bucket]; entry != null; - entry = entry.getNextInKToVBucket()) { - if (key.equals(entry.getKey())) { - return entry.getValue(); - } + ImmutableMap.Builder<V, K> builder = ImmutableMap.builder(); + for (Entry<K, V> entry : delegate.entrySet()) { + builder.put(entry.getValue(), entry.getKey()); } - return null; - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new ImmutableMapEntrySet<K, V>() { - @Override - ImmutableMap<K, V> map() { - return RegularImmutableBiMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<K, V>> createAsList() { - return new RegularImmutableAsList<Entry<K, V>>(this, entries); - } - - @Override - boolean isHashCodeFast() { - return true; - } - - @Override - public int hashCode() { - return hashCode; - } - }; + ImmutableMap<V, K> backwardMap = builder.build(); + this.inverse = new RegularImmutableBiMap<V, K>(backwardMap, this); } - @Override - boolean isPartialView() { - return false; + RegularImmutableBiMap(ImmutableMap<K, V> delegate, + ImmutableBiMap<V, K> inverse) { + this.delegate = delegate; + this.inverse = inverse; } - @Override - public int size() { - return entries.length; + @Override ImmutableMap<K, V> delegate() { + return delegate; } - - private transient ImmutableBiMap<V, K> inverse; - @Override - public ImmutableBiMap<V, K> inverse() { - ImmutableBiMap<V, K> result = inverse; - return (result == null) ? inverse = new Inverse() : result; + @Override public ImmutableBiMap<V, K> inverse() { + return inverse; } - - private final class Inverse extends ImmutableBiMap<V, K> { - - @Override - public int size() { - return inverse().size(); - } - - @Override - public ImmutableBiMap<K, V> inverse() { - return RegularImmutableBiMap.this; - } - - @Override - public K get(@Nullable Object value) { - if (value == null) { - return null; - } - int bucket = Hashing.smear(value.hashCode()) & mask; - for (BiMapEntry<K, V> entry = vToKTable[bucket]; entry != null; - entry = entry.getNextInVToKBucket()) { - if (value.equals(entry.getValue())) { - return entry.getKey(); - } - } - return null; - } - - @Override - ImmutableSet<Entry<V, K>> createEntrySet() { - return new InverseEntrySet(); - } - - final class InverseEntrySet extends ImmutableMapEntrySet<V, K> { - @Override - ImmutableMap<V, K> map() { - return Inverse.this; - } - @Override - boolean isHashCodeFast() { - return true; - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public UnmodifiableIterator<Entry<V, K>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<V, K>> createAsList() { - return new ImmutableAsList<Entry<V, K>>() { - @Override - public Entry<V, K> get(int index) { - Entry<K, V> entry = entries[index]; - return Maps.immutableEntry(entry.getValue(), entry.getKey()); - } - - @Override - ImmutableCollection<Entry<V, K>> delegateCollection() { - return InverseEntrySet.this; - } - }; - } - } - - @Override - boolean isPartialView() { - return false; - } - - @Override - Object writeReplace() { - return new InverseSerializedForm<K, V>(RegularImmutableBiMap.this); - } - } - - private static class InverseSerializedForm<K, V> implements Serializable { - private final ImmutableBiMap<K, V> forward; - - InverseSerializedForm(ImmutableBiMap<K, V> forward) { - this.forward = forward; - } - - Object readResolve() { - return forward.inverse(); - } - - private static final long serialVersionUID = 1; + @Override boolean isPartialView() { + return delegate.isPartialView() || inverse.delegate().isPartialView(); } } diff --git a/guava/src/com/google/common/collect/RegularImmutableList.java b/guava/src/com/google/common/collect/RegularImmutableList.java index 4314b6e..27f47f5 100644 --- a/guava/src/com/google/common/collect/RegularImmutableList.java +++ b/guava/src/com/google/common/collect/RegularImmutableList.java @@ -58,6 +58,16 @@ class RegularImmutableList<E> extends ImmutableList<E> { return offset != 0 || size != array.length; } + @Override public boolean contains(@Nullable Object target) { + return indexOf(target) != -1; + } + + // The fake cast to E is safe because the creation methods only allow E's + @SuppressWarnings("unchecked") + @Override public UnmodifiableIterator<E> iterator() { + return (UnmodifiableIterator<E>) Iterators.forArray(array, offset, size); + } + @Override public Object[] toArray() { Object[] newArray = new Object[size()]; System.arraycopy(array, offset, newArray, 0, size); @@ -82,19 +92,45 @@ class RegularImmutableList<E> extends ImmutableList<E> { return (E) array[index + offset]; } - @Override - ImmutableList<E> subListUnchecked(int fromIndex, int toIndex) { - return new RegularImmutableList<E>( - array, offset + fromIndex, toIndex - fromIndex); + @Override public int indexOf(@Nullable Object target) { + if (target != null) { + for (int i = offset; i < offset + size; i++) { + if (array[i].equals(target)) { + return i - offset; + } + } + } + return -1; } - @SuppressWarnings("unchecked") - @Override - public UnmodifiableListIterator<E> listIterator(int index) { - // for performance - // The fake cast to E is safe because the creation methods only allow E's - return (UnmodifiableListIterator<E>) - Iterators.forArray(array, offset, size, index); + @Override public int lastIndexOf(@Nullable Object target) { + if (target != null) { + for (int i = offset + size - 1; i >= offset; i--) { + if (array[i].equals(target)) { + return i - offset; + } + } + } + return -1; + } + + @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (fromIndex == toIndex) + ? ImmutableList.<E>of() + : new RegularImmutableList<E>( + array, offset + fromIndex, toIndex - fromIndex); + } + + @Override public UnmodifiableListIterator<E> listIterator(final int start) { + return new AbstractIndexedListIterator<E>(size, start) { + // The fake cast to E is safe because the creation methods only allow E's + @SuppressWarnings("unchecked") + @Override protected E get(int index) { + return (E) array[index + offset]; + } + + }; } @Override public boolean equals(@Nullable Object object) { @@ -128,6 +164,16 @@ class RegularImmutableList<E> extends ImmutableList<E> { return true; } + @Override public int hashCode() { + // not caching hash code since it could change if the elements are mutable + // in a way that modifies their hash codes + int hashCode = 1; + for (int i = offset; i < offset + size; i++) { + hashCode = 31 * hashCode + array[i].hashCode(); + } + return hashCode; + } + @Override public String toString() { StringBuilder sb = Collections2.newStringBuilderForCollection(size()) .append('[').append(array[offset]); diff --git a/guava/src/com/google/common/collect/RegularImmutableMap.java b/guava/src/com/google/common/collect/RegularImmutableMap.java index 6d9be99..e2b7150 100644 --- a/guava/src/com/google/common/collect/RegularImmutableMap.java +++ b/guava/src/com/google/common/collect/RegularImmutableMap.java @@ -19,6 +19,8 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.GwtCompatible; +import com.google.common.collect.ImmutableSet.ArrayImmutableSet; +import com.google.common.collect.ImmutableSet.TransformedImmutableSet; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -39,6 +41,7 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { private final transient LinkedEntry<K, V>[] table; // 'and' with an int to get a table index private final transient int mask; + private final transient int keySetHashCode; // TODO(gak): investigate avoiding the creation of ImmutableEntries since we // re-copy them anyway. @@ -46,16 +49,18 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { int size = immutableEntries.length; entries = createEntryArray(size); - int tableSize = Hashing.closedTableSize(size, MAX_LOAD_FACTOR); + int tableSize = chooseTableSize(size); table = createEntryArray(tableSize); mask = tableSize - 1; + int keySetHashCodeMutable = 0; for (int entryIndex = 0; entryIndex < size; entryIndex++) { // each of our 6 callers carefully put only Entry<K, V>s into the array! @SuppressWarnings("unchecked") Entry<K, V> entry = (Entry<K, V>) immutableEntries[entryIndex]; K key = entry.getKey(); int keyHashCode = key.hashCode(); + keySetHashCodeMutable += keyHashCode; int tableIndex = Hashing.smear(keyHashCode) & mask; @Nullable LinkedEntry<K, V> existing = table[tableIndex]; // prepend, not append, so the entries can be immutable @@ -68,14 +73,15 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { existing = existing.next(); } } + keySetHashCode = keySetHashCodeMutable; } - /** - * Closed addressing tends to perform well even with high load factors. - * Being conservative here ensures that the table is still likely to be - * relatively sparse (hence it misses fast) while saving space. - */ - private static final double MAX_LOAD_FACTOR = 1.2; + private static int chooseTableSize(int size) { + // least power of 2 greater than size + int tableSize = Integer.highestOneBit(size) << 1; + checkArgument(tableSize > 0, "table too large: %s", size); + return tableSize; + } /** * Creates a {@link LinkedEntry} array to hold parameterized entries. The @@ -160,31 +166,125 @@ final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> { public int size() { return entries.length; } - + + @Override public boolean isEmpty() { + return false; + } + + @Override public boolean containsValue(@Nullable Object value) { + if (value == null) { + return false; + } + for (Entry<K, V> entry : entries) { + if (entry.getValue().equals(value)) { + return true; + } + } + return false; + } + @Override boolean isPartialView() { return false; } - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new EntrySet(); + private transient ImmutableSet<Entry<K, V>> entrySet; + + @Override public ImmutableSet<Entry<K, V>> entrySet() { + ImmutableSet<Entry<K, V>> es = entrySet; + return (es == null) ? (entrySet = new EntrySet<K, V>(this)) : es; } @SuppressWarnings("serial") // uses writeReplace(), not default serialization - private class EntrySet extends ImmutableMapEntrySet<K, V> { - @Override ImmutableMap<K, V> map() { - return RegularImmutableMap.this; + private static class EntrySet<K, V> extends ArrayImmutableSet<Entry<K, V>> { + final transient RegularImmutableMap<K, V> map; + + EntrySet(RegularImmutableMap<K, V> map) { + super(map.entries); + this.map = map; } - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return asList().iterator(); + @Override public boolean contains(Object target) { + if (target instanceof Entry) { + Entry<?, ?> entry = (Entry<?, ?>) target; + V mappedValue = map.get(entry.getKey()); + return mappedValue != null && mappedValue.equals(entry.getValue()); + } + return false; + } + } + + private transient ImmutableSet<K> keySet; + + @Override public ImmutableSet<K> keySet() { + ImmutableSet<K> ks = keySet; + return (ks == null) ? (keySet = new KeySet<K, V>(this)) : ks; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class KeySet<K, V> + extends TransformedImmutableSet<Entry<K, V>, K> { + final RegularImmutableMap<K, V> map; + + KeySet(RegularImmutableMap<K, V> map) { + super(map.entries, map.keySetHashCode); + this.map = map; + } + + @Override K transform(Entry<K, V> element) { + return element.getKey(); + } + + @Override public boolean contains(Object target) { + return map.containsKey(target); + } + + @Override boolean isPartialView() { + return true; + } + } + + private transient ImmutableCollection<V> values; + + @Override public ImmutableCollection<V> values() { + ImmutableCollection<V> v = values; + return (v == null) ? (values = new Values<V>(this)) : v; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class Values<V> extends ImmutableCollection<V> { + final RegularImmutableMap<?, V> map; + + Values(RegularImmutableMap<?, V> map) { + this.map = map; } @Override - ImmutableList<Entry<K, V>> createAsList() { - return new RegularImmutableAsList<Entry<K, V>>(this, entries); + public int size() { + return map.entries.length; + } + + @Override public UnmodifiableIterator<V> iterator() { + return new AbstractIndexedListIterator<V>(map.entries.length) { + @Override protected V get(int index) { + return map.entries[index].getValue(); + } + }; } + + @Override public boolean contains(Object target) { + return map.containsValue(target); + } + + @Override boolean isPartialView() { + return true; + } + } + + @Override public String toString() { + StringBuilder result + = Collections2.newStringBuilderForCollection(size()).append('{'); + Collections2.STANDARD_JOINER.appendTo(result, entries); + return result.append('}').toString(); } // This class is never actually serialized directly, but we have to make the diff --git a/guava/src/com/google/common/collect/RegularImmutableMultiset.java b/guava/src/com/google/common/collect/RegularImmutableMultiset.java index e46f424..090c091 100644 --- a/guava/src/com/google/common/collect/RegularImmutableMultiset.java +++ b/guava/src/com/google/common/collect/RegularImmutableMultiset.java @@ -18,13 +18,14 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; import java.util.Map; import javax.annotation.Nullable; /** * Implementation of {@link ImmutableMultiset} with one or more elements. - * + * * @author Jared Levy * @author Louis Wasserman */ @@ -66,45 +67,31 @@ class RegularImmutableMultiset<E> extends ImmutableMultiset<E> { return map.keySet(); } - private static <E> Entry<E> entryFromMapEntry(Map.Entry<E, Integer> entry) { - return Multisets.immutableEntry(entry.getKey(), entry.getValue()); - } - @Override - ImmutableSet<Entry<E>> createEntrySet() { - return new EntrySet(); - } - - private class EntrySet extends ImmutableMultiset<E>.EntrySet { - @Override - public int size() { - return map.size(); - } - - @Override - public UnmodifiableIterator<Entry<E>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<E>> createAsList() { - final ImmutableList<Map.Entry<E, Integer>> entryList = map.entrySet().asList(); - return new ImmutableAsList<Entry<E>>() { - @Override - public Entry<E> get(int index) { - return entryFromMapEntry(entryList.get(index)); - } - - @Override - ImmutableCollection<Entry<E>> delegateCollection() { - return EntrySet.this; - } - }; - } + UnmodifiableIterator<Entry<E>> entryIterator() { + final Iterator<Map.Entry<E, Integer>> mapIterator = + map.entrySet().iterator(); + return new UnmodifiableIterator<Entry<E>>() { + @Override + public boolean hasNext() { + return mapIterator.hasNext(); + } + + @Override + public Entry<E> next() { + Map.Entry<E, Integer> mapEntry = mapIterator.next(); + return Multisets.immutableEntry(mapEntry.getKey(), mapEntry.getValue()); + } + }; } @Override public int hashCode() { return map.hashCode(); } + + @Override + int distinctElements() { + return map.size(); + } } diff --git a/guava/src/com/google/common/collect/RegularImmutableSortedMap.java b/guava/src/com/google/common/collect/RegularImmutableSortedMap.java deleted file mode 100644 index dd93b7f..0000000 --- a/guava/src/com/google/common/collect/RegularImmutableSortedMap.java +++ /dev/null @@ -1,130 +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 static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.GwtCompatible; - -import javax.annotation.Nullable; - -/** - * An implementation of an immutable sorted map with one or more entries. - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -@SuppressWarnings("serial") // uses writeReplace, not default serialization -final class RegularImmutableSortedMap<K, V> extends ImmutableSortedMap<K, V> { - private final transient RegularImmutableSortedSet<K> keySet; - private final transient ImmutableList<V> valueList; - - RegularImmutableSortedMap(RegularImmutableSortedSet<K> keySet, ImmutableList<V> valueList) { - this.keySet = keySet; - this.valueList = valueList; - } - - RegularImmutableSortedMap( - RegularImmutableSortedSet<K> keySet, - ImmutableList<V> valueList, - ImmutableSortedMap<K, V> descendingMap) { - super(descendingMap); - this.keySet = keySet; - this.valueList = valueList; - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return new EntrySet(); - } - - private class EntrySet extends ImmutableMapEntrySet<K, V> { - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Entry<K, V>> createAsList() { - return new ImmutableAsList<Entry<K, V>>() { - // avoid additional indirection - private final ImmutableList<K> keyList = keySet().asList(); - - @Override - public Entry<K, V> get(int index) { - return Maps.immutableEntry(keyList.get(index), valueList.get(index)); - } - - @Override - ImmutableCollection<Entry<K, V>> delegateCollection() { - return EntrySet.this; - } - }; - } - - @Override - ImmutableMap<K, V> map() { - return RegularImmutableSortedMap.this; - } - } - - @Override - public ImmutableSortedSet<K> keySet() { - return keySet; - } - - @Override - public ImmutableCollection<V> values() { - return valueList; - } - - @Override - public V get(@Nullable Object key) { - int index = keySet.indexOf(key); - return (index == -1) ? null : valueList.get(index); - } - - private ImmutableSortedMap<K, V> getSubMap(int fromIndex, int toIndex) { - if (fromIndex == 0 && toIndex == size()) { - return this; - } else if (fromIndex == toIndex) { - return emptyMap(comparator()); - } else { - return from( - keySet.getSubSet(fromIndex, toIndex), - valueList.subList(fromIndex, toIndex)); - } - } - - @Override - public ImmutableSortedMap<K, V> headMap(K toKey, boolean inclusive) { - return getSubMap(0, keySet.headIndex(checkNotNull(toKey), inclusive)); - } - - @Override - public ImmutableSortedMap<K, V> tailMap(K fromKey, boolean inclusive) { - return getSubMap(keySet.tailIndex(checkNotNull(fromKey), inclusive), size()); - } - - @Override - ImmutableSortedMap<K, V> createDescendingMap() { - return new RegularImmutableSortedMap<K, V>( - (RegularImmutableSortedSet<K>) keySet.descendingSet(), - valueList.reverse(), - this); - } - -} diff --git a/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java b/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java index 3db3405..7318d9b 100644 --- a/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java +++ b/guava/src/com/google/common/collect/RegularImmutableSortedMultiset.java @@ -15,11 +15,16 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkPositionIndexes; -import static com.google.common.collect.BoundType.CLOSED; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.INVERTED_INSERTION_INDEX; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER; +import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER; +import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT; import com.google.common.primitives.Ints; +import java.util.Comparator; +import java.util.List; + import javax.annotation.Nullable; /** @@ -27,119 +32,167 @@ import javax.annotation.Nullable; * * @author Louis Wasserman */ -@SuppressWarnings("serial") // uses writeReplace, not default serialization final class RegularImmutableSortedMultiset<E> extends ImmutableSortedMultiset<E> { - private final transient RegularImmutableSortedSet<E> elementSet; - private final transient int[] counts; - private final transient long[] cumulativeCounts; - private final transient int offset; - private final transient int length; + private static final class CumulativeCountEntry<E> extends Multisets.AbstractEntry<E> { + final E element; + final int count; + final long cumulativeCount; + + CumulativeCountEntry(E element, int count, @Nullable CumulativeCountEntry<E> previous) { + this.element = element; + this.count = count; + this.cumulativeCount = count + ((previous == null) ? 0 : previous.cumulativeCount); + } + + @Override + public E getElement() { + return element; + } + + @Override + public int getCount() { + return count; + } + } + + static <E> RegularImmutableSortedMultiset<E> createFromSorted(Comparator<? super E> comparator, + List<? extends Multiset.Entry<E>> entries) { + List<CumulativeCountEntry<E>> newEntries = Lists.newArrayListWithCapacity(entries.size()); + CumulativeCountEntry<E> previous = null; + for (Multiset.Entry<E> entry : entries) { + newEntries.add( + previous = new CumulativeCountEntry<E>(entry.getElement(), entry.getCount(), previous)); + } + return new RegularImmutableSortedMultiset<E>(comparator, ImmutableList.copyOf(newEntries)); + } + + final transient ImmutableList<CumulativeCountEntry<E>> entries; RegularImmutableSortedMultiset( - RegularImmutableSortedSet<E> elementSet, - int[] counts, - long[] cumulativeCounts, - int offset, - int length) { - this.elementSet = elementSet; - this.counts = counts; - this.cumulativeCounts = cumulativeCounts; - this.offset = offset; - this.length = length; + Comparator<? super E> comparator, ImmutableList<CumulativeCountEntry<E>> entries) { + super(comparator); + this.entries = entries; + assert !entries.isEmpty(); } - private Entry<E> getEntry(int index) { - return Multisets.immutableEntry( - elementSet.asList().get(index), - counts[offset + index]); + ImmutableList<E> elementList() { + return new TransformedImmutableList<CumulativeCountEntry<E>, E>(entries) { + @Override + E transform(CumulativeCountEntry<E> entry) { + return entry.getElement(); + } + }; } @Override - public Entry<E> firstEntry() { - return getEntry(0); + ImmutableSortedSet<E> createElementSet() { + return new RegularImmutableSortedSet<E>(elementList(), comparator()); } @Override - public Entry<E> lastEntry() { - return getEntry(length - 1); + ImmutableSortedSet<E> createDescendingElementSet() { + return new RegularImmutableSortedSet<E>(elementList().reverse(), reverseComparator()); } + @SuppressWarnings("unchecked") @Override - public int count(@Nullable Object element) { - int index = elementSet.indexOf(element); - return (index == -1) ? 0 : counts[index + offset]; + UnmodifiableIterator<Multiset.Entry<E>> entryIterator() { + return (UnmodifiableIterator) entries.iterator(); } + @SuppressWarnings("unchecked") @Override - public int size() { - long size = cumulativeCounts[offset + length] - cumulativeCounts[offset]; - return Ints.saturatedCast(size); + UnmodifiableIterator<Multiset.Entry<E>> descendingEntryIterator() { + return (UnmodifiableIterator) entries.reverse().iterator(); } @Override - public ImmutableSortedSet<E> elementSet() { - return elementSet; + public CumulativeCountEntry<E> firstEntry() { + return entries.get(0); } @Override - public ImmutableSortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { - return getSubMultiset(0, elementSet.headIndex(upperBound, checkNotNull(boundType) == CLOSED)); + public CumulativeCountEntry<E> lastEntry() { + return entries.get(entries.size() - 1); } @Override - public ImmutableSortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { - return getSubMultiset(elementSet.tailIndex(lowerBound, checkNotNull(boundType) == CLOSED), - length); + public int size() { + CumulativeCountEntry<E> firstEntry = firstEntry(); + CumulativeCountEntry<E> lastEntry = lastEntry(); + return Ints.saturatedCast( + lastEntry.cumulativeCount - firstEntry.cumulativeCount + firstEntry.count); } - ImmutableSortedMultiset<E> getSubMultiset(int from, int to) { - checkPositionIndexes(from, to, length); - if (from == to) { - return emptyMultiset(comparator()); - } else if (from == 0 && to == length) { - return this; - } else { - RegularImmutableSortedSet<E> subElementSet = - (RegularImmutableSortedSet<E>) elementSet.getSubSet(from, to); - return new RegularImmutableSortedMultiset<E>( - subElementSet, counts, cumulativeCounts, offset + from, to - from); - } + @Override + int distinctElements() { + return entries.size(); } @Override - ImmutableSet<Entry<E>> createEntrySet() { - return new EntrySet(); + boolean isPartialView() { + return entries.isPartialView(); } - private final class EntrySet extends ImmutableMultiset<E>.EntrySet { - @Override - public int size() { - return length; + @SuppressWarnings("unchecked") + @Override + public int count(@Nullable Object element) { + if (element == null) { + return 0; } - - @Override - public UnmodifiableIterator<Entry<E>> iterator() { - return asList().iterator(); + try { + int index = SortedLists.binarySearch( + elementList(), (E) element, comparator(), ANY_PRESENT, INVERTED_INSERTION_INDEX); + return (index >= 0) ? entries.get(index).getCount() : 0; + } catch (ClassCastException e) { + return 0; } + } - @Override - ImmutableList<Entry<E>> createAsList() { - return new ImmutableAsList<Entry<E>>() { - @Override - public Entry<E> get(int index) { - return getEntry(index); - } - - @Override - ImmutableCollection<Entry<E>> delegateCollection() { - return EntrySet.this; - } - }; + @Override + public ImmutableSortedMultiset<E> headMultiset(E upperBound, BoundType boundType) { + int index; + switch (boundType) { + case OPEN: + index = SortedLists.binarySearch( + elementList(), checkNotNull(upperBound), comparator(), ANY_PRESENT, NEXT_HIGHER); + break; + case CLOSED: + index = SortedLists.binarySearch( + elementList(), checkNotNull(upperBound), comparator(), ANY_PRESENT, NEXT_LOWER) + 1; + break; + default: + throw new AssertionError(); } + return createSubMultiset(0, index); } @Override - boolean isPartialView() { - return offset > 0 || length < counts.length; + public ImmutableSortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType) { + int index; + switch (boundType) { + case OPEN: + index = SortedLists.binarySearch( + elementList(), checkNotNull(lowerBound), comparator(), ANY_PRESENT, NEXT_LOWER) + 1; + break; + case CLOSED: + index = SortedLists.binarySearch( + elementList(), checkNotNull(lowerBound), comparator(), ANY_PRESENT, NEXT_HIGHER); + break; + default: + throw new AssertionError(); + } + return createSubMultiset(index, distinctElements()); + } + + private ImmutableSortedMultiset<E> createSubMultiset(int newFromIndex, int newToIndex) { + if (newFromIndex == 0 && newToIndex == entries.size()) { + return this; + } else if (newFromIndex >= newToIndex) { + return emptyMultiset(comparator()); + } else { + return new RegularImmutableSortedMultiset<E>( + comparator(), entries.subList(newFromIndex, newToIndex)); + } } } diff --git a/guava/src/com/google/common/collect/RegularImmutableSortedSet.java b/guava/src/com/google/common/collect/RegularImmutableSortedSet.java index 9dc2e5c..3d2b7e0 100644 --- a/guava/src/com/google/common/collect/RegularImmutableSortedSet.java +++ b/guava/src/com/google/common/collect/RegularImmutableSortedSet.java @@ -25,7 +25,6 @@ import static com.google.common.collect.SortedLists.KeyPresentBehavior.FIRST_AFT import static com.google.common.collect.SortedLists.KeyPresentBehavior.FIRST_PRESENT; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.util.Collection; import java.util.Collections; @@ -60,11 +59,6 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { return elements.iterator(); } - @GwtIncompatible("NavigableSet") - @Override public UnmodifiableIterator<E> descendingIterator() { - return elements.reverse().iterator(); - } - @Override public boolean isEmpty() { return false; } @@ -75,8 +69,11 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { } @Override public boolean contains(Object o) { + if (o == null) { + return false; + } try { - return o != null && unsafeBinarySearch(o) >= 0; + return binarySearch(o) >= 0; } catch (ClassCastException e) { return false; } @@ -87,7 +84,7 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { // targets.size() < size() / log(size()) // TODO(kevinb): see if we can share code with OrderedIterator after it // graduates from labs. - if (!SortedIterables.hasSameComparator(comparator(), targets) + if (!SortedIterables.hasSameComparator(comparator(), targets) || (targets.size() <= 1)) { return super.containsAll(targets); } @@ -128,8 +125,18 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { return false; } - private int unsafeBinarySearch(Object key) throws ClassCastException { - return Collections.binarySearch(elements, key, unsafeComparator()); + private int binarySearch(Object key) { + // TODO(kevinb): split this into binarySearch(E) and + // unsafeBinarySearch(Object), use each appropriately. name all methods that + // might throw CCE "unsafe*". + + // Pretend the comparator can compare anything. If it turns out it can't + // compare a and b, we should get a CCE on the subsequent line. Only methods + // that are spec'd to throw CCE should call this. + @SuppressWarnings("unchecked") + Comparator<Object> unsafeComparator = (Comparator<Object>) comparator; + + return Collections.binarySearch(elements, key, unsafeComparator); } @Override boolean isPartialView() { @@ -190,38 +197,16 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { } @Override - public E lower(E element) { - int index = headIndex(element, false) - 1; - return (index == -1) ? null : elements.get(index); - } - - @Override - public E floor(E element) { - int index = headIndex(element, true) - 1; - return (index == -1) ? null : elements.get(index); - } - - @Override - public E ceiling(E element) { - int index = tailIndex(element, true); - return (index == size()) ? null : elements.get(index); - } - - @Override - public E higher(E element) { - int index = tailIndex(element, false); - return (index == size()) ? null : elements.get(index); - } - - @Override ImmutableSortedSet<E> headSetImpl(E toElement, boolean inclusive) { - return getSubSet(0, headIndex(toElement, inclusive)); - } - - int headIndex(E toElement, boolean inclusive) { - return SortedLists.binarySearch( - elements, checkNotNull(toElement), comparator(), - inclusive ? FIRST_AFTER : FIRST_PRESENT, NEXT_HIGHER); + int index; + if (inclusive) { + index = SortedLists.binarySearch( + elements, checkNotNull(toElement), comparator(), FIRST_AFTER, NEXT_HIGHER); + } else { + index = SortedLists.binarySearch( + elements, checkNotNull(toElement), comparator(), FIRST_PRESENT, NEXT_HIGHER); + } + return createSubset(0, index); } @Override @@ -233,15 +218,15 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { @Override ImmutableSortedSet<E> tailSetImpl(E fromElement, boolean inclusive) { - return getSubSet(tailIndex(fromElement, inclusive), size()); - } - - int tailIndex(E fromElement, boolean inclusive) { - return SortedLists.binarySearch( - elements, - checkNotNull(fromElement), - comparator(), - inclusive ? FIRST_PRESENT : FIRST_AFTER, NEXT_HIGHER); + int index; + if (inclusive) { + index = SortedLists.binarySearch( + elements, checkNotNull(fromElement), comparator(), FIRST_PRESENT, NEXT_HIGHER); + } else { + index = SortedLists.binarySearch( + elements, checkNotNull(fromElement), comparator(), FIRST_AFTER, NEXT_HIGHER); + } + return createSubset(index, size()); } // Pretend the comparator can compare anything. If it turns out it can't @@ -252,7 +237,7 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { return (Comparator<Object>) comparator; } - ImmutableSortedSet<E> getSubSet(int newFromIndex, int newToIndex) { + private ImmutableSortedSet<E> createSubset(int newFromIndex, int newToIndex) { if (newFromIndex == 0 && newToIndex == size()) { return this; } else if (newFromIndex < newToIndex) { @@ -263,27 +248,28 @@ final class RegularImmutableSortedSet<E> extends ImmutableSortedSet<E> { } } + @SuppressWarnings("unchecked") @Override int indexOf(@Nullable Object target) { if (target == null) { return -1; } int position; try { - position = SortedLists.binarySearch(elements, target, unsafeComparator(), + position = SortedLists.binarySearch(elements, (E) target, comparator(), ANY_PRESENT, INVERTED_INSERTION_INDEX); } catch (ClassCastException e) { return -1; } - return (position >= 0) ? position : -1; + // TODO(kevinb): reconsider if it's really worth making feeble attempts at + // sanity for inconsistent comparators. + + // The equals() check is needed when the comparator isn't compatible with + // equals(). + return (position >= 0 && elements.get(position).equals(target)) + ? position : -1; } @Override ImmutableList<E> createAsList() { return new ImmutableSortedAsList<E>(this, elements); } - - @Override - ImmutableSortedSet<E> createDescendingSet() { - return new RegularImmutableSortedSet<E>(elements.reverse(), - Ordering.from(comparator).reverse()); - } } diff --git a/guava/src/com/google/common/collect/RegularImmutableTable.java b/guava/src/com/google/common/collect/RegularImmutableTable.java index e4bfb70..c6ae3eb 100644 --- a/guava/src/com/google/common/collect/RegularImmutableTable.java +++ b/guava/src/com/google/common/collect/RegularImmutableTable.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. @@ -21,11 +21,11 @@ import static com.google.common.base.Preconditions.checkNotNull; 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 java.util.Collections; import java.util.Comparator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -36,63 +36,56 @@ import javax.annotation.concurrent.Immutable; * An implementation of {@link ImmutableTable} holding an arbitrary number of * cells. * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) */ @GwtCompatible abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { - private RegularImmutableTable() {} - - private transient ImmutableCollection<V> values; + private final ImmutableSet<Cell<R, C, V>> cellSet; - @Override public final ImmutableCollection<V> values() { - ImmutableCollection<V> result = values; - return (result == null) ? values = createValues() : result; + private RegularImmutableTable(ImmutableSet<Cell<R, C, V>> cellSet) { + this.cellSet = cellSet; } - - abstract ImmutableCollection<V> createValues(); - @Override public abstract int size(); + private static final Function<Cell<Object, Object, Object>, Object> + GET_VALUE_FUNCTION = + new Function<Cell<Object, Object, Object>, Object>() { + @Override public Object apply(Cell<Object, Object, Object> from) { + return from.getValue(); + } + }; + + @SuppressWarnings("unchecked") + private Function<Cell<R, C, V>, V> getValueFunction() { + return (Function) GET_VALUE_FUNCTION; + } - @Override public final boolean containsValue(@Nullable Object value) { - return values().contains(value); + @Nullable private transient volatile ImmutableList<V> valueList; + + @Override public final ImmutableCollection<V> values() { + ImmutableList<V> result = valueList; + if (result == null) { + valueList = result = ImmutableList.copyOf( + Iterables.transform(cellSet(), getValueFunction())); + } + return result; } - - private transient ImmutableSet<Cell<R, C, V>> cellSet; - @Override - public final ImmutableSet<Cell<R, C, V>> cellSet() { - ImmutableSet<Cell<R, C, V>> result = cellSet; - return (result == null) ? cellSet = createCellSet() : result; + @Override public final int size() { + return cellSet().size(); } - - abstract ImmutableSet<Cell<R, C, V>> createCellSet(); - - abstract class CellSet extends ImmutableSet<Cell<R, C, V>> { - @Override - public int size() { - return RegularImmutableTable.this.size(); - } - - @Override - public boolean contains(@Nullable Object object) { - if (object instanceof Cell) { - Cell<?, ?, ?> cell = (Cell<?, ?, ?>) object; - Object value = get(cell.getRowKey(), cell.getColumnKey()); - return value != null && value.equals(cell.getValue()); - } - return false; - } - @Override - boolean isPartialView() { - return false; - } + @Override public final boolean containsValue(@Nullable Object value) { + return values().contains(value); } @Override public final boolean isEmpty() { return false; } + @Override public final ImmutableSet<Cell<R, C, V>> cellSet() { + return cellSet; + } + static final <R, C, V> RegularImmutableTable<R, C, V> forCells( List<Cell<R, C, V>> cells, @Nullable final Comparator<? super R> rowComparator, @@ -137,13 +130,15 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { forCellsInternal(Iterable<Cell<R, C, V>> cells, @Nullable Comparator<? super R> rowComparator, @Nullable Comparator<? super C> columnComparator) { + ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder = ImmutableSet.builder(); ImmutableSet.Builder<R> rowSpaceBuilder = ImmutableSet.builder(); ImmutableSet.Builder<C> columnSpaceBuilder = ImmutableSet.builder(); - ImmutableList<Cell<R, C, V>> cellList = ImmutableList.copyOf(cells); - for (Cell<R, C, V> cell : cellList) { + for (Cell<R, C, V> cell : cells) { + cellSetBuilder.add(cell); rowSpaceBuilder.add(cell.getRowKey()); columnSpaceBuilder.add(cell.getColumnKey()); } + ImmutableSet<Cell<R, C, V>> cellSet = cellSetBuilder.build(); ImmutableSet<R> rowSpace = rowSpaceBuilder.build(); if (rowComparator != null) { @@ -160,9 +155,9 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { // use a dense table if more than half of the cells have values // TODO(gak): tune this condition based on empirical evidence - return (cellList.size() > ((rowSpace.size() * columnSpace.size()) / 2)) ? - new DenseImmutableTable<R, C, V>(cellList, rowSpace, columnSpace) : - new SparseImmutableTable<R, C, V>(cellList, rowSpace, columnSpace); + return (cellSet.size() > ((rowSpace.size() * columnSpace.size()) / 2 )) ? + new DenseImmutableTable<R, C, V>(cellSet, rowSpace, columnSpace) : + new SparseImmutableTable<R, C, V>(cellSet, rowSpace, columnSpace); } /** @@ -175,52 +170,50 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { private final ImmutableMap<R, Map<C, V>> rowMap; private final ImmutableMap<C, Map<R, V>> columnMap; - private final int[] iterationOrderRow; - private final int[] iterationOrderColumn; - SparseImmutableTable(ImmutableList<Cell<R, C, V>> cellList, - ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace) { - Map<R, Integer> rowIndex = Maps.newHashMap(); - Map<R, Map<C, V>> rows = Maps.newLinkedHashMap(); - for (R row : rowSpace) { - rowIndex.put(row, rows.size()); - rows.put(row, new LinkedHashMap<C, V>()); - } - Map<C, Map<R, V>> columns = Maps.newLinkedHashMap(); - for (C col : columnSpace) { - columns.put(col, new LinkedHashMap<R, V>()); + /** + * Creates a {@link Map} over the key space with + * {@link ImmutableMap.Builder}s ready for values. + */ + private static final <A, B, V> Map<A, ImmutableMap.Builder<B, V>> + makeIndexBuilder(ImmutableSet<A> keySpace) { + Map<A, ImmutableMap.Builder<B, V>> indexBuilder = Maps.newLinkedHashMap(); + for (A key : keySpace) { + indexBuilder.put(key, ImmutableMap.<B, V>builder()); } - int[] iterationOrderRow = new int[cellList.size()]; - int[] iterationOrderColumn = new int[cellList.size()]; - for (int i = 0; i < cellList.size(); i++) { - Cell<R, C, V> cell = cellList.get(i); + return indexBuilder; + } + + /** + * Builds the value maps of the index and creates an immutable copy of the + * map. + */ + private static final <A, B, V> ImmutableMap<A, Map<B, V>> buildIndex( + Map<A, ImmutableMap.Builder<B, V>> indexBuilder) { + return ImmutableMap.copyOf(Maps.transformValues(indexBuilder, + new Function<ImmutableMap.Builder<B, V>, Map<B, V>>() { + @Override public Map<B, V> apply(ImmutableMap.Builder<B, V> from) { + return from.build(); + } + })); + } + + SparseImmutableTable(ImmutableSet<Cell<R, C, V>> cellSet, + ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace) { + super(cellSet); + Map<R, ImmutableMap.Builder<C, V>> rowIndexBuilder + = makeIndexBuilder(rowSpace); + Map<C, ImmutableMap.Builder<R, V>> columnIndexBuilder + = makeIndexBuilder(columnSpace); + for (Cell<R, C, V> cell : cellSet) { R rowKey = cell.getRowKey(); C columnKey = cell.getColumnKey(); V value = cell.getValue(); - - iterationOrderRow[i] = rowIndex.get(rowKey); - Map<C, V> thisRow = rows.get(rowKey); - iterationOrderColumn[i] = thisRow.size(); - V oldValue = thisRow.put(columnKey, value); - if (oldValue != null) { - throw new IllegalArgumentException("Duplicate value for row=" + rowKey + ", column=" - + columnKey + ": " + value + ", " + oldValue); - } - columns.get(columnKey).put(rowKey, value); - } - this.iterationOrderRow = iterationOrderRow; - this.iterationOrderColumn = iterationOrderColumn; - ImmutableMap.Builder<R, Map<C, V>> rowBuilder = ImmutableMap.builder(); - for (Map.Entry<R, Map<C, V>> row : rows.entrySet()) { - rowBuilder.put(row.getKey(), ImmutableMap.copyOf(row.getValue())); + rowIndexBuilder.get(rowKey).put(columnKey, value); + columnIndexBuilder.get(columnKey).put(rowKey, value); } - this.rowMap = rowBuilder.build(); - - ImmutableMap.Builder<C, Map<R, V>> columnBuilder = ImmutableMap.builder(); - for (Map.Entry<C, Map<R, V>> col : columns.entrySet()) { - columnBuilder.put(col.getKey(), ImmutableMap.copyOf(col.getValue())); - } - this.columnMap = columnBuilder.build(); + this.rowMap = buildIndex(rowIndexBuilder); + this.columnMap = buildIndex(columnIndexBuilder); } @Override public ImmutableMap<R, V> column(C columnKey) { @@ -272,153 +265,6 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { Map<C, V> row = rowMap.get(rowKey); return (row == null) ? null : row.get(columnKey); } - - @Override - ImmutableCollection<V> createValues() { - return new ImmutableList<V>() { - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - public V get(int index) { - int rowIndex = iterationOrderRow[index]; - ImmutableMap<C, V> row = (ImmutableMap<C, V>) rowMap.values().asList().get(rowIndex); - int columnIndex = iterationOrderColumn[index]; - return row.values().asList().get(columnIndex); - } - - @Override - boolean isPartialView() { - return true; - } - }; - } - - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - ImmutableSet<Cell<R, C, V>> createCellSet() { - return new SparseCellSet(); - } - - class SparseCellSet extends CellSet { - @Override - public UnmodifiableIterator<Cell<R, C, V>> iterator() { - return asList().iterator(); - } - - @Override - ImmutableList<Cell<R, C, V>> createAsList() { - return new ImmutableAsList<Cell<R, C, V>>() { - @Override - public Cell<R, C, V> get(int index) { - int rowIndex = iterationOrderRow[index]; - Map.Entry<R, Map<C, V>> rowEntry = rowMap.entrySet().asList().get(rowIndex); - ImmutableMap<C, V> row = (ImmutableMap<C, V>) rowEntry.getValue(); - int columnIndex = iterationOrderColumn[index]; - Map.Entry<C, V> colEntry = row.entrySet().asList().get(columnIndex); - return Tables.immutableCell(rowEntry.getKey(), colEntry.getKey(), colEntry.getValue()); - } - - @Override - ImmutableCollection<Cell<R, C, V>> delegateCollection() { - return SparseCellSet.this; - } - }; - } - } - } - - /** - * An immutable map implementation backed by an indexed nullable array, used in - * DenseImmutableTable. - */ - private abstract static class ImmutableArrayMap<K, V> extends ImmutableMap<K, V> { - private final int size; - - ImmutableArrayMap(int size) { - this.size = size; - } - - abstract ImmutableMap<K, Integer> keyToIndex(); - - // True if getValue never returns null. - private boolean isFull() { - return size == keyToIndex().size(); - } - - K getKey(int index) { - return keyToIndex().keySet().asList().get(index); - } - - @Nullable abstract V getValue(int keyIndex); - - @Override - ImmutableSet<K> createKeySet() { - return isFull() ? keyToIndex().keySet() : super.createKeySet(); - } - - @Override - public int size() { - return size; - } - - @Override - public V get(@Nullable Object key) { - Integer keyIndex = keyToIndex().get(key); - return (keyIndex == null) ? null : getValue(keyIndex); - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - if (isFull()) { - return new ImmutableMapEntrySet<K, V>() { - @Override ImmutableMap<K, V> map() { - return ImmutableArrayMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return new AbstractIndexedListIterator<Entry<K, V>>(size()) { - @Override - protected Entry<K, V> get(int index) { - return Maps.immutableEntry(getKey(index), getValue(index)); - } - }; - } - }; - } else { - return new ImmutableMapEntrySet<K, V>() { - @Override ImmutableMap<K, V> map() { - return ImmutableArrayMap.this; - } - - @Override - public UnmodifiableIterator<Entry<K, V>> iterator() { - return new AbstractIterator<Entry<K, V>>() { - private int index = -1; - private final int maxIndex = keyToIndex().size(); - - @Override - protected Entry<K, V> computeNext() { - for (index++; index < maxIndex; index++) { - V value = getValue(index); - if (value != null) { - return Maps.immutableEntry(getKey(index), value); - } - } - return endOfData(); - } - }; - } - }; - } - } } /** @@ -428,20 +274,14 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { static final class DenseImmutableTable<R, C, V> extends RegularImmutableTable<R, C, V> { - private final ImmutableMap<R, Integer> rowKeyToIndex; - private final ImmutableMap<C, Integer> columnKeyToIndex; - private final ImmutableMap<R, Map<C, V>> rowMap; - private final ImmutableMap<C, Map<R, V>> columnMap; - private final int[] rowCounts; - private final int[] columnCounts; + private final ImmutableBiMap<R, Integer> rowKeyToIndex; + private final ImmutableBiMap<C, Integer> columnKeyToIndex; private final V[][] values; - private final int[] iterationOrderRow; - private final int[] iterationOrderColumn; - private static <E> ImmutableMap<E, Integer> makeIndex( + private static <E> ImmutableBiMap<E, Integer> makeIndex( ImmutableSet<E> set) { - ImmutableMap.Builder<E, Integer> indexBuilder = - ImmutableMap.builder(); + ImmutableBiMap.Builder<E, Integer> indexBuilder = + ImmutableBiMap.builder(); int i = 0; for (E key : set) { indexBuilder.put(key, i); @@ -450,19 +290,15 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { return indexBuilder.build(); } - DenseImmutableTable(ImmutableList<Cell<R, C, V>> cellList, + DenseImmutableTable(ImmutableSet<Cell<R, C, V>> cellSet, ImmutableSet<R> rowSpace, ImmutableSet<C> columnSpace) { + super(cellSet); @SuppressWarnings("unchecked") V[][] array = (V[][]) new Object[rowSpace.size()][columnSpace.size()]; this.values = array; this.rowKeyToIndex = makeIndex(rowSpace); this.columnKeyToIndex = makeIndex(columnSpace); - rowCounts = new int[rowKeyToIndex.size()]; - columnCounts = new int[columnKeyToIndex.size()]; - int[] iterationOrderRow = new int[cellList.size()]; - int[] iterationOrderColumn = new int[cellList.size()]; - for (int i = 0; i < cellList.size(); i++) { - Cell<R, C, V> cell = cellList.get(i); + for (Cell<R, C, V> cell : cellSet) { R rowKey = cell.getRowKey(); C columnKey = cell.getColumnKey(); int rowIndex = rowKeyToIndex.get(rowKey); @@ -471,113 +307,25 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { checkArgument(existingValue == null, "duplicate key: (%s, %s)", rowKey, columnKey); values[rowIndex][columnIndex] = cell.getValue(); - rowCounts[rowIndex]++; - columnCounts[columnIndex]++; - iterationOrderRow[i] = rowIndex; - iterationOrderColumn[i] = columnIndex; - } - this.iterationOrderRow = iterationOrderRow; - this.iterationOrderColumn = iterationOrderColumn; - this.rowMap = new RowMap(); - this.columnMap = new ColumnMap(); - } - - private final class Row extends ImmutableArrayMap<C, V> { - private final int rowIndex; - - Row(int rowIndex) { - super(rowCounts[rowIndex]); - this.rowIndex = rowIndex; - } - - @Override - ImmutableMap<C, Integer> keyToIndex() { - return columnKeyToIndex; - } - - @Override - V getValue(int keyIndex) { - return values[rowIndex][keyIndex]; - } - - @Override - boolean isPartialView() { - return true; - } - } - - private final class Column extends ImmutableArrayMap<R, V> { - private final int columnIndex; - - Column(int columnIndex) { - super(columnCounts[columnIndex]); - this.columnIndex = columnIndex; - } - - @Override - ImmutableMap<R, Integer> keyToIndex() { - return rowKeyToIndex; - } - - @Override - V getValue(int keyIndex) { - return values[keyIndex][columnIndex]; - } - - @Override - boolean isPartialView() { - return true; - } - } - - private final class RowMap extends ImmutableArrayMap<R, Map<C, V>> { - private RowMap() { - super(rowCounts.length); - } - - @Override - ImmutableMap<R, Integer> keyToIndex() { - return rowKeyToIndex; - } - - @Override - Map<C, V> getValue(int keyIndex) { - return new Row(keyIndex); - } - - @Override - boolean isPartialView() { - return false; - } - } - - private final class ColumnMap extends ImmutableArrayMap<C, Map<R, V>> { - private ColumnMap() { - super(columnCounts.length); - } - - @Override - ImmutableMap<C, Integer> keyToIndex() { - return columnKeyToIndex; - } - - @Override - Map<R, V> getValue(int keyIndex) { - return new Column(keyIndex); - } - - @Override - boolean isPartialView() { - return false; } } @Override public ImmutableMap<R, V> column(C columnKey) { - Integer columnIndex = columnKeyToIndex.get(checkNotNull(columnKey)); - if (columnIndex == null) { + checkNotNull(columnKey); + Integer columnIndexInteger = columnKeyToIndex.get(columnKey); + if (columnIndexInteger == null) { return ImmutableMap.of(); } else { - return new Column(columnIndex); + // unbox only once + int columnIndex = columnIndexInteger; + ImmutableMap.Builder<R, V> columnBuilder = ImmutableMap.builder(); + for (int i = 0; i < values.length; i++) { + V value = values[i][columnIndex]; + if (value != null) { + columnBuilder.put(rowKeyToIndex.inverse().get(i), value); + } + } + return columnBuilder.build(); } } @@ -585,8 +333,31 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { return columnKeyToIndex.keySet(); } + private transient volatile ImmutableMap<C, Map<R, V>> columnMap; + + private ImmutableMap<C, Map<R, V>> makeColumnMap() { + ImmutableMap.Builder<C, Map<R, V>> columnMapBuilder = + ImmutableMap.builder(); + for (int c = 0; c < columnKeyToIndex.size(); c++) { + ImmutableMap.Builder<R, V> rowMapBuilder = ImmutableMap.builder(); + for (int r = 0; r < rowKeyToIndex.size(); r++) { + V value = values[r][c]; + if (value != null) { + rowMapBuilder.put(rowKeyToIndex.inverse().get(r), value); + } + } + columnMapBuilder.put(columnKeyToIndex.inverse().get(c), + rowMapBuilder.build()); + } + return columnMapBuilder.build(); + } + @Override public ImmutableMap<C, Map<R, V>> columnMap() { - return columnMap; + ImmutableMap<C, Map<R, V>> result = columnMap; + if (result == null) { + columnMap = result = makeColumnMap(); + } + return result; } @Override public boolean contains(@Nullable Object rowKey, @@ -616,7 +387,15 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { if (rowIndex == null) { return ImmutableMap.of(); } else { - return new Row(rowIndex); + ImmutableMap.Builder<C, V> rowBuilder = ImmutableMap.builder(); + V[] row = values[rowIndex]; + for (int r = 0; r < row.length; r++) { + V value = row[r]; + if (value != null) { + rowBuilder.put(columnKeyToIndex.inverse().get(r), value); + } + } + return rowBuilder.build(); } } @@ -624,66 +403,31 @@ abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { return rowKeyToIndex.keySet(); } - @Override - public ImmutableMap<R, Map<C, V>> rowMap() { - return rowMap; - } + private transient volatile ImmutableMap<R, Map<C, V>> rowMap; - @Override - ImmutableCollection<V> createValues() { - return new ImmutableList<V>() { - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - public V get(int index) { - return values[iterationOrderRow[index]][iterationOrderColumn[index]]; - } - - @Override - boolean isPartialView() { - return true; + private ImmutableMap<R, Map<C, V>> makeRowMap() { + ImmutableMap.Builder<R, Map<C, V>> rowMapBuilder = ImmutableMap.builder(); + for (int r = 0; r < values.length; r++) { + V[] row = values[r]; + ImmutableMap.Builder<C, V> columnMapBuilder = ImmutableMap.builder(); + for (int c = 0; c < row.length; c++) { + V value = row[c]; + if (value != null) { + columnMapBuilder.put(columnKeyToIndex.inverse().get(c), value); + } } - }; - } - - @Override - public int size() { - return iterationOrderRow.length; - } - - @Override - ImmutableSet<Cell<R, C, V>> createCellSet() { - return new DenseCellSet(); - } - - class DenseCellSet extends CellSet { - @Override - public UnmodifiableIterator<Cell<R, C, V>> iterator() { - return asList().iterator(); + rowMapBuilder.put(rowKeyToIndex.inverse().get(r), + columnMapBuilder.build()); } + return rowMapBuilder.build(); + } - @Override - ImmutableList<Cell<R, C, V>> createAsList() { - return new ImmutableAsList<Cell<R, C, V>>() { - @Override - public Cell<R, C, V> get(int index) { - int rowIndex = iterationOrderRow[index]; - int columnIndex = iterationOrderColumn[index]; - R rowKey = rowKeySet().asList().get(rowIndex); - C columnKey = columnKeySet().asList().get(columnIndex); - V value = values[rowIndex][columnIndex]; - return Tables.immutableCell(rowKey, columnKey, value); - } - - @Override - ImmutableCollection<Cell<R, C, V>> delegateCollection() { - return DenseCellSet.this; - } - }; + @Override public ImmutableMap<R, Map<C, V>> rowMap() { + ImmutableMap<R, Map<C, V>> result = rowMap; + if (result == null) { + rowMap = result = makeRowMap(); } + return result; } } } diff --git a/guava/src/com/google/common/collect/SetMultimap.java b/guava/src/com/google/common/collect/SetMultimap.java index 18f4a18..1409d8d 100644 --- a/guava/src/com/google/common/collect/SetMultimap.java +++ b/guava/src/com/google/common/collect/SetMultimap.java @@ -26,22 +26,13 @@ import javax.annotation.Nullable; /** * A {@code Multimap} that cannot hold duplicate key-value pairs. Adding a - * key-value pair that's already in the multimap has no effect. See the {@link - * Multimap} documentation for information common to all multimaps. + * key-value pair that's already in the multimap has no effect. * * <p>The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods * each return a {@link Set} of values, while {@link #entries} returns a {@code * Set} of map entries. Though the method signature doesn't say so explicitly, * the map returned by {@link #asMap} has {@code Set} values. * - * <p>If the values corresponding to a single key should be ordered according to - * a {@link java.util.Comparator} (or the natural order), see the - * {@link SortedSetMultimap} subinterface. - * - * <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) */ diff --git a/guava/src/com/google/common/collect/Sets.java b/guava/src/com/google/common/collect/Sets.java index 95298e7..14ad867 100644 --- a/guava/src/com/google/common/collect/Sets.java +++ b/guava/src/com/google/common/collect/Sets.java @@ -19,11 +19,16 @@ 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.annotations.GwtIncompatible; +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.IOException; import java.io.ObjectInputStream; @@ -39,22 +44,16 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.NavigableSet; import java.util.NoSuchElementException; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.concurrent.CopyOnWriteArraySet; 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 @@ -66,22 +65,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}. * @@ -96,7 +79,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)); } /** @@ -114,25 +97,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); } /** @@ -143,6 +121,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; @@ -367,38 +359,6 @@ public final class Sets { } /** - * Creates an empty {@code CopyOnWriteArraySet} instance. - * - * <p><b>Note:</b> if you need an immutable empty {@link Set}, use - * {@link Collections#emptySet} instead. - * - * @return a new, empty {@code CopyOnWriteArraySet} - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArraySet") - public static <E> CopyOnWriteArraySet<E> newCopyOnWriteArraySet() { - return new CopyOnWriteArraySet<E>(); - } - - /** - * Creates a {@code CopyOnWriteArraySet} instance containing the given elements. - * - * @param elements the elements that the set should contain, in order - * @return a new {@code CopyOnWriteArraySet} containing those elements - * @since 12.0 - */ - @GwtIncompatible("CopyOnWriteArraySet") - public static <E> CopyOnWriteArraySet<E> newCopyOnWriteArraySet( - Iterable<? extends E> elements) { - // We copy elements to an ArrayList first, rather than incurring the - // quadratic cost of adding them to the COWAS directly. - Collection<? extends E> elementsCollection = (elements instanceof Collection) - ? Collections2.cast(elements) - : Lists.newArrayList(elements); - return new CopyOnWriteArraySet<E>(elementsCollection); - } - - /** * Creates an {@code EnumSet} consisting of all enum values that are not in * the specified collection. If the collection is an {@link EnumSet}, this * method has the same behavior as {@link EnumSet#complementOf}. Otherwise, @@ -618,11 +578,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) { @@ -853,13 +808,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. @@ -874,13 +826,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(); @@ -921,145 +881,6 @@ public final class Sets { } /** - * Returns the elements of a {@code NavigableSet}, {@code unfiltered}, that - * satisfy a predicate. The returned set is a live view of {@code unfiltered}; - * changes to one affect the other. - * - * <p>The resulting set's iterator does not support {@code remove()}, but all - * other set methods are supported. When given an element that doesn't satisfy - * the predicate, the set's {@code add()} and {@code addAll()} methods throw - * an {@link IllegalArgumentException}. When methods such as - * {@code removeAll()} and {@code clear()} are called on the filtered set, - * only elements that satisfy the filter will be removed from the underlying - * set. - * - * <p>The returned set isn't threadsafe or serializable, even if - * {@code unfiltered} is. - * - * <p>Many of the filtered set's methods, such as {@code size()}, iterate across - * every element in the underlying set and determine which elements satisfy - * the filter. When a live view is <i>not</i> needed, it may be faster to copy - * {@code Iterables.filter(unfiltered, predicate)} and use the copy. - * - * <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, - * as documented at {@link Predicate#apply}. Do not provide a predicate such as - * {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent with - * equals. (See {@link Iterables#filter(Iterable, Class)} for related - * functionality.) - * - * @since 14.0 - */ - @GwtIncompatible("NavigableSet") - @SuppressWarnings("unchecked") - public static <E> NavigableSet<E> filter( - NavigableSet<E> unfiltered, Predicate<? super E> predicate) { - if (unfiltered instanceof FilteredSet) { - // Support clear(), removeAll(), and retainAll() when filtering a filtered - // collection. - FilteredSet<E> filtered = (FilteredSet<E>) unfiltered; - Predicate<E> combinedPredicate - = Predicates.<E>and(filtered.predicate, predicate); - return new FilteredNavigableSet<E>( - (NavigableSet<E>) filtered.unfiltered, combinedPredicate); - } - - return new FilteredNavigableSet<E>( - checkNotNull(unfiltered), checkNotNull(predicate)); - } - - @GwtIncompatible("NavigableSet") - private static class FilteredNavigableSet<E> extends FilteredSortedSet<E> - implements NavigableSet<E> { - FilteredNavigableSet(NavigableSet<E> unfiltered, Predicate<? super E> predicate) { - super(unfiltered, predicate); - } - - NavigableSet<E> unfiltered() { - return (NavigableSet<E>) unfiltered; - } - - @Override - @Nullable - public E lower(E e) { - return Iterators.getNext(headSet(e, false).descendingIterator(), null); - } - - @Override - @Nullable - public E floor(E e) { - return Iterators.getNext(headSet(e, true).descendingIterator(), null); - } - - @Override - public E ceiling(E e) { - return Iterables.getFirst(tailSet(e, true), null); - } - - @Override - public E higher(E e) { - return Iterables.getFirst(tailSet(e, false), null); - } - - @Override - public E pollFirst() { - Iterator<E> unfilteredIterator = unfiltered().iterator(); - while (unfilteredIterator.hasNext()) { - E e = unfilteredIterator.next(); - if (predicate.apply(e)) { - unfilteredIterator.remove(); - return e; - } - } - return null; - } - - @Override - public E pollLast() { - Iterator<E> unfilteredIterator = unfiltered().descendingIterator(); - while (unfilteredIterator.hasNext()) { - E e = unfilteredIterator.next(); - if (predicate.apply(e)) { - unfilteredIterator.remove(); - return e; - } - } - return null; - } - - @Override - public NavigableSet<E> descendingSet() { - return Sets.filter(unfiltered().descendingSet(), predicate); - } - - @Override - public Iterator<E> descendingIterator() { - return Iterators.filter(unfiltered().descendingIterator(), predicate); - } - - @Override - public E last() { - return descendingIterator().next(); - } - - @Override - public NavigableSet<E> subSet( - E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { - return filter( - unfiltered().subSet(fromElement, fromInclusive, toElement, toInclusive), predicate); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return filter(unfiltered().headSet(toElement, inclusive), predicate); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return filter(unfiltered().tailSet(fromElement, inclusive), predicate); - } - } - - /** * Returns every possible list that can be formed by choosing one element * from each of the given sets in order; the "n-ary * <a href="http://en.wikipedia.org/wiki/Cartesian_product">Cartesian @@ -1080,22 +901,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 @@ -1121,7 +932,8 @@ public final class Sets { return ImmutableSet.of(); } } - return CartesianSet.create(sets); + CartesianSet<B> cartesianSet = new CartesianSet<B>(sets); + return cartesianSet; } /** @@ -1145,22 +957,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 @@ -1184,51 +986,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) { @@ -1241,26 +1065,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; + } + + 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; + } - hash = ~~hash; + @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; } } @@ -1398,9 +1252,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; } @@ -1427,345 +1278,117 @@ public final class Sets { } /** - * Returns an unmodifiable view of the specified navigable set. This method - * allows modules to provide users with "read-only" access to internal - * navigable sets. Query operations on the returned set "read through" to the - * specified set, and attempts to modify the returned set, whether direct or - * via its collection views, result in an - * {@code UnsupportedOperationException}. - * - * <p>The returned navigable set will be serializable if the specified - * navigable set is serializable. - * - * @param set the navigable set for which an unmodifiable view is to be - * returned - * @return an unmodifiable view of the specified navigable set - * @since 12.0 + * 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>. */ - @GwtIncompatible("NavigableSet") - public static <E> NavigableSet<E> unmodifiableNavigableSet( - NavigableSet<E> set) { - if (set instanceof ImmutableSortedSet - || set instanceof UnmodifiableNavigableSet) { - return set; - } - return new UnmodifiableNavigableSet<E>(set); + 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") + ); } - @GwtIncompatible("NavigableSet") - static final class UnmodifiableNavigableSet<E> - extends ForwardingSortedSet<E> implements NavigableSet<E>, Serializable { - private final NavigableSet<E> delegate; - - UnmodifiableNavigableSet(NavigableSet<E> delegate) { - this.delegate = checkNotNull(delegate); - } - - @Override - protected SortedSet<E> delegate() { - return Collections.unmodifiableSortedSet(delegate); - } + /** + * 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); - @Override - public E lower(E e) { - return delegate.lower(e); - } + public InvertibleFunction<B, A> inverse() { + return new InvertibleFunction<B, A>() { + @Override public A apply(B b) { + return InvertibleFunction.this.invert(b); + } - @Override - public E floor(E e) { - return delegate.floor(e); - } + @Override B invert(A a) { + return InvertibleFunction.this.apply(a); + } - @Override - public E ceiling(E e) { - return delegate.ceiling(e); + // Not required per se, but just for good karma. + @Override public InvertibleFunction<A, B> inverse() { + return InvertibleFunction.this; + } + }; } + } - @Override - public E higher(E e) { - return delegate.higher(e); - } + private static class TransformedSet<A, B> extends AbstractSet<B> { + final Set<A> delegate; + final InvertibleFunction<A, B> bijection; - @Override - public E pollFirst() { - throw new UnsupportedOperationException(); + TransformedSet(Set<A> delegate, InvertibleFunction<A, B> bijection) { + this.delegate = delegate; + this.bijection = bijection; } - @Override - public E pollLast() { - throw new UnsupportedOperationException(); + @Override public Iterator<B> iterator() { + return Iterators.transform(delegate.iterator(), bijection); } - private transient UnmodifiableNavigableSet<E> descendingSet; - - @Override - public NavigableSet<E> descendingSet() { - UnmodifiableNavigableSet<E> result = descendingSet; - if (result == null) { - result = descendingSet = new UnmodifiableNavigableSet<E>( - delegate.descendingSet()); - result.descendingSet = this; - } - return result; + @Override public int size() { + return delegate.size(); } - @Override - public Iterator<E> descendingIterator() { - return Iterators.unmodifiableIterator(delegate.descendingIterator()); + @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 NavigableSet<E> subSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return unmodifiableNavigableSet(delegate.subSet( - fromElement, - fromInclusive, - toElement, - toInclusive)); + @Override public boolean add(B b) { + return delegate.add(bijection.invert(b)); } - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return unmodifiableNavigableSet(delegate.headSet(toElement, inclusive)); + @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 NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return unmodifiableNavigableSet( - delegate.tailSet(fromElement, inclusive)); + @Override public void clear() { + delegate.clear(); } - - private static final long serialVersionUID = 0; - } - - /** - * Returns a synchronized (thread-safe) navigable set backed by the specified - * navigable set. In order to guarantee serial access, it is critical that - * <b>all</b> access to the backing navigable set is accomplished - * through the returned navigable set (or its views). - * - * <p>It is imperative that the user manually synchronize on the returned - * sorted set when iterating over it or any of its {@code descendingSet}, - * {@code subSet}, {@code headSet}, or {@code tailSet} views. <pre> {@code - * - * NavigableSet<E> set = synchronizedNavigableSet(new TreeSet<E>()); - * ... - * synchronized (set) { - * // Must be in the synchronized block - * Iterator<E> it = set.iterator(); - * while (it.hasNext()){ - * foo(it.next()); - * } - * }}</pre> - * - * or: <pre> {@code - * - * NavigableSet<E> set = synchronizedNavigableSet(new TreeSet<E>()); - * NavigableSet<E> set2 = set.descendingSet().headSet(foo); - * ... - * synchronized (set) { // Note: set, not set2!!! - * // Must be in the synchronized block - * Iterator<E> it = set2.descendingIterator(); - * while (it.hasNext()) - * foo(it.next()); - * } - * }}</pre> - * - * Failure to follow this advice may result in non-deterministic behavior. - * - * <p>The returned navigable set will be serializable if the specified - * navigable set is serializable. - * - * @param navigableSet the navigable set to be "wrapped" in a synchronized - * navigable set. - * @return a synchronized view of the specified navigable set. - * @since 13.0 - */ - @GwtIncompatible("NavigableSet") - public static <E> NavigableSet<E> synchronizedNavigableSet( - NavigableSet<E> navigableSet) { - return Synchronized.navigableSet(navigableSet); } /** * Remove each element in an iterable from a set. */ - static boolean removeAllImpl(Set<?> set, Iterator<?> iterator) { + static boolean removeAllImpl(Set<?> set, Iterable<?> iterable) { + // TODO(jlevy): Have ForwardingSet.standardRemoveAll() call this method. boolean changed = false; - while (iterator.hasNext()) { - changed |= set.remove(iterator.next()); + for (Object o : iterable) { + changed |= set.remove(o); } return changed; } - - static boolean removeAllImpl(Set<?> set, Collection<?> collection) { - checkNotNull(collection); // for GWT - if (collection instanceof Multiset) { - collection = ((Multiset<?>) collection).elementSet(); - } - /* - * 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()); - } - } - - @GwtIncompatible("NavigableSet") - static class DescendingSet<E> extends ForwardingNavigableSet<E> { - private final NavigableSet<E> forward; - - DescendingSet(NavigableSet<E> forward) { - this.forward = forward; - } - - @Override - protected NavigableSet<E> delegate() { - return forward; - } - - @Override - public E lower(E e) { - return forward.higher(e); - } - - @Override - public E floor(E e) { - return forward.ceiling(e); - } - - @Override - public E ceiling(E e) { - return forward.floor(e); - } - - @Override - public E higher(E e) { - return forward.lower(e); - } - - @Override - public E pollFirst() { - return forward.pollLast(); - } - - @Override - public E pollLast() { - return forward.pollFirst(); - } - - @Override - public NavigableSet<E> descendingSet() { - return forward; - } - - @Override - public Iterator<E> descendingIterator() { - return forward.iterator(); - } - - @Override - public NavigableSet<E> subSet( - E fromElement, - boolean fromInclusive, - E toElement, - boolean toInclusive) { - return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); - } - - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return forward.tailSet(toElement, inclusive).descendingSet(); - } - - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return forward.headSet(fromElement, inclusive).descendingSet(); - } - - @SuppressWarnings("unchecked") - @Override - public Comparator<? super E> comparator() { - Comparator<? super E> forwardComparator = forward.comparator(); - if (forwardComparator == null) { - return (Comparator) Ordering.natural().reverse(); - } else { - return reverse(forwardComparator); - } - } - - // If we inline this, we get a javac error. - private static <T> Ordering<T> reverse(Comparator<T> forward) { - return Ordering.from(forward).reverse(); - } - - @Override - public E first() { - return forward.last(); - } - - @Override - public SortedSet<E> headSet(E toElement) { - return standardHeadSet(toElement); - } - - @Override - public E last() { - return forward.first(); - } - - @Override - public SortedSet<E> subSet(E fromElement, E toElement) { - return standardSubSet(fromElement, toElement); - } - - @Override - public SortedSet<E> tailSet(E fromElement) { - return standardTailSet(fromElement); - } - - @Override - public Iterator<E> iterator() { - return forward.descendingIterator(); - } - - @Override - public Object[] toArray() { - return standardToArray(); - } - - @Override - public <T> T[] toArray(T[] array) { - return standardToArray(array); - } - - @Override - public String toString() { - return standardToString(); - } - } - - /** - * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 - */ - static <T> SortedSet<T> cast(Iterable<T> iterable) { - return (SortedSet<T>) iterable; - } } diff --git a/guava/src/com/google/common/collect/SingletonImmutableBiMap.java b/guava/src/com/google/common/collect/SingletonImmutableBiMap.java deleted file mode 100644 index 2b6f462..0000000 --- a/guava/src/com/google/common/collect/SingletonImmutableBiMap.java +++ /dev/null @@ -1,96 +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 javax.annotation.Nullable; - -/** - * Implementation of {@link ImmutableMap} with exactly one entry. - * - * @author Jesse Wilson - * @author Kevin Bourrillion - */ -@GwtCompatible(serializable = true, emulated = true) -@SuppressWarnings("serial") // uses writeReplace(), not default serialization -final class SingletonImmutableBiMap<K, V> extends ImmutableBiMap<K, V> { - - final transient K singleKey; - final transient V singleValue; - - SingletonImmutableBiMap(K singleKey, V singleValue) { - this.singleKey = singleKey; - this.singleValue = singleValue; - } - - private SingletonImmutableBiMap(K singleKey, V singleValue, - ImmutableBiMap<V, K> inverse) { - this.singleKey = singleKey; - this.singleValue = singleValue; - this.inverse = inverse; - } - - SingletonImmutableBiMap(Entry<K, V> entry) { - this(entry.getKey(), entry.getValue()); - } - - @Override public V get(@Nullable Object key) { - return singleKey.equals(key) ? singleValue : null; - } - - @Override - public int size() { - return 1; - } - - @Override public boolean containsKey(@Nullable Object key) { - return singleKey.equals(key); - } - - @Override public boolean containsValue(@Nullable Object value) { - return singleValue.equals(value); - } - - @Override boolean isPartialView() { - return false; - } - - @Override - ImmutableSet<Entry<K, V>> createEntrySet() { - return ImmutableSet.of(Maps.immutableEntry(singleKey, singleValue)); - } - - @Override - ImmutableSet<K> createKeySet() { - return ImmutableSet.of(singleKey); - } - - transient ImmutableBiMap<V, K> inverse; - - @Override - public ImmutableBiMap<V, K> inverse() { - // racy single-check idiom - ImmutableBiMap<V, K> result = inverse; - if (result == null) { - return inverse = new SingletonImmutableBiMap<V, K>( - singleValue, singleKey, this); - } else { - return result; - } - } -} diff --git a/guava/src/com/google/common/collect/SingletonImmutableList.java b/guava/src/com/google/common/collect/SingletonImmutableList.java index 2e20c41..e546d2b 100644 --- a/guava/src/com/google/common/collect/SingletonImmutableList.java +++ b/guava/src/com/google/common/collect/SingletonImmutableList.java @@ -22,6 +22,7 @@ import com.google.common.annotations.GwtCompatible; import com.google.common.base.Preconditions; import java.util.List; +import java.util.NoSuchElementException; import javax.annotation.Nullable; @@ -55,7 +56,47 @@ final class SingletonImmutableList<E> extends ImmutableList<E> { } @Override public int lastIndexOf(@Nullable Object object) { - return indexOf(object); + return element.equals(object) ? 0 : -1; + } + + @Override public UnmodifiableListIterator<E> listIterator(final int start) { + Preconditions.checkPositionIndex(start, 1); + return new UnmodifiableListIterator<E>() { + + boolean hasNext = start == 0; + + @Override public boolean hasNext() { + return hasNext; + } + + @Override public boolean hasPrevious() { + return !hasNext; + } + + @Override public E next() { + if (!hasNext) { + throw new NoSuchElementException(); + } + hasNext = false; + return element; + } + + @Override public int nextIndex() { + return hasNext ? 0 : 1; + } + + @Override public E previous() { + if (hasNext) { + throw new NoSuchElementException(); + } + hasNext = true; + return element; + } + + @Override public int previousIndex() { + return hasNext ? -1 : 0; + } + }; } @Override @@ -76,7 +117,7 @@ final class SingletonImmutableList<E> extends ImmutableList<E> { return element.equals(object); } - @Override public boolean equals(@Nullable Object object) { + @Override public boolean equals(Object object) { if (object == this) { return true; } diff --git a/guava/src/com/google/common/collect/SingletonImmutableMap.java b/guava/src/com/google/common/collect/SingletonImmutableMap.java new file mode 100644 index 0000000..9dd6e60 --- /dev/null +++ b/guava/src/com/google/common/collect/SingletonImmutableMap.java @@ -0,0 +1,162 @@ +/* + * 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; + +import javax.annotation.Nullable; + +/** + * Implementation of {@link ImmutableMap} with exactly one entry. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ +@GwtCompatible(serializable = true, emulated = true) +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +final class SingletonImmutableMap<K, V> extends ImmutableMap<K, V> { + + final transient K singleKey; + final transient V singleValue; + + private transient Entry<K, V> entry; + + SingletonImmutableMap(K singleKey, V singleValue) { + this.singleKey = singleKey; + this.singleValue = singleValue; + } + + SingletonImmutableMap(Entry<K, V> entry) { + this.entry = entry; + this.singleKey = entry.getKey(); + this.singleValue = entry.getValue(); + } + + private Entry<K, V> entry() { + Entry<K, V> e = entry; + return (e == null) + ? (entry = Maps.immutableEntry(singleKey, singleValue)) : e; + } + + @Override public V get(@Nullable Object key) { + return singleKey.equals(key) ? singleValue : null; + } + + @Override + public int size() { + return 1; + } + + @Override public boolean isEmpty() { + return false; + } + + @Override public boolean containsKey(@Nullable Object key) { + return singleKey.equals(key); + } + + @Override public boolean containsValue(@Nullable Object value) { + return singleValue.equals(value); + } + + @Override boolean isPartialView() { + return false; + } + + private transient ImmutableSet<Entry<K, V>> entrySet; + + @Override public ImmutableSet<Entry<K, V>> entrySet() { + ImmutableSet<Entry<K, V>> es = entrySet; + return (es == null) ? (entrySet = ImmutableSet.of(entry())) : es; + } + + private transient ImmutableSet<K> keySet; + + @Override public ImmutableSet<K> keySet() { + ImmutableSet<K> ks = keySet; + return (ks == null) ? (keySet = ImmutableSet.of(singleKey)) : ks; + } + + private transient ImmutableCollection<V> values; + + @Override public ImmutableCollection<V> values() { + ImmutableCollection<V> v = values; + return (v == null) ? (values = new Values<V>(singleValue)) : v; + } + + @SuppressWarnings("serial") // uses writeReplace(), not default serialization + private static class Values<V> extends ImmutableCollection<V> { + final V singleValue; + + Values(V singleValue) { + this.singleValue = singleValue; + } + + @Override public boolean contains(Object object) { + return singleValue.equals(object); + } + + @Override public boolean isEmpty() { + return false; + } + + @Override + public int size() { + return 1; + } + + @Override public UnmodifiableIterator<V> iterator() { + return Iterators.singletonIterator(singleValue); + } + + @Override boolean isPartialView() { + return true; + } + } + + @Override public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + if (object instanceof Map) { + Map<?, ?> that = (Map<?, ?>) object; + if (that.size() != 1) { + return false; + } + Entry<?, ?> entry = that.entrySet().iterator().next(); + return singleKey.equals(entry.getKey()) + && singleValue.equals(entry.getValue()); + } + return false; + } + + @Override public int hashCode() { + return singleKey.hashCode() ^ singleValue.hashCode(); + } + + @Override public String toString() { + return new StringBuilder() + .append('{') + .append(singleKey.toString()) + .append('=') + .append(singleValue.toString()) + .append('}') + .toString(); + } +} diff --git a/guava/src/com/google/common/collect/SingletonImmutableTable.java b/guava/src/com/google/common/collect/SingletonImmutableTable.java index e6e850c..4e33aea 100644 --- a/guava/src/com/google/common/collect/SingletonImmutableTable.java +++ b/guava/src/com/google/common/collect/SingletonImmutableTable.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. @@ -28,7 +28,7 @@ import javax.annotation.Nullable; /** * An implementation of {@link ImmutableTable} that holds a single cell. * - * @author Gregory Kick + * @author gak@google.com (Gregory Kick) */ @GwtCompatible final class SingletonImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { @@ -119,7 +119,7 @@ final class SingletonImmutableTable<R, C, V> extends ImmutableTable<R, C, V> { @Override public boolean equals(@Nullable Object obj) { if (obj == this) { return true; - } else if (obj instanceof Table) { + } else if (obj instanceof Table<?, ?, ?>) { Table<?, ?, ?> that = (Table<?, ?, ?>) obj; if (that.size() == 1) { Cell<?, ?, ?> thatCell = that.cellSet().iterator().next(); diff --git a/guava/src/com/google/common/collect/SortedIterables.java b/guava/src/com/google/common/collect/SortedIterables.java index 2158e9f..8089b10 100644 --- a/guava/src/com/google/common/collect/SortedIterables.java +++ b/guava/src/com/google/common/collect/SortedIterables.java @@ -17,8 +17,16 @@ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.collect.Multiset.Entry; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import java.util.SortedSet; /** @@ -39,22 +47,149 @@ final class SortedIterables { checkNotNull(elements); Comparator<?> comparator2; if (elements instanceof SortedSet) { - comparator2 = comparator((SortedSet<?>) elements); + SortedSet<?> sortedSet = (SortedSet<?>) elements; + comparator2 = sortedSet.comparator(); + if (comparator2 == null) { + comparator2 = (Comparator) Ordering.natural(); + } } else if (elements instanceof SortedIterable) { comparator2 = ((SortedIterable<?>) elements).comparator(); } else { - return false; + comparator2 = null; } return comparator.equals(comparator2); } + /** + * Returns a sorted collection of the unique elements according to the specified comparator. Does + * not check for null. + */ + @SuppressWarnings("unchecked") + public static <E> Collection<E> sortedUnique( + Comparator<? super E> comparator, Iterator<E> elements) { + SortedSet<E> sortedSet = Sets.newTreeSet(comparator); + Iterators.addAll(sortedSet, elements); + return sortedSet; + } + + /** + * Returns a sorted collection of the unique elements according to the specified comparator. Does + * not check for null. + */ @SuppressWarnings("unchecked") - // if sortedSet.comparator() is null, the set must be naturally ordered - public static <E> Comparator<? super E> comparator(SortedSet<E> sortedSet) { - Comparator<? super E> result = sortedSet.comparator(); - if (result == null) { - result = (Comparator<? super E>) Ordering.natural(); + public static <E> Collection<E> sortedUnique( + Comparator<? super E> comparator, Iterable<E> elements) { + if (elements instanceof Multiset) { + elements = ((Multiset<E>) elements).elementSet(); + } + if (elements instanceof Set) { + if (hasSameComparator(comparator, elements)) { + return (Set<E>) elements; + } + List<E> list = Lists.newArrayList(elements); + Collections.sort(list, comparator); + return list; + } + E[] array = (E[]) Iterables.toArray(elements); + if (!hasSameComparator(comparator, elements)) { + Arrays.sort(array, comparator); + } + return uniquifySortedArray(comparator, array); + } + + private static <E> Collection<E> uniquifySortedArray( + Comparator<? super E> comparator, E[] array) { + if (array.length == 0) { + return Collections.emptySet(); } - return result; + int length = 1; + for (int i = 1; i < array.length; i++) { + int cmp = comparator.compare(array[i], array[length - 1]); + if (cmp != 0) { + array[length++] = array[i]; + } + } + if (length < array.length) { + array = ObjectArrays.arraysCopyOf(array, length); + } + return Arrays.asList(array); + } + + /** + * Returns a collection of multiset entries representing the counts of the distinct elements, in + * sorted order. Does not check for null. + */ + public static <E> Collection<Multiset.Entry<E>> sortedCounts( + Comparator<? super E> comparator, Iterator<E> elements) { + TreeMultiset<E> multiset = TreeMultiset.create(comparator); + Iterators.addAll(multiset, elements); + return multiset.entrySet(); + } + + /** + * Returns a collection of multiset entries representing the counts of the distinct elements, in + * sorted order. Does not check for null. + */ + public static <E> Collection<Multiset.Entry<E>> sortedCounts( + Comparator<? super E> comparator, Iterable<E> elements) { + if (elements instanceof Multiset) { + Multiset<E> multiset = (Multiset<E>) elements; + if (hasSameComparator(comparator, elements)) { + return multiset.entrySet(); + } + List<Multiset.Entry<E>> entries = Lists.newArrayList(multiset.entrySet()); + Collections.sort( + entries, Ordering.from(comparator).onResultOf(new Function<Multiset.Entry<E>, E>() { + @Override + public E apply(Entry<E> entry) { + return entry.getElement(); + } + })); + return entries; + } else if (elements instanceof Set) { // known distinct + Collection<E> sortedElements; + if (hasSameComparator(comparator, elements)) { + sortedElements = (Collection<E>) elements; + } else { + List<E> list = Lists.newArrayList(elements); + Collections.sort(list, comparator); + sortedElements = list; + } + return singletonEntries(sortedElements); + } else if (hasSameComparator(comparator, elements)) { + E current = null; + int currentCount = 0; + List<Multiset.Entry<E>> sortedEntries = Lists.newArrayList(); + for (E e : elements) { + if (currentCount > 0) { + if (comparator.compare(current, e) == 0) { + currentCount++; + } else { + sortedEntries.add(Multisets.immutableEntry(current, currentCount)); + current = e; + currentCount = 1; + } + } else { + current = e; + currentCount = 1; + } + } + if (currentCount > 0) { + sortedEntries.add(Multisets.immutableEntry(current, currentCount)); + } + return sortedEntries; + } + TreeMultiset<E> multiset = TreeMultiset.create(comparator); + Iterables.addAll(multiset, elements); + return multiset.entrySet(); + } + + static <E> Collection<Multiset.Entry<E>> singletonEntries(Collection<E> set) { + return Collections2.transform(set, new Function<E, Multiset.Entry<E>>() { + @Override + public Entry<E> apply(E elem) { + return Multisets.immutableEntry(elem, 1); + } + }); } } diff --git a/guava/src/com/google/common/collect/SortedLists.java b/guava/src/com/google/common/collect/SortedLists.java index 6fc0d71..afcec59 100644 --- a/guava/src/com/google/common/collect/SortedLists.java +++ b/guava/src/com/google/common/collect/SortedLists.java @@ -143,7 +143,7 @@ import javax.annotation.Nullable; */ NEXT_LOWER { @Override - int resultIndex(int higherIndex) { + <E> int resultIndex(int higherIndex) { return higherIndex - 1; } }, @@ -153,7 +153,7 @@ import javax.annotation.Nullable; */ NEXT_HIGHER { @Override - public int resultIndex(int higherIndex) { + public <E> int resultIndex(int higherIndex) { return higherIndex; } }, @@ -171,12 +171,12 @@ import javax.annotation.Nullable; */ INVERTED_INSERTION_INDEX { @Override - public int resultIndex(int higherIndex) { + public <E> int resultIndex(int higherIndex) { return ~higherIndex; } }; - abstract int resultIndex(int higherIndex); + abstract <E> int resultIndex(int higherIndex); } /** @@ -200,7 +200,7 @@ import javax.annotation.Nullable; * KeyAbsentBehavior)} using {@link Ordering#natural}. */ public static <E, K extends Comparable> int binarySearch(List<E> list, - Function<? super E, K> keyFunction, @Nullable K key, KeyPresentBehavior presentBehavior, + Function<? super E, K> keyFunction, K key, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { return binarySearch( list, @@ -221,7 +221,7 @@ import javax.annotation.Nullable; public static <E, K> int binarySearch( List<E> list, Function<? super E, K> keyFunction, - @Nullable K key, + K key, Comparator<? super K> keyComparator, KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) { diff --git a/guava/src/com/google/common/collect/SortedMapDifference.java b/guava/src/com/google/common/collect/SortedMapDifference.java index 07f75ee..f44aa1a 100644 --- a/guava/src/com/google/common/collect/SortedMapDifference.java +++ b/guava/src/com/google/common/collect/SortedMapDifference.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.SortedMap; @@ -26,6 +27,7 @@ import java.util.SortedMap; * @author Louis Wasserman * @since 8.0 */ +@Beta @GwtCompatible public interface SortedMapDifference<K, V> extends MapDifference<K, V> { diff --git a/guava/src/com/google/common/collect/SortedMaps.java b/guava/src/com/google/common/collect/SortedMaps.java new file mode 100644 index 0000000..0055cbd --- /dev/null +++ b/guava/src/com/google/common/collect/SortedMaps.java @@ -0,0 +1,374 @@ +/* + * 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.checkNotNull; + +import java.util.Comparator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; + +import javax.annotation.Nullable; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Maps.EntryTransformer; + +/** + * Static utility methods pertaining to {@link SortedMap} instances. + * + * @author Louis Wasserman + * @since 8.0 + * @deprecated Use the identical methods in {@link Maps}. This class is + * scheduled for deletion from Guava in Guava release 12.0. + */ +@Beta +@Deprecated +@GwtCompatible +public final class SortedMaps { + private SortedMaps() {} + + /** + * Returns a view of a sorted map where each value is transformed by a + * function. All other properties of the map, such as iteration order, are + * left intact. For example, the code: <pre> {@code + * + * SortedMap<String, Integer> map = ImmutableSortedMap.of("a", 4, "b", 9); + * Function<Integer, Double> sqrt = + * new Function<Integer, Double>() { + * public Double apply(Integer in) { + * return Math.sqrt((int) in); + * } + * }; + * SortedMap<String, Double> transformed = + * Maps.transformSortedValues(map, sqrt); + * System.out.println(transformed);}</pre> + * + * ... prints {@code {a=2.0, b=3.0}}. + * + * <p>Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + * <p>It's acceptable for the underlying map to contain null keys, and even + * null values provided that the function is capable of accepting null input. + * The transformed map might contain null values, if the function sometimes + * gives a null result. + * + * <p>The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + * <p>The function is applied lazily, invoked when needed. This is necessary + * for the returned map to be a view, but it means that the function will be + * applied many times for bulk operations like {@link Map#containsValue} and + * {@code Map.toString()}. For this to perform well, {@code function} should + * be fast. To avoid lazy evaluation when the returned map doesn't need to be + * a view, copy the returned map into a new map of your choosing. + * + * @deprecated Use {@link Maps#transformValues(SortedMap, Function)} + */ + @Deprecated public static <K, V1, V2> SortedMap<K, V2> transformValues( + SortedMap<K, V1> fromMap, final Function<? super V1, V2> function) { + return Maps.transformValues(fromMap, function); + } + + /** + * Returns a view of a sorted map whose values are derived from the original + * sorted map's entries. In contrast to {@link #transformValues}, this + * method's entry-transformation logic may depend on the key as well as the + * value. + * + * <p>All other properties of the transformed map, such as iteration order, + * are left intact. For example, the code: <pre> {@code + * + * Map<String, Boolean> options = + * ImmutableSortedMap.of("verbose", true, "sort", false); + * EntryTransformer<String, Boolean, String> flagPrefixer = + * new EntryTransformer<String, Boolean, String>() { + * public String transformEntry(String key, Boolean value) { + * return value ? key : "yes" + key; + * } + * }; + * SortedMap<String, String> transformed = + * LabsMaps.transformSortedEntries(options, flagPrefixer); + * System.out.println(transformed);}</pre> + * + * ... prints {@code {sort=yessort, verbose=verbose}}. + * + * <p>Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + * <p>It's acceptable for the underlying map to contain null keys and null + * values provided that the transformer is capable of accepting null inputs. + * The transformed map might contain null values if the transformer sometimes + * gives a null result. + * + * <p>The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + * <p>The transformer is applied lazily, invoked when needed. This is + * necessary for the returned map to be a view, but it means that the + * transformer will be applied many times for bulk operations like {@link + * Map#containsValue} and {@link Object#toString}. For this to perform well, + * {@code transformer} should be fast. To avoid lazy evaluation when the + * returned map doesn't need to be a view, copy the returned map into a new + * map of your choosing. + * + * <p><b>Warning:</b> This method assumes that for any instance {@code k} of + * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies + * that {@code k2} is also of type {@code K}. Using an {@code + * EntryTransformer} key type for which this may not hold, such as {@code + * ArrayList}, may risk a {@code ClassCastException} when calling methods on + * the transformed map. + * + * @deprecated Use {@link Maps#transformEntries(SortedMap, EntryTransformer)} + */ + @Deprecated public static <K, V1, V2> SortedMap<K, V2> transformEntries( + final SortedMap<K, V1> fromMap, + EntryTransformer<? super K, ? super V1, V2> transformer) { + return Maps.transformEntries(fromMap, transformer); + } + + /** + * Computes the difference between two sorted maps, using the comparator of + * the left map, or {@code Ordering.natural()} if the left map uses the + * natural ordering of its elements. 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. + * + * <p>Since this method uses {@code TreeMap} instances internally, the keys of + * the right map must all compare as distinct according to the comparator + * of the left map. + * + * <p><b>Note:</b>If you only need to know whether two sorted maps have the + * same mappings, call {@code left.equals(right)} instead of this method. + * + * @param left the map to treat as the "left" map for purposes of comparison + * @param right the map to treat as the "right" map for purposes of comparison + * @return the difference between the two maps + * @deprecated Use {@link Maps#difference(SortedMap, Map)} + */ + @Deprecated public static <K, V> SortedMapDifference<K, V> difference( + SortedMap<K, ? extends V> left, Map<? extends K, ? extends V> right) { + return Maps.difference(left, right); + } + + /** + * Returns the specified comparator if not null; otherwise returns {@code + * Ordering.natural()}. This method is an abomination of generics; the only + * purpose of this method is to contain the ugly type-casting in one place. + */ + @SuppressWarnings("unchecked") + static <E> Comparator<? super E> orNaturalOrder( + @Nullable Comparator<? super E> comparator) { + if (comparator != null) { // can't use ? : because of javac bug 5080917 + return comparator; + } + return (Comparator<E>) Ordering.natural(); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose + * keys satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + * <p>The resulting map'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 map and its views. When given a key that + * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} + * methods throw an {@link IllegalArgumentException}. + * + * <p>When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose keys satisfy the + * filter will be removed from the underlying map. + * + * <p>The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + * <p>Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is <i>not</i> needed, it may be + * faster to copy the filtered map and use the copy. + * + * <p><b>Warning:</b> {@code keyPredicate} must be <i>consistent with + * equals</i>, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + */ + @GwtIncompatible("untested") + public static <K, V> SortedMap<K, V> filterKeys( + SortedMap<K, V> unfiltered, final Predicate<? super K> keyPredicate) { + // TODO: Return a subclass of Maps.FilteredKeyMap for slightly better + // performance. + checkNotNull(keyPredicate); + Predicate<Entry<K, V>> entryPredicate = new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return keyPredicate.apply(input.getKey()); + } + }; + return filterEntries(unfiltered, entryPredicate); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose + * values satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + * <p>The resulting map'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 map and its views. When given a value + * that doesn't satisfy the predicate, the map's {@code put()}, {@code + * putAll()}, and {@link Entry#setValue} methods throw an {@link + * IllegalArgumentException}. + * + * <p>When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose values satisfy the + * filter will be removed from the underlying map. + * + * <p>The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + * <p>Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is <i>not</i> needed, it may be + * faster to copy the filtered map and use the copy. + * + * <p><b>Warning:</b> {@code valuePredicate} must be <i>consistent with + * equals</i>, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + */ + @GwtIncompatible("untested") + public static <K, V> SortedMap<K, V> filterValues( + SortedMap<K, V> unfiltered, final Predicate<? super V> valuePredicate) { + checkNotNull(valuePredicate); + Predicate<Entry<K, V>> entryPredicate = + new Predicate<Entry<K, V>>() { + @Override + public boolean apply(Entry<K, V> input) { + return valuePredicate.apply(input.getValue()); + } + }; + return filterEntries(unfiltered, entryPredicate); + } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} that + * satisfy a predicate. The returned map is a live view of {@code unfiltered}; + * changes to one affect the other. + * + * <p>The resulting map'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 map and its views. When given a + * key/value pair that doesn't satisfy the predicate, the map's {@code put()} + * 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 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 map or its views, only mappings that satisfy the filter + * will be removed from the underlying map. + * + * <p>The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + * <p>Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is <i>not</i> needed, it may be + * faster to copy the filtered map and use the copy. + * + * <p><b>Warning:</b> {@code entryPredicate} must be <i>consistent with + * equals</i>, as documented at {@link Predicate#apply}. + */ + @GwtIncompatible("untested") + public static <K, V> SortedMap<K, V> filterEntries( + SortedMap<K, V> unfiltered, + Predicate<? super Entry<K, V>> entryPredicate) { + checkNotNull(entryPredicate); + return (unfiltered instanceof FilteredSortedMap) + ? filterFiltered((FilteredSortedMap<K, V>) unfiltered, entryPredicate) + : new FilteredSortedMap<K, V>(checkNotNull(unfiltered), entryPredicate); + } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when + * filtering a filtered sorted map. + */ + private static <K, V> SortedMap<K, V> filterFiltered( + FilteredSortedMap<K, V> map, + Predicate<? super Entry<K, V>> entryPredicate) { + Predicate<Entry<K, V>> predicate + = Predicates.and(map.predicate, entryPredicate); + return new FilteredSortedMap<K, V>(map.sortedMap(), predicate); + } + + private static class FilteredSortedMap<K, V> + extends Maps.FilteredEntryMap<K, V> implements SortedMap<K, V> { + + FilteredSortedMap(SortedMap<K, V> unfiltered, + Predicate<? super Entry<K, V>> entryPredicate) { + super(unfiltered, entryPredicate); + } + + SortedMap<K, V> sortedMap() { + return (SortedMap<K, V>) unfiltered; + } + + @Override public Comparator<? super K> comparator() { + return sortedMap().comparator(); + } + + @Override public K firstKey() { + // correctly throws NoSuchElementException when filtered map is empty. + return keySet().iterator().next(); + } + + @Override public K lastKey() { + SortedMap<K, V> headMap = sortedMap(); + while (true) { + // correctly throws NoSuchElementException when filtered map is empty. + K key = headMap.lastKey(); + if (apply(key, unfiltered.get(key))) { + return key; + } + headMap = sortedMap().headMap(key); + } + } + + @Override public SortedMap<K, V> headMap(K toKey) { + return new FilteredSortedMap<K, V>(sortedMap().headMap(toKey), predicate); + } + + @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { + return new FilteredSortedMap<K, V>( + sortedMap().subMap(fromKey, toKey), predicate); + } + + @Override public SortedMap<K, V> tailMap(K fromKey) { + return new FilteredSortedMap<K, V>( + sortedMap().tailMap(fromKey), predicate); + } + } +} diff --git a/guava/src/com/google/common/collect/SortedMultiset.java b/guava/src/com/google/common/collect/SortedMultiset.java index 9e14a80..0713151 100644 --- a/guava/src/com/google/common/collect/SortedMultiset.java +++ b/guava/src/com/google/common/collect/SortedMultiset.java @@ -22,7 +22,7 @@ import com.google.common.annotations.GwtCompatible; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; -import java.util.NavigableSet; +import java.util.SortedSet; /** * A {@link Multiset} which maintains the ordering of its elements, according to @@ -36,16 +36,12 @@ import java.util.NavigableSet; * resulting multiset will violate the {@link Collection} contract, which it is * specified in terms of {@link Object#equals}. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset"> - * {@code Multiset}</a>. - * * @author Louis Wasserman * @since 11.0 */ @Beta -@GwtCompatible(emulated = true) -public interface SortedMultiset<E> extends SortedMultisetBridge<E>, SortedIterable<E> { +@GwtCompatible +public interface SortedMultiset<E> extends Multiset<E>, SortedIterable<E> { /** * Returns the comparator that orders this multiset, or * {@link Ordering#natural()} if the natural ordering of the elements is used. @@ -77,11 +73,9 @@ public interface SortedMultiset<E> extends SortedMultisetBridge<E>, SortedIterab Entry<E> pollLastEntry(); /** - * Returns a {@link NavigableSet} view of the distinct elements in this multiset. - * - * @since 14.0 (present with return type {@code SortedSet} since 11.0) + * Returns a {@link SortedSet} view of the distinct elements in this multiset. */ - @Override NavigableSet<E> elementSet(); + @Override SortedSet<E> elementSet(); /** * {@inheritDoc} diff --git a/guava/src/com/google/common/collect/SortedMultisetBridge.java b/guava/src/com/google/common/collect/SortedMultisetBridge.java deleted file mode 100644 index 669b54d..0000000 --- a/guava/src/com/google/common/collect/SortedMultisetBridge.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.SortedSet; - -/** - * Superinterface of {@link SortedMultiset} to introduce a bridge method for - * {@code elementSet()}, to ensure binary compatibility with older Guava versions - * that specified {@code elementSet()} to return {@code SortedSet}. - * - * @author Louis Wasserman - */ -interface SortedMultisetBridge<E> extends Multiset<E> { - @Override - SortedSet<E> elementSet(); -} diff --git a/guava/src/com/google/common/collect/SortedMultisets.java b/guava/src/com/google/common/collect/SortedMultisets.java index 1055664..ff18b74 100644 --- a/guava/src/com/google/common/collect/SortedMultisets.java +++ b/guava/src/com/google/common/collect/SortedMultisets.java @@ -1,12 +1,12 @@ /* * 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 @@ -16,28 +16,22 @@ 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.annotations.GwtIncompatible; import com.google.common.collect.Multiset.Entry; import java.util.Comparator; import java.util.Iterator; -import java.util.NavigableSet; import java.util.NoSuchElementException; +import java.util.Set; 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) +@GwtCompatible final class SortedMultisets { private SortedMultisets() { } @@ -45,32 +39,26 @@ final class SortedMultisets { /** * A skeleton implementation for {@link SortedMultiset#elementSet}. */ - static class ElementSet<E> extends Multisets.ElementSet<E> implements + static abstract 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 abstract SortedMultiset<E> 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(); + return multiset().subMultiset(fromElement, BoundType.CLOSED, toElement, + BoundType.OPEN).elementSet(); } @Override public SortedSet<E> headSet(E toElement) { - return multiset().headMultiset(toElement, OPEN).elementSet(); + return multiset().headMultiset(toElement, BoundType.OPEN).elementSet(); } @Override public SortedSet<E> tailSet(E fromElement) { - return multiset().tailMultiset(fromElement, CLOSED).elementSet(); + return multiset().tailMultiset(fromElement, BoundType.CLOSED) + .elementSet(); } @Override public E first() { @@ -82,84 +70,127 @@ final class SortedMultisets { } } + private static <E> E getElementOrThrow(Entry<E> entry) { + if (entry == null) { + throw new NoSuchElementException(); + } + return entry.getElement(); + } + /** - * A skeleton navigable implementation for {@link SortedMultiset#elementSet}. + * A skeleton implementation of a descending multiset. Only needs + * {@code forwardMultiset()} and {@code entryIterator()}. */ - @GwtIncompatible("Navigable") - static class NavigableElementSet<E> extends ElementSet<E> implements NavigableSet<E> { - NavigableElementSet(SortedMultiset<E> multiset) { - super(multiset); + static 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; } - @Override - public E lower(E e) { - return getElementOrNull(multiset().headMultiset(e, OPEN).lastEntry()); + private transient SortedSet<E> elementSet; + + @Override public SortedSet<E> elementSet() { + SortedSet<E> result = elementSet; + if (result == null) { + return elementSet = new SortedMultisets.ElementSet<E>() { + @Override SortedMultiset<E> multiset() { + return DescendingMultiset.this; + } + }; + } + return result; } - @Override - public E floor(E e) { - return getElementOrNull(multiset().headMultiset(e, CLOSED).lastEntry()); + @Override public Entry<E> pollFirstEntry() { + return forwardMultiset().pollLastEntry(); } - @Override - public E ceiling(E e) { - return getElementOrNull(multiset().tailMultiset(e, CLOSED).firstEntry()); + @Override public Entry<E> pollLastEntry() { + return forwardMultiset().pollFirstEntry(); } - @Override - public E higher(E e) { - return getElementOrNull(multiset().tailMultiset(e, OPEN).firstEntry()); + @Override public SortedMultiset<E> headMultiset(E toElement, + BoundType boundType) { + return forwardMultiset().tailMultiset(toElement, boundType) + .descendingMultiset(); } - @Override - public NavigableSet<E> descendingSet() { - return new NavigableElementSet<E>(multiset().descendingMultiset()); + @Override public SortedMultiset<E> subMultiset(E fromElement, + BoundType fromBoundType, E toElement, BoundType toBoundType) { + return forwardMultiset().subMultiset(toElement, toBoundType, fromElement, + fromBoundType).descendingMultiset(); } - @Override - public Iterator<E> descendingIterator() { - return descendingSet().iterator(); + @Override public SortedMultiset<E> tailMultiset(E fromElement, + BoundType boundType) { + return forwardMultiset().headMultiset(fromElement, boundType) + .descendingMultiset(); } - @Override - public E pollFirst() { - return getElementOrNull(multiset().pollFirstEntry()); + @Override protected Multiset<E> delegate() { + return forwardMultiset(); } - @Override - public E pollLast() { - return getElementOrNull(multiset().pollLastEntry()); + @Override public SortedMultiset<E> descendingMultiset() { + return forwardMultiset(); } - @Override - public NavigableSet<E> subSet( - E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { - return new NavigableElementSet<E>(multiset().subMultiset( - fromElement, BoundType.forBoolean(fromInclusive), - toElement, BoundType.forBoolean(toInclusive))); + @Override public Entry<E> firstEntry() { + return forwardMultiset().lastEntry(); } - @Override - public NavigableSet<E> headSet(E toElement, boolean inclusive) { - return new NavigableElementSet<E>( - multiset().headMultiset(toElement, BoundType.forBoolean(inclusive))); + @Override public Entry<E> lastEntry() { + return forwardMultiset().firstEntry(); } - @Override - public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - return new NavigableElementSet<E>( - multiset().tailMultiset(fromElement, BoundType.forBoolean(inclusive))); + 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; } - } - private static <E> E getElementOrThrow(Entry<E> entry) { - if (entry == null) { - throw new NoSuchElementException(); + 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); } - return entry.getElement(); - } - private static <E> E getElementOrNull(@Nullable Entry<E> entry) { - return (entry == null) ? null : entry.getElement(); + @Override public String toString() { + return entrySet().toString(); + } } } diff --git a/guava/src/com/google/common/collect/SortedSetMultimap.java b/guava/src/com/google/common/collect/SortedSetMultimap.java index 1b9c641..4785de3 100644 --- a/guava/src/com/google/common/collect/SortedSetMultimap.java +++ b/guava/src/com/google/common/collect/SortedSetMultimap.java @@ -31,18 +31,13 @@ import javax.annotation.Nullable; * that is, they comprise a {@link SortedSet}. It cannot hold duplicate * key-value pairs; adding a key-value pair that's already in the multimap has * no effect. This interface does not specify the ordering of the multimap's - * keys. See the {@link Multimap} documentation for information common to all - * multimaps. + * keys. * * <p>The {@link #get}, {@link #removeAll}, and {@link #replaceValues} methods * each return a {@link SortedSet} of values, while {@link Multimap#entries()} * returns a {@link Set} of map entries. Though the method signature doesn't say * so explicitly, the map returned by {@link #asMap} has {@code SortedSet} * values. - * - * <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) diff --git a/guava/src/com/google/common/collect/StandardTable.java b/guava/src/com/google/common/collect/StandardTable.java index 22c0a90..5edee5b 100644 --- a/guava/src/com/google/common/collect/StandardTable.java +++ b/guava/src/com/google/common/collect/StandardTable.java @@ -391,13 +391,17 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { @Override public V remove(Object key) { - Map<C, V> backingRowMap = backingRowMap(); - if (backingRowMap == null) { + try { + Map<C, V> backingRowMap = backingRowMap(); + if (backingRowMap == null) { + return null; + } + V result = backingRowMap.remove(key); + maintainEmptyInvariant(); + return result; + } catch (ClassCastException e) { return null; } - V result = Maps.safeRemove(backingRowMap, key); - maintainEmptyInvariant(); - return result; } @Override @@ -555,7 +559,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return changed; } - class EntrySet extends Sets.ImprovedAbstractSet<Entry<R, V>> { + class EntrySet extends AbstractSet<Entry<R, V>> { @Override public Iterator<Entry<R, V>> iterator() { return new EntrySetIterator(); } @@ -595,6 +599,14 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return false; } + @Override public boolean removeAll(Collection<?> c) { + boolean changed = false; + for (Object obj : c) { + changed |= remove(obj); + } + return changed; + } + @Override public boolean retainAll(Collection<?> c) { return removePredicate(Predicates.not(Predicates.in(c))); } @@ -631,9 +643,9 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return result == null ? keySet = new KeySet() : result; } - class KeySet extends Sets.ImprovedAbstractSet<R> { + class KeySet extends AbstractSet<R> { @Override public Iterator<R> iterator() { - return Maps.keyIterator(Column.this.entrySet().iterator()); + return keyIteratorImpl(Column.this); } @Override public int size() { @@ -656,6 +668,14 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { entrySet().clear(); } + @Override public boolean removeAll(final Collection<?> c) { + boolean changed = false; + for (Object obj : c) { + changed |= remove(obj); + } + return changed; + } + @Override public boolean retainAll(final Collection<?> c) { checkNotNull(c); Predicate<Entry<R, V>> predicate = new Predicate<Entry<R, V>>() { @@ -670,7 +690,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class Values extends AbstractCollection<V> { @Override public Iterator<V> iterator() { - return Maps.valueIterator(Column.this.entrySet().iterator()); + return valueIteratorImpl(Column.this); } @Override public int size() { @@ -736,7 +756,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class RowKeySet extends TableSet<R> { @Override public Iterator<R> iterator() { - return Maps.keyIterator(rowMap().entrySet().iterator()); + return keyIteratorImpl(rowMap()); } @Override public int size() { @@ -890,10 +910,16 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { private class Values extends TableCollection<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(); + final Iterator<Cell<R, C, V>> cellIterator = cellSet().iterator(); + return new Iterator<V>() { + @Override public boolean hasNext() { + return cellIterator.hasNext(); + } + @Override public V next() { + return cellIterator.next().getValue(); + } + @Override public void remove() { + cellIterator.remove(); } }; } @@ -935,13 +961,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class EntrySet extends TableSet<Entry<R, Map<C, V>>> { @Override public Iterator<Entry<R, Map<C, V>>> iterator() { - return new TransformedIterator<R, Entry<R, Map<C, V>>>( - backingMap.keySet().iterator()) { - @Override - Entry<R, Map<C, V>> transform(R rowKey) { - return new ImmutableEntry<R, Map<C, V>>(rowKey, row(rowKey)); - } - }; + return new EntryIterator(); } @Override public int size() { @@ -968,6 +988,23 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { return false; } } + + class EntryIterator implements Iterator<Entry<R, Map<C, V>>> { + final Iterator<R> delegate = backingMap.keySet().iterator(); + + @Override public boolean hasNext() { + return delegate.hasNext(); + } + + @Override public Entry<R, Map<C, V>> next() { + R rowKey = delegate.next(); + return new ImmutableEntry<R, Map<C, V>>(rowKey, row(rowKey)); + } + + @Override public void remove() { + delegate.remove(); + } + } } private transient ColumnMap columnMap; @@ -1011,10 +1048,13 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { class ColumnMapEntrySet extends TableSet<Entry<C, Map<R, V>>> { @Override public Iterator<Entry<C, Map<R, V>>> iterator() { - return new TransformedIterator<C, Entry<C, Map<R, V>>>( - columnKeySet().iterator()) { - @Override - Entry<C, Map<R, V>> transform(C columnKey) { + final Iterator<C> columnIterator = columnKeySet().iterator(); + return new UnmodifiableIterator<Entry<C, Map<R, V>>>() { + @Override public boolean hasNext() { + return columnIterator.hasNext(); + } + @Override public Entry<C, Map<R, V>> next() { + C columnKey = columnIterator.next(); return new ImmutableEntry<C, Map<R, V>>( columnKey, column(columnKey)); } @@ -1071,7 +1111,7 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { private class ColumnMapValues extends TableCollection<Map<R, V>> { @Override public Iterator<Map<R, V>> iterator() { - return Maps.valueIterator(ColumnMap.this.entrySet().iterator()); + return valueIteratorImpl(ColumnMap.this); } @Override public boolean remove(Object obj) { @@ -1115,4 +1155,44 @@ class StandardTable<R, C, V> implements Table<R, C, V>, Serializable { } private static final long serialVersionUID = 0; + + // TODO(kevinb): Move keyIteratorImpl and valueIteratorImpl to Maps, reuse + + /** + * Generates the iterator of a map's key set from the map's entry set + * iterator. + */ + static <K, V> Iterator<K> keyIteratorImpl(Map<K, V> map) { + final Iterator<Entry<K, V>> entryIterator = map.entrySet().iterator(); + return new Iterator<K>() { + @Override public boolean hasNext() { + return entryIterator.hasNext(); + } + @Override public K next() { + return entryIterator.next().getKey(); + } + @Override public void remove() { + entryIterator.remove(); + } + }; + } + + /** + * Generates the iterator of a map's value collection from the map's entry set + * iterator. + */ + static <K, V> Iterator<V> valueIteratorImpl(Map<K, V> map) { + final Iterator<Entry<K, V>> entryIterator = map.entrySet().iterator(); + return new Iterator<V>() { + @Override public boolean hasNext() { + return entryIterator.hasNext(); + } + @Override public V next() { + return entryIterator.next().getValue(); + } + @Override public void remove() { + entryIterator.remove(); + } + }; + } } diff --git a/guava/src/com/google/common/collect/Synchronized.java b/guava/src/com/google/common/collect/Synchronized.java index bbf4c1e..c021c55 100644 --- a/guava/src/com/google/common/collect/Synchronized.java +++ b/guava/src/com/google/common/collect/Synchronized.java @@ -31,10 +31,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.NavigableMap; -import java.util.NavigableSet; -import java.util.Queue; import java.util.RandomAccess; import java.util.Set; import java.util.SortedMap; @@ -211,7 +207,7 @@ final class Synchronized { static class SynchronizedSet<E> extends SynchronizedCollection<E> implements Set<E> { - + SynchronizedSet(Set<E> delegate, @Nullable Object mutex) { super(delegate, mutex); } @@ -851,7 +847,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; @@ -1043,12 +1039,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> { @@ -1212,410 +1208,11 @@ 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; } - - @GwtIncompatible("NavigableSet") - @VisibleForTesting - static class SynchronizedNavigableSet<E> extends SynchronizedSortedSet<E> - implements NavigableSet<E> { - SynchronizedNavigableSet(NavigableSet<E> delegate, @Nullable Object mutex) { - super(delegate, mutex); - } - - @Override NavigableSet<E> delegate() { - return (NavigableSet<E>) super.delegate(); - } - - @Override public E ceiling(E e) { - synchronized (mutex) { - return delegate().ceiling(e); - } - } - - @Override public Iterator<E> descendingIterator() { - return delegate().descendingIterator(); // manually synchronized - } - - transient NavigableSet<E> descendingSet; - - @Override public NavigableSet<E> descendingSet() { - synchronized (mutex) { - if (descendingSet == null) { - NavigableSet<E> dS = - Synchronized.navigableSet(delegate().descendingSet(), mutex); - descendingSet = dS; - return dS; - } - return descendingSet; - } - } - - @Override public E floor(E e) { - synchronized (mutex) { - return delegate().floor(e); - } - } - - @Override public NavigableSet<E> headSet(E toElement, boolean inclusive) { - synchronized (mutex) { - return Synchronized.navigableSet( - delegate().headSet(toElement, inclusive), mutex); - } - } - - @Override public E higher(E e) { - synchronized (mutex) { - return delegate().higher(e); - } - } - - @Override public E lower(E e) { - synchronized (mutex) { - return delegate().lower(e); - } - } - - @Override public E pollFirst() { - synchronized (mutex) { - return delegate().pollFirst(); - } - } - - @Override public E pollLast() { - synchronized (mutex) { - return delegate().pollLast(); - } - } - - @Override public NavigableSet<E> subSet(E fromElement, - boolean fromInclusive, E toElement, boolean toInclusive) { - synchronized (mutex) { - return Synchronized.navigableSet(delegate().subSet( - fromElement, fromInclusive, toElement, toInclusive), mutex); - } - } - - @Override public NavigableSet<E> tailSet(E fromElement, boolean inclusive) { - synchronized (mutex) { - return Synchronized.navigableSet( - delegate().tailSet(fromElement, inclusive), mutex); - } - } - - @Override public SortedSet<E> headSet(E toElement) { - return headSet(toElement, false); - } - - @Override public SortedSet<E> subSet(E fromElement, E toElement) { - return subSet(fromElement, true, toElement, false); - } - - @Override public SortedSet<E> tailSet(E fromElement) { - return tailSet(fromElement, true); - } - - private static final long serialVersionUID = 0; - } - - @GwtIncompatible("NavigableSet") - static <E> NavigableSet<E> navigableSet( - NavigableSet<E> navigableSet, @Nullable Object mutex) { - return new SynchronizedNavigableSet<E>(navigableSet, mutex); - } - - @GwtIncompatible("NavigableSet") - static <E> NavigableSet<E> navigableSet(NavigableSet<E> navigableSet) { - return navigableSet(navigableSet, null); - } - - @GwtIncompatible("NavigableMap") - static <K, V> NavigableMap<K, V> navigableMap( - NavigableMap<K, V> navigableMap) { - return navigableMap(navigableMap, null); - } - - @GwtIncompatible("NavigableMap") - static <K, V> NavigableMap<K, V> navigableMap( - NavigableMap<K, V> navigableMap, @Nullable Object mutex) { - return new SynchronizedNavigableMap<K, V>(navigableMap, mutex); - } - - @GwtIncompatible("NavigableMap") - @VisibleForTesting static class SynchronizedNavigableMap<K, V> - extends SynchronizedSortedMap<K, V> implements NavigableMap<K, V> { - - SynchronizedNavigableMap( - NavigableMap<K, V> delegate, @Nullable Object mutex) { - super(delegate, mutex); - } - - @Override NavigableMap<K, V> delegate() { - return (NavigableMap<K, V>) super.delegate(); - } - - @Override public Entry<K, V> ceilingEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().ceilingEntry(key), mutex); - } - } - - @Override public K ceilingKey(K key) { - synchronized (mutex) { - return delegate().ceilingKey(key); - } - } - - transient NavigableSet<K> descendingKeySet; - - @Override public NavigableSet<K> descendingKeySet() { - synchronized (mutex) { - if (descendingKeySet == null) { - return descendingKeySet = - Synchronized.navigableSet(delegate().descendingKeySet(), mutex); - } - return descendingKeySet; - } - } - - transient NavigableMap<K, V> descendingMap; - - @Override public NavigableMap<K, V> descendingMap() { - synchronized (mutex) { - if (descendingMap == null) { - return descendingMap = - navigableMap(delegate().descendingMap(), mutex); - } - return descendingMap; - } - } - - @Override public Entry<K, V> firstEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().firstEntry(), mutex); - } - } - - @Override public Entry<K, V> floorEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().floorEntry(key), mutex); - } - } - - @Override public K floorKey(K key) { - synchronized (mutex) { - return delegate().floorKey(key); - } - } - - @Override public NavigableMap<K, V> headMap(K toKey, boolean inclusive) { - synchronized (mutex) { - return navigableMap( - delegate().headMap(toKey, inclusive), mutex); - } - } - - @Override public Entry<K, V> higherEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().higherEntry(key), mutex); - } - } - - @Override public K higherKey(K key) { - synchronized (mutex) { - return delegate().higherKey(key); - } - } - - @Override public Entry<K, V> lastEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().lastEntry(), mutex); - } - } - - @Override public Entry<K, V> lowerEntry(K key) { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().lowerEntry(key), mutex); - } - } - - @Override public K lowerKey(K key) { - synchronized (mutex) { - return delegate().lowerKey(key); - } - } - - @Override public Set<K> keySet() { - return navigableKeySet(); - } - - transient NavigableSet<K> navigableKeySet; - - @Override public NavigableSet<K> navigableKeySet() { - synchronized (mutex) { - if (navigableKeySet == null) { - return navigableKeySet = - Synchronized.navigableSet(delegate().navigableKeySet(), mutex); - } - return navigableKeySet; - } - } - - @Override public Entry<K, V> pollFirstEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().pollFirstEntry(), mutex); - } - } - - @Override public Entry<K, V> pollLastEntry() { - synchronized (mutex) { - return nullableSynchronizedEntry(delegate().pollLastEntry(), mutex); - } - } - - @Override public NavigableMap<K, V> subMap( - K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { - synchronized (mutex) { - return navigableMap( - delegate().subMap(fromKey, fromInclusive, toKey, toInclusive), - mutex); - } - } - - @Override public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive) { - synchronized (mutex) { - return navigableMap( - delegate().tailMap(fromKey, inclusive), mutex); - } - } - - @Override public SortedMap<K, V> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override public SortedMap<K, V> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - private static final long serialVersionUID = 0; - } - - @GwtIncompatible("works but is needed only for NavigableMap") - private static <K, V> Entry<K, V> nullableSynchronizedEntry( - @Nullable Entry<K, V> entry, @Nullable Object mutex) { - if (entry == null) { - return null; - } - return new SynchronizedEntry<K, V>(entry, mutex); - } - - @GwtIncompatible("works but is needed only for NavigableMap") - private static class SynchronizedEntry<K, V> extends SynchronizedObject - implements Entry<K, V> { - - SynchronizedEntry(Entry<K, V> delegate, @Nullable Object mutex) { - super(delegate, mutex); - } - - @SuppressWarnings("unchecked") // guaranteed by the constructor - @Override Entry<K, V> delegate() { - return (Entry<K, V>) super.delegate(); - } - - @Override public boolean equals(Object obj) { - synchronized (mutex) { - return delegate().equals(obj); - } - } - - @Override public int hashCode() { - synchronized (mutex) { - return delegate().hashCode(); - } - } - - @Override public K getKey() { - synchronized (mutex) { - return delegate().getKey(); - } - } - - @Override public V getValue() { - synchronized (mutex) { - return delegate().getValue(); - } - } - - @Override public V setValue(V value) { - synchronized (mutex) { - return delegate().setValue(value); - } - } - - 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/src/com/google/common/collect/Table.java b/guava/src/com/google/common/collect/Table.java index c384bf8..f2313be 100644 --- a/guava/src/com/google/common/collect/Table.java +++ b/guava/src/com/google/common/collect/Table.java @@ -16,6 +16,7 @@ package com.google.common.collect; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Objects; @@ -43,10 +44,6 @@ import javax.annotation.Nullable; * <p>All methods that modify the table are optional, and the views returned by * the table may or may not be modifiable. When modification isn't supported, * those methods will throw an {@link UnsupportedOperationException}. - * - * <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 * @param <R> the type of the table row keys @@ -55,6 +52,7 @@ import javax.annotation.Nullable; * @since 7.0 */ @GwtCompatible +@Beta public interface Table<R, C, V> { // TODO(jlevy): Consider adding methods similar to ConcurrentMap methods. @@ -261,6 +259,7 @@ public interface Table<R, C, V> { * * @since 7.0 */ + @Beta interface Cell<R, C, V> { /** * Returns the row key of this cell. diff --git a/guava/src/com/google/common/collect/Tables.java b/guava/src/com/google/common/collect/Tables.java index 028aba3..a9c69b9 100644 --- a/guava/src/com/google/common/collect/Tables.java +++ b/guava/src/com/google/common/collect/Tables.java @@ -39,16 +39,13 @@ import javax.annotation.Nullable; /** * Provides static methods that involve a {@code Table}. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Tables"> - * {@code Tables}</a>. * * @author Jared Levy * @author Louis Wasserman * @since 7.0 */ @GwtCompatible +@Beta public final class Tables { private Tables() {} @@ -364,7 +361,6 @@ public final class Tables { * @throws IllegalArgumentException if {@code backingMap} is not empty * @since 10.0 */ - @Beta public static <R, C, V> Table<R, C, V> newCustomTable( Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) { checkArgument(backingMap.isEmpty()); @@ -399,7 +395,6 @@ public final class Tables { * * @since 10.0 */ - @Beta public static <R, C, V1, V2> Table<R, C, V2> transformValues( Table<R, C, V1> fromTable, Function<? super V1, V2> function) { return new TransformedTable<R, C, V1, V2>(fromTable, function); @@ -701,7 +696,6 @@ public final class Tables { * @return an unmodifiable view of the specified table * @since 11.0 */ - @Beta public static <R, C, V> RowSortedTable<R, C, V> unmodifiableRowSortedTable( RowSortedTable<R, ? extends C, ? extends V> table) { /* diff --git a/guava/src/com/google/common/collect/TransformedImmutableList.java b/guava/src/com/google/common/collect/TransformedImmutableList.java new file mode 100644 index 0000000..d9e8588 --- /dev/null +++ b/guava/src/com/google/common/collect/TransformedImmutableList.java @@ -0,0 +1,128 @@ +/* + * 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.checkNotNull; + +import com.google.common.annotations.GwtCompatible; + +import java.util.List; + +import javax.annotation.Nullable; + +/** + * A transforming wrapper around an ImmutableList. For internal use only. {@link + * #transform(Object)} must be functional. + * + * @author Louis Wasserman + */ +@GwtCompatible +@SuppressWarnings("serial") // uses writeReplace(), not default serialization +abstract class TransformedImmutableList<D, E> extends ImmutableList<E> { + private class TransformedView extends TransformedImmutableList<D, E> { + TransformedView(ImmutableList<D> backingList) { + super(backingList); + } + + @Override E transform(D d) { + return TransformedImmutableList.this.transform(d); + } + } + + private transient final ImmutableList<D> backingList; + + TransformedImmutableList(ImmutableList<D> backingList) { + this.backingList = checkNotNull(backingList); + } + + abstract E transform(D d); + + @Override public int indexOf(@Nullable Object object) { + if (object == null) { + return -1; + } + for (int i = 0; i < size(); i++) { + if (get(i).equals(object)) { + return i; + } + } + return -1; + } + + @Override public int lastIndexOf(@Nullable Object object) { + if (object == null) { + return -1; + } + for (int i = size() - 1; i >= 0; i--) { + if (get(i).equals(object)) { + return i; + } + } + return -1; + } + + @Override public E get(int index) { + return transform(backingList.get(index)); + } + + @Override public UnmodifiableListIterator<E> listIterator(int index) { + return new AbstractIndexedListIterator<E>(size(), index) { + @Override protected E get(int index) { + return TransformedImmutableList.this.get(index); + } + }; + } + + @Override public int size() { + return backingList.size(); + } + + @Override public ImmutableList<E> subList(int fromIndex, int toIndex) { + return new TransformedView(backingList.subList(fromIndex, toIndex)); + } + + @Override public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof List) { + List<?> list = (List<?>) obj; + return size() == list.size() + && Iterators.elementsEqual(iterator(), list.iterator()); + } + return false; + } + + @Override public int hashCode() { + int hashCode = 1; + for (E e : this) { + hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); + } + return hashCode; + } + + @Override public Object[] toArray() { + return ObjectArrays.toArrayImpl(this); + } + + @Override public <T> T[] toArray(T[] array) { + return ObjectArrays.toArrayImpl(this, array); + } + + @Override boolean isPartialView() { + return true; + } +} diff --git a/guava/src/com/google/common/collect/TransformedIterator.java b/guava/src/com/google/common/collect/TransformedIterator.java deleted file mode 100644 index c082d7d..0000000 --- a/guava/src/com/google/common/collect/TransformedIterator.java +++ /dev/null @@ -1,55 +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 static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.GwtCompatible; - -import java.util.Iterator; - -/** - * An iterator that transforms a backing iterator; for internal use. This avoids - * the object overhead of constructing a {@link Function} for internal methods. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class TransformedIterator<F, T> implements Iterator<T> { - final Iterator<? extends F> backingIterator; - - TransformedIterator(Iterator<? extends F> backingIterator) { - this.backingIterator = checkNotNull(backingIterator); - } - - abstract T transform(F from); - - @Override - public final boolean hasNext() { - return backingIterator.hasNext(); - } - - @Override - public final T next() { - return transform(backingIterator.next()); - } - - @Override - public final void remove() { - backingIterator.remove(); - } -} diff --git a/guava/src/com/google/common/collect/TransformedListIterator.java b/guava/src/com/google/common/collect/TransformedListIterator.java deleted file mode 100644 index c743030..0000000 --- a/guava/src/com/google/common/collect/TransformedListIterator.java +++ /dev/null @@ -1,71 +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 com.google.common.base.Function; - -import java.util.ListIterator; - -/** - * An iterator that transforms a backing list iterator; for internal use. This - * avoids the object overhead of constructing a {@link Function} for internal - * methods. - * - * @author Louis Wasserman - */ -@GwtCompatible -abstract class TransformedListIterator<F, T> extends TransformedIterator<F, T> - implements ListIterator<T> { - TransformedListIterator(ListIterator<? extends F> backingIterator) { - super(backingIterator); - } - - private ListIterator<? extends F> backingIterator() { - return Iterators.cast(backingIterator); - } - - @Override - public final boolean hasPrevious() { - return backingIterator().hasPrevious(); - } - - @Override - public final T previous() { - return transform(backingIterator().previous()); - } - - @Override - public final int nextIndex() { - return backingIterator().nextIndex(); - } - - @Override - public final int previousIndex() { - return backingIterator().previousIndex(); - } - - @Override - public void set(T element) { - throw new UnsupportedOperationException(); - } - - @Override - public void add(T element) { - throw new UnsupportedOperationException(); - } -} diff --git a/guava/src/com/google/common/collect/TreeBasedTable.java b/guava/src/com/google/common/collect/TreeBasedTable.java index 2ead64e..d996c27 100644 --- a/guava/src/com/google/common/collect/TreeBasedTable.java +++ b/guava/src/com/google/common/collect/TreeBasedTable.java @@ -66,10 +66,6 @@ import javax.annotation.Nullable; * access this table concurrently and one of the threads modifies the table, it * must be synchronized externally. * - * <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 * @author Louis Wasserman * @since 7.0 diff --git a/guava/src/com/google/common/collect/TreeMultimap.java b/guava/src/com/google/common/collect/TreeMultimap.java index 433774d..efd11be 100644 --- a/guava/src/com/google/common/collect/TreeMultimap.java +++ b/guava/src/com/google/common/collect/TreeMultimap.java @@ -26,14 +26,11 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collection; import java.util.Comparator; -import java.util.NavigableMap; -import java.util.NavigableSet; +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 @@ -67,16 +64,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; @@ -142,14 +134,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. */ @@ -162,79 +146,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. - */ - - @Override - @GwtIncompatible("NavigableMap") - NavigableMap<K, Collection<V>> backingMap() { - return (NavigableMap<K, Collection<V>>) super.backingMap(); - } - - /** - * @since 14.0 (present with return type {@code SortedSet} since 2.0) - */ - @Override - @GwtIncompatible("NavigableSet") - public NavigableSet<V> get(@Nullable K key) { - return (NavigableSet<V>) super.get(key); - } - - @Override - @GwtIncompatible("NavigableSet") - Collection<V> unmodifiableCollectionSubclass(Collection<V> collection) { - return Sets.unmodifiableNavigableSet((NavigableSet<V>) collection); - } - - @Override - @GwtIncompatible("NavigableSet") - Collection<V> wrapCollection(K key, Collection<V> collection) { - return new WrappedNavigableSet(key, (NavigableSet<V>) collection, null); - } - /** * {@inheritDoc} * * <p>Because a {@code TreeMultimap} has unique sorted keys, this method - * returns a {@link NavigableSet}, instead of the {@link java.util.Set} specified + * returns a {@link SortedSet}, instead of the {@link java.util.Set} specified * in the {@link Multimap} interface. - * - * @since 14.0 (present with return type {@code SortedSet} since 2.0) */ - @Override - @GwtIncompatible("NavigableSet") - public NavigableSet<K> keySet() { - return (NavigableSet<K>) super.keySet(); - } - - @Override - @GwtIncompatible("NavigableSet") - NavigableSet<K> createKeySet() { - return new NavigableKeySet(backingMap()); + @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 NavigableMap}, instead of the {@link java.util.Map} specified + * returns a {@link SortedMap}, instead of the {@link java.util.Map} specified * in the {@link Multimap} interface. - * - * @since 14.0 (present with return type {@code SortedMap} since 2.0) */ - @Override - @GwtIncompatible("NavigableMap") - public NavigableMap<K, Collection<V>> asMap() { - return (NavigableMap<K, Collection<V>>) super.asMap(); - } - - @Override - @GwtIncompatible("NavigableMap") - NavigableMap<K, Collection<V>> createAsMap() { - return new NavigableAsMap(backingMap()); + @Override public SortedMap<K, Collection<V>> asMap() { + return (SortedMap<K, Collection<V>>) super.asMap(); } /** diff --git a/guava/src/com/google/common/collect/TreeMultiset.java b/guava/src/com/google/common/collect/TreeMultiset.java index 6876cd0..d44a4bb 100644 --- a/guava/src/com/google/common/collect/TreeMultiset.java +++ b/guava/src/com/google/common/collect/TreeMultiset.java @@ -17,12 +17,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.base.Preconditions.checkState; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; -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.IOException; import java.io.ObjectInputStream; @@ -31,207 +29,160 @@ 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.annotations.GwtIncompatible; +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) { @@ -239,718 +190,360 @@ 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; - - // elemCount is 0 iff this node has been deleted. - private int elemCount; + private static final class Node<E> extends BstNode<E, Node<E>> implements Serializable { + private final long size; + private final int distinct; - 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; - } + this.size = (long) elemCount + sizeOrZero(left) + sizeOrZero(right); + this.distinct = 1 + distinctOrZero(left) + distinctOrZero(right); } - 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; + int elemCount() { + long result = size - sizeOrZero(childOrNull(LEFT)) + - sizeOrZero(childOrNull(RIGHT)); + return Ints.checkedCast(result); } - 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; - } - - 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 final class AddModifier extends MultisetModifier { + private final int countToAdd; - private void recompute() { - recomputeMultiset(); - recomputeHeight(); + private AddModifier(int countToAdd) { + checkArgument(countToAdd > 0); + this.countToAdd = 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; - } + @Override + int newCount(int oldCount) { + checkArgument(countToAdd <= Integer.MAX_VALUE - oldCount, "Cannot add this many elements"); + return oldCount + countToAdd; } + } - private int balanceFactor() { - return height(left) - height(right); - } + private final class RemoveModifier extends MultisetModifier { + private final int countToRemove; - 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 RemoveModifier(int countToRemove) { + checkArgument(countToRemove > 0); + this.countToRemove = 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; + @Override + int newCount(int oldCount) { + return Math.max(0, oldCount - countToRemove); } + } - private static long totalCount(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.totalCount; - } + private final class SetCountModifier extends MultisetModifier { + private final int countToSet; - private static int height(@Nullable AvlNode<?> node) { - return (node == null) ? 0 : node.height; - } - - @Nullable private AvlNode<E> ceiling(Comparator<? super E> comparator, E e) { - int cmp = comparator.compare(e, elem); - if (cmp < 0) { - return (left == null) ? this : Objects.firstNonNull(left.ceiling(comparator, e), this); - } else if (cmp == 0) { - return this; - } else { - return (right == null) ? null : right.ceiling(comparator, e); - } - } - - @Nullable private AvlNode<E> floor(Comparator<? super E> comparator, E e) { - int cmp = comparator.compare(e, elem); - if (cmp > 0) { - return (right == null) ? this : Objects.firstNonNull(right.floor(comparator, e), this); - } else if (cmp == 0) { - return this; - } else { - return (left == null) ? null : left.floor(comparator, e); - } + 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. */ /** - * @serialData the comparator, the number of distinct elements, the first element, its count, the - * second element, its count, and so on + * @serialData the comparator, the number of distinct elements, the first + * element, its count, the second element, its count, and so on */ @GwtIncompatible("java.io.ObjectOutputStream") private void writeObject(ObjectOutputStream stream) throws IOException { @@ -960,23 +553,19 @@ public final class TreeMultiset<E> extends AbstractSortedMultiset<E> implements } @GwtIncompatible("java.io.ObjectInputStream") - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { stream.defaultReadObject(); - @SuppressWarnings("unchecked") - // reading data stored by writeObject + @SuppressWarnings("unchecked") // reading data stored by writeObject Comparator<? super E> comparator = (Comparator<? super E>) stream.readObject(); Serialization.getFieldSetter(AbstractSortedMultiset.class, "comparator").set(this, comparator); - Serialization.getFieldSetter(TreeMultiset.class, "range").set( - this, + Serialization.getFieldSetter(TreeMultiset.class, "range").set(this, GeneralRange.all(comparator)); - Serialization.getFieldSetter(TreeMultiset.class, "rootReference").set( - this, - new Reference<AvlNode<E>>()); - AvlNode<E> header = new AvlNode<E>(null, 1); - Serialization.getFieldSetter(TreeMultiset.class, "header").set(this, header); - successor(header, header); + Serialization.getFieldSetter(TreeMultiset.class, "rootReference").set(this, + new Reference<Node<E>>()); Serialization.populateMultiset(this, stream); } - @GwtIncompatible("not needed in emulated source") private static final long serialVersionUID = 1; + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 1; } diff --git a/guava/src/com/google/common/collect/TreeRangeMap.java b/guava/src/com/google/common/collect/TreeRangeMap.java deleted file mode 100644 index e5b5f47..0000000 --- a/guava/src/com/google/common/collect/TreeRangeMap.java +++ /dev/null @@ -1,618 +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 static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Predicates.compose; -import static com.google.common.base.Predicates.in; -import static com.google.common.base.Predicates.not; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; - -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NoSuchElementException; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * An implementation of {@code RangeMap} based on a {@code TreeMap}, supporting - * all optional operations. - * - * <p>Like all {@code RangeMap} implementations, this supports neither null - * keys nor null values. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -@GwtIncompatible("NavigableMap") -public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> { - - private final NavigableMap<Cut<K>, RangeMapEntry<K, V>> entriesByLowerBound; - - public static <K extends Comparable, V> TreeRangeMap<K, V> create() { - return new TreeRangeMap<K, V>(); - } - - private TreeRangeMap() { - this.entriesByLowerBound = Maps.newTreeMap(); - } - - private static final class RangeMapEntry<K extends Comparable, V> - extends AbstractMapEntry<Range<K>, V> { - private final Range<K> range; - private final V value; - - RangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) { - this(Range.create(lowerBound, upperBound), value); - } - - RangeMapEntry(Range<K> range, V value) { - this.range = range; - this.value = value; - } - - @Override - public Range<K> getKey() { - return range; - } - - @Override - public V getValue() { - return value; - } - - public boolean contains(K value) { - return range.contains(value); - } - - Cut<K> getLowerBound() { - return range.lowerBound; - } - - Cut<K> getUpperBound() { - return range.upperBound; - } - } - - @Override - @Nullable - public V get(K key) { - Entry<Range<K>, V> entry = getEntry(key); - return (entry == null) ? null : entry.getValue(); - } - - @Override - @Nullable - public Entry<Range<K>, V> getEntry(K key) { - Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntry = - entriesByLowerBound.floorEntry(Cut.belowValue(key)); - if (mapEntry != null && mapEntry.getValue().contains(key)) { - return mapEntry.getValue(); - } else { - return null; - } - } - - @Override - public void put(Range<K> range, V value) { - if (!range.isEmpty()) { - checkNotNull(value); - remove(range); - entriesByLowerBound.put(range.lowerBound, new RangeMapEntry<K, V>(range, value)); - } - } - - @Override - public void putAll(RangeMap<K, V> rangeMap) { - for (Map.Entry<Range<K>, V> entry : rangeMap.asMapOfRanges().entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - @Override - public void clear() { - entriesByLowerBound.clear(); - } - - @Override - public Range<K> span() { - Entry<Cut<K>, RangeMapEntry<K, V>> firstEntry = entriesByLowerBound.firstEntry(); - Entry<Cut<K>, RangeMapEntry<K, V>> lastEntry = entriesByLowerBound.lastEntry(); - if (firstEntry == null) { - throw new NoSuchElementException(); - } - return Range.create( - firstEntry.getValue().getKey().lowerBound, - lastEntry.getValue().getKey().upperBound); - } - - private void putRangeMapEntry(Cut<K> lowerBound, Cut<K> upperBound, V value) { - entriesByLowerBound.put(lowerBound, new RangeMapEntry<K, V>(lowerBound, upperBound, value)); - } - - @Override - public void remove(Range<K> rangeToRemove) { - if (rangeToRemove.isEmpty()) { - return; - } - - /* - * The comments for this method will use [ ] to indicate the bounds of rangeToRemove and ( ) to - * indicate the bounds of ranges in the range map. - */ - Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryBelowToTruncate = - entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound); - if (mapEntryBelowToTruncate != null) { - // we know ( [ - RangeMapEntry<K, V> rangeMapEntry = mapEntryBelowToTruncate.getValue(); - if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) { - // we know ( [ ) - if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { - // we know ( [ ] ), so insert the range ] ) back into the map -- - // it's being split apart - putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), - mapEntryBelowToTruncate.getValue().getValue()); - } - // overwrite mapEntryToTruncateBelow with a truncated range - putRangeMapEntry(rangeMapEntry.getLowerBound(), rangeToRemove.lowerBound, - mapEntryBelowToTruncate.getValue().getValue()); - } - } - - Map.Entry<Cut<K>, RangeMapEntry<K, V>> mapEntryAboveToTruncate = - entriesByLowerBound.lowerEntry(rangeToRemove.upperBound); - if (mapEntryAboveToTruncate != null) { - // we know ( ] - RangeMapEntry<K, V> rangeMapEntry = mapEntryAboveToTruncate.getValue(); - if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { - // we know ( ] ), and since we dealt with truncating below already, - // we know [ ( ] ) - putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), - mapEntryAboveToTruncate.getValue().getValue()); - entriesByLowerBound.remove(rangeToRemove.lowerBound); - } - } - entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); - } - - @Override - public Map<Range<K>, V> asMapOfRanges() { - return new AsMapOfRanges(); - } - - private final class AsMapOfRanges extends AbstractMap<Range<K>, V> { - - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - public V get(@Nullable Object key) { - if (key instanceof Range) { - Range<?> range = (Range<?>) key; - RangeMapEntry<K, V> rangeMapEntry = entriesByLowerBound.get(range.lowerBound); - if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) { - return rangeMapEntry.getValue(); - } - } - return null; - } - - @Override - public Set<Entry<Range<K>, V>> entrySet() { - return new AbstractSet<Entry<Range<K>, V>>() { - - @SuppressWarnings("unchecked") // it's safe to upcast iterators - @Override - public Iterator<Entry<Range<K>, V>> iterator() { - return (Iterator) entriesByLowerBound.values().iterator(); - } - - @Override - public int size() { - return entriesByLowerBound.size(); - } - }; - } - } - - @Override - public RangeMap<K, V> subRangeMap(Range<K> subRange) { - if (subRange.equals(Range.all())) { - return this; - } else { - return new SubRangeMap(subRange); - } - } - - @SuppressWarnings("unchecked") - private RangeMap<K, V> emptySubRangeMap() { - return EMPTY_SUB_RANGE_MAP; - } - - private static final RangeMap EMPTY_SUB_RANGE_MAP = - new RangeMap() { - @Override - @Nullable - public Object get(Comparable key) { - return null; - } - - @Override - @Nullable - public Entry<Range, Object> getEntry(Comparable key) { - return null; - } - - @Override - public Range span() { - throw new NoSuchElementException(); - } - - @Override - public void put(Range range, Object value) { - checkNotNull(range); - throw new IllegalArgumentException( - "Cannot insert range " + range + " into an empty subRangeMap"); - } - - @Override - public void putAll(RangeMap rangeMap) { - if (!rangeMap.asMapOfRanges().isEmpty()) { - throw new IllegalArgumentException( - "Cannot putAll(nonEmptyRangeMap) into an empty " + "subRangeMap"); - } - } - - @Override - public void clear() {} - - @Override - public void remove(Range range) { - checkNotNull(range); - } - - @Override - public Map<Range, Object> asMapOfRanges() { - return Collections.emptyMap(); - } - - @Override - public RangeMap subRangeMap(Range range) { - checkNotNull(range); - return this; - } - }; - - private class SubRangeMap implements RangeMap<K, V> { - - private final Range<K> subRange; - - SubRangeMap(Range<K> subRange) { - this.subRange = subRange; - } - - @Override - @Nullable - public V get(K key) { - return subRange.contains(key) - ? TreeRangeMap.this.get(key) - : null; - } - - @Override - @Nullable - public Entry<Range<K>, V> getEntry(K key) { - if (subRange.contains(key)) { - Entry<Range<K>, V> entry = TreeRangeMap.this.getEntry(key); - if (entry != null) { - return Maps.immutableEntry(entry.getKey().intersection(subRange), entry.getValue()); - } - } - return null; - } - - @Override - public Range<K> span() { - Cut<K> lowerBound; - Entry<Cut<K>, RangeMapEntry<K, V>> lowerEntry = - entriesByLowerBound.floorEntry(subRange.lowerBound); - if (lowerEntry != null && - lowerEntry.getValue().getUpperBound().compareTo(subRange.lowerBound) > 0) { - lowerBound = subRange.lowerBound; - } else { - lowerBound = entriesByLowerBound.ceilingKey(subRange.lowerBound); - if (lowerBound == null || lowerBound.compareTo(subRange.upperBound) >= 0) { - throw new NoSuchElementException(); - } - } - - Cut<K> upperBound; - Entry<Cut<K>, RangeMapEntry<K, V>> upperEntry = - entriesByLowerBound.lowerEntry(subRange.upperBound); - if (upperEntry == null) { - throw new NoSuchElementException(); - } else if (upperEntry.getValue().getUpperBound().compareTo(subRange.upperBound) >= 0) { - upperBound = subRange.upperBound; - } else { - upperBound = upperEntry.getValue().getUpperBound(); - } - return Range.create(lowerBound, upperBound); - } - - @Override - public void put(Range<K> range, V value) { - checkArgument(subRange.encloses(range), - "Cannot put range %s into a subRangeMap(%s)", range, subRange); - TreeRangeMap.this.put(range, value); - } - - @Override - public void putAll(RangeMap<K, V> rangeMap) { - if (rangeMap.asMapOfRanges().isEmpty()) { - return; - } - Range<K> span = rangeMap.span(); - checkArgument(subRange.encloses(span), - "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", span, subRange); - TreeRangeMap.this.putAll(rangeMap); - } - - @Override - public void clear() { - TreeRangeMap.this.remove(subRange); - } - - @Override - public void remove(Range<K> range) { - if (range.isConnected(subRange)) { - TreeRangeMap.this.remove(range.intersection(subRange)); - } - } - - @Override - public RangeMap<K, V> subRangeMap(Range<K> range) { - if (!range.isConnected(subRange)) { - return emptySubRangeMap(); - } else { - return TreeRangeMap.this.subRangeMap(range.intersection(subRange)); - } - } - - @Override - public Map<Range<K>, V> asMapOfRanges() { - return new SubRangeMapAsMap(); - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof RangeMap) { - RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; - return asMapOfRanges().equals(rangeMap.asMapOfRanges()); - } - return false; - } - - @Override - public int hashCode() { - return asMapOfRanges().hashCode(); - } - - @Override - public String toString() { - return asMapOfRanges().toString(); - } - - class SubRangeMapAsMap extends AbstractMap<Range<K>, V> { - - @Override - public boolean containsKey(Object key) { - return get(key) != null; - } - - @Override - public V get(Object key) { - try { - if (key instanceof Range) { - @SuppressWarnings("unchecked") // we catch ClassCastExceptions - Range<K> r = (Range<K>) key; - if (!subRange.encloses(r) || r.isEmpty()) { - return null; - } - RangeMapEntry<K, V> candidate = null; - if (r.lowerBound.compareTo(subRange.lowerBound) == 0) { - // r could be truncated on the left - Entry<Cut<K>, RangeMapEntry<K, V>> entry = - entriesByLowerBound.floorEntry(r.lowerBound); - if (entry != null) { - candidate = entry.getValue(); - } - } else { - candidate = entriesByLowerBound.get(r.lowerBound); - } - - if (candidate != null && candidate.getKey().isConnected(subRange) - && candidate.getKey().intersection(subRange).equals(r)) { - return candidate.getValue(); - } - } - } catch (ClassCastException e) { - return null; - } - return null; - } - - @Override - public V remove(Object key) { - V value = get(key); - if (value != null) { - @SuppressWarnings("unchecked") // it's definitely in the map, so safe - Range<K> range = (Range<K>) key; - TreeRangeMap.this.remove(range); - return value; - } - return null; - } - - @Override - public void clear() { - SubRangeMap.this.clear(); - } - - private boolean removeIf(Predicate<? super Entry<Range<K>, V>> predicate) { - List<Range<K>> toRemove = Lists.newArrayList(); - for (Entry<Range<K>, V> entry : entrySet()) { - if (predicate.apply(entry)) { - toRemove.add(entry.getKey()); - } - } - for (Range<K> range : toRemove) { - TreeRangeMap.this.remove(range); - } - return !toRemove.isEmpty(); - } - - @Override - public Set<Range<K>> keySet() { - return new Maps.KeySet<Range<K>, V>() { - @Override - Map<Range<K>, V> map() { - return SubRangeMapAsMap.this; - } - - @Override - public boolean remove(@Nullable Object o) { - return SubRangeMapAsMap.this.remove(o) != null; - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<Range<K>>keyFunction())); - } - }; - } - - @Override - public Set<Entry<Range<K>, V>> entrySet() { - return new Maps.EntrySet<Range<K>, V>() { - @Override - Map<Range<K>, V> map() { - return SubRangeMapAsMap.this; - } - - @Override - public Iterator<Entry<Range<K>, V>> iterator() { - if (subRange.isEmpty()) { - return Iterators.emptyIterator(); - } - Cut<K> cutToStart = Objects.firstNonNull( - entriesByLowerBound.floorKey(subRange.lowerBound), - subRange.lowerBound); - final Iterator<RangeMapEntry<K, V>> backingItr = - entriesByLowerBound.tailMap(cutToStart, true).values().iterator(); - return new AbstractIterator<Entry<Range<K>, V>>() { - - @Override - protected Entry<Range<K>, V> computeNext() { - while (backingItr.hasNext()) { - RangeMapEntry<K, V> entry = backingItr.next(); - if (entry.getLowerBound().compareTo(subRange.upperBound) >= 0) { - break; - } else if (entry.getUpperBound().compareTo(subRange.lowerBound) > 0) { - // this might not be true e.g. at the start of the iteration - return Maps.immutableEntry( - entry.getKey().intersection(subRange), entry.getValue()); - } - } - return endOfData(); - } - }; - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(not(in(c))); - } - - @Override - public int size() { - return Iterators.size(iterator()); - } - - @Override - public boolean isEmpty() { - return !iterator().hasNext(); - } - }; - } - - @Override - public Collection<V> values() { - return new Maps.Values<Range<K>, V>() { - @Override - Map<Range<K>, V> map() { - return SubRangeMapAsMap.this; - } - - @Override - public boolean removeAll(Collection<?> c) { - return removeIf(compose(in(c), Maps.<V>valueFunction())); - } - - @Override - public boolean retainAll(Collection<?> c) { - return removeIf(compose(not(in(c)), Maps.<V>valueFunction())); - } - }; - } - } - } - - @Override - public boolean equals(@Nullable Object o) { - if (o instanceof RangeMap) { - RangeMap<?, ?> rangeMap = (RangeMap<?, ?>) o; - return asMapOfRanges().equals(rangeMap.asMapOfRanges()); - } - return false; - } - - @Override - public int hashCode() { - return asMapOfRanges().hashCode(); - } - - @Override - public String toString() { - return entriesByLowerBound.values().toString(); - } -} diff --git a/guava/src/com/google/common/collect/TreeRangeSet.java b/guava/src/com/google/common/collect/TreeRangeSet.java deleted file mode 100644 index d67c5f4..0000000 --- a/guava/src/com/google/common/collect/TreeRangeSet.java +++ /dev/null @@ -1,851 +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.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.GwtIncompatible; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.TreeMap; - -import javax.annotation.Nullable; - -/** - * An implementation of {@link RangeSet} backed by a {@link TreeMap}. - * - * @author Louis Wasserman - * @since 14.0 - */ -@Beta -@GwtIncompatible("uses NavigableMap") -public class TreeRangeSet<C extends Comparable<?>> - extends AbstractRangeSet<C> { - - @VisibleForTesting - final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; - - /** - * Creates an empty {@code TreeRangeSet} instance. - */ - public static <C extends Comparable<?>> TreeRangeSet<C> create() { - return new TreeRangeSet<C>(new TreeMap<Cut<C>, Range<C>>()); - } - - /** - * Returns a {@code TreeRangeSet} initialized with the ranges in the specified range set. - */ - public static <C extends Comparable<?>> TreeRangeSet<C> create(RangeSet<C> rangeSet) { - TreeRangeSet<C> result = create(); - result.addAll(rangeSet); - return result; - } - - private TreeRangeSet(NavigableMap<Cut<C>, Range<C>> rangesByLowerCut) { - this.rangesByLowerBound = rangesByLowerCut; - } - - private transient Set<Range<C>> asRanges; - - @Override - public Set<Range<C>> asRanges() { - Set<Range<C>> result = asRanges; - return (result == null) ? asRanges = new AsRanges() : result; - } - - final class AsRanges extends ForwardingCollection<Range<C>> implements Set<Range<C>> { - @Override - protected Collection<Range<C>> delegate() { - return rangesByLowerBound.values(); - } - - @Override - public int hashCode() { - return Sets.hashCodeImpl(this); - } - - @Override - public boolean equals(@Nullable Object o) { - return Sets.equalsImpl(this, o); - } - } - - @Override - @Nullable - public Range<C> rangeContaining(C value) { - checkNotNull(value); - Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(Cut.belowValue(value)); - if (floorEntry != null && floorEntry.getValue().contains(value)) { - return floorEntry.getValue(); - } else { - // TODO(kevinb): revisit this design choice - return null; - } - } - - @Override - public boolean encloses(Range<C> range) { - checkNotNull(range); - Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); - return floorEntry != null && floorEntry.getValue().encloses(range); - } - - @Nullable - private Range<C> rangeEnclosing(Range<C> range) { - checkNotNull(range); - Entry<Cut<C>, Range<C>> floorEntry = rangesByLowerBound.floorEntry(range.lowerBound); - return (floorEntry != null && floorEntry.getValue().encloses(range)) - ? floorEntry.getValue() - : null; - } - - @Override - public Range<C> span() { - Entry<Cut<C>, Range<C>> firstEntry = rangesByLowerBound.firstEntry(); - Entry<Cut<C>, Range<C>> lastEntry = rangesByLowerBound.lastEntry(); - if (firstEntry == null) { - throw new NoSuchElementException(); - } - return Range.create(firstEntry.getValue().lowerBound, lastEntry.getValue().upperBound); - } - - @Override - public void add(Range<C> rangeToAdd) { - checkNotNull(rangeToAdd); - - if (rangeToAdd.isEmpty()) { - return; - } - - // We will use { } to illustrate ranges currently in the range set, and < > - // to illustrate rangeToAdd. - Cut<C> lbToAdd = rangeToAdd.lowerBound; - Cut<C> ubToAdd = rangeToAdd.upperBound; - - Entry<Cut<C>, Range<C>> entryBelowLB = rangesByLowerBound.lowerEntry(lbToAdd); - if (entryBelowLB != null) { - // { < - Range<C> rangeBelowLB = entryBelowLB.getValue(); - if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) { - // { < }, and we will need to coalesce - if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) { - // { < > } - ubToAdd = rangeBelowLB.upperBound; - /* - * TODO(cpovirk): can we just "return;" here? Or, can we remove this if() entirely? If - * not, add tests to demonstrate the problem with each approach - */ - } - lbToAdd = rangeBelowLB.lowerBound; - } - } - - Entry<Cut<C>, Range<C>> entryBelowUB = rangesByLowerBound.floorEntry(ubToAdd); - if (entryBelowUB != null) { - // { > - Range<C> rangeBelowUB = entryBelowUB.getValue(); - if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) { - // { > }, and we need to coalesce - ubToAdd = rangeBelowUB.upperBound; - } - } - - // Remove ranges which are strictly enclosed. - rangesByLowerBound.subMap(lbToAdd, ubToAdd).clear(); - - replaceRangeWithSameLowerBound(Range.create(lbToAdd, ubToAdd)); - } - - @Override - public void remove(Range<C> rangeToRemove) { - checkNotNull(rangeToRemove); - - if (rangeToRemove.isEmpty()) { - return; - } - - // We will use { } to illustrate ranges currently in the range set, and < > - // to illustrate rangeToRemove. - - Entry<Cut<C>, Range<C>> entryBelowLB = rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); - if (entryBelowLB != null) { - // { < - Range<C> rangeBelowLB = entryBelowLB.getValue(); - if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { - // { < }, and we will need to subdivide - if (rangeToRemove.hasUpperBound() - && rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { - // { < > } - replaceRangeWithSameLowerBound( - Range.create(rangeToRemove.upperBound, rangeBelowLB.upperBound)); - } - replaceRangeWithSameLowerBound( - Range.create(rangeBelowLB.lowerBound, rangeToRemove.lowerBound)); - } - } - - Entry<Cut<C>, Range<C>> entryBelowUB = rangesByLowerBound.floorEntry(rangeToRemove.upperBound); - if (entryBelowUB != null) { - // { > - Range<C> rangeBelowUB = entryBelowUB.getValue(); - if (rangeToRemove.hasUpperBound() - && rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { - // { > } - replaceRangeWithSameLowerBound( - Range.create(rangeToRemove.upperBound, rangeBelowUB.upperBound)); - } - } - - rangesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); - } - - private void replaceRangeWithSameLowerBound(Range<C> range) { - if (range.isEmpty()) { - rangesByLowerBound.remove(range.lowerBound); - } else { - rangesByLowerBound.put(range.lowerBound, range); - } - } - - private transient RangeSet<C> complement; - - @Override - public RangeSet<C> complement() { - RangeSet<C> result = complement; - return (result == null) ? complement = new Complement() : result; - } - - @VisibleForTesting - static final class RangesByUpperBound<C extends Comparable<?>> - extends AbstractNavigableMap<Cut<C>, Range<C>> { - private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; - - /** - * upperBoundWindow represents the headMap/subMap/tailMap view of the entire "ranges by upper - * bound" map; it's a constraint on the *keys*, and does not affect the values. - */ - private final Range<Cut<C>> upperBoundWindow; - - RangesByUpperBound(NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { - this.rangesByLowerBound = rangesByLowerBound; - this.upperBoundWindow = Range.all(); - } - - private RangesByUpperBound( - NavigableMap<Cut<C>, Range<C>> rangesByLowerBound, Range<Cut<C>> upperBoundWindow) { - this.rangesByLowerBound = rangesByLowerBound; - this.upperBoundWindow = upperBoundWindow; - } - - private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { - if (window.isConnected(upperBoundWindow)) { - return new RangesByUpperBound<C>(rangesByLowerBound, window.intersection(upperBoundWindow)); - } else { - return ImmutableSortedMap.of(); - } - } - - @Override - public NavigableMap<Cut<C>, Range<C>> subMap( - Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { - return subMap(Range.range( - fromKey, BoundType.forBoolean(fromInclusive), - toKey, BoundType.forBoolean(toInclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { - return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { - return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); - } - - @Override - public Comparator<? super Cut<C>> comparator() { - return Ordering.<Cut<C>>natural(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - public Range<C> get(@Nullable Object key) { - if (key instanceof Cut) { - try { - @SuppressWarnings("unchecked") // we catch CCEs - Cut<C> cut = (Cut<C>) key; - if (!upperBoundWindow.contains(cut)) { - return null; - } - Entry<Cut<C>, Range<C>> candidate = rangesByLowerBound.lowerEntry(cut); - if (candidate != null && candidate.getValue().upperBound.equals(cut)) { - return candidate.getValue(); - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { - /* - * We want to start the iteration at the first range where the upper bound is in - * upperBoundWindow. - */ - final Iterator<Range<C>> backingItr; - if (!upperBoundWindow.hasLowerBound()) { - backingItr = rangesByLowerBound.values().iterator(); - } else { - Entry<Cut<C>, Range<C>> lowerEntry = - rangesByLowerBound.lowerEntry(upperBoundWindow.lowerEndpoint()); - if (lowerEntry == null) { - backingItr = rangesByLowerBound.values().iterator(); - } else if (upperBoundWindow.lowerBound.isLessThan(lowerEntry.getValue().upperBound)) { - backingItr = rangesByLowerBound.tailMap(lowerEntry.getKey(), true).values().iterator(); - } else { - backingItr = rangesByLowerBound.tailMap(upperBoundWindow.lowerEndpoint(), true) - .values().iterator(); - } - } - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!backingItr.hasNext()) { - return endOfData(); - } - Range<C> range = backingItr.next(); - if (upperBoundWindow.upperBound.isLessThan(range.upperBound)) { - return endOfData(); - } else { - return Maps.immutableEntry(range.upperBound, range); - } - } - }; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { - Collection<Range<C>> candidates; - if (upperBoundWindow.hasUpperBound()) { - candidates = rangesByLowerBound.headMap(upperBoundWindow.upperEndpoint(), false) - .descendingMap().values(); - } else { - candidates = rangesByLowerBound.descendingMap().values(); - } - final PeekingIterator<Range<C>> backingItr = Iterators.peekingIterator(candidates.iterator()); - if (backingItr.hasNext() - && upperBoundWindow.upperBound.isLessThan(backingItr.peek().upperBound)) { - backingItr.next(); - } - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!backingItr.hasNext()) { - return endOfData(); - } - Range<C> range = backingItr.next(); - return upperBoundWindow.lowerBound.isLessThan(range.upperBound) - ? Maps.immutableEntry(range.upperBound, range) - : endOfData(); - } - }; - } - - @Override - public int size() { - if (upperBoundWindow.equals(Range.all())) { - return rangesByLowerBound.size(); - } - return Iterators.size(entryIterator()); - } - - @Override - public boolean isEmpty() { - return upperBoundWindow.equals(Range.all()) - ? rangesByLowerBound.isEmpty() - : !entryIterator().hasNext(); - } - } - - private static final class ComplementRangesByLowerBound<C extends Comparable<?>> - extends AbstractNavigableMap<Cut<C>, Range<C>> { - private final NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound; - private final NavigableMap<Cut<C>, Range<C>> positiveRangesByUpperBound; - - /** - * complementLowerBoundWindow represents the headMap/subMap/tailMap view of the entire - * "complement ranges by lower bound" map; it's a constraint on the *keys*, and does not affect - * the values. - */ - private final Range<Cut<C>> complementLowerBoundWindow; - - ComplementRangesByLowerBound(NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound) { - this(positiveRangesByLowerBound, Range.<Cut<C>>all()); - } - - private ComplementRangesByLowerBound(NavigableMap<Cut<C>, Range<C>> positiveRangesByLowerBound, - Range<Cut<C>> window) { - this.positiveRangesByLowerBound = positiveRangesByLowerBound; - this.positiveRangesByUpperBound = new RangesByUpperBound<C>(positiveRangesByLowerBound); - this.complementLowerBoundWindow = window; - } - - private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> subWindow) { - if (!complementLowerBoundWindow.isConnected(subWindow)) { - return ImmutableSortedMap.of(); - } else { - subWindow = subWindow.intersection(complementLowerBoundWindow); - return new ComplementRangesByLowerBound<C>(positiveRangesByLowerBound, subWindow); - } - } - - @Override - public NavigableMap<Cut<C>, Range<C>> subMap( - Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { - return subMap(Range.range( - fromKey, BoundType.forBoolean(fromInclusive), - toKey, BoundType.forBoolean(toInclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { - return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { - return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); - } - - @Override - public Comparator<? super Cut<C>> comparator() { - return Ordering.<Cut<C>>natural(); - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { - /* - * firstComplementRangeLowerBound is the first complement range lower bound inside - * complementLowerBoundWindow. Complement range lower bounds are either positive range upper - * bounds, or Cut.belowAll(). - * - * positiveItr starts at the first positive range with lower bound greater than - * firstComplementRangeLowerBound. (Positive range lower bounds correspond to complement range - * upper bounds.) - */ - Collection<Range<C>> positiveRanges; - if (complementLowerBoundWindow.hasLowerBound()) { - positiveRanges = positiveRangesByUpperBound.tailMap( - complementLowerBoundWindow.lowerEndpoint(), - complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values(); - } else { - positiveRanges = positiveRangesByUpperBound.values(); - } - final PeekingIterator<Range<C>> positiveItr = Iterators.peekingIterator( - positiveRanges.iterator()); - final Cut<C> firstComplementRangeLowerBound; - if (complementLowerBoundWindow.contains(Cut.<C>belowAll()) && - (!positiveItr.hasNext() || positiveItr.peek().lowerBound != Cut.<C>belowAll())) { - firstComplementRangeLowerBound = Cut.belowAll(); - } else if (positiveItr.hasNext()) { - firstComplementRangeLowerBound = positiveItr.next().upperBound; - } else { - return Iterators.emptyIterator(); - } - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - Cut<C> nextComplementRangeLowerBound = firstComplementRangeLowerBound; - - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (complementLowerBoundWindow.upperBound.isLessThan(nextComplementRangeLowerBound) - || nextComplementRangeLowerBound == Cut.<C>aboveAll()) { - return endOfData(); - } - Range<C> negativeRange; - if (positiveItr.hasNext()) { - Range<C> positiveRange = positiveItr.next(); - negativeRange = Range.create(nextComplementRangeLowerBound, positiveRange.lowerBound); - nextComplementRangeLowerBound = positiveRange.upperBound; - } else { - negativeRange = Range.create(nextComplementRangeLowerBound, Cut.<C>aboveAll()); - nextComplementRangeLowerBound = Cut.aboveAll(); - } - return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); - } - }; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { - Iterator<Range<C>> itr; - /* - * firstComplementRangeUpperBound is the upper bound of the last complement range with lower - * bound inside complementLowerBoundWindow. - * - * positiveItr starts at the first positive range with upper bound less than - * firstComplementRangeUpperBound. (Positive range upper bounds correspond to complement range - * lower bounds.) - */ - Cut<C> startingPoint = complementLowerBoundWindow.hasUpperBound() - ? complementLowerBoundWindow.upperEndpoint() - : Cut.<C>aboveAll(); - boolean inclusive = complementLowerBoundWindow.hasUpperBound() - && complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; - final PeekingIterator<Range<C>> positiveItr = - Iterators.peekingIterator(positiveRangesByUpperBound.headMap(startingPoint, inclusive) - .descendingMap().values().iterator()); - Cut<C> cut; - if (positiveItr.hasNext()) { - cut = (positiveItr.peek().upperBound == Cut.<C>aboveAll()) - ? positiveItr.next().lowerBound - : positiveRangesByLowerBound.higherKey(positiveItr.peek().upperBound); - } else if (!complementLowerBoundWindow.contains(Cut.<C>belowAll()) - || positiveRangesByLowerBound.containsKey(Cut.belowAll())) { - return Iterators.emptyIterator(); - } else { - cut = positiveRangesByLowerBound.higherKey(Cut.<C>belowAll()); - } - final Cut<C> firstComplementRangeUpperBound = Objects.firstNonNull(cut, Cut.<C>aboveAll()); - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - Cut<C> nextComplementRangeUpperBound = firstComplementRangeUpperBound; - - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (nextComplementRangeUpperBound == Cut.<C>belowAll()) { - return endOfData(); - } else if (positiveItr.hasNext()) { - Range<C> positiveRange = positiveItr.next(); - Range<C> negativeRange = - Range.create(positiveRange.upperBound, nextComplementRangeUpperBound); - nextComplementRangeUpperBound = positiveRange.lowerBound; - if (complementLowerBoundWindow.lowerBound.isLessThan(negativeRange.lowerBound)) { - return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); - } - } else if (complementLowerBoundWindow.lowerBound.isLessThan(Cut.<C>belowAll())) { - Range<C> negativeRange = - Range.create(Cut.<C>belowAll(), nextComplementRangeUpperBound); - nextComplementRangeUpperBound = Cut.belowAll(); - return Maps.immutableEntry(Cut.<C>belowAll(), negativeRange); - } - return endOfData(); - } - }; - } - - @Override - public int size() { - return Iterators.size(entryIterator()); - } - - @Override - @Nullable - public Range<C> get(Object key) { - if (key instanceof Cut) { - try { - @SuppressWarnings("unchecked") - Cut<C> cut = (Cut<C>) key; - // tailMap respects the current window - Entry<Cut<C>, Range<C>> firstEntry = tailMap(cut, true).firstEntry(); - if (firstEntry != null && firstEntry.getKey().equals(cut)) { - return firstEntry.getValue(); - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - @Override - public boolean containsKey(Object key) { - return get(key) != null; - } - } - - private final class Complement extends TreeRangeSet<C> { - Complement() { - super(new ComplementRangesByLowerBound<C>(TreeRangeSet.this.rangesByLowerBound)); - } - - @Override - public void add(Range<C> rangeToAdd) { - TreeRangeSet.this.remove(rangeToAdd); - } - - @Override - public void remove(Range<C> rangeToRemove) { - TreeRangeSet.this.add(rangeToRemove); - } - - @Override - public boolean contains(C value) { - return !TreeRangeSet.this.contains(value); - } - - @Override - public RangeSet<C> complement() { - return TreeRangeSet.this; - } - } - - private static final class SubRangeSetRangesByLowerBound<C extends Comparable<?>> - extends AbstractNavigableMap<Cut<C>, Range<C>> { - /** - * lowerBoundWindow is the headMap/subMap/tailMap view; it only restricts the keys, and does not - * affect the values. - */ - private final Range<Cut<C>> lowerBoundWindow; - - /** - * restriction is the subRangeSet view; ranges are truncated to their intersection with - * restriction. - */ - private final Range<C> restriction; - - private final NavigableMap<Cut<C>, Range<C>> rangesByLowerBound; - private final NavigableMap<Cut<C>, Range<C>> rangesByUpperBound; - - private SubRangeSetRangesByLowerBound(Range<Cut<C>> lowerBoundWindow, Range<C> restriction, - NavigableMap<Cut<C>, Range<C>> rangesByLowerBound) { - this.lowerBoundWindow = checkNotNull(lowerBoundWindow); - this.restriction = checkNotNull(restriction); - this.rangesByLowerBound = checkNotNull(rangesByLowerBound); - this.rangesByUpperBound = new RangesByUpperBound<C>(rangesByLowerBound); - } - - private NavigableMap<Cut<C>, Range<C>> subMap(Range<Cut<C>> window) { - if (!window.isConnected(lowerBoundWindow)) { - return ImmutableSortedMap.of(); - } else { - return new SubRangeSetRangesByLowerBound<C>( - lowerBoundWindow.intersection(window), restriction, rangesByLowerBound); - } - } - - @Override - public NavigableMap<Cut<C>, Range<C>> subMap( - Cut<C> fromKey, boolean fromInclusive, Cut<C> toKey, boolean toInclusive) { - return subMap(Range.range( - fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> headMap(Cut<C> toKey, boolean inclusive) { - return subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); - } - - @Override - public NavigableMap<Cut<C>, Range<C>> tailMap(Cut<C> fromKey, boolean inclusive) { - return subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); - } - - @Override - public Comparator<? super Cut<C>> comparator() { - return Ordering.<Cut<C>>natural(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - return get(key) != null; - } - - @Override - @Nullable - public Range<C> get(@Nullable Object key) { - if (key instanceof Cut) { - try { - @SuppressWarnings("unchecked") // we catch CCE's - Cut<C> cut = (Cut<C>) key; - if (!lowerBoundWindow.contains(cut) || cut.compareTo(restriction.lowerBound) < 0 - || cut.compareTo(restriction.upperBound) >= 0) { - return null; - } else if (cut.equals(restriction.lowerBound)) { - // it might be present, truncated on the left - Range<C> candidate = Maps.valueOrNull(rangesByLowerBound.floorEntry(cut)); - if (candidate != null && candidate.upperBound.compareTo(restriction.lowerBound) > 0) { - return candidate.intersection(restriction); - } - } else { - Range<C> result = rangesByLowerBound.get(cut); - if (result != null) { - return result.intersection(restriction); - } - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> entryIterator() { - if (restriction.isEmpty()) { - return Iterators.emptyIterator(); - } - final Iterator<Range<C>> completeRangeItr; - if (lowerBoundWindow.upperBound.isLessThan(restriction.lowerBound)) { - return Iterators.emptyIterator(); - } else if (lowerBoundWindow.lowerBound.isLessThan(restriction.lowerBound)) { - // starts at the first range with upper bound strictly greater than restriction.lowerBound - completeRangeItr = - rangesByUpperBound.tailMap(restriction.lowerBound, false).values().iterator(); - } else { - // starts at the first range with lower bound above lowerBoundWindow.lowerBound - completeRangeItr = rangesByLowerBound.tailMap(lowerBoundWindow.lowerBound.endpoint(), - lowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values().iterator(); - } - final Cut<Cut<C>> upperBoundOnLowerBounds = Ordering.natural() - .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!completeRangeItr.hasNext()) { - return endOfData(); - } - Range<C> nextRange = completeRangeItr.next(); - if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { - return endOfData(); - } else { - nextRange = nextRange.intersection(restriction); - return Maps.immutableEntry(nextRange.lowerBound, nextRange); - } - } - }; - } - - @Override - Iterator<Entry<Cut<C>, Range<C>>> descendingEntryIterator() { - if (restriction.isEmpty()) { - return Iterators.emptyIterator(); - } - Cut<Cut<C>> upperBoundOnLowerBounds = Ordering.natural() - .min(lowerBoundWindow.upperBound, Cut.belowValue(restriction.upperBound)); - final Iterator<Range<C>> completeRangeItr = rangesByLowerBound.headMap( - upperBoundOnLowerBounds.endpoint(), - upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED) - .descendingMap().values().iterator(); - return new AbstractIterator<Entry<Cut<C>, Range<C>>>() { - @Override - protected Entry<Cut<C>, Range<C>> computeNext() { - if (!completeRangeItr.hasNext()) { - return endOfData(); - } - Range<C> nextRange = completeRangeItr.next(); - if (restriction.lowerBound.compareTo(nextRange.upperBound) >= 0) { - return endOfData(); - } - nextRange = nextRange.intersection(restriction); - if (lowerBoundWindow.contains(nextRange.lowerBound)) { - return Maps.immutableEntry(nextRange.lowerBound, nextRange); - } else { - return endOfData(); - } - } - }; - } - - @Override - public int size() { - return Iterators.size(entryIterator()); - } - } - - @Override - public RangeSet<C> subRangeSet(Range<C> view) { - return view.equals(Range.<C>all()) ? this : new SubRangeSet(view); - } - - private final class SubRangeSet extends TreeRangeSet<C> { - private final Range<C> restriction; - - SubRangeSet(Range<C> restriction) { - super(new SubRangeSetRangesByLowerBound<C>( - Range.<Cut<C>>all(), restriction, TreeRangeSet.this.rangesByLowerBound)); - this.restriction = restriction; - } - - @Override - public boolean encloses(Range<C> range) { - if (!restriction.isEmpty() && restriction.encloses(range)) { - Range<C> enclosing = TreeRangeSet.this.rangeEnclosing(range); - return enclosing != null && !enclosing.intersection(restriction).isEmpty(); - } - return false; - } - - @Override - @Nullable - public Range<C> rangeContaining(C value) { - if (!restriction.contains(value)) { - return null; - } - Range<C> result = TreeRangeSet.this.rangeContaining(value); - return (result == null) ? null : result.intersection(restriction); - } - - @Override - public void add(Range<C> rangeToAdd) { - checkArgument(restriction.encloses(rangeToAdd), "Cannot add range %s to subRangeSet(%s)", - rangeToAdd, restriction); - super.add(rangeToAdd); - } - - @Override - public void remove(Range<C> rangeToRemove) { - if (rangeToRemove.isConnected(restriction)) { - TreeRangeSet.this.remove(rangeToRemove.intersection(restriction)); - } - } - - @Override - public boolean contains(C value) { - return restriction.contains(value) && TreeRangeSet.this.contains(value); - } - - @Override - public void clear() { - TreeRangeSet.this.remove(restriction); - } - - @Override - public RangeSet<C> subRangeSet(Range<C> view) { - if (view.encloses(restriction)) { - return this; - } else if (view.isConnected(restriction)) { - return new SubRangeSet(restriction.intersection(view)); - } else { - return ImmutableRangeSet.of(); - } - } - } -} diff --git a/guava/src/com/google/common/collect/UnmodifiableIterator.java b/guava/src/com/google/common/collect/UnmodifiableIterator.java index 55ceef9..5cff61b 100644 --- a/guava/src/com/google/common/collect/UnmodifiableIterator.java +++ b/guava/src/com/google/common/collect/UnmodifiableIterator.java @@ -30,14 +30,12 @@ import java.util.Iterator; public abstract class UnmodifiableIterator<E> implements Iterator<E> { /** Constructor for use by subclasses. */ protected UnmodifiableIterator() {} - + /** * Guaranteed to throw an exception and leave the underlying data unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void remove() { throw new UnsupportedOperationException(); diff --git a/guava/src/com/google/common/collect/UnmodifiableListIterator.java b/guava/src/com/google/common/collect/UnmodifiableListIterator.java index 8e535a3..fa71bdc 100644 --- a/guava/src/com/google/common/collect/UnmodifiableListIterator.java +++ b/guava/src/com/google/common/collect/UnmodifiableListIterator.java @@ -37,9 +37,8 @@ public abstract class UnmodifiableListIterator<E> * Guaranteed to throw an exception and leave the underlying data unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void add(E e) { + @Override public final void add(E e) { throw new UnsupportedOperationException(); } @@ -47,9 +46,8 @@ public abstract class UnmodifiableListIterator<E> * Guaranteed to throw an exception and leave the underlying data unmodified. * * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. */ - @Deprecated @Override public final void set(E e) { + @Override public final void set(E e) { throw new UnsupportedOperationException(); } } diff --git a/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java b/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java deleted file mode 100644 index 4b353cb..0000000 --- a/guava/src/com/google/common/collect/UnmodifiableSortedMultiset.java +++ /dev/null @@ -1,114 +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 com.google.common.collect.Multisets.UnmodifiableMultiset; - -import java.util.Comparator; -import java.util.NavigableSet; - -/** - * Implementation of {@link Multisets#unmodifiableSortedMultiset(SortedMultiset)}, - * split out into its own file so it can be GWT emulated (to deal with the differing - * elementSet() types in GWT and non-GWT). - * - * @author Louis Wasserman - */ -@GwtCompatible(emulated = true) -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 - NavigableSet<E> createElementSet() { - return Sets.unmodifiableNavigableSet(delegate().elementSet()); - } - - @Override - public NavigableSet<E> elementSet() { - return (NavigableSet<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/src/com/google/common/collect/UsingToStringOrdering.java b/guava/src/com/google/common/collect/UsingToStringOrdering.java index adb6aa7..d1c9feb 100644 --- a/guava/src/com/google/common/collect/UsingToStringOrdering.java +++ b/guava/src/com/google/common/collect/UsingToStringOrdering.java @@ -20,10 +20,7 @@ import com.google.common.annotations.GwtCompatible; import java.io.Serializable; -/** - * An ordering that uses the natural order of the string representation of the - * values. - */ +/** An ordering that uses the reverse of the natural order of the values. */ @GwtCompatible(serializable = true) final class UsingToStringOrdering extends Ordering<Object> implements Serializable { diff --git a/guava/src/com/google/common/collect/WellBehavedMap.java b/guava/src/com/google/common/collect/WellBehavedMap.java index c68cc5e..e8aa1f6 100644 --- a/guava/src/com/google/common/collect/WellBehavedMap.java +++ b/guava/src/com/google/common/collect/WellBehavedMap.java @@ -18,35 +18,32 @@ package com.google.common.collect; import com.google.common.annotations.GwtCompatible; -import java.util.Iterator; import java.util.Map; import java.util.Set; /** - * Workaround for + * Workaround for * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6312706"> * EnumMap bug</a>. If you want to pass an {@code EnumMap}, with the * intention of using its {@code entrySet()} method, you should - * wrap the {@code EnumMap} in this class instead. - * - * <p>This class is not thread-safe even if the underlying map is. - * + * wrap the {@code EnumMap} in this class instead. + * * @author Dimitris Andreou */ @GwtCompatible final class WellBehavedMap<K, V> extends ForwardingMap<K, V> { private final Map<K, V> delegate; private Set<Entry<K, V>> entrySet; - + private WellBehavedMap(Map<K, V> delegate) { this.delegate = delegate; } - + /** * Wraps the given map into a {@code WellBehavedEntriesMap}, which - * intercepts its {@code entrySet()} method by taking the + * intercepts its {@code entrySet()} method by taking the * {@code Set<K> keySet()} and transforming it to - * {@code Set<Entry<K, V>>}. All other invocations are delegated as-is. + * {@code Set<Entry<K, V>>}. All other invocations are delegated as-is. */ static <K, V> WellBehavedMap<K, V> wrap(Map<K, V> delegate) { return new WellBehavedMap<K, V>(delegate); @@ -61,38 +58,34 @@ final class WellBehavedMap<K, V> extends ForwardingMap<K, V> { if (es != null) { return es; } - return entrySet = new EntrySet(); + return entrySet = Sets.transform( + delegate.keySet(), new KeyToEntryConverter<K, V>(this)); } - - private final class EntrySet extends Maps.EntrySet<K, V> { - @Override - Map<K, V> map() { - return WellBehavedMap.this; + + private static class KeyToEntryConverter<K, V> + extends Sets.InvertibleFunction<K, Map.Entry<K, V>> { + final Map<K, V> map; + + KeyToEntryConverter(Map<K, V> map) { + this.map = map; } - @Override - public Iterator<Entry<K, V>> iterator() { - return new TransformedIterator<K, Entry<K, V>>(keySet().iterator()) { - @Override - Entry<K, V> transform(final K key) { - return new AbstractMapEntry<K, V>() { - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return get(key); - } - - @Override - public V setValue(V value) { - return put(key, value); - } - }; + @Override public Map.Entry<K, V> apply(final K key) { + return new AbstractMapEntry<K, V>() { + @Override public K getKey() { + return key; + } + @Override public V getValue() { + return map.get(key); + } + @Override public V setValue(V value) { + return map.put(key, value); } }; } + + @Override public K invert(Map.Entry<K, V> entry) { + return entry.getKey(); + } } } diff --git a/guava/src/com/google/common/collect/package-info.java b/guava/src/com/google/common/collect/package-info.java index 3ffbad9..f5c833c 100644 --- a/guava/src/com/google/common/collect/package-info.java +++ b/guava/src/com/google/common/collect/package-info.java @@ -85,7 +85,7 @@ * <ul> * <li>{@link com.google.common.collect.ImmutableSet} * <li>{@link com.google.common.collect.ImmutableSortedSet} - * <li>{@link com.google.common.collect.ContiguousSet} (see {@code Range}) + * <li>{@link com.google.common.collect.ContiguousSet} (see {@code Ranges}) * </ul> * * <h3>of {@link java.util.Map}</h3> @@ -147,10 +147,10 @@ * <li>{@link com.google.common.collect.Iterables} * <li>{@link com.google.common.collect.Lists} * <li>{@link com.google.common.collect.Maps} - * <li>{@link com.google.common.collect.Queues} * <li>{@link com.google.common.collect.Sets} * <li>{@link com.google.common.collect.Multisets} * <li>{@link com.google.common.collect.Multimaps} + * <li>{@link com.google.common.collect.SortedMaps} * <li>{@link com.google.common.collect.Tables} * <li>{@link com.google.common.collect.ObjectArrays} * </ul> @@ -166,7 +166,7 @@ * * <ul> * <li>{@link com.google.common.collect.AbstractIterator} - * <li>{@link com.google.common.collect.AbstractSequentialIterator} + * <li>{@link com.google.common.collect.AbstractLinkedIterator} * <li>{@link com.google.common.collect.ImmutableCollection} * <li>{@link com.google.common.collect.UnmodifiableIterator} * <li>{@link com.google.common.collect.UnmodifiableListIterator} @@ -176,6 +176,7 @@ * * <ul> * <li>{@link com.google.common.collect.Range} + * <li>{@link com.google.common.collect.Ranges} * <li>{@link com.google.common.collect.DiscreteDomain} * <li>{@link com.google.common.collect.DiscreteDomains} * <li>{@link com.google.common.collect.ContiguousSet} @@ -209,13 +210,12 @@ * <li>{@link com.google.common.collect.ForwardingMapEntry} * <li>{@link com.google.common.collect.ForwardingMultimap} * <li>{@link com.google.common.collect.ForwardingMultiset} - * <li>{@link com.google.common.collect.ForwardingNavigableMap} - * <li>{@link com.google.common.collect.ForwardingNavigableSet} * <li>{@link com.google.common.collect.ForwardingObject} * <li>{@link com.google.common.collect.ForwardingQueue} * <li>{@link com.google.common.collect.ForwardingSet} * <li>{@link com.google.common.collect.ForwardingSetMultimap} * <li>{@link com.google.common.collect.ForwardingSortedMap} + * <li>{@link com.google.common.collect.ForwardingSortedMultiset} * <li>{@link com.google.common.collect.ForwardingSortedSet} * <li>{@link com.google.common.collect.ForwardingSortedSetMultimap} * <li>{@link com.google.common.collect.ForwardingTable} diff --git a/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java b/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java index f614598..5699d73 100644 --- a/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java +++ b/guava/src/com/google/common/eventbus/AllowConcurrentEvents.java @@ -17,7 +17,6 @@ package com.google.common.eventbus; import com.google.common.annotations.Beta; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/guava/src/com/google/common/eventbus/AnnotatedHandlerFinder.java b/guava/src/com/google/common/eventbus/AnnotatedHandlerFinder.java index fe93b21..7153c66 100644 --- a/guava/src/com/google/common/eventbus/AnnotatedHandlerFinder.java +++ b/guava/src/com/google/common/eventbus/AnnotatedHandlerFinder.java @@ -16,97 +16,50 @@ package com.google.common.eventbus; -import com.google.common.base.Throwables; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; -import com.google.common.reflect.TypeToken; -import com.google.common.util.concurrent.UncheckedExecutionException; - import java.lang.reflect.Method; -import java.util.Set; /** - * A {@link HandlerFindingStrategy} for collecting all event handler methods that are marked with - * the {@link Subscribe} annotation. + * A {@link HandlerFindingStrategy} for collecting all event handler methods + * that are marked with the {@link Subscribe} annotation. * * @author Cliff Biffle - * @author Louis Wasserman */ class AnnotatedHandlerFinder implements HandlerFindingStrategy { - /** - * A thread-safe cache that contains the mapping from each class to all methods in that class and - * all super-classes, that are annotated with {@code @Subscribe}. The cache is shared across all - * instances of this class; this greatly improves performance if multiple EventBus instances are - * created and objects of the same class are registered on all of them. - */ - private static final LoadingCache<Class<?>, ImmutableList<Method>> handlerMethodsCache = - CacheBuilder.newBuilder() - .weakKeys() - .build(new CacheLoader<Class<?>, ImmutableList<Method>>() { - @Override - public ImmutableList<Method> load(Class<?> concreteClass) throws Exception { - return getAnnotatedMethodsInternal(concreteClass); - } - }); /** * {@inheritDoc} * - * This implementation finds all methods marked with a {@link Subscribe} annotation. + * This implementation finds all methods marked with a {@link Subscribe} + * annotation. */ @Override public Multimap<Class<?>, EventHandler> findAllHandlers(Object listener) { - Multimap<Class<?>, EventHandler> methodsInListener = HashMultimap.create(); - Class<?> clazz = listener.getClass(); - for (Method method : getAnnotatedMethods(clazz)) { - Class<?>[] parameterTypes = method.getParameterTypes(); - Class<?> eventType = parameterTypes[0]; - EventHandler handler = makeHandler(listener, method); - methodsInListener.put(eventType, handler); - } - return methodsInListener; - } + Multimap<Class<?>, EventHandler> methodsInListener = + HashMultimap.create(); + Class clazz = listener.getClass(); + while (clazz != null) { + for (Method method : clazz.getMethods()) { + Subscribe annotation = method.getAnnotation(Subscribe.class); - private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) { - try { - return handlerMethodsCache.getUnchecked(clazz); - } catch (UncheckedExecutionException e) { - throw Throwables.propagate(e.getCause()); - } - } - - private static ImmutableList<Method> getAnnotatedMethodsInternal(Class<?> clazz) { - Set<? extends Class<?>> supers = TypeToken.of(clazz).getTypes().rawTypes(); - ImmutableList.Builder<Method> result = ImmutableList.builder(); - for (Method method : clazz.getMethods()) { - /* - * Iterate over each distinct method of {@code clazz}, checking if it is annotated with - * @Subscribe by any of the superclasses or superinterfaces that declare it. - */ - for (Class<?> c : supers) { - try { - Method m = c.getMethod(method.getName(), method.getParameterTypes()); - if (m.isAnnotationPresent(Subscribe.class)) { - Class<?>[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length != 1) { - throw new IllegalArgumentException("Method " + method - + " has @Subscribe annotation, but requires " + parameterTypes.length - + " arguments. Event handler methods must require a single argument."); - } - Class<?> eventType = parameterTypes[0]; - result.add(method); - break; + if (annotation != null) { + Class<?>[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length != 1) { + throw new IllegalArgumentException( + "Method " + method + " has @Subscribe annotation, but requires " + + parameterTypes.length + " arguments. Event handler methods " + + "must require a single argument."); } - } catch (NoSuchMethodException ignored) { - // Move on. + Class<?> eventType = parameterTypes[0]; + EventHandler handler = makeHandler(listener, method); + + methodsInListener.put(eventType, handler); } } + clazz = clazz.getSuperclass(); } - return result.build(); + return methodsInListener; } /** diff --git a/guava/src/com/google/common/eventbus/AsyncEventBus.java b/guava/src/com/google/common/eventbus/AsyncEventBus.java index eb57aba..8018cee 100644 --- a/guava/src/com/google/common/eventbus/AsyncEventBus.java +++ b/guava/src/com/google/common/eventbus/AsyncEventBus.java @@ -16,10 +16,7 @@ package com.google.common.eventbus; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.Beta; - import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; @@ -49,7 +46,7 @@ public class AsyncEventBus extends EventBus { */ public AsyncEventBus(String identifier, Executor executor) { super(identifier); - this.executor = checkNotNull(executor); + this.executor = executor; } /** @@ -61,11 +58,11 @@ public class AsyncEventBus extends EventBus { * been posted to this event bus. */ public AsyncEventBus(Executor executor) { - this.executor = checkNotNull(executor); + this.executor = executor; } @Override - void enqueueEvent(Object event, EventHandler handler) { + protected void enqueueEvent(Object event, EventHandler handler) { eventsToDispatch.offer(new EventWithHandler(event, handler)); } @@ -73,7 +70,6 @@ public class AsyncEventBus extends EventBus { * Dispatch {@code events} in the order they were posted, regardless of * the posting thread. */ - @SuppressWarnings("deprecation") // only deprecated for external subclasses @Override protected void dispatchQueuedEvents() { while (true) { @@ -90,15 +86,14 @@ public class AsyncEventBus extends EventBus { * Calls the {@link #executor} to dispatch {@code event} to {@code handler}. */ @Override - void dispatch(final Object event, final EventHandler handler) { - checkNotNull(event); - checkNotNull(handler); - executor.execute( - new Runnable() { + protected void dispatch(final Object event, final EventHandler handler) { + executor.execute(new Runnable() { @Override + @SuppressWarnings("synthetic-access") public void run() { AsyncEventBus.super.dispatch(event, handler); } }); } + } diff --git a/guava/src/com/google/common/eventbus/DeadEvent.java b/guava/src/com/google/common/eventbus/DeadEvent.java index 03b4a2e..8265d5a 100644 --- a/guava/src/com/google/common/eventbus/DeadEvent.java +++ b/guava/src/com/google/common/eventbus/DeadEvent.java @@ -16,8 +16,6 @@ package com.google.common.eventbus; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.Beta; /** @@ -44,8 +42,8 @@ public class DeadEvent { * @param event the event that could not be delivered. */ public DeadEvent(Object source, Object event) { - this.source = checkNotNull(source); - this.event = checkNotNull(event); + this.source = source; + this.event = event; } /** diff --git a/guava/src/com/google/common/eventbus/EventBus.java b/guava/src/com/google/common/eventbus/EventBus.java index 94cf2e9..a962fa8 100644 --- a/guava/src/com/google/common/eventbus/EventBus.java +++ b/guava/src/com/google/common/eventbus/EventBus.java @@ -16,28 +16,28 @@ package com.google.common.eventbus; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Supplier; import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; -import com.google.common.reflect.TypeToken; -import com.google.common.util.concurrent.UncheckedExecutionException; +import com.google.common.collect.Sets; import java.lang.reflect.InvocationTargetException; import java.util.Collection; -import java.util.LinkedList; +import java.util.List; import java.util.Map.Entry; -import java.util.Queue; import java.util.Set; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; @@ -101,10 +101,6 @@ import java.util.logging.Logger; * receive any Object will never receive a DeadEvent. * * <p>This class is safe for concurrent use. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/EventBusExplained"> - * {@code EventBus}</a>. * * @author Cliff Biffle * @since 10.0 @@ -113,32 +109,18 @@ import java.util.logging.Logger; public class EventBus { /** - * A thread-safe cache for flattenHierarchy(). The Class class is immutable. This cache is shared - * across all EventBus instances, which greatly improves performance if multiple such instances - * are created and objects of the same class are posted on all of them. + * All registered event handlers, indexed by event type. */ - private static final LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache = - CacheBuilder.newBuilder() - .weakKeys() - .build(new CacheLoader<Class<?>, Set<Class<?>>>() { - @SuppressWarnings({"unchecked", "rawtypes"}) // safe cast + private final SetMultimap<Class<?>, EventHandler> handlersByType = + Multimaps.newSetMultimap(new ConcurrentHashMap<Class<?>, Collection<EventHandler>>(), + new Supplier<Set<EventHandler>>() { @Override - public Set<Class<?>> load(Class<?> concreteClass) { - return (Set) TypeToken.of(concreteClass).getTypes().rawTypes(); + public Set<EventHandler> get() { + return new CopyOnWriteArraySet<EventHandler>(); } }); /** - * All registered event handlers, indexed by event type. - * - * <p>This SetMultimap is NOT safe for concurrent use; all access should be - * made after acquiring a read or write lock via {@link #handlersByTypeLock}. - */ - private final SetMultimap<Class<?>, EventHandler> handlersByType = - HashMultimap.create(); - private final ReadWriteLock handlersByTypeLock = new ReentrantReadWriteLock(); - - /** * Logger for event dispatch failures. Named by the fully-qualified name of * this class, followed by the identifier provided at construction. */ @@ -152,10 +134,11 @@ public class EventBus { private final HandlerFindingStrategy finder = new AnnotatedHandlerFinder(); /** queues of events for the current thread to dispatch */ - private final ThreadLocal<Queue<EventWithHandler>> eventsToDispatch = - new ThreadLocal<Queue<EventWithHandler>>() { - @Override protected Queue<EventWithHandler> initialValue() { - return new LinkedList<EventWithHandler>(); + private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> + eventsToDispatch = + new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() { + @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() { + return new ConcurrentLinkedQueue<EventWithHandler>(); } }; @@ -168,6 +151,38 @@ public class EventBus { }; /** + * A thread-safe cache for flattenHierarch(). The Class class is immutable. + */ + private LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache = + CacheBuilder.newBuilder() + .weakKeys() + .build(new CacheLoader<Class<?>, Set<Class<?>>>() { + @Override + public Set<Class<?>> load(Class<?> concreteClass) throws Exception { + List<Class<?>> parents = Lists.newLinkedList(); + Set<Class<?>> classes = Sets.newHashSet(); + + parents.add(concreteClass); + + while (!parents.isEmpty()) { + Class<?> clazz = parents.remove(0); + classes.add(clazz); + + Class<?> parent = clazz.getSuperclass(); + if (parent != null) { + parents.add(parent); + } + + for (Class<?> iface : clazz.getInterfaces()) { + parents.add(iface); + } + } + + return classes; + } + }); + + /** * Creates a new EventBus named "default". */ public EventBus() { @@ -181,7 +196,7 @@ public class EventBus { * be a valid Java identifier. */ public EventBus(String identifier) { - logger = Logger.getLogger(EventBus.class.getName() + "." + checkNotNull(identifier)); + logger = Logger.getLogger(EventBus.class.getName() + "." + identifier); } /** @@ -193,14 +208,7 @@ public class EventBus { * @param object object whose handler methods should be registered. */ public void register(Object object) { - Multimap<Class<?>, EventHandler> methodsInListener = - finder.findAllHandlers(object); - handlersByTypeLock.writeLock().lock(); - try { - handlersByType.putAll(methodsInListener); - } finally { - handlersByTypeLock.writeLock().unlock(); - } + handlersByType.putAll(finder.findAllHandlers(object)); } /** @@ -212,20 +220,14 @@ public class EventBus { public void unregister(Object object) { Multimap<Class<?>, EventHandler> methodsInListener = finder.findAllHandlers(object); for (Entry<Class<?>, Collection<EventHandler>> entry : methodsInListener.asMap().entrySet()) { - Class<?> eventType = entry.getKey(); + Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey()); Collection<EventHandler> eventMethodsInListener = entry.getValue(); - - handlersByTypeLock.writeLock().lock(); - try { - Set<EventHandler> currentHandlers = handlersByType.get(eventType); - if (!currentHandlers.containsAll(eventMethodsInListener)) { - throw new IllegalArgumentException( - "missing event handler for an annotated method. Is " + object + " registered?"); - } - currentHandlers.removeAll(eventMethodsInListener); - } finally { - handlersByTypeLock.writeLock().unlock(); + + if (currentHandlers == null || !currentHandlers.containsAll(entry.getValue())) { + throw new IllegalArgumentException( + "missing event handler for an annotated method. Is " + object + " registered?"); } + currentHandlers.removeAll(eventMethodsInListener); } } @@ -245,18 +247,13 @@ public class EventBus { boolean dispatched = false; for (Class<?> eventType : dispatchTypes) { - handlersByTypeLock.readLock().lock(); - try { - Set<EventHandler> wrappers = handlersByType.get(eventType); - - if (!wrappers.isEmpty()) { - dispatched = true; - for (EventHandler wrapper : wrappers) { - enqueueEvent(event, wrapper); - } + Set<EventHandler> wrappers = getHandlersForEventType(eventType); + + if (wrappers != null && !wrappers.isEmpty()) { + dispatched = true; + for (EventHandler wrapper : wrappers) { + enqueueEvent(event, wrapper); } - } finally { - handlersByTypeLock.readLock().unlock(); } } @@ -272,7 +269,7 @@ public class EventBus { * {@link #dispatchQueuedEvents()}. Events are queued in-order of occurrence * so they can be dispatched in the same order. */ - void enqueueEvent(Object event, EventHandler handler) { + protected void enqueueEvent(Object event, EventHandler handler) { eventsToDispatch.get().offer(new EventWithHandler(event, handler)); } @@ -280,7 +277,7 @@ public class EventBus { * Drain the queue of events to be dispatched. As the queue is being drained, * new events may be posted to the end of the queue. */ - void dispatchQueuedEvents() { + protected void dispatchQueuedEvents() { // don't dispatch if we're already dispatching, that would allow reentrancy // and out-of-order events. Instead, leave the events to be dispatched // after the in-progress dispatch is complete. @@ -290,14 +287,16 @@ public class EventBus { isDispatching.set(true); try { - Queue<EventWithHandler> events = eventsToDispatch.get(); - EventWithHandler eventWithHandler; - while ((eventWithHandler = events.poll()) != null) { + while (true) { + EventWithHandler eventWithHandler = eventsToDispatch.get().poll(); + if (eventWithHandler == null) { + break; + } + dispatch(eventWithHandler.event, eventWithHandler.handler); } } finally { - isDispatching.remove(); - eventsToDispatch.remove(); + isDispatching.set(false); } } @@ -309,7 +308,7 @@ public class EventBus { * @param event event to dispatch. * @param wrapper wrapper that will call the handler. */ - void dispatch(Object event, EventHandler wrapper) { + protected void dispatch(Object event, EventHandler wrapper) { try { wrapper.handleEvent(event); } catch (InvocationTargetException e) { @@ -319,6 +318,29 @@ public class EventBus { } /** + * Retrieves a mutable set of the currently registered handlers for + * {@code type}. If no handlers are currently registered for {@code type}, + * this method may either return {@code null} or an empty set. + * + * @param type type of handlers to retrieve. + * @return currently registered handlers, or {@code null}. + */ + Set<EventHandler> getHandlersForEventType(Class<?> type) { + return handlersByType.get(type); + } + + /** + * Creates a new Set for insertion into the handler map. This is provided + * as an override point for subclasses. The returned set should support + * concurrent access. + * + * @return a new, mutable set for handlers. + */ + protected Set<EventHandler> newHandlerSet() { + return new CopyOnWriteArraySet<EventHandler>(); + } + + /** * Flattens a class's type hierarchy into a set of Class objects. The set * will include all superclasses (transitively), and all interfaces * implemented by these superclasses. @@ -329,8 +351,8 @@ public class EventBus { @VisibleForTesting Set<Class<?>> flattenHierarchy(Class<?> concreteClass) { try { - return flattenHierarchyCache.getUnchecked(concreteClass); - } catch (UncheckedExecutionException e) { + return flattenHierarchyCache.get(concreteClass); + } catch (ExecutionException e) { throw Throwables.propagate(e.getCause()); } } @@ -340,8 +362,8 @@ public class EventBus { final Object event; final EventHandler handler; public EventWithHandler(Object event, EventHandler handler) { - this.event = checkNotNull(event); - this.handler = checkNotNull(handler); + this.event = event; + this.handler = handler; } } } diff --git a/guava/src/com/google/common/eventbus/EventHandler.java b/guava/src/com/google/common/eventbus/EventHandler.java index 45efa80..be89973 100644 --- a/guava/src/com/google/common/eventbus/EventHandler.java +++ b/guava/src/com/google/common/eventbus/EventHandler.java @@ -16,15 +16,10 @@ package com.google.common.eventbus; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.base.Preconditions; - import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import javax.annotation.Nullable; - /** * Wraps a single-argument 'handler' method on a specific object. * @@ -65,11 +60,10 @@ class EventHandler { * * @param event event to handle * @throws InvocationTargetException if the wrapped method throws any - * {@link Throwable} that is not an {@link Error} ({@code Error} instances are + * {@link Throwable} that is not an {@link Error} ({@code Error}s are * propagated as-is). */ public void handleEvent(Object event) throws InvocationTargetException { - checkNotNull(event); try { method.invoke(target, new Object[] { event }); } catch (IllegalArgumentException e) { @@ -90,18 +84,25 @@ class EventHandler { @Override public int hashCode() { final int PRIME = 31; - return (PRIME + method.hashCode()) * PRIME - + System.identityHashCode(target); + return (PRIME + method.hashCode()) * PRIME + target.hashCode(); } - @Override public boolean equals(@Nullable Object obj) { - if (obj instanceof EventHandler) { - EventHandler that = (EventHandler) obj; - // Use == so that different equal instances will still receive events. - // We only guard against the case that the same object is registered - // multiple times - return target == that.target && method.equals(that.method); + @Override public boolean equals(Object obj) { + if(this == obj) { + return true; + } + + if(obj == null) { + return false; } - return false; + + if(getClass() != obj.getClass()) { + return false; + } + + final EventHandler other = (EventHandler) obj; + + return method.equals(other.method) && target == other.target; } + } diff --git a/guava/src/com/google/common/eventbus/HandlerFindingStrategy.java b/guava/src/com/google/common/eventbus/HandlerFindingStrategy.java index 3f62cae..7144203 100644 --- a/guava/src/com/google/common/eventbus/HandlerFindingStrategy.java +++ b/guava/src/com/google/common/eventbus/HandlerFindingStrategy.java @@ -28,7 +28,7 @@ interface HandlerFindingStrategy { /** * Finds all suitable event handler methods in {@code source}, organizes them - * by the type of event they handle, and wraps them in {@link EventHandler} instances. + * by the type of event they handle, and wraps them in {@link EventHandler}s. * * @param source object whose handlers are desired. * @return EventHandler objects for each handler method, organized by event diff --git a/guava/src/com/google/common/eventbus/Subscribe.java b/guava/src/com/google/common/eventbus/Subscribe.java index 34cb587..568c1c0 100644 --- a/guava/src/com/google/common/eventbus/Subscribe.java +++ b/guava/src/com/google/common/eventbus/Subscribe.java @@ -17,7 +17,6 @@ package com.google.common.eventbus; import com.google.common.annotations.Beta; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/guava/src/com/google/common/eventbus/package-info.java b/guava/src/com/google/common/eventbus/package-info.java index 28637cd..0c83104 100644 --- a/guava/src/com/google/common/eventbus/package-info.java +++ b/guava/src/com/google/common/eventbus/package-info.java @@ -21,10 +21,6 @@ * traditional Java in-process event distribution using explicit registration. * It is <em>not</em> a general-purpose publish-subscribe system, nor is it * intended for interprocess communication. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/EventBusExplained"> - * {@code EventBus}</a>. * * <h2>One-Minute Guide</h2> * @@ -123,6 +119,20 @@ * <p>In short, the EventBus is not a singleton because we'd rather not make * that decision for you. Use it how you like. * + * <h3>Can I unregister a listener from the Event Bus?</h3> + * Currently, no -- a listener registered with an EventBus instance will + * continue to receive events until the EventBus itself is disposed. + * + * <p>In the apps using EventBus so far, this has not been a problem: + * <ul> + * <li>Most listeners are registered on startup or lazy initialization, and + * persist for the life of the application. + * <li>Scope-specific EventBus instances can handle temporary event + * distribution (e.g. distributing events among request-scoped objects) + * <li>For testing, EventBus instances can be easily created and thrown away, + * removing the need for explicit unregistration. + * </ul> + * * <h3>Why use an annotation to mark handler methods, rather than requiring the * listener to implement an interface?</h3> * We feel that the Event Bus's {@code @Subscribe} annotation conveys your diff --git a/guava/src/com/google/common/hash/AbstractByteHasher.java b/guava/src/com/google/common/hash/AbstractByteHasher.java deleted file mode 100644 index 90210df..0000000 --- a/guava/src/com/google/common/hash/AbstractByteHasher.java +++ /dev/null @@ -1,122 +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.hash; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkPositionIndexes; - -import com.google.common.primitives.Chars; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; -import com.google.common.primitives.Shorts; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Abstract {@link Hasher} that handles converting primitives to bytes using a scratch {@code - * ByteBuffer} and streams all bytes to a sink to compute the hash. - * - * @author Colin Decker - */ -abstract class AbstractByteHasher extends AbstractHasher { - - private final ByteBuffer scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); - - /** - * Updates this hasher with the given byte. - */ - protected abstract void update(byte b); - - /** - * Updates this hasher with the given bytes. - */ - protected void update(byte[] b) { - update(b, 0, b.length); - } - - /** - * Updates this hasher with {@code len} bytes starting at {@code off} in the given buffer. - */ - protected void update(byte[] b, int off, int len) { - for (int i = off; i < off + len; i++) { - update(b[i]); - } - } - - @Override - public Hasher putByte(byte b) { - update(b); - return this; - } - - @Override - public Hasher putBytes(byte[] bytes) { - checkNotNull(bytes); - update(bytes); - return this; - } - - @Override - public Hasher putBytes(byte[] bytes, int off, int len) { - checkPositionIndexes(off, off + len, bytes.length); - update(bytes, off, len); - return this; - } - - /** - * Updates the sink with the given number of bytes from the buffer. - */ - private Hasher update(int bytes) { - try { - update(scratch.array(), 0, bytes); - } finally { - scratch.clear(); - } - return this; - } - - @Override - public Hasher putShort(short s) { - scratch.putShort(s); - return update(Shorts.BYTES); - } - - @Override - public Hasher putInt(int i) { - scratch.putInt(i); - return update(Ints.BYTES); - } - - @Override - public Hasher putLong(long l) { - scratch.putLong(l); - return update(Longs.BYTES); - } - - @Override - public Hasher putChar(char c) { - scratch.putChar(c); - return update(Chars.BYTES); - } - - @Override - public <T> Hasher putObject(T instance, Funnel<? super T> funnel) { - funnel.funnel(instance, this); - return this; - } -} diff --git a/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java b/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java index 6a0f452..41c4836 100644 --- a/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java +++ b/guava/src/com/google/common/hash/AbstractCompositeHashFunction.java @@ -1,42 +1,23 @@ -/* - * 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. - */ +// Copyright 2011 Google Inc. All Rights Reserved. package com.google.common.hash; -import static com.google.common.base.Preconditions.checkNotNull; - import java.nio.charset.Charset; /** * An abstract composition of multiple hash functions. {@linkplain #newHasher()} delegates to the * {@code Hasher} objects of the delegate hash functions, and in the end, they are used by * {@linkplain #makeHash(Hasher[])} that constructs the final {@code HashCode}. - * - * @author Dimitris Andreou + * + * @author andreou@google.com (Dimitris Andreou) */ abstract class AbstractCompositeHashFunction extends AbstractStreamingHashFunction { final HashFunction[] functions; - + AbstractCompositeHashFunction(HashFunction... functions) { - for (HashFunction function : functions) { - checkNotNull(function); - } this.functions = functions; } - + /** * Constructs a {@code HashCode} from the {@code Hasher} objects of the functions. Each of them * has consumed the entire input and they are ready to output a {@code HashCode}. The order of @@ -44,7 +25,7 @@ abstract class AbstractCompositeHashFunction extends AbstractStreamingHashFuncti */ // this could be cleaner if it passed HashCode[], but that would create yet another array... /* protected */ abstract HashCode makeHash(Hasher[] hashers); - + @Override public Hasher newHasher() { final Hasher[] hashers = new Hasher[functions.length]; @@ -148,6 +129,6 @@ abstract class AbstractCompositeHashFunction extends AbstractStreamingHashFuncti } }; } - + private static final long serialVersionUID = 0L; } diff --git a/guava/src/com/google/common/hash/AbstractHasher.java b/guava/src/com/google/common/hash/AbstractHasher.java index f60f928..4abc6a3 100644 --- a/guava/src/com/google/common/hash/AbstractHasher.java +++ b/guava/src/com/google/common/hash/AbstractHasher.java @@ -14,6 +14,8 @@ package com.google.common.hash; +import com.google.common.base.Charsets; + import java.nio.charset.Charset; /** @@ -21,7 +23,7 @@ import java.nio.charset.Charset; * {@link #putFloat(float)}, {@link #putString(CharSequence)}, and * {@link #putString(CharSequence, Charset)} as prescribed by {@link Hasher}. * - * @author Dimitris Andreou + * @author andreou@google.com (Dimitris Andreou) */ abstract class AbstractHasher implements Hasher { @Override public final Hasher putBoolean(boolean b) { @@ -37,13 +39,15 @@ abstract class AbstractHasher implements Hasher { } @Override public Hasher putString(CharSequence charSequence) { - for (int i = 0, len = charSequence.length(); i < len; i++) { - putChar(charSequence.charAt(i)); - } - return this; + // TODO(user): Should we instead loop over the CharSequence and call #putChar? + return putString(charSequence, Charsets.UTF_16LE); } @Override public Hasher putString(CharSequence charSequence, Charset charset) { - return putBytes(charSequence.toString().getBytes(charset)); + try { + return putBytes(charSequence.toString().getBytes(charset.name())); + } catch (java.io.UnsupportedEncodingException impossible) { + throw new AssertionError(impossible); + } } } diff --git a/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java b/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java index 0025d3c..68e9ace 100644 --- a/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java +++ b/guava/src/com/google/common/hash/AbstractNonStreamingHashFunction.java @@ -1,18 +1,4 @@ -/* - * 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. - */ +// Copyright 2011 Google Inc. All Rights Reserved. package com.google.common.hash; @@ -21,14 +7,13 @@ import com.google.common.base.Throwables; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.charset.Charset; /** * Skeleton implementation of {@link HashFunction}, appropriate for non-streaming algorithms. - * All the hash computation done using {@linkplain #newHasher()} are delegated to the {@linkplain - * #hashBytes(byte[], int, int)} method. - * - * @author Dimitris Andreou + * All the hash computation done using {@linkplain #newHasher()} are delegated to the {@linkplain + * #hashBytes(byte[], int, int)} method. + * + * @author andreou@google.com (Dimitris Andreou) */ abstract class AbstractNonStreamingHashFunction implements HashFunction { @Override @@ -41,43 +26,14 @@ abstract class AbstractNonStreamingHashFunction implements HashFunction { Preconditions.checkArgument(expectedInputSize >= 0); return new BufferingHasher(expectedInputSize); } - - @Override public <T> HashCode hashObject(T instance, Funnel<? super T> funnel) { - return newHasher().putObject(instance, funnel).hash(); - } - - @Override public HashCode hashString(CharSequence input) { - int len = input.length(); - Hasher hasher = newHasher(len * 2); - for (int i = 0; i < len; i++) { - hasher.putChar(input.charAt(i)); - } - return hasher.hash(); - } - - @Override public HashCode hashString(CharSequence input, Charset charset) { - return hashBytes(input.toString().getBytes(charset)); - } - - @Override public HashCode hashInt(int input) { - return newHasher(4).putInt(input).hash(); - } - - @Override public HashCode hashLong(long input) { - return newHasher(8).putLong(input).hash(); - } - - @Override public HashCode hashBytes(byte[] input) { - return hashBytes(input, 0, input.length); - } - + /** - * In-memory stream-based implementation of Hasher. + * In-memory stream-based implementation of Hasher. */ private final class BufferingHasher extends AbstractHasher { final ExposedByteArrayOutputStream stream; static final int BOTTOM_BYTE = 0xFF; - + BufferingHasher(int expectedInputSize) { this.stream = new ExposedByteArrayOutputStream(expectedInputSize); } @@ -97,7 +53,7 @@ abstract class AbstractNonStreamingHashFunction implements HashFunction { } return this; } - + @Override public Hasher putBytes(byte[] bytes, int off, int len) { stream.write(bytes, off, len); @@ -146,7 +102,7 @@ abstract class AbstractNonStreamingHashFunction implements HashFunction { return hashBytes(stream.byteArray(), 0, stream.length()); } } - + // Just to access the byte[] without introducing an unnecessary copy private static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream { ExposedByteArrayOutputStream(int expectedInputSize) { diff --git a/guava/src/com/google/common/hash/AbstractStreamingHashFunction.java b/guava/src/com/google/common/hash/AbstractStreamingHashFunction.java index ea6f692..de7748f 100644 --- a/guava/src/com/google/common/hash/AbstractStreamingHashFunction.java +++ b/guava/src/com/google/common/hash/AbstractStreamingHashFunction.java @@ -33,10 +33,6 @@ import java.nio.charset.Charset; * @author Kevin Bourrillion */ abstract class AbstractStreamingHashFunction implements HashFunction { - @Override public <T> HashCode hashObject(T instance, Funnel<? super T> funnel) { - return newHasher().putObject(instance, funnel).hash(); - } - @Override public HashCode hashString(CharSequence input) { return newHasher().putString(input).hash(); } @@ -45,10 +41,6 @@ abstract class AbstractStreamingHashFunction implements HashFunction { return newHasher().putString(input, charset).hash(); } - @Override public HashCode hashInt(int input) { - return newHasher().putInt(input).hash(); - } - @Override public HashCode hashLong(long input) { return newHasher().putLong(input).hash(); } @@ -70,8 +62,8 @@ abstract class AbstractStreamingHashFunction implements HashFunction { * A convenience base class for implementors of {@code Hasher}; handles accumulating data * until an entire "chunk" (of implementation-dependent length) is ready to be hashed. * - * @author Kevin Bourrillion - * @author Dimitris Andreou + * @author kevinb@google.com (Kevin Bourrillion) + * @author andreou@google.com (Dimitris Andreou) */ // TODO(kevinb): this class still needs some design-and-document-for-inheritance love protected static abstract class AbstractStreamingHasher extends AbstractHasher { @@ -150,7 +142,7 @@ abstract class AbstractStreamingHashFunction implements HashFunction { return putBytes(ByteBuffer.wrap(bytes, off, len).order(ByteOrder.LITTLE_ENDIAN)); } - private Hasher putBytes(ByteBuffer readBuffer) { + private final Hasher putBytes(ByteBuffer readBuffer) { // If we have room for all of it, this is easy if (readBuffer.remaining() <= buffer.remaining()) { buffer.put(readBuffer); diff --git a/guava/src/com/google/common/hash/BloomFilter.java b/guava/src/com/google/common/hash/BloomFilter.java index 4f78bb9..04bba08 100644 --- a/guava/src/com/google/common/hash/BloomFilter.java +++ b/guava/src/com/google/common/hash/BloomFilter.java @@ -19,305 +19,206 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; +import com.google.common.base.Preconditions; import com.google.common.hash.BloomFilterStrategies.BitArray; import java.io.Serializable; -import javax.annotation.Nullable; - /** * A Bloom filter for instances of {@code T}. A Bloom filter offers an approximate containment test - * with one-sided error: if it claims that an element is contained in it, this might be in error, + * with one-sided error: if it claims that an element is contained in it, this might be in error, * but if it claims that an element is <i>not</i> contained in it, then this is definitely true. - * - * <p>If you are unfamiliar with Bloom filters, this nice - * <a href="http://llimllib.github.com/bloomfilter-tutorial/">tutorial</a> may help you understand + * + * <p>If you are unfamiliar with Bloom filters, this nice + * <a href="http://llimllib.github.com/bloomfilter-tutorial/">tutorial</a> may help you understand * how they work. - * - * <p>The false positive probability ({@code FPP}) of a bloom filter is defined as the probability - * that {@linkplain #mightContain(Object)} will erroneously return {@code true} for an object that - * has not actually been put in the {@code BloomFilter}. - * - * + * * @param <T> the type of instances that the {@code BloomFilter} accepts - * @author Dimitris Andreou * @author Kevin Bourrillion + * @author Dimitris Andreou * @since 11.0 */ @Beta -public final class BloomFilter<T> implements Predicate<T>, Serializable { +public final class BloomFilter<T> implements Serializable { /** * A strategy to translate T instances, to {@code numHashFunctions} bit indexes. - * - * <p>Implementations should be collections of pure functions (i.e. stateless). */ interface Strategy extends java.io.Serializable { - /** - * Sets {@code numHashFunctions} bits of the given bit array, by hashing a user element. - * - * <p>Returns whether any bits changed as a result of this operation. + * Sets {@code numHashFunctions} bits of the given bit array, by hashing a user element. */ - <T> boolean put(T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits); - + <T> void put(T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits); + /** * Queries {@code numHashFunctions} bits of the given bit array, by hashing a user element; - * returns {@code true} if and only if all selected bits are set. + * returns {@code true} if and only if all selected bits are set. */ <T> boolean mightContain( T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits); - - /** - * Identifier used to encode this strategy, when marshalled as part of a BloomFilter. - * Only values in the [-128, 127] range are valid for the compact serial form. - * Non-negative values are reserved for enums defined in BloomFilterStrategies; - * negative values are reserved for any custom, stateful strategy we may define - * (e.g. any kind of strategy that would depend on user input). - */ - int ordinal(); } - + /** The bit set of the BloomFilter (not necessarily power of 2!)*/ private final BitArray bits; - - /** Number of hashes per element */ + + /** Number of hashes per element */ private final int numHashFunctions; - + /** The funnel to translate Ts to bytes */ private final Funnel<T> funnel; - + /** * The strategy we employ to map an element T to {@code numHashFunctions} bit indexes. */ private final Strategy strategy; - + /** - * Creates a BloomFilter. + * Creates a BloomFilter. */ private BloomFilter(BitArray bits, int numHashFunctions, Funnel<T> funnel, Strategy strategy) { - checkArgument(numHashFunctions > 0, - "numHashFunctions (%s) must be > 0", numHashFunctions); - checkArgument(numHashFunctions <= 255, - "numHashFunctions (%s) must be <= 255", numHashFunctions); + Preconditions.checkArgument(numHashFunctions > 0, "numHashFunctions zero or negative"); this.bits = checkNotNull(bits); this.numHashFunctions = numHashFunctions; this.funnel = checkNotNull(funnel); - this.strategy = checkNotNull(strategy); - } - - /** - * Creates a new {@code BloomFilter} that's a copy of this instance. The new instance is equal to - * this instance but shares no mutable state. - * - * @since 12.0 - */ - public BloomFilter<T> copy() { - return new BloomFilter<T>(bits.copy(), numHashFunctions, funnel, strategy); + this.strategy = strategy; } - + /** - * Returns {@code true} if the element <i>might</i> have been put in this Bloom filter, - * {@code false} if this is <i>definitely</i> not the case. + * Returns {@code true} if the element <i>might</i> have been put in this Bloom filter, + * {@code false} if this is <i>definitely</i> not the case. */ public boolean mightContain(T object) { return strategy.mightContain(object, funnel, numHashFunctions, bits); } /** - * Equivalent to {@link #mightContain}; provided only to satisfy the {@link Predicate} interface. - * When using a reference of type {@code BloomFilter}, always invoke {@link #mightContain} - * directly instead. - */ - @Override public boolean apply(T input) { - return mightContain(input); - } - - /** - * Puts an element into this {@code BloomFilter}. Ensures that subsequent invocations of + * Puts an element into this {@code BloomFilter}. Ensures that subsequent invocations of * {@link #mightContain(Object)} with the same element will always return {@code true}. - * - * @return true if the bloom filter's bits changed as a result of this operation. If the bits - * changed, this is <i>definitely</i> the first time {@code object} has been added to the - * filter. If the bits haven't changed, this <i>might</i> be the first time {@code object} - * has been added to the filter. Note that {@code put(t)} always returns the - * <i>opposite</i> result to what {@code mightContain(t)} would have returned at the time - * it is called." - * @since 12.0 (present in 11.0 with {@code void} return type}) - */ - public boolean put(T object) { - return strategy.put(object, funnel, numHashFunctions, bits); - } - - /** - * Returns the probability that {@linkplain #mightContain(Object)} will erroneously return - * {@code true} for an object that has not actually been put in the {@code BloomFilter}. - * - * <p>Ideally, this number should be close to the {@code fpp} parameter - * passed in {@linkplain #create(Funnel, int, double)}, or smaller. If it is - * significantly higher, it is usually the case that too many elements (more than - * expected) have been put in the {@code BloomFilter}, degenerating it. - * - * @since 14.0 (since 11.0 as expectedFalsePositiveProbability()) - */ - public double expectedFpp() { - // You down with FPP? (Yeah you know me!) Who's down with FPP? (Every last homie!) - return Math.pow((double) bits.bitCount() / bits.size(), numHashFunctions); - } - - /** - * @deprecated Use {@link #expectedFpp} instead. */ - @Deprecated - public double expectedFalsePositiveProbability() { - return expectedFpp(); + public void put(T object) { + strategy.put(object, funnel, numHashFunctions, bits); } - - @Override - public boolean equals(@Nullable Object object) { - if (object == this) { - return true; - } - if (object instanceof BloomFilter) { - BloomFilter<?> that = (BloomFilter<?>) object; - return this.numHashFunctions == that.numHashFunctions - && this.funnel.equals(that.funnel) - && this.bits.equals(that.bits) - && this.strategy.equals(that.strategy); - } - return false; + + @VisibleForTesting int getHashCount() { + return numHashFunctions; } - - @Override - public int hashCode() { - return Objects.hashCode(numHashFunctions, funnel, strategy, bits); + + @VisibleForTesting double computeExpectedFalsePositiveRate(int insertions) { + return Math.pow( + 1 - Math.exp(-numHashFunctions * ((double) insertions / (bits.size()))), + numHashFunctions); } - + /** - * Creates a {@code Builder} of a {@link BloomFilter BloomFilter<T>}, with the expected number + * Creates a {@code Builder} of a {@link BloomFilter BloomFilter<T>}, with the expected number * of insertions and expected false positive probability. - * - * <p>Note that overflowing a {@code BloomFilter} with significantly more elements + * + * <p>Note that overflowing a {@code BloomFilter} with significantly more elements * than specified, will result in its saturation, and a sharp deterioration of its * false positive probability. - * - * <p>The constructed {@code BloomFilter<T>} will be serializable if the provided + * + * <p>The constructed {@code BloomFilter<T>} will be serializable if the provided * {@code Funnel<T>} is. - * - * <p>It is recommended the funnel is implemented as a Java enum. This has the benefit of ensuring - * proper serialization and deserialization, which is important since {@link #equals} also relies - * on object identity of funnels. - * + * * @param funnel the funnel of T's that the constructed {@code BloomFilter<T>} will use - * @param expectedInsertions the number of expected insertions to the constructed - * {@code BloomFilter<T>}; must be positive - * @param fpp the desired false positive probability (must be positive and less than 1.0) - * @return a {@code BloomFilter} + * @param expectedInsertions the number of expected insertions to the constructed + * {@code BloomFilter<T>}; must be positive + * @param falsePositiveProbability the desired false positive probability (must be positive and + * less than 1.0) + * @return a {@code Builder} */ - public static <T> BloomFilter<T> create( - Funnel<T> funnel, int expectedInsertions /* n */, double fpp) { + public static <T> BloomFilter<T> create(Funnel<T> funnel, int expectedInsertions /* n */, + double falsePositiveProbability) { checkNotNull(funnel); - checkArgument(expectedInsertions >= 0, "Expected insertions (%s) must be >= 0", - expectedInsertions); - checkArgument(fpp > 0.0, "False positive probability (%s) must be > 0.0", fpp); - checkArgument(fpp < 1.0, "False positive probability (%s) must be < 1.0", fpp); - if (expectedInsertions == 0) { - expectedInsertions = 1; - } - /* - * TODO(user): Put a warning in the javadoc about tiny fpp values, + checkArgument(expectedInsertions > 0, "Expected insertions must be positive"); + checkArgument(falsePositiveProbability > 0.0 & falsePositiveProbability < 1.0, + "False positive probability in (0.0, 1.0)"); + /* + * andreou: I wanted to put a warning in the javadoc about tiny fpp values, * since the resulting size is proportional to -log(p), but there is not * much of a point after all, e.g. optimalM(1000, 0.0000000000000001) = 76680 * which is less that 10kb. Who cares! */ - long numBits = optimalNumOfBits(expectedInsertions, fpp); + int numBits = optimalNumOfBits(expectedInsertions, falsePositiveProbability); int numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits); - try { - return new BloomFilter<T>(new BitArray(numBits), numHashFunctions, funnel, - BloomFilterStrategies.MURMUR128_MITZ_32); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Could not create BloomFilter of " + numBits + " bits", e); - } + return new BloomFilter<T>(new BitArray(numBits), numHashFunctions, funnel, + BloomFilterStrategies.MURMUR128_MITZ_32); } - + /** - * Creates a {@code Builder} of a {@link BloomFilter BloomFilter<T>}, with the expected number + * Creates a {@code Builder} of a {@link BloomFilter BloomFilter<T>}, with the expected number * of insertions, and a default expected false positive probability of 3%. - * - * <p>Note that overflowing a {@code BloomFilter} with significantly more elements + * + * <p>Note that overflowing a {@code BloomFilter} with significantly more elements * than specified, will result in its saturation, and a sharp deterioration of its * false positive probability. - * - * <p>The constructed {@code BloomFilter<T>} will be serializable if the provided + * + * <p>The constructed {@code BloomFilter<T>} will be serializable if the provided * {@code Funnel<T>} is. - * + * * @param funnel the funnel of T's that the constructed {@code BloomFilter<T>} will use - * @param expectedInsertions the number of expected insertions to the constructed - * {@code BloomFilter<T>}; must be positive - * @return a {@code BloomFilter} + * @param expectedInsertions the number of expected insertions to the constructed + * {@code BloomFilter<T>}; must be positive + * @return a {@code Builder} */ public static <T> BloomFilter<T> create(Funnel<T> funnel, int expectedInsertions /* n */) { - return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions + return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions } - + /* * Cheat sheet: - * + * * m: total bits * n: expected insertions * b: m/n, bits per insertion * p: expected false positive probability - * + * * 1) Optimal k = b * ln2 * 2) p = (1 - e ^ (-kn/m))^k * 3) For optimal k: p = 2 ^ (-k) ~= 0.6185^b * 4) For optimal k: m = -nlnp / ((ln2) ^ 2) */ - + + private static final double LN2 = Math.log(2); + private static final double LN2_SQUARED = LN2 * LN2; + /** - * Computes the optimal k (number of hashes per element inserted in Bloom filter), given the + * Computes the optimal k (number of hashes per element inserted in Bloom filter), given the * expected insertions and total number of bits in the Bloom filter. - * + * * See http://en.wikipedia.org/wiki/File:Bloom_filter_fp_probability.svg for the formula. - * + * * @param n expected insertions (must be positive) * @param m total number of bits in Bloom filter (must be positive) */ - @VisibleForTesting - static int optimalNumOfHashFunctions(long n, long m) { - return Math.max(1, (int) Math.round(m / n * Math.log(2))); + @VisibleForTesting static int optimalNumOfHashFunctions(int n, int m) { + return Math.max(1, (int) Math.round(m / n * LN2)); } - + /** - * Computes m (total bits of Bloom filter) which is expected to achieve, for the specified + * Computes m (total bits of Bloom filter) which is expected to achieve, for the specified * expected insertions, the required false positive probability. - * + * * See http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives for the formula. - * + * * @param n expected insertions (must be positive) * @param p false positive rate (must be 0 < p < 1) */ - @VisibleForTesting - static long optimalNumOfBits(long n, double p) { - if (p == 0) { - p = Double.MIN_VALUE; - } - return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2))); + @VisibleForTesting static int optimalNumOfBits(int n, double p) { + return (int) (-n * Math.log(p) / LN2_SQUARED); } - + private Object writeReplace() { return new SerialForm<T>(this); } - + private static class SerialForm<T> implements Serializable { final long[] data; final int numHashFunctions; final Funnel<T> funnel; final Strategy strategy; - + SerialForm(BloomFilter<T> bf) { this.data = bf.bits.data; this.numHashFunctions = bf.numHashFunctions; diff --git a/guava/src/com/google/common/hash/BloomFilterStrategies.java b/guava/src/com/google/common/hash/BloomFilterStrategies.java index 0a4fa79..8c215e5 100644 --- a/guava/src/com/google/common/hash/BloomFilterStrategies.java +++ b/guava/src/com/google/common/hash/BloomFilterStrategies.java @@ -1,37 +1,20 @@ -/* - * 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. - */ +// Copyright 2011 Google Inc. All Rights Reserved. package com.google.common.hash; import static com.google.common.base.Preconditions.checkArgument; -import com.google.common.math.LongMath; -import com.google.common.primitives.Ints; +import com.google.common.math.IntMath; import java.math.RoundingMode; -import java.util.Arrays; /** - * Collections of strategies of generating the k * log(M) bits required for an element to - * be mapped to a BloomFilter of M bits and k hash functions. These + * Collections of strategies of generating the {@code k * log(M)} bits required for an element to + * be mapped to a {@link BloomFilter} of {@code M} bits and {@code k} hash functions. These * strategies are part of the serialized form of the Bloom filters that use them, thus they must be * preserved as is (no updates allowed, only introduction of new versions). * - * Important: the order of the constants cannot change, and they cannot be deleted - we depend - * on their ordinal for BloomFilter serialization. - * - * @author Dimitris Andreou + * @author andreou@google.com (Dimitris Andreou) */ enum BloomFilterStrategies implements BloomFilter.Strategy { /** @@ -40,25 +23,25 @@ enum BloomFilterStrategies implements BloomFilter.Strategy { * performance of a Bloom filter (yet only needs two 32bit hash functions). */ MURMUR128_MITZ_32() { - @Override public <T> boolean put(T object, Funnel<? super T> funnel, + @Override public <T> void put(T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits) { - long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); + // TODO(user): when the murmur's shortcuts are implemented, update this code + long hash64 = Hashing.murmur3_128().newHasher().putObject(object, funnel).hash().asLong(); int hash1 = (int) hash64; int hash2 = (int) (hash64 >>> 32); - boolean bitsChanged = false; for (int i = 1; i <= numHashFunctions; i++) { int nextHash = hash1 + i * hash2; if (nextHash < 0) { nextHash = ~nextHash; } - bitsChanged |= bits.set(nextHash % bits.size()); + // up to here, the code is identical with the next method + bits.set(nextHash % bits.size()); } - return bitsChanged; } @Override public <T> boolean mightContain(T object, Funnel<? super T> funnel, int numHashFunctions, BitArray bits) { - long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); + long hash64 = Hashing.murmur3_128().newHasher().putObject(object, funnel).hash().asLong(); int hash1 = (int) hash64; int hash2 = (int) (hash64 >>> 32); for (int i = 1; i <= numHashFunctions; i++) { @@ -66,6 +49,7 @@ enum BloomFilterStrategies implements BloomFilter.Strategy { if (nextHash < 0) { nextHash = ~nextHash; } + // up to here, the code is identical with the previous method if (!bits.get(nextHash % bits.size())) { return false; } @@ -74,34 +58,21 @@ enum BloomFilterStrategies implements BloomFilter.Strategy { } }; - // Note: We use this instead of java.util.BitSet because we need access to the long[] data field static class BitArray { final long[] data; - int bitCount; - BitArray(long bits) { - this(new long[Ints.checkedCast(LongMath.divide(bits, 64, RoundingMode.CEILING))]); + BitArray(int bits) { + this(new long[IntMath.divide(bits, 64, RoundingMode.CEILING)]); } // Used by serialization BitArray(long[] data) { checkArgument(data.length > 0, "data length is zero!"); this.data = data; - int bitCount = 0; - for (long value : data) { - bitCount += Long.bitCount(value); - } - this.bitCount = bitCount; } - /** Returns true if the bit changed value. */ - boolean set(int index) { - if (!get(index)) { - data[index >> 6] |= (1L << index); - bitCount++; - return true; - } - return false; + void set(int index) { + data[index >> 6] |= (1L << index); } boolean get(int index) { @@ -112,26 +83,5 @@ enum BloomFilterStrategies implements BloomFilter.Strategy { int size() { return data.length * Long.SIZE; } - - /** Number of set bits (1s) */ - int bitCount() { - return bitCount; - } - - BitArray copy() { - return new BitArray(data.clone()); - } - - @Override public boolean equals(Object o) { - if (o instanceof BitArray) { - BitArray bitArray = (BitArray) o; - return Arrays.equals(data, bitArray.data); - } - return false; - } - - @Override public int hashCode() { - return Arrays.hashCode(data); - } } } diff --git a/guava/src/com/google/common/hash/ChecksumHashFunction.java b/guava/src/com/google/common/hash/ChecksumHashFunction.java deleted file mode 100644 index bbcc00f..0000000 --- a/guava/src/com/google/common/hash/ChecksumHashFunction.java +++ /dev/null @@ -1,96 +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.hash; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.base.Supplier; - -import java.io.Serializable; -import java.util.zip.Checksum; - -/** - * {@link HashFunction} adapter for {@link Checksum} instances. - * - * @author Colin Decker - */ -final class ChecksumHashFunction extends AbstractStreamingHashFunction implements Serializable { - - private final Supplier<? extends Checksum> checksumSupplier; - private final int bits; - private final String toString; - - ChecksumHashFunction(Supplier<? extends Checksum> checksumSupplier, int bits, String toString) { - this.checksumSupplier = checkNotNull(checksumSupplier); - checkArgument(bits == 32 || bits == 64, "bits (%s) must be either 32 or 64", bits); - this.bits = bits; - this.toString = checkNotNull(toString); - } - - @Override - public int bits() { - return bits; - } - - @Override - public Hasher newHasher() { - return new ChecksumHasher(checksumSupplier.get()); - } - - @Override - public String toString() { - return toString; - } - - /** - * Hasher that updates a checksum. - */ - private final class ChecksumHasher extends AbstractByteHasher { - - private final Checksum checksum; - - private ChecksumHasher(Checksum checksum) { - this.checksum = checkNotNull(checksum); - } - - @Override - protected void update(byte b) { - checksum.update(b); - } - - @Override - protected void update(byte[] bytes, int off, int len) { - checksum.update(bytes, off, len); - } - - @Override - public HashCode hash() { - long value = checksum.getValue(); - if (bits == 32) { - /* - * The long returned from a 32-bit Checksum will have all 0s for its second word, so the - * cast won't lose any information and is necessary to return a HashCode of the correct - * size. - */ - return HashCodes.fromInt((int) value); - } else { - return HashCodes.fromLong(value); - } - } - } - - private static final long serialVersionUID = 0L; -} diff --git a/guava/src/com/google/common/hash/Funnel.java b/guava/src/com/google/common/hash/Funnel.java index 6d62648..61cff7b 100644 --- a/guava/src/com/google/common/hash/Funnel.java +++ b/guava/src/com/google/common/hash/Funnel.java @@ -16,37 +16,18 @@ package com.google.common.hash; import com.google.common.annotations.Beta; -import java.io.Serializable; - /** - * An object which can send data from an object of type {@code T} into a {@code PrimitiveSink}. - * - * <p>Note that serialization of {@linkplain BloomFilter bloom filters} requires the proper - * serialization of funnels. When possible, it is recommended that funnels be implemented as a - * single-element enum to maintain serialization guarantees. See Effective Java (2nd Edition), - * Item 3: "Enforce the singleton property with a private constructor or an enum type". For example: - * <pre> {@code - * public enum PersonFunnel implements Funnel<Person> { - * INSTANCE; - * public void funnel(Person person, PrimitiveSink into) { - * into.putString(person.getFirstName()) - * .putString(person.getLastName()) - * .putInt(person.getAge()); - * } - * }}</pre> + * An object which can send data from an object of type {@code T} into a {@code Sink}. * * @author Dimitris Andreou * @since 11.0 */ @Beta -public interface Funnel<T> extends Serializable { - +public interface Funnel<T> { /** * Sends a stream of data from the {@code from} object into the sink {@code into}. There * is no requirement that this data be complete enough to fully reconstitute the object * later. - * - * @since 12.0 (in Guava 11.0, {@code PrimitiveSink} was named {@code Sink}) */ - void funnel(T from, PrimitiveSink into); + void funnel(T from, Sink into); } diff --git a/guava/src/com/google/common/hash/Funnels.java b/guava/src/com/google/common/hash/Funnels.java index 7f76202..2cb04f4 100644 --- a/guava/src/com/google/common/hash/Funnels.java +++ b/guava/src/com/google/common/hash/Funnels.java @@ -15,13 +15,10 @@ package com.google.common.hash; import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; - -import java.io.OutputStream; /** * Funnels for common types. All implementations are serializable. - * + * * @author Dimitris Andreou * @since 11.0 */ @@ -39,7 +36,7 @@ public final class Funnels { private enum ByteArrayFunnel implements Funnel<byte[]> { INSTANCE; - public void funnel(byte[] from, PrimitiveSink into) { + public void funnel(byte[] from, Sink into) { into.putBytes(from); } @@ -58,7 +55,7 @@ public final class Funnels { private enum StringFunnel implements Funnel<CharSequence> { INSTANCE; - public void funnel(CharSequence from, PrimitiveSink into) { + public void funnel(CharSequence from, Sink into) { into.putString(from); } @@ -66,83 +63,4 @@ public final class Funnels { return "Funnels.stringFunnel()"; } } - - /** - * Returns a funnel for integers. - * - * @since 13.0 - */ - public static Funnel<Integer> integerFunnel() { - return IntegerFunnel.INSTANCE; - } - - private enum IntegerFunnel implements Funnel<Integer> { - INSTANCE; - - public void funnel(Integer from, PrimitiveSink into) { - into.putInt(from); - } - - @Override public String toString() { - return "Funnels.integerFunnel()"; - } - } - - /** - * Returns a funnel for longs. - * - * @since 13.0 - */ - public static Funnel<Long> longFunnel() { - return LongFunnel.INSTANCE; - } - - private enum LongFunnel implements Funnel<Long> { - INSTANCE; - - public void funnel(Long from, PrimitiveSink into) { - into.putLong(from); - } - - @Override public String toString() { - return "Funnels.longFunnel()"; - } - } - - /** - * Wraps a {@code PrimitiveSink} as an {@link OutputStream}, so it is easy to - * {@link Funnel#funnel funnel} an object to a {@code PrimitiveSink} - * if there is already a way to write the contents of the object to an {@code OutputStream}. - * - * <p>The {@code close} and {@code flush} methods of the returned {@code OutputStream} - * do nothing, and no method throws {@code IOException}. - * - * @since 13.0 - */ - public static OutputStream asOutputStream(PrimitiveSink sink) { - return new SinkAsStream(sink); - } - - private static class SinkAsStream extends OutputStream { - final PrimitiveSink sink; - SinkAsStream(PrimitiveSink sink) { - this.sink = Preconditions.checkNotNull(sink); - } - - @Override public void write(int b) { - sink.putByte((byte) b); - } - - @Override public void write(byte[] bytes) { - sink.putBytes(bytes); - } - - @Override public void write(byte[] bytes, int off, int len) { - sink.putBytes(bytes, off, len); - } - - @Override public String toString() { - return "Funnels.asOutputStream(" + sink + ")"; - } - } } diff --git a/guava/src/com/google/common/hash/HashCode.java b/guava/src/com/google/common/hash/HashCode.java index f955699..ecd8706 100644 --- a/guava/src/com/google/common/hash/HashCode.java +++ b/guava/src/com/google/common/hash/HashCode.java @@ -20,8 +20,6 @@ import com.google.common.primitives.Ints; import java.security.MessageDigest; -import javax.annotation.Nullable; - /** * An immutable hash code of arbitrary bit length. * @@ -35,8 +33,6 @@ public abstract class HashCode { /** * Returns the first four bytes of {@linkplain #asBytes() this hashcode's bytes}, converted to * an {@code int} value in little-endian order. - * - * @throws IllegalStateException if {@code bits() < 32} */ public abstract int asInt(); @@ -49,15 +45,6 @@ public abstract class HashCode { public abstract long asLong(); /** - * If this hashcode has enough bits, returns {@code asLong()}, otherwise returns a {@code long} - * value with {@code asInt()} as the least-significant four bytes and {@code 0x00} as - * each of the most-significant four bytes. - * - * @since 14.0 (since 11.0 as {@code Hashing.padToLong(HashCode)}) - */ - public abstract long padToLong(); - - /** * Returns the value of this hash code as a byte array. The caller may modify the byte array; * changes to it will <i>not</i> be reflected in this {@code HashCode} object or any other arrays * returned by this method. @@ -83,11 +70,11 @@ public abstract class HashCode { } /** - * Returns the number of bits in this hash code; a positive multiple of 8. + * Returns the number of bits in this hash code; a positive multiple of 32. */ public abstract int bits(); - @Override public boolean equals(@Nullable Object object) { + @Override public boolean equals(Object object) { if (object instanceof HashCode) { HashCode that = (HashCode) object; // Undocumented: this is a non-short-circuiting equals(), in case this is a cryptographic @@ -121,6 +108,7 @@ public abstract class HashCode { */ @Override public String toString() { byte[] bytes = asBytes(); + // TODO(user): Use c.g.common.base.ByteArrays once it is open sourced. StringBuilder sb = new StringBuilder(2 * bytes.length); for (byte b : bytes) { sb.append(hexDigits[(b >> 4) & 0xf]).append(hexDigits[b & 0xf]); diff --git a/guava/src/com/google/common/hash/HashCodes.java b/guava/src/com/google/common/hash/HashCodes.java index b911c28..b6101a5 100644 --- a/guava/src/com/google/common/hash/HashCodes.java +++ b/guava/src/com/google/common/hash/HashCodes.java @@ -14,35 +14,23 @@ package com.google.common.hash; -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.primitives.UnsignedInts; - -import java.io.Serializable; - /** - * Static factories for creating {@link HashCode} instances; most users should never have to use - * this. All returned instances are {@link Serializable}. - * - * @author Dimitris Andreou - * @since 12.0 + * Static factories for {@link HashCode} instances. + * + * @author andreou@google.com (Dimitris Andreou) */ -@Beta -public final class HashCodes { - private HashCodes() {} - +final class HashCodes { + private HashCodes() { } + /** * Creates a 32-bit {@code HashCode}, of which the bytes will form the passed int, interpreted * in little endian order. */ - public static HashCode fromInt(int hash) { + static HashCode fromInt(int hash) { return new IntHashCode(hash); } - private static final class IntHashCode extends HashCode implements Serializable { + private static class IntHashCode extends HashCode { final int hash; IntHashCode(int hash) { @@ -68,24 +56,17 @@ public final class HashCodes { @Override public long asLong() { throw new IllegalStateException("this HashCode only has 32 bits; cannot create a long"); } - - @Override - public long padToLong() { - return UnsignedInts.toLong(hash); - } - - private static final long serialVersionUID = 0; } /** * Creates a 64-bit {@code HashCode}, of which the bytes will form the passed long, interpreted * in little endian order. */ - public static HashCode fromLong(long hash) { + static HashCode fromLong(long hash) { return new LongHashCode(hash); } - private static final class LongHashCode extends HashCode implements Serializable { + private static class LongHashCode extends HashCode { final long hash; LongHashCode(long hash) { @@ -115,37 +96,22 @@ public final class HashCodes { @Override public long asLong() { return hash; } - - @Override - public long padToLong() { - return hash; - } - - private static final long serialVersionUID = 0; } - - /** - * Creates a {@code HashCode} from a byte array. The array is defensively copied to preserve - * the immutability contract of {@code HashCode}. The array cannot be empty. - */ - public static HashCode fromBytes(byte[] bytes) { - checkArgument(bytes.length >= 1, "A HashCode must contain at least 1 byte."); - return fromBytesNoCopy(bytes.clone()); - } - + /** * Creates a {@code HashCode} from a byte array. The array is <i>not</i> copied defensively, * so it must be handed-off so as to preserve the immutability contract of {@code HashCode}. + * The array must be at least of length 4 (not checked). */ - static HashCode fromBytesNoCopy(byte[] bytes) { + static HashCode fromBytes(byte[] bytes) { return new BytesHashCode(bytes); } - private static final class BytesHashCode extends HashCode implements Serializable { + private static class BytesHashCode extends HashCode { final byte[] bytes; BytesHashCode(byte[] bytes) { - this.bytes = checkNotNull(bytes); + this.bytes = bytes; } @Override public int bits() { @@ -157,8 +123,6 @@ public final class HashCodes { } @Override public int asInt() { - checkState(bytes.length >= 4, - "HashCode#asInt() requires >= 4 bytes (it only has %s bytes).", bytes.length); return (bytes[0] & 0xFF) | ((bytes[1] & 0xFF) << 8) | ((bytes[2] & 0xFF) << 16) @@ -166,8 +130,10 @@ public final class HashCodes { } @Override public long asLong() { - checkState(bytes.length >= 8, - "HashCode#asLong() requires >= 8 bytes (it only has %s bytes).", bytes.length); + if (bytes.length < 8) { + // Checking this to throw the correct type of exception + throw new IllegalStateException("Not enough bytes"); + } return (bytes[0] & 0xFFL) | ((bytes[1] & 0xFFL) << 8) | ((bytes[2] & 0xFFL) << 16) @@ -177,25 +143,5 @@ public final class HashCodes { | ((bytes[6] & 0xFFL) << 48) | ((bytes[7] & 0xFFL) << 56); } - - @Override - public long padToLong() { - return (bytes.length < 8) ? UnsignedInts.toLong(asInt()) : asLong(); - } - - @Override - public int hashCode() { - if (bytes.length >= 4) { - return asInt(); - } else { - int val = (bytes[0] & 0xFF); - for (int i = 1; i < bytes.length; i++) { - val |= ((bytes[i] & 0xFF) << (i * 8)); - } - return val; - } - } - - private static final long serialVersionUID = 0; } } diff --git a/guava/src/com/google/common/hash/HashFunction.java b/guava/src/com/google/common/hash/HashFunction.java index 0386261..ba3c992 100644 --- a/guava/src/com/google/common/hash/HashFunction.java +++ b/guava/src/com/google/common/hash/HashFunction.java @@ -40,8 +40,7 @@ import java.nio.charset.Charset; * represents a hash code as an instance of {@link HashCode}. * * <li><b>pure function:</b> the value produced must depend only on the input bytes, in - * the order they appear. Input data is never modified. {@link HashFunction} instances - * should always be stateless, and therefore thread-safe. + * the order they appear. Input data is never modified. * * <li><b>collision-averse:</b> while it can't be helped that a hash function will * sometimes produce the same hash code for distinct inputs (a "collision"), every @@ -143,21 +142,12 @@ public interface HashFunction { Hasher newHasher(); /** - * Begins a new hash code computation as {@link #newHasher()}, but provides a hint of the + * Begins a new hash code computation as {@link #newHasher()}, but provides a hint of the * expected size of the input (in bytes). This is only important for non-streaming hash - * functions (hash functions that need to buffer their whole input before processing any - * of it). + * functions (hash functions that need to buffer their whole input before processing any + * of it). */ - Hasher newHasher(int expectedInputSize); - - /** - * Shortcut for {@code newHasher().putInt(input).hash()}; returns the hash code for the given - * {@code int} value, interpreted in little-endian byte order. The implementation <i>might</i> - * perform better than its longhand equivalent, but should not perform worse. - * - * @since 12.0 - */ - HashCode hashInt(int input); + Hasher newHasher(int expectedInputSize); /** * Shortcut for {@code newHasher().putLong(input).hash()}; returns the hash code for the @@ -175,9 +165,9 @@ public interface HashFunction { /** * Shortcut for {@code newHasher().putBytes(input, off, len).hash()}. The implementation - * <i>might</i> perform better than its longhand equivalent, but should not perform - * worse. - * + * <i>might</i> perform better than its longhand equivalent, but should not perform + * worse. + * * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > bytes.length} * or {@code len < 0} */ @@ -200,14 +190,6 @@ public interface HashFunction { HashCode hashString(CharSequence input, Charset charset); /** - * Shortcut for {@code newHasher().putObject(instance, funnel).hash()}. The implementation - * <i>might</i> perform better than its longhand equivalent, but should not perform worse. - * - * @since 14.0 - */ - <T> HashCode hashObject(T instance, Funnel<? super T> funnel); - - /** * Returns the number of bits (a multiple of 32) that each hash code produced by this * hash function has. */ diff --git a/guava/src/com/google/common/hash/Hasher.java b/guava/src/com/google/common/hash/Hasher.java index e7adc82..b10d536 100644 --- a/guava/src/com/google/common/hash/Hasher.java +++ b/guava/src/com/google/common/hash/Hasher.java @@ -19,62 +19,40 @@ import com.google.common.annotations.Beta; import java.nio.charset.Charset; /** - * A {@link PrimitiveSink} that can compute a hash code after reading the input. Each hasher should - * translate all multibyte values ({@link #putInt(int)}, {@link #putLong(long)}, etc) to bytes + * A {@link Sink} that can compute a hash code after reading the input. Each hasher should + * translate all multibyte values ({@link #putInt(int)}, {@link #putLong(long)}, etc) to bytes * in little-endian order. * - * <p>The result of calling any methods after calling {@link #hash} is undefined. - * - * <p><b>Warning:</b> Chunks of data that are put into the {@link Hasher} are not delimited. - * The resulting {@link HashCode} is dependent only on the bytes inserted, and the order in which - * they were inserted, not how those bytes were chunked into discrete put() operations. For example, - * the following three expressions all generate colliding hash codes: <pre> {@code - * - * newHasher().putString("ab").putString("c").hash() - * newHasher().putString("a").putString("bc").hash() - * newHasher().putChar('a').putChar('b').putChar('c').hash()}</pre> - * - * If you wish to avoid this, you should either prepend or append the size of each chunk. - * For example: - * <pre> {@code - * newHasher().putInt(s1.length()).putString(s1).putInt(s2.length()).putString(s2).hash()}</pre> - * * @author Kevin Bourrillion * @since 11.0 */ @Beta -public interface Hasher extends PrimitiveSink { +public interface Hasher extends Sink { @Override Hasher putByte(byte b); @Override Hasher putBytes(byte[] bytes); @Override Hasher putBytes(byte[] bytes, int off, int len); @Override Hasher putShort(short s); @Override Hasher putInt(int i); @Override Hasher putLong(long l); - /** * Equivalent to {@code putInt(Float.floatToRawIntBits(f))}. */ @Override Hasher putFloat(float f); - /** * Equivalent to {@code putLong(Double.doubleToRawLongBits(d))}. */ @Override Hasher putDouble(double d); - /** * Equivalent to {@code putByte(b ? (byte) 1 : (byte) 0)}. */ @Override Hasher putBoolean(boolean b); @Override Hasher putChar(char c); - /** - * Equivalent to processing each {@code char} value in the {@code CharSequence}, in order. - * The input must not be updated while this method is in progress. + * Equivalent to {@code putBytes(charSequence.toString().getBytes(Charsets.UTF_16LE)}. */ @Override Hasher putString(CharSequence charSequence); - /** - * Equivalent to {@code putBytes(charSequence.toString().getBytes(charset))}. + * Equivalent to {@code putBytes(charSequence.toString().getBytes(charset)}. */ @Override Hasher putString(CharSequence charSequence, Charset charset); @@ -85,7 +63,7 @@ public interface Hasher extends PrimitiveSink { /** * Computes a hash code based on the data that have been provided to this hasher. The result is - * unspecified if this method is called more than once on the same instance. + * unspecified if this method is called more than once on the same instance. */ HashCode hash(); } diff --git a/guava/src/com/google/common/hash/Hashing.java b/guava/src/com/google/common/hash/Hashing.java index 0e81d0e..f2582c7 100644 --- a/guava/src/com/google/common/hash/Hashing.java +++ b/guava/src/com/google/common/hash/Hashing.java @@ -17,19 +17,15 @@ package com.google.common.hash; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Supplier; +import com.google.common.primitives.UnsignedInts; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.util.Iterator; -import java.util.zip.Adler32; -import java.util.zip.CRC32; -import java.util.zip.Checksum; /** - * Static methods to obtain {@link HashFunction} instances, and other static hashing-related - * utilities. + * Static methods to obtain {@link HashFunction} instances, and other static + * hashing-related utilities. * * @author Kevin Bourrillion * @author Dimitris Andreou @@ -41,27 +37,11 @@ public final class Hashing { private Hashing() {} /** - * Used to randomize {@link #goodFastHash} instances, so that programs which persist anything - * dependent on hashcodes of those, will fail sooner than later. - */ - private static final int GOOD_FAST_HASH_SEED = (int) System.currentTimeMillis(); - - // Used by goodFastHash when minimumBits == 32. - private static final HashFunction GOOD_FAST_HASH_FUNCTION_32 = murmur3_32(GOOD_FAST_HASH_SEED); - - // Used by goodFastHash when 32 < minimumBits <= 128. - private static final HashFunction GOOD_FAST_HASH_FUNCTION_128 = murmur3_128(GOOD_FAST_HASH_SEED); - - /** * Returns a general-purpose, <b>non-cryptographic-strength</b>, streaming hash function that * produces hash codes of length at least {@code minimumBits}. Users without specific * compatibility requirements and who do not persist the hash codes are encouraged to * choose this hash function. * - * <p>Repeated calls to {@link #goodFastHash} with the same {@code minimumBits} value will - * return {@link HashFunction} instances with identical behavior (but not necessarily the - * same instance) for the duration of the current virtual machine. - * * <p><b>Warning: the implementation is unspecified and is subject to change.</b> * * @throws IllegalArgumentException if {@code minimumBits} is not positive @@ -70,31 +50,24 @@ public final class Hashing { int bits = checkPositiveAndMakeMultipleOf32(minimumBits); if (bits == 32) { - return GOOD_FAST_HASH_FUNCTION_32; - } - if (bits <= 128) { - return GOOD_FAST_HASH_FUNCTION_128; - } - - // Otherwise, join together some 128-bit murmur3s - int hashFunctionsNeeded = (bits + 127) / 128; - HashFunction[] hashFunctions = new HashFunction[hashFunctionsNeeded]; - hashFunctions[0] = GOOD_FAST_HASH_FUNCTION_128; - int seed = GOOD_FAST_HASH_SEED; - for (int i = 1; i < hashFunctionsNeeded; i++) { - seed += 1500450271; // a prime; shouldn't matter - hashFunctions[i] = murmur3_128(seed); + return murmur3_32(); + } else if (bits <= 128) { + return murmur3_128(); + } else { + // Join some 128-bit murmur3s + int hashFunctionsNeeded = (bits + 127) / 128; + HashFunction[] hashFunctions = new HashFunction[hashFunctionsNeeded]; + for (int i = 0; i < hashFunctionsNeeded; i++) { + hashFunctions[i] = murmur3_128(i * 1500450271 /* a prime; shouldn't matter */); + } + return new ConcatenatedHashFunction(hashFunctions); } - return new ConcatenatedHashFunction(hashFunctions); } /** * Returns a hash function implementing the - * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp"> - * 32-bit murmur3 algorithm, x86 variant</a> (little-endian variant), - * using the given seed value. - * - * <p>The exact C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A). + * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp">32-bit murmur3 + * algorithm</a> (little-endian variant), using the given seed value. */ public static HashFunction murmur3_32(int seed) { return new Murmur3_32HashFunction(seed); @@ -102,25 +75,20 @@ public final class Hashing { /** * Returns a hash function implementing the - * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp"> - * 32-bit murmur3 algorithm, x86 variant</a> (little-endian variant), - * using a seed value of zero. - * - * <p>The exact C++ equivalent is the MurmurHash3_x86_32 function (Murmur3A). + * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp">32-bit murmur3 + * algorithm</a> (little-endian variant), using a seed value of zero. */ public static HashFunction murmur3_32() { return MURMUR3_32; } - private static final HashFunction MURMUR3_32 = new Murmur3_32HashFunction(0); + private static final Murmur3_32HashFunction MURMUR3_32 = new Murmur3_32HashFunction(0); /** * Returns a hash function implementing the * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp"> - * 128-bit murmur3 algorithm, x64 variant</a> (little-endian variant), - * using the given seed value. - * - * <p>The exact C++ equivalent is the MurmurHash3_x64_128 function (Murmur3F). + * 128-bit murmur3 algorithm, x64 variant</a> (little-endian variant), using the given seed + * value. */ public static HashFunction murmur3_128(int seed) { return new Murmur3_128HashFunction(seed); @@ -129,133 +97,62 @@ public final class Hashing { /** * Returns a hash function implementing the * <a href="http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp"> - * 128-bit murmur3 algorithm, x64 variant</a> (little-endian variant), - * using a seed value of zero. - * - * <p>The exact C++ equivalent is the MurmurHash3_x64_128 function (Murmur3F). + * 128-bit murmur3 algorithm, x64 variant</a> (little-endian variant), using a seed value + * of zero. */ public static HashFunction murmur3_128() { return MURMUR3_128; } - private static final HashFunction MURMUR3_128 = new Murmur3_128HashFunction(0); + private static final Murmur3_128HashFunction MURMUR3_128 = new Murmur3_128HashFunction(0); /** - * Returns a hash function implementing the MD5 hash algorithm (128 hash bits) by delegating to - * the MD5 {@link MessageDigest}. + * Returns a hash function implementing the MD5 hash algorithm by delegating to the MD5 + * {@link MessageDigest}. */ public static HashFunction md5() { return MD5; } - private static final HashFunction MD5 = new MessageDigestHashFunction("MD5", "Hashing.md5()"); + private static final HashFunction MD5 = new MessageDigestHashFunction("MD5"); /** - * Returns a hash function implementing the SHA-1 algorithm (160 hash bits) by delegating to the - * SHA-1 {@link MessageDigest}. + * Returns a hash function implementing the SHA-1 algorithm by delegating to the SHA-1 + * {@link MessageDigest}. */ public static HashFunction sha1() { return SHA_1; } - private static final HashFunction SHA_1 = - new MessageDigestHashFunction("SHA-1", "Hashing.sha1()"); + private static final HashFunction SHA_1 = new MessageDigestHashFunction("SHA-1"); /** - * Returns a hash function implementing the SHA-256 algorithm (256 hash bits) by delegating to - * the SHA-256 {@link MessageDigest}. + * Returns a hash function implementing the SHA-256 algorithm by delegating to the SHA-256 + * {@link MessageDigest}. */ public static HashFunction sha256() { return SHA_256; } - private static final HashFunction SHA_256 = - new MessageDigestHashFunction("SHA-256", "Hashing.sha256()"); + private static final HashFunction SHA_256 = new MessageDigestHashFunction("SHA-256"); /** - * Returns a hash function implementing the SHA-512 algorithm (512 hash bits) by delegating to the - * SHA-512 {@link MessageDigest}. + * Returns a hash function implementing the SHA-512 algorithm by delegating to the SHA-512 + * {@link MessageDigest}. */ public static HashFunction sha512() { return SHA_512; } - private static final HashFunction SHA_512 = - new MessageDigestHashFunction("SHA-512", "Hashing.sha512()"); - - /** - * Returns a hash function implementing the CRC-32 checksum algorithm (32 hash bits) by delegating - * to the {@link CRC32} {@link Checksum}. - * - * <p>To get the {@code long} value equivalent to {@link Checksum#getValue()} for a - * {@code HashCode} produced by this function, use {@link HashCode#padToLong()}. - * - * @since 14.0 - */ - public static HashFunction crc32() { - return CRC_32; - } - - private static final HashFunction CRC_32 = - checksumHashFunction(ChecksumType.CRC_32, "Hashing.crc32()"); - - /** - * Returns a hash function implementing the Adler-32 checksum algorithm (32 hash bits) by - * delegating to the {@link Adler32} {@link Checksum}. - * - * <p>To get the {@code long} value equivalent to {@link Checksum#getValue()} for a - * {@code HashCode} produced by this function, use {@link HashCode#padToLong()}. - * - * @since 14.0 - */ - public static HashFunction adler32() { - return ADLER_32; - } - - private static final HashFunction ADLER_32 = - checksumHashFunction(ChecksumType.ADLER_32, "Hashing.adler32()"); - - private static HashFunction checksumHashFunction(ChecksumType type, String toString) { - return new ChecksumHashFunction(type, type.bits, toString); - } - - enum ChecksumType implements Supplier<Checksum> { - CRC_32(32) { - @Override - public Checksum get() { - return new CRC32(); - } - }, - ADLER_32(32) { - @Override - public Checksum get() { - return new Adler32(); - } - }; - - private final int bits; - - ChecksumType(int bits) { - this.bits = bits; - } - - @Override - public abstract Checksum get(); - } - - // Lazy initialization holder class idiom. + private static final HashFunction SHA_512 = new MessageDigestHashFunction("SHA-512"); /** * If {@code hashCode} has enough bits, returns {@code hashCode.asLong()}, otherwise * returns a {@code long} value with {@code hashCode.asInt()} as the least-significant * four bytes and {@code 0x00} as each of the most-significant four bytes. - * - * @deprecated Use {@code HashCode.padToLong()} instead. This method is scheduled to be - * removed in Guava 15.0. */ - @Deprecated public static long padToLong(HashCode hashCode) { - return hashCode.padToLong(); + return (hashCode.bits() < 64) ? UnsignedInts.toLong(hashCode.asInt()) : hashCode.asLong(); } /** @@ -270,12 +167,9 @@ public final class Hashing { * * <p>See the <a href="http://en.wikipedia.org/wiki/Consistent_hashing">wikipedia * article on consistent hashing</a> for more information. - * <p> - * If you might want to have weights for the buckets in the future, take a look at - * {@code weightedConsistentHash}. */ public static int consistentHash(HashCode hashCode, int buckets) { - return consistentHash(hashCode.padToLong(), buckets); + return consistentHash(padToLong(hashCode), buckets); } /** @@ -290,19 +184,21 @@ public final class Hashing { * * <p>See the <a href="http://en.wikipedia.org/wiki/Consistent_hashing">wikipedia * article on consistent hashing</a> for more information. - * <p> - * If you might want to have weights for the buckets in the future, take a look at - * {@code weightedConsistentHash}. */ public static int consistentHash(long input, int buckets) { checkArgument(buckets > 0, "buckets must be positive: %s", buckets); - LinearCongruentialGenerator generator = new LinearCongruentialGenerator(input); + long h = input; int candidate = 0; int next; // Jump from bucket to bucket until we go out of range while (true) { - next = (int) ((candidate + 1) / generator.nextDouble()); + // See http://en.wikipedia.org/wiki/Linear_congruential_generator + // These values for a and m come from the C++ version of this function. + h = 2862933555777941757L * h + 1; + double inv = 0x1.0p31 / ((int) (h >>> 33) + 1); + next = (int) ((candidate + 1) * inv); + if (next >= 0 && next < buckets) { candidate = next; } else { @@ -334,7 +230,7 @@ public final class Hashing { resultBytes[i] = (byte) (resultBytes[i] * 37 ^ nextBytes[i]); } } - return HashCodes.fromBytesNoCopy(resultBytes); + return HashCodes.fromBytes(resultBytes); } /** @@ -359,7 +255,7 @@ public final class Hashing { resultBytes[i] += nextBytes[i]; } } - return HashCodes.fromBytesNoCopy(resultBytes); + return HashCodes.fromBytes(resultBytes); } /** @@ -370,29 +266,27 @@ public final class Hashing { return (bits + 31) & ~31; } - // TODO(kevinb): Maybe expose this class via a static Hashing method? - @VisibleForTesting - static final class ConcatenatedHashFunction extends AbstractCompositeHashFunction { - private final int bits; + // TODO(kevinb): probably expose this via a Hashing method at some point? + private static class ConcatenatedHashFunction extends AbstractCompositeHashFunction { + final int bits; - ConcatenatedHashFunction(HashFunction... functions) { + ConcatenatedHashFunction(HashFunction[] functions) { super(functions); int bitSum = 0; - for (HashFunction function : functions) { - bitSum += function.bits(); + for (HashFunction f : this.functions) { + bitSum += f.bits(); } this.bits = bitSum; } @Override HashCode makeHash(Hasher[] hashers) { - // TODO(user): Get rid of the ByteBuffer here? byte[] bytes = new byte[bits / 8]; ByteBuffer buffer = ByteBuffer.wrap(bytes); for (Hasher hasher : hashers) { buffer.put(hasher.hash().asBytes()); } - return HashCodes.fromBytesNoCopy(bytes); + return HashCodes.fromBytes(bytes); } @Override @@ -400,21 +294,4 @@ public final class Hashing { return bits; } } - - /** - * Linear CongruentialGenerator to use for consistent hashing. - * See http://en.wikipedia.org/wiki/Linear_congruential_generator - */ - private static final class LinearCongruentialGenerator { - private long state; - - public LinearCongruentialGenerator(long seed) { - this.state = seed; - } - - public double nextDouble() { - state = 2862933555777941757L * state + 1; - return ((double) ((int) (state >>> 33) + 1)) / (0x1.0p31); - } - } } diff --git a/guava/src/com/google/common/hash/MessageDigestHashFunction.java b/guava/src/com/google/common/hash/MessageDigestHashFunction.java index 029a494..03c6471 100644 --- a/guava/src/com/google/common/hash/MessageDigestHashFunction.java +++ b/guava/src/com/google/common/hash/MessageDigestHashFunction.java @@ -14,60 +14,37 @@ package com.google.common.hash; -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.base.Preconditions.checkPositionIndexes; -import java.io.Serializable; +import com.google.common.primitives.Chars; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import com.google.common.primitives.Shorts; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; /** - * {@link HashFunction} adapter for {@link MessageDigest} instances. + * {@link HashFunction} adapter for {@link MessageDigest}s. * - * @author Kevin Bourrillion - * @author Dimitris Andreou + * @author kevinb@google.com (Kevin Bourrillion) + * @author andreou@google.com (Dimitris Andreou) */ -final class MessageDigestHashFunction extends AbstractStreamingHashFunction - implements Serializable { - private final MessageDigest prototype; - private final int bytes; - private final boolean supportsClone; - private final String toString; - - MessageDigestHashFunction(String algorithmName, String toString) { - this.prototype = getMessageDigest(algorithmName); - this.bytes = prototype.getDigestLength(); - this.toString = checkNotNull(toString); - this.supportsClone = supportsClone(); - } - - MessageDigestHashFunction(String algorithmName, int bytes, String toString) { - this.toString = checkNotNull(toString); - this.prototype = getMessageDigest(algorithmName); - int maxLength = prototype.getDigestLength(); - checkArgument(bytes >= 4 && bytes <= maxLength, - "bytes (%s) must be >= 4 and < %s", bytes, maxLength); - this.bytes = bytes; - this.supportsClone = supportsClone(); - } +final class MessageDigestHashFunction extends AbstractStreamingHashFunction { + private final String algorithmName; + private final int bits; - private boolean supportsClone() { - try { - prototype.clone(); - return true; - } catch (CloneNotSupportedException e) { - return false; - } + MessageDigestHashFunction(String algorithmName) { + this.algorithmName = algorithmName; + this.bits = getMessageDigest(algorithmName).getDigestLength() * 8; } - @Override public int bits() { - return bytes * Byte.SIZE; - } - - @Override public String toString() { - return toString; + public int bits() { + return bits; } private static MessageDigest getMessageDigest(String algorithmName) { @@ -79,80 +56,118 @@ final class MessageDigestHashFunction extends AbstractStreamingHashFunction } @Override public Hasher newHasher() { - if (supportsClone) { - try { - return new MessageDigestHasher((MessageDigest) prototype.clone(), bytes); - } catch (CloneNotSupportedException e) { - // falls through - } - } - return new MessageDigestHasher(getMessageDigest(prototype.getAlgorithm()), bytes); + return new MessageDigestHasher(getMessageDigest(algorithmName)); } - private static final class SerializedForm implements Serializable { - private final String algorithmName; - private final int bytes; - private final String toString; + private static class MessageDigestHasher implements Hasher { + private final MessageDigest digest; + private final ByteBuffer scratch; // lazy convenience + private boolean done; - private SerializedForm(String algorithmName, int bytes, String toString) { - this.algorithmName = algorithmName; - this.bytes = bytes; - this.toString = toString; + private MessageDigestHasher(MessageDigest digest) { + this.digest = digest; + this.scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); } - private Object readResolve() { - return new MessageDigestHashFunction(algorithmName, bytes, toString); + @Override public Hasher putByte(byte b) { + checkNotDone(); + digest.update(b); + return this; } - private static final long serialVersionUID = 0; - } + @Override public Hasher putBytes(byte[] bytes) { + checkNotDone(); + digest.update(bytes); + return this; + } - Object writeReplace() { - return new SerializedForm(prototype.getAlgorithm(), bytes, toString); - } + @Override public Hasher putBytes(byte[] bytes, int off, int len) { + checkNotDone(); + checkPositionIndexes(off, off + len, bytes.length); + digest.update(bytes, off, len); + return this; + } - /** - * Hasher that updates a message digest. - */ - private static final class MessageDigestHasher extends AbstractByteHasher { + @Override public Hasher putShort(short s) { + checkNotDone(); + scratch.putShort(s); + digest.update(scratch.array(), 0, Shorts.BYTES); + scratch.clear(); + return this; + } - private final MessageDigest digest; - private final int bytes; - private boolean done; + @Override public Hasher putInt(int i) { + checkNotDone(); + scratch.putInt(i); + digest.update(scratch.array(), 0, Ints.BYTES); + scratch.clear(); + return this; + } - private MessageDigestHasher(MessageDigest digest, int bytes) { - this.digest = digest; - this.bytes = bytes; + @Override public Hasher putLong(long l) { + checkNotDone(); + scratch.putLong(l); + digest.update(scratch.array(), 0, Longs.BYTES); + scratch.clear(); + return this; } - @Override - protected void update(byte b) { + @Override public Hasher putFloat(float f) { checkNotDone(); - digest.update(b); + scratch.putFloat(f); + digest.update(scratch.array(), 0, 4); + scratch.clear(); + return this; } - @Override - protected void update(byte[] b) { + @Override public Hasher putDouble(double d) { checkNotDone(); - digest.update(b); + scratch.putDouble(d); + digest.update(scratch.array(), 0, 8); + scratch.clear(); + return this; + } + + @Override public Hasher putBoolean(boolean b) { + return putByte(b ? (byte) 1 : (byte) 0); + } + + @Override public Hasher putChar(char c) { + checkNotDone(); + scratch.putChar(c); + digest.update(scratch.array(), 0, Chars.BYTES); + scratch.clear(); + return this; + } + + @Override public Hasher putString(CharSequence charSequence) { + for (int i = 0; i < charSequence.length(); i++) { + putChar(charSequence.charAt(i)); + } + return this; + } + + @Override public Hasher putString(CharSequence charSequence, Charset charset) { + try { + return putBytes(charSequence.toString().getBytes(charset.name())); + } catch (java.io.UnsupportedEncodingException impossible) { + throw new AssertionError(impossible); + } } - @Override - protected void update(byte[] b, int off, int len) { + @Override public <T> Hasher putObject(T instance, Funnel<? super T> funnel) { checkNotDone(); - digest.update(b, off, len); + funnel.funnel(instance, this); + return this; } private void checkNotDone() { checkState(!done, "Cannot use Hasher after calling #hash() on it"); } - @Override public HashCode hash() { done = true; - return (bytes == digest.getDigestLength()) - ? HashCodes.fromBytesNoCopy(digest.digest()) - : HashCodes.fromBytesNoCopy(Arrays.copyOf(digest.digest(), bytes)); + return HashCodes.fromBytes(digest.digest()); } } } diff --git a/guava/src/com/google/common/hash/Murmur3_128HashFunction.java b/guava/src/com/google/common/hash/Murmur3_128HashFunction.java index aab08a3..cd49f87 100644 --- a/guava/src/com/google/common/hash/Murmur3_128HashFunction.java +++ b/guava/src/com/google/common/hash/Murmur3_128HashFunction.java @@ -12,17 +12,6 @@ * the License. */ -/* - * MurmurHash3 was written by Austin Appleby, and is placed in the public - * domain. The author hereby disclaims copyright to this source code. - */ - -/* - * Source: - * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - * (Modified to adapt to Guava coding conventions and to use the HashFunction interface) - */ - package com.google.common.hash; import static com.google.common.primitives.UnsignedBytes.toInt; @@ -35,8 +24,8 @@ import java.nio.ByteOrder; * See http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp * MurmurHash3_x64_128 * - * @author Austin Appleby - * @author Dimitris Andreou + * @author aappleby@google.com (Austin Appleby) + * @author andreou@google.com (Dimitris Andreou) */ final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implements Serializable { // TODO(user): when the shortcuts are implemented, update BloomFilterStrategies @@ -54,41 +43,40 @@ final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implem return new Murmur3_128Hasher(seed); } - @Override - public String toString() { - return "Hashing.murmur3_128(" + seed + ")"; - } - private static final class Murmur3_128Hasher extends AbstractStreamingHasher { - private static final int CHUNK_SIZE = 16; - private static final long C1 = 0x87c37b91114253d5L; - private static final long C2 = 0x4cf5ad432745937fL; - private long h1; - private long h2; - private int length; + long h1; + long h2; + long c1 = 0x87c37b91114253d5L; + long c2 = 0x4cf5ad432745937fL; + int len; Murmur3_128Hasher(int seed) { - super(CHUNK_SIZE); - this.h1 = seed; - this.h2 = seed; - this.length = 0; + super(16); + h1 = seed; + h2 = seed; } @Override protected void process(ByteBuffer bb) { long k1 = bb.getLong(); long k2 = bb.getLong(); + len += 16; bmix64(k1, k2); - length += CHUNK_SIZE; } private void bmix64(long k1, long k2) { - h1 ^= mixK1(k1); + k1 *= c1; + k1 = Long.rotateLeft(k1, 31); + k1 *= c2; + h1 ^= k1; h1 = Long.rotateLeft(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; - h2 ^= mixK2(k2); + k2 *= c2; + k2 = Long.rotateLeft(k2, 33); + k2 *= c1; + h2 ^= k2; h2 = Long.rotateLeft(h2, 31); h2 += h1; @@ -98,7 +86,7 @@ final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implem @Override protected void processRemaining(ByteBuffer bb) { long k1 = 0; long k2 = 0; - length += bb.remaining(); + len += bb.remaining(); switch (bb.remaining()) { case 15: k2 ^= (long) toInt(bb.get(14)) << 48; // fall through @@ -113,10 +101,14 @@ final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implem case 10: k2 ^= (long) toInt(bb.get(9)) << 8; // fall through case 9: - k2 ^= (long) toInt(bb.get(8)); // fall through + k2 ^= (long) toInt(bb.get(8)) << 0; + k2 *= c2; + k2 = Long.rotateLeft(k2, 33); + k2 *= c1; + h2 ^= k2; + // fall through case 8: - k1 ^= bb.getLong(); - break; + k1 ^= (long) toInt(bb.get(7)) << 56; // fall through case 7: k1 ^= (long) toInt(bb.get(6)) << 48; // fall through case 6: @@ -130,18 +122,19 @@ final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implem case 2: k1 ^= (long) toInt(bb.get(1)) << 8; // fall through case 1: - k1 ^= (long) toInt(bb.get(0)); - break; + k1 ^= (long) toInt(bb.get(0)) << 0; + k1 *= c1; + k1 = Long.rotateLeft(k1, 31); + k1 *= c2; + h1 ^= k1; + // fall through default: - throw new AssertionError("Should never get here."); } - h1 ^= mixK1(k1); - h2 ^= mixK2(k2); } @Override public HashCode makeHash() { - h1 ^= length; - h2 ^= length; + h1 ^= len; + h2 ^= len; h1 += h2; h2 += h1; @@ -152,15 +145,13 @@ final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implem h1 += h2; h2 += h1; - return HashCodes.fromBytesNoCopy(ByteBuffer - .wrap(new byte[CHUNK_SIZE]) - .order(ByteOrder.LITTLE_ENDIAN) - .putLong(h1) - .putLong(h2) - .array()); + ByteBuffer bb = ByteBuffer.wrap(new byte[16]).order(ByteOrder.LITTLE_ENDIAN); + bb.putLong(h1); + bb.putLong(h2); + return HashCodes.fromBytes(bb.array()); } - private static long fmix64(long k) { + private long fmix64(long k) { k ^= k >>> 33; k *= 0xff51afd7ed558ccdL; k ^= k >>> 33; @@ -168,20 +159,6 @@ final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implem k ^= k >>> 33; return k; } - - private static long mixK1(long k1) { - k1 *= C1; - k1 = Long.rotateLeft(k1, 31); - k1 *= C2; - return k1; - } - - private static long mixK2(long k2) { - k2 *= C2; - k2 = Long.rotateLeft(k2, 33); - k2 *= C1; - return k2; - } } private static final long serialVersionUID = 0L; diff --git a/guava/src/com/google/common/hash/Murmur3_32HashFunction.java b/guava/src/com/google/common/hash/Murmur3_32HashFunction.java index c6637de..5c03b92 100644 --- a/guava/src/com/google/common/hash/Murmur3_32HashFunction.java +++ b/guava/src/com/google/common/hash/Murmur3_32HashFunction.java @@ -12,42 +12,23 @@ * the License. */ -/* - * MurmurHash3 was written by Austin Appleby, and is placed in the public - * domain. The author hereby disclaims copyright to this source code. - */ - -/* - * Source: - * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - * (Modified to adapt to Guava coding conventions and to use the HashFunction interface) - */ - package com.google.common.hash; import static com.google.common.primitives.UnsignedBytes.toInt; -import com.google.common.primitives.Chars; -import com.google.common.primitives.Ints; -import com.google.common.primitives.Longs; - import java.io.Serializable; import java.nio.ByteBuffer; /** * See http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp * MurmurHash3_x86_32 - * - * @author Austin Appleby - * @author Dimitris Andreou - * @author Kurt Alfred Kluever + * + * @author aappleby@google.com (Austin Appleby) + * @author andreou@google.com (Dimitris Andreou) */ final class Murmur3_32HashFunction extends AbstractStreamingHashFunction implements Serializable { - private static final int C1 = 0xcc9e2d51; - private static final int C2 = 0x1b873593; - private final int seed; - + Murmur3_32HashFunction(int seed) { this.seed = seed; } @@ -60,107 +41,63 @@ final class Murmur3_32HashFunction extends AbstractStreamingHashFunction impleme return new Murmur3_32Hasher(seed); } - @Override - public String toString() { - return "Hashing.murmur3_32(" + seed + ")"; - } - - @Override public HashCode hashInt(int input) { - int k1 = mixK1(input); - int h1 = mixH1(seed, k1); - - return fmix(h1, Ints.BYTES); - } - - @Override public HashCode hashLong(long input) { - int low = (int) input; - int high = (int) (input >>> 32); - - int k1 = mixK1(low); - int h1 = mixH1(seed, k1); - - k1 = mixK1(high); - h1 = mixH1(h1, k1); - - return fmix(h1, Longs.BYTES); - } - - // TODO(user): Maybe implement #hashBytes instead? - @Override public HashCode hashString(CharSequence input) { - int h1 = seed; - - // step through the CharSequence 2 chars at a time - for (int i = 1; i < input.length(); i += 2) { - int k1 = input.charAt(i - 1) | (input.charAt(i) << 16); - k1 = mixK1(k1); - h1 = mixH1(h1, k1); - } - - // deal with any remaining characters - if ((input.length() & 1) == 1) { - int k1 = input.charAt(input.length() - 1); - k1 = mixK1(k1); - h1 ^= k1; - } - - return fmix(h1, Chars.BYTES * input.length()); - } - - private static int mixK1(int k1) { - k1 *= C1; - k1 = Integer.rotateLeft(k1, 15); - k1 *= C2; - return k1; - } - - private static int mixH1(int h1, int k1) { - h1 ^= k1; - h1 = Integer.rotateLeft(h1, 13); - h1 = h1 * 5 + 0xe6546b64; - return h1; - } - - // Finalization mix - force all bits of a hash block to avalanche - private static HashCode fmix(int h1, int length) { - h1 ^= length; - h1 ^= h1 >>> 16; - h1 *= 0x85ebca6b; - h1 ^= h1 >>> 13; - h1 *= 0xc2b2ae35; - h1 ^= h1 >>> 16; - return HashCodes.fromInt(h1); - } - private static final class Murmur3_32Hasher extends AbstractStreamingHasher { - private static final int CHUNK_SIZE = 4; - private int h1; - private int length; + int h1; + int c1 = 0xcc9e2d51; + int c2 = 0x1b873593; + int len; Murmur3_32Hasher(int seed) { - super(CHUNK_SIZE); - this.h1 = seed; - this.length = 0; + super(4); + h1 = seed; } @Override protected void process(ByteBuffer bb) { - int k1 = Murmur3_32HashFunction.mixK1(bb.getInt()); - h1 = Murmur3_32HashFunction.mixH1(h1, k1); - length += CHUNK_SIZE; + int k1 = bb.getInt(); + len += 4; + + k1 *= c1; + k1 = Integer.rotateLeft(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = Integer.rotateLeft(h1, 13); + h1 = h1 * 5 + 0xe6546b64; } - + @Override protected void processRemaining(ByteBuffer bb) { - length += bb.remaining(); + len += bb.remaining(); int k1 = 0; - for (int i = 0; bb.hasRemaining(); i += 8) { - k1 ^= toInt(bb.get()) << i; + switch (bb.remaining()) { + case 3: + k1 ^= toInt(bb.get(2)) << 16; + // fall through + case 2: + k1 ^= toInt(bb.get(1)) << 8; + // fall through + case 1: + k1 ^= toInt(bb.get(0)); + // fall through + default: + k1 *= c1; + k1 = Integer.rotateLeft(k1, 15); + k1 *= c2; + h1 ^= k1; } - h1 ^= Murmur3_32HashFunction.mixK1(k1); } - + @Override public HashCode makeHash() { - return Murmur3_32HashFunction.fmix(h1, length); + h1 ^= len; + + h1 ^= h1 >>> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >>> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >>> 16; + + return HashCodes.fromInt(h1); } } - + private static final long serialVersionUID = 0L; } diff --git a/guava/src/com/google/common/hash/PrimitiveSink.java b/guava/src/com/google/common/hash/Sink.java index c851a1e..37167fc 100644 --- a/guava/src/com/google/common/hash/PrimitiveSink.java +++ b/guava/src/com/google/common/hash/Sink.java @@ -22,17 +22,17 @@ import java.nio.charset.Charset; * An object which can receive a stream of primitive values. * * @author Kevin Bourrillion - * @since 12.0 (in 11.0 as {@code Sink}) + * @since 11.0 */ @Beta -public interface PrimitiveSink { +public interface Sink { /** * Puts a byte into this sink. * * @param b a byte * @return this instance */ - PrimitiveSink putByte(byte b); + Sink putByte(byte b); /** * Puts an array of bytes into this sink. @@ -40,7 +40,7 @@ public interface PrimitiveSink { * @param bytes a byte array * @return this instance */ - PrimitiveSink putBytes(byte[] bytes); + Sink putBytes(byte[] bytes); /** * Puts a chunk of an array of bytes into this sink. {@code bytes[off]} is the first byte written, @@ -53,50 +53,50 @@ public interface PrimitiveSink { * @throws IndexOutOfBoundsException if {@code off < 0} or {@code off + len > bytes.length} or * {@code len < 0} */ - PrimitiveSink putBytes(byte[] bytes, int off, int len); + Sink putBytes(byte[] bytes, int off, int len); /** * Puts a short into this sink. */ - PrimitiveSink putShort(short s); + Sink putShort(short s); /** * Puts an int into this sink. */ - PrimitiveSink putInt(int i); + Sink putInt(int i); /** * Puts a long into this sink. */ - PrimitiveSink putLong(long l); + Sink putLong(long l); /** * Puts a float into this sink. */ - PrimitiveSink putFloat(float f); + Sink putFloat(float f); /** * Puts a double into this sink. */ - PrimitiveSink putDouble(double d); + Sink putDouble(double d); /** * Puts a boolean into this sink. */ - PrimitiveSink putBoolean(boolean b); + Sink putBoolean(boolean b); /** * Puts a character into this sink. */ - PrimitiveSink putChar(char c); + Sink putChar(char c); /** * Puts a string into this sink. */ - PrimitiveSink putString(CharSequence charSequence); + Sink putString(CharSequence charSequence); /** * Puts a string into this sink using the given charset. */ - PrimitiveSink putString(CharSequence charSequence, Charset charset); + Sink putString(CharSequence charSequence, Charset charset); } diff --git a/guava/src/com/google/common/hash/package-info.java b/guava/src/com/google/common/hash/package-info.java index 7df4e73..4acc096 100644 --- a/guava/src/com/google/common/hash/package-info.java +++ b/guava/src/com/google/common/hash/package-info.java @@ -15,10 +15,6 @@ // TODO(user): when things stabilize, flesh this out /** * Hash functions and related structures. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/HashingExplained"> - * hashing</a>. */ @ParametersAreNonnullByDefault package com.google.common.hash; diff --git a/guava/src/com/google/common/io/AppendableWriter.java b/guava/src/com/google/common/io/AppendableWriter.java index 7705183..8033e46 100644 --- a/guava/src/com/google/common/io/AppendableWriter.java +++ b/guava/src/com/google/common/io/AppendableWriter.java @@ -16,15 +16,11 @@ package com.google.common.io; -import static com.google.common.base.Preconditions.checkNotNull; - import java.io.Closeable; import java.io.Flushable; import java.io.IOException; import java.io.Writer; -import javax.annotation.Nullable; - /** * Writer that places all output on an {@link Appendable} target. If the target * is {@link Flushable} or {@link Closeable}, flush()es and close()s will also @@ -44,7 +40,7 @@ class AppendableWriter extends Writer { * @param target target to which to append output */ AppendableWriter(Appendable target) { - this.target = checkNotNull(target); + this.target = target; } /* @@ -83,12 +79,12 @@ class AppendableWriter extends Writer { target.append((char) c); } - @Override public void write(@Nullable String str) throws IOException { + @Override public void write(String str) throws IOException { checkNotClosed(); target.append(str); } - @Override public void write(@Nullable String str, int off, int len) throws IOException { + @Override public void write(String str, int off, int len) throws IOException { checkNotClosed(); // tricky: append takes start, end pair... target.append(str, off, off + len); @@ -100,13 +96,13 @@ class AppendableWriter extends Writer { return this; } - @Override public Writer append(@Nullable CharSequence charSeq) throws IOException { + @Override public Writer append(CharSequence charSeq) throws IOException { checkNotClosed(); target.append(charSeq); return this; } - @Override public Writer append(@Nullable CharSequence charSeq, int start, int end) + @Override public Writer append(CharSequence charSeq, int start, int end) throws IOException { checkNotClosed(); target.append(charSeq, start, end); diff --git a/guava/src/com/google/common/io/BaseEncoding.java b/guava/src/com/google/common/io/BaseEncoding.java deleted file mode 100644 index 4af0719..0000000 --- a/guava/src/com/google/common/io/BaseEncoding.java +++ /dev/null @@ -1,882 +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.asCharOutput; -import static com.google.common.io.GwtWorkarounds.asInputStream; -import static com.google.common.io.GwtWorkarounds.asOutputStream; -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.annotations.GwtIncompatible; -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.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -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(); - } - - /** - * Returns an {@code OutputStream} that encodes bytes using this encoding into the specified - * {@code Writer}. When the returned {@code OutputStream} is closed, so is the backing - * {@code Writer}. - */ - @GwtIncompatible("Writer,OutputStream") - public final OutputStream encodingStream(Writer writer) { - return asOutputStream(encodingStream(asCharOutput(writer))); - } - - /** - * Returns an {@code OutputSupplier} that supplies streams that encode bytes using this encoding - * into writers from the specified {@code OutputSupplier}. - */ - @GwtIncompatible("Writer,OutputStream") - public final OutputSupplier<OutputStream> encodingStream( - final OutputSupplier<? extends Writer> writerSupplier) { - checkNotNull(writerSupplier); - return new OutputSupplier<OutputStream>() { - @Override - public OutputStream getOutput() throws IOException { - return encodingStream(writerSupplier.getOutput()); - } - }; - } - - /** - * Returns a {@code ByteSink} that writes base-encoded bytes to the specified {@code CharSink}. - */ - @GwtIncompatible("ByteSink,CharSink") - public final ByteSink encodingSink(final CharSink encodedSink) { - checkNotNull(encodedSink); - return new ByteSink() { - @Override - public OutputStream openStream() throws IOException { - return encodingStream(encodedSink.openStream()); - } - }; - } - - // 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); - } - - /** - * Returns an {@code InputStream} that decodes base-encoded input from the specified - * {@code Reader}. - */ - @GwtIncompatible("Reader,InputStream") - public final InputStream decodingStream(Reader reader) { - return asInputStream(decodingStream(asCharInput(reader))); - } - - /** - * Returns an {@code InputSupplier} that supplies input streams that decode base-encoded input - * from readers from the specified supplier. - */ - @GwtIncompatible("Reader,InputStream") - public final InputSupplier<InputStream> decodingStream( - final InputSupplier<? extends Reader> readerSupplier) { - checkNotNull(readerSupplier); - return new InputSupplier<InputStream>() { - @Override - public InputStream getInput() throws IOException { - return decodingStream(readerSupplier.getInput()); - } - }; - } - - /** - * Returns a {@code ByteSource} that reads base-encoded bytes from the specified - * {@code CharSource}. - */ - @GwtIncompatible("ByteSource,CharSource") - public final ByteSource decodingSource(final CharSource encodedSource) { - checkNotNull(encodedSource); - return new ByteSource() { - @Override - public InputStream openStream() throws IOException { - return decodingStream(encodedSource.openStream()); - } - }; - } - - // 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/src/com/google/common/io/ByteArrayDataInput.java b/guava/src/com/google/common/io/ByteArrayDataInput.java index 3f4a467..f374b4e 100644 --- a/guava/src/com/google/common/io/ByteArrayDataInput.java +++ b/guava/src/com/google/common/io/ByteArrayDataInput.java @@ -22,44 +22,26 @@ import java.io.IOException; /** * An extension of {@code DataInput} for reading from in-memory byte arrays; its * methods offer identical functionality but do not throw {@link IOException}. - * - * <p><b>Warning:<b> The caller is responsible for not attempting to read past - * the end of the array. If any method encounters the end of the array - * prematurely, it throws {@link IllegalStateException} to signify <i>programmer - * error</i>. This behavior is a technical violation of the supertype's - * contract, which specifies a checked exception. + * If any method encounters the end of the array prematurely, it throws {@link + * IllegalStateException}. * * @author Kevin Bourrillion * @since 1.0 */ public interface ByteArrayDataInput extends DataInput { @Override void readFully(byte b[]); - @Override void readFully(byte b[], int off, int len); - @Override int skipBytes(int n); - @Override boolean readBoolean(); - @Override byte readByte(); - @Override int readUnsignedByte(); - @Override short readShort(); - @Override int readUnsignedShort(); - @Override char readChar(); - @Override int readInt(); - @Override long readLong(); - @Override float readFloat(); - @Override double readDouble(); - @Override String readLine(); - @Override String readUTF(); } diff --git a/guava/src/com/google/common/io/ByteSink.java b/guava/src/com/google/common/io/ByteSink.java deleted file mode 100644 index 781b2c3..0000000 --- a/guava/src/com/google/common/io/ByteSink.java +++ /dev/null @@ -1,144 +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 java.io.BufferedOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.nio.charset.Charset; - -/** - * A destination to which bytes can be written, such as a file. Unlike an {@link OutputStream}, a - * {@code ByteSink} is not an open, stateful stream that can be written to and closed. Instead, it - * is an immutable <i>supplier</i> of {@code OutputStream} instances. - * - * <p>{@code ByteSink} provides two kinds of methods: - * <ul> - * <li><b>Methods that return a stream:</b> These methods should return a <i>new</i>, independent - * instance each time they are called. The caller is responsible for ensuring that the returned - * stream is closed. - * <li><b>Convenience methods:</b> These are implementations of common operations that are - * typically implemented by opening a stream using one of the methods in the first category, doing - * something and finally closing the stream or channel that was opened. - * </ul> - * - * @since 14.0 - * @author Colin Decker - */ -public abstract class ByteSink { - - /** - * Returns a {@link CharSink} view of this {@code ByteSink} that writes characters to this sink - * as bytes encoded with the given {@link Charset charset}. - */ - public CharSink asCharSink(Charset charset) { - return new AsCharSink(charset); - } - - /** - * Opens a new {@link OutputStream} for writing to this sink. This method should return a new, - * independent stream each time it is called. - * - * <p>The caller is responsible for ensuring that the returned stream is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the stream - */ - public abstract OutputStream openStream() throws IOException; - - /** - * Opens a new {@link BufferedOutputStream} for writing to this sink. This method should return a - * new, independent stream each time it is called. - * - * <p>The caller is responsible for ensuring that the returned stream is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the stream - */ - public BufferedOutputStream openBufferedStream() throws IOException { - OutputStream out = openStream(); - return (out instanceof BufferedOutputStream) - ? (BufferedOutputStream) out - : new BufferedOutputStream(out); - } - - /** - * Writes all the given bytes to this sink. - * - * @throws IOException if an I/O occurs in the process of writing to this sink - */ - public void write(byte[] bytes) throws IOException { - checkNotNull(bytes); - - Closer closer = Closer.create(); - try { - OutputStream out = closer.register(openStream()); - out.write(bytes); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Writes all the bytes from the given {@code InputStream} to this sink. Does not close - * {@code input}. - * - * @throws IOException if an I/O occurs in the process of reading from {@code input} or writing to - * this sink - */ - public long writeFrom(InputStream input) throws IOException { - checkNotNull(input); - - Closer closer = Closer.create(); - try { - OutputStream out = closer.register(openStream()); - return ByteStreams.copy(input, out); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * A char sink that encodes written characters with a charset and writes resulting bytes to this - * byte sink. - */ - private final class AsCharSink extends CharSink { - - private final Charset charset; - - private AsCharSink(Charset charset) { - this.charset = checkNotNull(charset); - } - - @Override - public Writer openStream() throws IOException { - return new OutputStreamWriter(ByteSink.this.openStream(), charset); - } - - @Override - public String toString() { - return ByteSink.this.toString() + ".asCharSink(" + charset + ")"; - } - } -} diff --git a/guava/src/com/google/common/io/ByteSource.java b/guava/src/com/google/common/io/ByteSource.java deleted file mode 100644 index 44bb6ff..0000000 --- a/guava/src/com/google/common/io/ByteSource.java +++ /dev/null @@ -1,343 +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 com.google.common.hash.Funnels; -import com.google.common.hash.HashCode; -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hasher; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.Reader; -import java.nio.charset.Charset; -import java.util.Arrays; - -/** - * A readable source of bytes, such as a file. Unlike an {@link InputStream}, a - * {@code ByteSource} is not an open, stateful stream for input that can be read and closed. - * Instead, it is an immutable <i>supplier</i> of {@code InputStream} instances. - * - * <p>{@code ByteSource} provides two kinds of methods: - * <ul> - * <li><b>Methods that return a stream:</b> These methods should return a <i>new</i>, independent - * instance each time they are called. The caller is responsible for ensuring that the returned - * stream is closed. - * <li><b>Convenience methods:</b> These are implementations of common operations that are - * typically implemented by opening a stream using one of the methods in the first category, doing - * something and finally closing the stream that was opened. - * </ul> - * - * @since 14.0 - * @author Colin Decker - */ -public abstract class ByteSource { - - private static final int BUF_SIZE = 0x1000; // 4K - - /** - * Returns a {@link CharSource} view of this byte source that decodes bytes read from this source - * as characters using the given {@link Charset}. - */ - public CharSource asCharSource(Charset charset) { - return new AsCharSource(charset); - } - - /** - * Opens a new {@link InputStream} for reading from this source. This method should return a new, - * independent stream each time it is called. - * - * <p>The caller is responsible for ensuring that the returned stream is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the stream - */ - public abstract InputStream openStream() throws IOException; - - /** - * Opens a new {@link BufferedInputStream} for reading from this source. This method should return - * a new, independent stream each time it is called. - * - * <p>The caller is responsible for ensuring that the returned stream is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the stream - */ - public BufferedInputStream openBufferedStream() throws IOException { - InputStream in = openStream(); - return (in instanceof BufferedInputStream) - ? (BufferedInputStream) in - : new BufferedInputStream(in); - } - - /** - * Returns a view of a slice of this byte source that is at most {@code length} bytes long - * starting at the given {@code offset}. - * - * @throws IllegalArgumentException if {@code offset} or {@code length} is negative - */ - public ByteSource slice(long offset, long length) { - return new SlicedByteSource(offset, length); - } - - /** - * Returns the size of this source in bytes. For most implementations, this is a heavyweight - * operation that will open a stream, read (or {@link InputStream#skip(long) skip}, if possible) - * to the end of the stream and return the total number of bytes that were read. - * - * <p>For some sources, such as a file, this method may use a more efficient implementation. Note - * that in such cases, it is <i>possible</i> that this method will return a different number of - * bytes than would be returned by reading all of the bytes (for example, some special files may - * return a size of 0 despite actually having content when read). - * - * <p>In either case, if this is a mutable source such as a file, the size it returns may not be - * the same number of bytes a subsequent read would return. - * - * @throws IOException if an I/O error occurs in the process of reading the size of this source - */ - public long size() throws IOException { - Closer closer = Closer.create(); - try { - InputStream in = closer.register(openStream()); - return countBySkipping(in); - } catch (IOException e) { - // skip may not be supported... at any rate, try reading - } finally { - closer.close(); - } - - closer = Closer.create(); - try { - InputStream in = closer.register(openStream()); - return countByReading(in); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Counts the bytes in the given input stream using skip if possible. Returns SKIP_FAILED if the - * first call to skip threw, in which case skip may just not be supported. - */ - private long countBySkipping(InputStream in) throws IOException { - long count = 0; - while (true) { - // don't try to skip more than available() - // things may work really wrong with FileInputStream otherwise - long skipped = in.skip(Math.min(in.available(), Integer.MAX_VALUE)); - if (skipped <= 0) { - if (in.read() == -1) { - return count; - } - count++; - } else { - count += skipped; - } - } - } - - private static final byte[] countBuffer = new byte[BUF_SIZE]; - - private long countByReading(InputStream in) throws IOException { - long count = 0; - long read; - while ((read = in.read(countBuffer)) != -1) { - count += read; - } - return count; - } - - /** - * Copies the contents of this byte source to the given {@code OutputStream}. Does not close - * {@code output}. - * - * @throws IOException if an I/O error occurs in the process of reading from this source or - * writing to {@code output} - */ - public long copyTo(OutputStream output) throws IOException { - checkNotNull(output); - - Closer closer = Closer.create(); - try { - InputStream in = closer.register(openStream()); - return ByteStreams.copy(in, output); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Copies the contents of this byte source to the given {@code ByteSink}. - * - * @throws IOException if an I/O error occurs in the process of reading from this source or - * writing to {@code sink} - */ - public long copyTo(ByteSink sink) throws IOException { - checkNotNull(sink); - - Closer closer = Closer.create(); - try { - InputStream in = closer.register(openStream()); - OutputStream out = closer.register(sink.openStream()); - return ByteStreams.copy(in, out); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Reads the full contents of this byte source as a byte array. - * - * @throws IOException if an I/O error occurs in the process of reading from this source - */ - public byte[] read() throws IOException { - Closer closer = Closer.create(); - try { - InputStream in = closer.register(openStream()); - return ByteStreams.toByteArray(in); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Hashes the contents of this byte source using the given hash function. - * - * @throws IOException if an I/O error occurs in the process of reading from this source - */ - public HashCode hash(HashFunction hashFunction) throws IOException { - Hasher hasher = hashFunction.newHasher(); - copyTo(Funnels.asOutputStream(hasher)); - return hasher.hash(); - } - - /** - * Checks that the contents of this byte source are equal to the contents of the given byte - * source. - * - * @throws IOException if an I/O error occurs in the process of reading from this source or - * {@code other} - */ - public boolean contentEquals(ByteSource other) throws IOException { - checkNotNull(other); - - byte[] buf1 = new byte[BUF_SIZE]; - byte[] buf2 = new byte[BUF_SIZE]; - - Closer closer = Closer.create(); - try { - InputStream in1 = closer.register(openStream()); - InputStream in2 = closer.register(other.openStream()); - while (true) { - int read1 = ByteStreams.read(in1, buf1, 0, BUF_SIZE); - int read2 = ByteStreams.read(in2, buf2, 0, BUF_SIZE); - if (read1 != read2 || !Arrays.equals(buf1, buf2)) { - return false; - } else if (read1 != BUF_SIZE) { - return true; - } - } - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * A char source that reads bytes from this source and decodes them as characters using a - * charset. - */ - private final class AsCharSource extends CharSource { - - private final Charset charset; - - private AsCharSource(Charset charset) { - this.charset = checkNotNull(charset); - } - - @Override - public Reader openStream() throws IOException { - return new InputStreamReader(ByteSource.this.openStream(), charset); - } - - @Override - public String toString() { - return ByteSource.this.toString() + ".asCharSource(" + charset + ")"; - } - } - - /** - * A view of a subsection of the containing byte source. - */ - private final class SlicedByteSource extends ByteSource { - - private final long offset; - private final long length; - - private SlicedByteSource(long offset, long length) { - checkArgument(offset >= 0, "offset (%s) may not be negative", offset); - checkArgument(length >= 0, "length (%s) may not be negative", length); - this.offset = offset; - this.length = length; - } - - @Override - public InputStream openStream() throws IOException { - InputStream in = ByteSource.this.openStream(); - if (offset > 0) { - try { - ByteStreams.skipFully(in, offset); - } catch (Throwable e) { - Closer closer = Closer.create(); - closer.register(in); - try { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - } - return ByteStreams.limit(in, length); - } - - @Override - public ByteSource slice(long offset, long length) { - checkArgument(offset >= 0, "offset (%s) may not be negative", offset); - checkArgument(length >= 0, "length (%s) may not be negative", length); - long maxLength = this.length - offset; - return ByteSource.this.slice(this.offset + offset, Math.min(length, maxLength)); - } - - @Override - public String toString() { - return ByteSource.this.toString() + ".slice(" + offset + ", " + length + ")"; - } - } -} diff --git a/guava/src/com/google/common/io/ByteStreams.java b/guava/src/com/google/common/io/ByteStreams.java index 4259d2b..78e63c5 100644 --- a/guava/src/com/google/common/io/ByteStreams.java +++ b/guava/src/com/google/common/io/ByteStreams.java @@ -16,13 +16,8 @@ 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.checkPositionIndex; - import com.google.common.annotations.Beta; -import com.google.common.hash.HashCode; -import com.google.common.hash.HashFunction; +import com.google.common.base.Preconditions; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -31,21 +26,22 @@ import java.io.DataInputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.EOFException; -import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; +import java.security.MessageDigest; import java.util.Arrays; import java.util.zip.Checksum; /** * Provides utility methods for working with byte arrays and I/O streams. * + * <p>All method parameters must be non-null unless documented otherwise. + * * @author Chris Nokleberg - * @author Colin Decker * @since 1.0 */ @Beta @@ -63,7 +59,7 @@ public final class ByteStreams { */ public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier( byte[] b) { - return ByteStreams.asInputSupplier(asByteSource(b)); + return newInputStreamSupplier(b, 0, b.length); } /** @@ -77,58 +73,12 @@ public final class ByteStreams { */ public static InputSupplier<ByteArrayInputStream> newInputStreamSupplier( final byte[] b, final int off, final int len) { - return ByteStreams.asInputSupplier(asByteSource(b).slice(off, len)); - } - - /** - * Returns a new {@link ByteSource} that reads bytes from the given byte array. - * - * @since 14.0 - */ - public static ByteSource asByteSource(byte[] b) { - return new ByteArrayByteSource(b); - } - - private static final class ByteArrayByteSource extends ByteSource { - - private final byte[] bytes; - - private ByteArrayByteSource(byte[] bytes) { - this.bytes = checkNotNull(bytes); - } - - @Override - public InputStream openStream() throws IOException { - return new ByteArrayInputStream(bytes); - } - - @Override - public long size() throws IOException { - return bytes.length; - } - - @Override - public byte[] read() throws IOException { - return bytes.clone(); - } - - @Override - public long copyTo(OutputStream output) throws IOException { - output.write(bytes); - return bytes.length; - } - - @Override - public HashCode hash(HashFunction hashFunction) throws IOException { - return hashFunction.hashBytes(bytes); - } - - // TODO(user): Possibly override slice() - - @Override - public String toString() { - return "ByteStreams.asByteSource(" + BaseEncoding.base16().encode(bytes) + ")"; - } + return new InputSupplier<ByteArrayInputStream>() { + @Override + public ByteArrayInputStream getInput() { + return new ByteArrayInputStream(b, off, len); + } + }; } /** @@ -140,7 +90,15 @@ public final class ByteStreams { */ public static void write(byte[] from, OutputSupplier<? extends OutputStream> to) throws IOException { - asByteSink(to).write(from); + Preconditions.checkNotNull(from); + boolean threw = true; + OutputStream out = to.getOutput(); + try { + out.write(from); + threw = false; + } finally { + Closeables.close(out, threw); + } } /** @@ -154,7 +112,21 @@ public final class ByteStreams { */ public static long copy(InputSupplier<? extends InputStream> from, OutputSupplier<? extends OutputStream> to) throws IOException { - return asByteSource(from).copyTo(asByteSink(to)); + int successfulOps = 0; + InputStream in = from.getInput(); + try { + OutputStream out = to.getOutput(); + try { + long count = copy(in, out); + successfulOps++; + return count; + } finally { + Closeables.close(out, successfulOps < 1); + successfulOps++; + } + } finally { + Closeables.close(in, successfulOps < 2); + } } /** @@ -169,13 +141,21 @@ public final class ByteStreams { */ public static long copy(InputSupplier<? extends InputStream> from, OutputStream to) throws IOException { - return asByteSource(from).copyTo(to); + boolean threw = true; + InputStream in = from.getInput(); + try { + long count = copy(in, to); + threw = false; + return count; + } finally { + Closeables.close(in, threw); + } } /** * Opens an output stream from the supplier, copies all bytes from the input * to the output, and closes the output stream. Does not close or flush the - * input stream. + * output stream. * * @param from the input stream to read from * @param to the output factory @@ -185,7 +165,15 @@ public final class ByteStreams { */ public static long copy(InputStream from, OutputSupplier<? extends OutputStream> to) throws IOException { - return asByteSink(to).writeFrom(from); + boolean threw = true; + OutputStream out = to.getOutput(); + try { + long count = copy(from, out); + threw = false; + return count; + } finally { + Closeables.close(out, threw); + } } /** @@ -199,8 +187,6 @@ public final class ByteStreams { */ public static long copy(InputStream from, OutputStream to) throws IOException { - checkNotNull(from); - checkNotNull(to); byte[] buf = new byte[BUF_SIZE]; long total = 0; while (true) { @@ -225,8 +211,6 @@ public final class ByteStreams { */ public static long copy(ReadableByteChannel from, WritableByteChannel to) throws IOException { - checkNotNull(from); - checkNotNull(to); ByteBuffer buf = ByteBuffer.allocate(BUF_SIZE); long total = 0; while (from.read(buf) != -1) { @@ -261,7 +245,15 @@ public final class ByteStreams { */ public static byte[] toByteArray( InputSupplier<? extends InputStream> supplier) throws IOException { - return asByteSource(supplier).read(); + boolean threw = true; + InputStream in = supplier.getInput(); + try { + byte[] result = toByteArray(in); + threw = false; + return result; + } finally { + Closeables.close(in, threw); + } } /** @@ -280,7 +272,7 @@ public final class ByteStreams { * than the length of the array */ public static ByteArrayDataInput newDataInput(byte[] bytes, int start) { - checkPositionIndex(start, bytes.length); + Preconditions.checkPositionIndex(start, bytes.length); return new ByteArrayDataInputStream(bytes, start); } @@ -433,7 +425,7 @@ public final class ByteStreams { * @throws IllegalArgumentException if {@code size} is negative */ public static ByteArrayDataOutput newDataOutput(int size) { - checkArgument(size >= 0, "Invalid size: %s", size); + Preconditions.checkArgument(size >= 0, "Invalid size: %s", size); return new ByteArrayDataOutputStream(size); } @@ -572,123 +564,35 @@ public final class ByteStreams { @Override public byte[] toByteArray() { return byteArrayOutputSteam.toByteArray(); } - } - - private static final OutputStream NULL_OUTPUT_STREAM = - new OutputStream() { - /** Discards the specified byte. */ - @Override public void write(int b) { - } - /** Discards the specified byte array. */ - @Override public void write(byte[] b) { - checkNotNull(b); - } - /** Discards the specified byte array. */ - @Override public void write(byte[] b, int off, int len) { - checkNotNull(b); - } - - @Override - public String toString() { - return "ByteStreams.nullOutputStream()"; - } - }; - - /** - * Returns an {@link OutputStream} that simply discards written bytes. - * - * @since 14.0 (since 1.0 as com.google.common.io.NullOutputStream) - */ - public static OutputStream nullOutputStream() { - return NULL_OUTPUT_STREAM; - } - /** - * Wraps a {@link InputStream}, limiting the number of bytes which can be - * read. - * - * @param in the input stream to be wrapped - * @param limit the maximum number of bytes to be read - * @return a length-limited {@link InputStream} - * @since 14.0 (since 1.0 as com.google.common.io.LimitInputStream) - */ - public static InputStream limit(InputStream in, long limit) { - return new LimitedInputStream(in, limit); } - private static final class LimitedInputStream extends FilterInputStream { - - private long left; - private long mark = -1; - - LimitedInputStream(InputStream in, long limit) { - super(in); - checkNotNull(in); - checkArgument(limit >= 0, "limit must be non-negative"); - left = limit; - } - - @Override public int available() throws IOException { - return (int) Math.min(in.available(), left); - } - - // it's okay to mark even if mark isn't supported, as reset won't work - @Override public synchronized void mark(int readLimit) { - in.mark(readLimit); - mark = left; - } - - @Override public int read() throws IOException { - if (left == 0) { - return -1; - } - - int result = in.read(); - if (result != -1) { - --left; - } - return result; - } - - @Override public int read(byte[] b, int off, int len) throws IOException { - if (left == 0) { - return -1; - } - - len = (int) Math.min(len, left); - int result = in.read(b, off, len); - if (result != -1) { - left -= result; - } - return result; - } - - @Override public synchronized void reset() throws IOException { - if (!in.markSupported()) { - throw new IOException("Mark not supported"); - } - if (mark == -1) { - throw new IOException("Mark not set"); + // TODO(chrisn): Not all streams support skipping. + /** Returns the length of a supplied input stream, in bytes. */ + public static long length(InputSupplier<? extends InputStream> supplier) + throws IOException { + long count = 0; + boolean threw = true; + InputStream in = supplier.getInput(); + try { + while (true) { + // We skip only Integer.MAX_VALUE due to JDK overflow bugs. + long amt = in.skip(Integer.MAX_VALUE); + if (amt == 0) { + if (in.read() == -1) { + threw = false; + return count; + } + count++; + } else { + count += amt; + } } - - in.reset(); - left = mark; - } - - @Override public long skip(long n) throws IOException { - n = Math.min(n, left); - long skipped = in.skip(n); - left -= skipped; - return skipped; + } finally { + Closeables.close(in, threw); } } - /** Returns the length of a supplied input stream, in bytes. */ - public static long length( - InputSupplier<? extends InputStream> supplier) throws IOException { - return asByteSource(supplier).size(); - } - /** * Returns true if the supplied input streams contain the same bytes. * @@ -696,7 +600,31 @@ public final class ByteStreams { */ public static boolean equal(InputSupplier<? extends InputStream> supplier1, InputSupplier<? extends InputStream> supplier2) throws IOException { - return asByteSource(supplier1).contentEquals(asByteSource(supplier2)); + byte[] buf1 = new byte[BUF_SIZE]; + byte[] buf2 = new byte[BUF_SIZE]; + + boolean threw = true; + InputStream in1 = supplier1.getInput(); + try { + InputStream in2 = supplier2.getInput(); + try { + while (true) { + int read1 = read(in1, buf1, 0, BUF_SIZE); + int read2 = read(in2, buf2, 0, BUF_SIZE); + if (read1 != read2 || !Arrays.equals(buf1, buf2)) { + threw = false; + return false; + } else if (read1 != BUF_SIZE) { + threw = false; + return true; + } + } + } finally { + Closeables.close(in2, threw); + } + } finally { + Closeables.close(in1, threw); + } } /** @@ -728,12 +656,10 @@ public final class ByteStreams { * the bytes. * @throws IOException if an I/O error occurs. */ - public static void readFully( - InputStream in, byte[] b, int off, int len) throws IOException { - int read = read(in, b, off, len); - if (read != len) { - throw new EOFException("reached end of stream after reading " - + read + " bytes; " + len + " bytes expected"); + public static void readFully(InputStream in, byte[] b, int off, int len) + throws IOException { + if (read(in, b, off, len) != len) { + throw new EOFException(); } } @@ -750,15 +676,12 @@ public final class ByteStreams { * support skipping */ public static void skipFully(InputStream in, long n) throws IOException { - long toSkip = n; while (n > 0) { long amt = in.skip(n); if (amt == 0) { // Force a blocking read to avoid infinite loop if (in.read() == -1) { - long skipped = toSkip - n; - throw new EOFException("reached end of stream after skipping " - + skipped + " bytes; " + toSkip + " bytes expected"); + throw new EOFException(); } n--; } else { @@ -775,46 +698,27 @@ public final class ByteStreams { * @return the result of the byte processor * @throws IOException if an I/O error occurs */ - public static <T> T readBytes( - InputSupplier<? extends InputStream> supplier, + public static <T> T readBytes(InputSupplier<? extends InputStream> supplier, ByteProcessor<T> processor) throws IOException { - checkNotNull(supplier); - checkNotNull(processor); - - Closer closer = Closer.create(); + byte[] buf = new byte[BUF_SIZE]; + boolean threw = true; + InputStream in = supplier.getInput(); try { - InputStream in = closer.register(supplier.getInput()); - return readBytes(in, processor); - } catch (Throwable e) { - throw closer.rethrow(e); + int amt; + do { + amt = in.read(buf); + if (amt == -1) { + threw = false; + break; + } + } while (processor.processBytes(buf, 0, amt)); + return processor.getResult(); } finally { - closer.close(); + Closeables.close(in, threw); } } /** - * Process the bytes of the given input stream using the given processor. - * - * @param input the input stream to process - * @param processor the object to which to pass the bytes of the stream - * @return the result of the byte processor - * @throws IOException if an I/O error occurs - * @since 14.0 - */ - public static <T> T readBytes( - InputStream input, ByteProcessor<T> processor) throws IOException { - checkNotNull(input); - checkNotNull(processor); - - byte[] buf = new byte[BUF_SIZE]; - int read; - do { - read = input.read(buf); - } while (read != -1 && processor.processBytes(buf, 0, read)); - return processor.getResult(); - } - - /** * Computes and returns the checksum value for a supplied input stream. * The checksum object is reset when this method returns successfully. * @@ -823,21 +727,16 @@ public final class ByteStreams { * @return the result of {@link Checksum#getValue} after updating the * checksum object with all of the bytes in the stream * @throws IOException if an I/O error occurs - * @deprecated Use {@code hash} with the {@code Hashing.crc32()} or - * {@code Hashing.adler32()} hash functions instead. This method is - * scheduled to be removed in Guava 15.0. */ - @Deprecated - public static long getChecksum( - InputSupplier<? extends InputStream> supplier, final Checksum checksum) - throws IOException { - checkNotNull(checksum); + public static long getChecksum(InputSupplier<? extends InputStream> supplier, + final Checksum checksum) throws IOException { return readBytes(supplier, new ByteProcessor<Long>() { @Override public boolean processBytes(byte[] buf, int off, int len) { checksum.update(buf, off, len); return true; } + @Override public Long getResult() { long result = checksum.getValue(); @@ -848,19 +747,29 @@ public final class ByteStreams { } /** - * Computes the hash code of the data supplied by {@code supplier} using {@code - * hashFunction}. + * Computes and returns the digest value for a supplied input stream. + * The digest object is reset when this method returns successfully. * * @param supplier the input stream factory - * @param hashFunction the hash function to use to hash the data - * @return the {@link HashCode} of all of the bytes in the input stream + * @param md the digest object + * @return the result of {@link MessageDigest#digest()} after updating the + * digest object with all of the bytes in the stream * @throws IOException if an I/O error occurs - * @since 12.0 */ - public static HashCode hash( - InputSupplier<? extends InputStream> supplier, HashFunction hashFunction) - throws IOException { - return asByteSource(supplier).hash(hashFunction); + public static byte[] getDigest(InputSupplier<? extends InputStream> supplier, + final MessageDigest md) throws IOException { + return readBytes(supplier, new ByteProcessor<byte[]>() { + @Override + public boolean processBytes(byte[] buf, int off, int len) { + md.update(buf, off, len); + return true; + } + + @Override + public byte[] getResult() { + return md.digest(); + } + }); } /** @@ -889,8 +798,6 @@ public final class ByteStreams { */ public static int read(InputStream in, byte[] b, int off, int len) throws IOException { - checkNotNull(in); - checkNotNull(b); if (len < 0) { throw new IndexOutOfBoundsException("len is negative"); } @@ -920,7 +827,23 @@ public final class ByteStreams { final InputSupplier<? extends InputStream> supplier, final long offset, final long length) { - return asInputSupplier(asByteSource(supplier).slice(offset, length)); + Preconditions.checkNotNull(supplier); + Preconditions.checkArgument(offset >= 0, "offset is negative"); + Preconditions.checkArgument(length >= 0, "length is negative"); + return new InputSupplier<InputStream>() { + @Override public InputStream getInput() throws IOException { + InputStream in = supplier.getInput(); + if (offset > 0) { + try { + skipFully(in, offset); + } catch (IOException e) { + Closeables.closeQuietly(in); + throw e; + } + } + return new LimitInputStream(in, length); + } + }; } /** @@ -940,7 +863,6 @@ public final class ByteStreams { */ public static InputSupplier<InputStream> join( final Iterable<? extends InputSupplier<? extends InputStream>> suppliers) { - checkNotNull(suppliers); return new InputSupplier<InputStream>() { @Override public InputStream getInput() throws IOException { return new MultiInputStream(suppliers.iterator()); @@ -953,52 +875,4 @@ public final class ByteStreams { InputSupplier<? extends InputStream>... suppliers) { return join(Arrays.asList(suppliers)); } - - // TODO(user): Remove these once Input/OutputSupplier methods are removed - - static <S extends InputStream> InputSupplier<S> asInputSupplier( - final ByteSource source) { - checkNotNull(source); - return new InputSupplier<S>() { - @SuppressWarnings("unchecked") // used internally where known to be safe - @Override - public S getInput() throws IOException { - return (S) source.openStream(); - } - }; - } - - static <S extends OutputStream> OutputSupplier<S> asOutputSupplier( - final ByteSink sink) { - checkNotNull(sink); - return new OutputSupplier<S>() { - @SuppressWarnings("unchecked") // used internally where known to be safe - @Override - public S getOutput() throws IOException { - return (S) sink.openStream(); - } - }; - } - - static ByteSource asByteSource( - final InputSupplier<? extends InputStream> supplier) { - checkNotNull(supplier); - return new ByteSource() { - @Override - public InputStream openStream() throws IOException { - return supplier.getInput(); - } - }; - } - - static ByteSink asByteSink( - final OutputSupplier<? extends OutputStream> supplier) { - checkNotNull(supplier); - return new ByteSink() { - @Override - public OutputStream openStream() throws IOException { - return supplier.getOutput(); - } - }; - } } diff --git a/guava/src/com/google/common/io/CharSink.java b/guava/src/com/google/common/io/CharSink.java deleted file mode 100644 index 064adcd..0000000 --- a/guava/src/com/google/common/io/CharSink.java +++ /dev/null @@ -1,150 +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 java.io.BufferedWriter; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.nio.charset.Charset; - -/** - * A destination to which characters can be written, such as a text file. Unlike a {@link Writer}, a - * {@code CharSink} is not an open, stateful stream that can be written to and closed. Instead, it - * is an immutable <i>supplier</i> of {@code Writer} instances. - * - * <p>{@code CharSink} provides two kinds of methods: - * <ul> - * <li><b>Methods that return a writer:</b> These methods should return a <i>new</i>, - * independent instance each time they are called. The caller is responsible for ensuring that the - * returned writer is closed. - * <li><b>Convenience methods:</b> These are implementations of common operations that are - * typically implemented by opening a writer using one of the methods in the first category, - * doing something and finally closing the writer that was opened. - * </ul> - * - * <p>Any {@link ByteSink} may be viewed as a {@code CharSink} with a specific {@linkplain Charset - * character encoding} using {@link ByteSink#asCharSink(Charset)}. Characters written to the - * resulting {@code CharSink} will written to the {@code ByteSink} as encoded bytes. - * - * @since 14.0 - * @author Colin Decker - */ -public abstract class CharSink { - - /** - * Opens a new {@link Writer} for writing to this sink. This method should return a new, - * independent writer each time it is called. - * - * <p>The caller is responsible for ensuring that the returned writer is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the writer - */ - public abstract Writer openStream() throws IOException; - - /** - * Opens a new {@link BufferedWriter} for writing to this sink. This method should return a new, - * independent writer each time it is called. - * - * <p>The caller is responsible for ensuring that the returned writer is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the writer - */ - public BufferedWriter openBufferedStream() throws IOException { - Writer writer = openStream(); - return (writer instanceof BufferedWriter) - ? (BufferedWriter) writer - : new BufferedWriter(writer); - } - - /** - * Writes the given character sequence to this sink. - * - * @throws IOException if an I/O error in the process of writing to this sink - */ - public void write(CharSequence charSequence) throws IOException { - checkNotNull(charSequence); - - Closer closer = Closer.create(); - try { - Writer out = closer.register(openStream()); - out.append(charSequence); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Writes the given lines of text to this sink with each line (including the last) terminated with - * the operating system's default line separator. This method is equivalent to - * {@code writeLines(lines, System.getProperty("line.separator"))}. - * - * @throws IOException if an I/O error occurs in the process of writing to this sink - */ - public void writeLines(Iterable<? extends CharSequence> lines) throws IOException { - writeLines(lines, System.getProperty("line.separator")); - } - - /** - * Writes the given lines of text to this sink with each line (including the last) terminated with - * the given line separator. - * - * @throws IOException if an I/O error occurs in the process of writing to this sink - */ - public void writeLines(Iterable<? extends CharSequence> lines, String lineSeparator) - throws IOException { - checkNotNull(lines); - checkNotNull(lineSeparator); - - Closer closer = Closer.create(); - try { - BufferedWriter out = closer.register(openBufferedStream()); - for (CharSequence line : lines) { - out.append(line).append(lineSeparator); - } - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Writes all the text from the given {@link Readable} (such as a {@link Reader}) to this sink. - * Does not close {@code readable} if it is {@code Closeable}. - * - * @throws IOException if an I/O error occurs in the process of reading from {@code readable} or - * writing to this sink - */ - public long writeFrom(Readable readable) throws IOException { - checkNotNull(readable); - - Closer closer = Closer.create(); - try { - Writer out = closer.register(openStream()); - return CharStreams.copy(readable, out); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } -} diff --git a/guava/src/com/google/common/io/CharSource.java b/guava/src/com/google/common/io/CharSource.java deleted file mode 100644 index 057deaf..0000000 --- a/guava/src/com/google/common/io/CharSource.java +++ /dev/null @@ -1,192 +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.collect.ImmutableList; -import com.google.common.collect.Lists; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.nio.charset.Charset; -import java.util.List; - -import javax.annotation.Nullable; - -/** - * A readable source of characters, such as a text file. Unlike a {@link Reader}, a - * {@code CharSource} is not an open, stateful stream of characters that can be read and closed. - * Instead, it is an immutable <i>supplier</i> of {@code InputStream} instances. - * - * <p>{@code CharSource} provides two kinds of methods: - * <ul> - * <li><b>Methods that return a reader:</b> These methods should return a <i>new</i>, independent - * instance each time they are called. The caller is responsible for ensuring that the returned - * reader is closed. - * <li><b>Convenience methods:</b> These are implementations of common operations that are - * typically implemented by opening a reader using one of the methods in the first category, - * doing something and finally closing the reader that was opened. - * </ul> - * - * <p>Several methods in this class, such as {@link #readLines()}, break the contents of the - * source into lines. Like {@link BufferedReader}, these methods break lines on any of {@code \n}, - * {@code \r} or {@code \r\n}, do not include the line separator in each line and do not consider - * there to be an empty line at the end if the contents are terminated with a line separator. - * - * <p>Any {@link ByteSource} containing text encoded with a specific {@linkplain Charset character - * encoding} may be viewed as a {@code CharSource} using {@link ByteSource#asCharSource(Charset)}. - * - * @since 14.0 - * @author Colin Decker - */ -public abstract class CharSource { - - /** - * Opens a new {@link Reader} for reading from this source. This method should return a new, - * independent reader each time it is called. - * - * <p>The caller is responsible for ensuring that the returned reader is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the reader - */ - public abstract Reader openStream() throws IOException; - - /** - * Opens a new {@link BufferedReader} for reading from this source. This method should return a - * new, independent reader each time it is called. - * - * <p>The caller is responsible for ensuring that the returned reader is closed. - * - * @throws IOException if an I/O error occurs in the process of opening the reader - */ - public BufferedReader openBufferedStream() throws IOException { - Reader reader = openStream(); - return (reader instanceof BufferedReader) - ? (BufferedReader) reader - : new BufferedReader(reader); - } - - /** - * Appends the contents of this source to the given {@link Appendable} (such as a {@link Writer}). - * Does not close {@code appendable} if it is {@code Closeable}. - * - * @throws IOException if an I/O error occurs in the process of reading from this source or - * writing to {@code appendable} - */ - public long copyTo(Appendable appendable) throws IOException { - checkNotNull(appendable); - - Closer closer = Closer.create(); - try { - Reader reader = closer.register(openStream()); - return CharStreams.copy(reader, appendable); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Copies the contents of this source to the given sink. - * - * @throws IOException if an I/O error occurs in the process of reading from this source or - * writing to {@code sink} - */ - public long copyTo(CharSink sink) throws IOException { - checkNotNull(sink); - - Closer closer = Closer.create(); - try { - Reader reader = closer.register(openStream()); - Writer writer = closer.register(sink.openStream()); - return CharStreams.copy(reader, writer); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Reads the contents of this source as a string. - * - * @throws IOException if an I/O error occurs in the process of reading from this source - */ - public String read() throws IOException { - Closer closer = Closer.create(); - try { - Reader reader = closer.register(openStream()); - return CharStreams.toString(reader); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Reads the first link of this source as a string. Returns {@code null} if this source is empty. - * - * <p>Like {@link BufferedReader}, this method breaks lines on any of {@code \n}, {@code \r} or - * {@code \r\n}, does not include the line separator in the returned line and does not consider - * there to be an extra empty line at the end if the content is terminated with a line separator. - * - * @throws IOException if an I/O error occurs in the process of reading from this source - */ - public @Nullable String readFirstLine() throws IOException { - Closer closer = Closer.create(); - try { - BufferedReader reader = closer.register(openBufferedStream()); - return reader.readLine(); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - /** - * Reads all the lines of this source as a list of strings. The returned list will be empty if - * this source is empty. - * - * <p>Like {@link BufferedReader}, this method breaks lines on any of {@code \n}, {@code \r} or - * {@code \r\n}, does not include the line separator in the returned lines and does not consider - * there to be an extra empty line at the end if the content is terminated with a line separator. - * - * @throws IOException if an I/O error occurs in the process of reading from this source - */ - public ImmutableList<String> readLines() throws IOException { - Closer closer = Closer.create(); - try { - BufferedReader reader = closer.register(openBufferedStream()); - List<String> result = Lists.newArrayList(); - String line; - while ((line = reader.readLine()) != null) { - result.add(line); - } - return ImmutableList.copyOf(result); - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } -} diff --git a/guava/src/com/google/common/io/CharStreams.java b/guava/src/com/google/common/io/CharStreams.java index 6d9999d..0d53d90 100644 --- a/guava/src/com/google/common/io/CharStreams.java +++ b/guava/src/com/google/common/io/CharStreams.java @@ -16,13 +16,8 @@ package com.google.common.io; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.Beta; -import com.google.common.base.Charsets; -import com.google.common.base.Splitter; -import com.google.common.collect.AbstractIterator; -import com.google.common.collect.ImmutableList; +import com.google.common.base.Preconditions; import java.io.Closeable; import java.io.EOFException; @@ -38,9 +33,7 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; -import java.util.regex.Pattern; /** * Provides utility methods for working with character streams. @@ -54,7 +47,6 @@ import java.util.regex.Pattern; * * @author Chris Nokleberg * @author Bin Zhu - * @author Colin Decker * @since 1.0 */ @Beta @@ -72,85 +64,13 @@ public final class CharStreams { */ public static InputSupplier<StringReader> newReaderSupplier( final String value) { - return CharStreams.asInputSupplier(asCharSource(value)); - } - - /** - * Returns a {@link CharSource} that reads the given string value. - * - * @since 14.0 - */ - public static CharSource asCharSource(String string) { - return new StringCharSource(string); - } - - private static final class StringCharSource extends CharSource { - - private static final Splitter LINE_SPLITTER - = Splitter.on(Pattern.compile("\r\n|\n|\r")); - - private final String string; - - private StringCharSource(String string) { - this.string = checkNotNull(string); - } - - @Override - public Reader openStream() { - return new StringReader(string); - } - - @Override - public String read() { - return string; - } - - /** - * Returns an iterable over the lines in the string. If the string ends in - * a newline, a final empty string is not included to match the behavior of - * BufferedReader/LineReader.readLine(). - */ - private Iterable<String> lines() { - return new Iterable<String>() { - @Override - public Iterator<String> iterator() { - return new AbstractIterator<String>() { - Iterator<String> lines = LINE_SPLITTER.split(string).iterator(); - - @Override - protected String computeNext() { - if (lines.hasNext()) { - String next = lines.next(); - // skip last line if it's empty - if (lines.hasNext() || !next.isEmpty()) { - return next; - } - } - return endOfData(); - } - }; - } - }; - } - - @Override - public String readFirstLine() { - Iterator<String> lines = lines().iterator(); - return lines.hasNext() ? lines.next() : null; - } - - @Override - public ImmutableList<String> readLines() { - return ImmutableList.copyOf(lines()); - } - - @Override - public String toString() { - String limited = (string.length() <= 15) - ? string - : string.substring(0, 12) + "..."; - return "CharStreams.asCharSource(" + limited + ")"; - } + Preconditions.checkNotNull(value); + return new InputSupplier<StringReader>() { + @Override + public StringReader getInput() { + return new StringReader(value); + } + }; } /** @@ -158,14 +78,19 @@ public final class CharStreams { * using the given {@link InputStream} factory and character set. * * @param in the factory that will be used to open input streams - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used to decode the input stream * @return the factory */ public static InputSupplier<InputStreamReader> newReaderSupplier( final InputSupplier<? extends InputStream> in, final Charset charset) { - return CharStreams.asInputSupplier( - ByteStreams.asByteSource(in).asCharSource(charset)); + Preconditions.checkNotNull(in); + Preconditions.checkNotNull(charset); + return new InputSupplier<InputStreamReader>() { + @Override + public InputStreamReader getInput() throws IOException { + return new InputStreamReader(in.getInput(), charset); + } + }; } /** @@ -173,14 +98,19 @@ public final class CharStreams { * using the given {@link OutputStream} factory and character set. * * @param out the factory that will be used to open output streams - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used to encode the output stream * @return the factory */ public static OutputSupplier<OutputStreamWriter> newWriterSupplier( final OutputSupplier<? extends OutputStream> out, final Charset charset) { - return CharStreams.asOutputSupplier( - ByteStreams.asByteSink(out).asCharSink(charset)); + Preconditions.checkNotNull(out); + Preconditions.checkNotNull(charset); + return new OutputSupplier<OutputStreamWriter>() { + @Override + public OutputStreamWriter getOutput() throws IOException { + return new OutputStreamWriter(out.getOutput(), charset); + } + }; } /** @@ -193,7 +123,15 @@ public final class CharStreams { */ public static <W extends Appendable & Closeable> void write(CharSequence from, OutputSupplier<W> to) throws IOException { - asCharSink(to).write(from); + Preconditions.checkNotNull(from); + boolean threw = true; + W out = to.getOutput(); + try { + out.append(from); + threw = false; + } finally { + Closeables.close(out, threw); + } } /** @@ -209,7 +147,21 @@ public final class CharStreams { public static <R extends Readable & Closeable, W extends Appendable & Closeable> long copy(InputSupplier<R> from, OutputSupplier<W> to) throws IOException { - return asCharSource(from).copyTo(asCharSink(to)); + int successfulOps = 0; + R in = from.getInput(); + try { + W out = to.getOutput(); + try { + long count = copy(in, out); + successfulOps++; + return count; + } finally { + Closeables.close(out, successfulOps < 1); + successfulOps++; + } + } finally { + Closeables.close(in, successfulOps < 2); + } } /** @@ -224,7 +176,15 @@ public final class CharStreams { */ public static <R extends Readable & Closeable> long copy( InputSupplier<R> from, Appendable to) throws IOException { - return asCharSource(from).copyTo(to); + boolean threw = true; + R in = from.getInput(); + try { + long count = copy(in, to); + threw = false; + return count; + } finally { + Closeables.close(in, threw); + } } /** @@ -237,15 +197,16 @@ public final class CharStreams { * @throws IOException if an I/O error occurs */ public static long copy(Readable from, Appendable to) throws IOException { - checkNotNull(from); - checkNotNull(to); CharBuffer buf = CharBuffer.allocate(BUF_SIZE); long total = 0; - while (from.read(buf) != -1) { + while (true) { + int r = from.read(buf); + if (r == -1) { + break; + } buf.flip(); - to.append(buf); - total += buf.remaining(); - buf.clear(); + to.append(buf, 0, r); + total += r; } return total; } @@ -272,7 +233,7 @@ public final class CharStreams { */ public static <R extends Readable & Closeable> String toString( InputSupplier<R> supplier) throws IOException { - return asCharSource(supplier).read(); + return toStringBuilder(supplier).toString(); } /** @@ -290,6 +251,26 @@ public final class CharStreams { } /** + * Returns the characters from a {@link Readable} & {@link Closeable} object + * supplied by a factory as a new {@link StringBuilder} instance. + * + * @param supplier the factory to read from + * @throws IOException if an I/O error occurs + */ + private static <R extends Readable & Closeable> StringBuilder toStringBuilder( + InputSupplier<R> supplier) throws IOException { + boolean threw = true; + R r = supplier.getInput(); + try { + StringBuilder result = toStringBuilder(r); + threw = false; + return result; + } finally { + Closeables.close(r, threw); + } + } + + /** * Reads the first line from a {@link Readable} & {@link Closeable} object * supplied by a factory. The line does not include line-termination * characters, but does include other leading and trailing whitespace. @@ -300,7 +281,15 @@ public final class CharStreams { */ public static <R extends Readable & Closeable> String readFirstLine( InputSupplier<R> supplier) throws IOException { - return asCharSource(supplier).readFirstLine(); + boolean threw = true; + R r = supplier.getInput(); + try { + String line = new LineReader(r).readLine(); + threw = false; + return line; + } finally { + Closeables.close(r, threw); + } } /** @@ -314,14 +303,14 @@ public final class CharStreams { */ public static <R extends Readable & Closeable> List<String> readLines( InputSupplier<R> supplier) throws IOException { - Closer closer = Closer.create(); + boolean threw = true; + R r = supplier.getInput(); try { - R r = closer.register(supplier.getInput()); - return readLines(r); - } catch (Throwable e) { - throw closer.rethrow(e); + List<String> result = readLines(r); + threw = false; + return result; } finally { - closer.close(); + Closeables.close(r, threw); } } @@ -349,31 +338,6 @@ public final class CharStreams { } /** - * Streams lines from a {@link Readable} object, stopping when the processor - * returns {@code false} or all lines have been read and returning the result - * produced by the processor. Does not close {@code readable}. Note that this - * method may not fully consume the contents of {@code readable} if the - * processor stops processing early. - * - * @throws IOException if an I/O error occurs - * @since 14.0 - */ - public static <T> T readLines( - Readable readable, LineProcessor<T> processor) throws IOException { - checkNotNull(readable); - checkNotNull(processor); - - LineReader lineReader = new LineReader(readable); - String line; - while ((line = lineReader.readLine()) != null) { - if (!processor.processLine(line)) { - break; - } - } - return processor.getResult(); - } - - /** * Streams lines from a {@link Readable} and {@link Closeable} object * supplied by a factory, stopping when our callback returns false, or we * have read all of the lines. @@ -385,18 +349,21 @@ public final class CharStreams { */ public static <R extends Readable & Closeable, T> T readLines( InputSupplier<R> supplier, LineProcessor<T> callback) throws IOException { - checkNotNull(supplier); - checkNotNull(callback); - - Closer closer = Closer.create(); + boolean threw = true; + R r = supplier.getInput(); try { - R r = closer.register(supplier.getInput()); - return readLines(r, callback); - } catch (Throwable e) { - throw closer.rethrow(e); + LineReader lineReader = new LineReader(r); + String line; + while ((line = lineReader.readLine()) != null) { + if (!callback.processLine(line)) { + break; + } + } + threw = false; } finally { - closer.close(); + Closeables.close(r, threw); } + return callback.getResult(); } /** @@ -416,7 +383,6 @@ public final class CharStreams { */ public static InputSupplier<Reader> join( final Iterable<? extends InputSupplier<? extends Reader>> suppliers) { - checkNotNull(suppliers); return new InputSupplier<Reader>() { @Override public Reader getInput() throws IOException { return new MultiReader(suppliers.iterator()); @@ -442,7 +408,6 @@ public final class CharStreams { * @throws IOException if an I/O error occurs */ public static void skipFully(Reader reader, long n) throws IOException { - checkNotNull(reader); while (n > 0) { long amt = reader.skip(n); if (amt == 0) { @@ -473,73 +438,4 @@ public final class CharStreams { } return new AppendableWriter(target); } - - // TODO(user): Remove these once Input/OutputSupplier methods are removed - - static <R extends Readable & Closeable> Reader asReader(final R readable) { - checkNotNull(readable); - if (readable instanceof Reader) { - return (Reader) readable; - } - return new Reader() { - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - return read(CharBuffer.wrap(cbuf, off, len)); - } - - @Override - public int read(CharBuffer target) throws IOException { - return readable.read(target); - } - - @Override - public void close() throws IOException { - readable.close(); - } - }; - } - - static <R extends Reader> InputSupplier<R> asInputSupplier( - final CharSource source) { - checkNotNull(source); - return new InputSupplier<R>() { - @Override - public R getInput() throws IOException { - return (R) source.openStream(); - } - }; - } - - static <W extends Writer> OutputSupplier<W> asOutputSupplier( - final CharSink sink) { - checkNotNull(sink); - return new OutputSupplier<W>() { - @Override - public W getOutput() throws IOException { - return (W) sink.openStream(); - } - }; - } - - static <R extends Readable & Closeable> CharSource asCharSource( - final InputSupplier<R> supplier) { - checkNotNull(supplier); - return new CharSource() { - @Override - public Reader openStream() throws IOException { - return asReader(supplier.getInput()); - } - }; - } - - static <W extends Appendable & Closeable> CharSink asCharSink( - final OutputSupplier<W> supplier) { - checkNotNull(supplier); - return new CharSink() { - @Override - public Writer openStream() throws IOException { - return asWriter(supplier.getOutput()); - } - }; - } } diff --git a/guava/src/com/google/common/io/Closeables.java b/guava/src/com/google/common/io/Closeables.java index d312184..e619887 100644 --- a/guava/src/com/google/common/io/Closeables.java +++ b/guava/src/com/google/common/io/Closeables.java @@ -40,33 +40,36 @@ public final class Closeables { private Closeables() {} /** - * Closes a {@link Closeable}, with control over whether an {@code IOException} may be thrown. - * This is primarily useful in a finally block, where a thrown exception needs to be logged but - * not propagated (otherwise the original exception will be lost). + * Closes a {@link Closeable}, with control over whether an + * {@code IOException} may be thrown. This is primarily useful in a + * finally block, where a thrown exception needs to be logged but not + * propagated (otherwise the original exception will be lost). * - * <p>If {@code swallowIOException} is true then we never throw {@code IOException} but merely log - * it. + * <p>If {@code swallowIOException} is true then we never throw + * {@code IOException} but merely log it. * - * <p>Example: <pre> {@code + * <p>Example: * - * public void useStreamNicely() throws IOException { - * SomeStream stream = new SomeStream("foo"); - * boolean threw = true; - * try { - * // ... code which does something with the stream ... - * threw = false; - * } finally { - * // If an exception occurs, rethrow it only if threw==false: - * Closeables.close(stream, threw); - * } - * }}</pre> + * <p><pre>public void useStreamNicely() throws IOException { + * SomeStream stream = new SomeStream("foo"); + * boolean threw = true; + * try { + * // Some code which does something with the Stream. May throw a + * // Throwable. + * threw = false; // No throwable thrown. + * } finally { + * // Close the stream. + * // If an exception occurs, only rethrow it if (threw==false). + * Closeables.close(stream, threw); + * } + * </pre> * - * @param closeable the {@code Closeable} object to be closed, or null, in which case this method - * does nothing - * @param swallowIOException if true, don't propagate IO exceptions thrown by the {@code close} - * methods - * @throws IOException if {@code swallowIOException} is false and {@code close} throws an - * {@code IOException}. + * @param closeable the {@code Closeable} object to be closed, or null, + * in which case this method does nothing + * @param swallowIOException if true, don't propagate IO exceptions + * thrown by the {@code close} methods + * @throws IOException if {@code swallowIOException} is false and + * {@code close} throws an {@code IOException}. */ public static void close(@Nullable Closeable closeable, boolean swallowIOException) throws IOException { @@ -86,23 +89,11 @@ public final class Closeables { } /** - * Equivalent to calling {@code close(closeable, true)}, but with no IOException in the signature. - * - * @param closeable the {@code Closeable} object to be closed, or null, in which case this method - * does nothing - * @deprecated Where possible, use the - * <a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"> - * try-with-resources</a> statement if using JDK7 or {@link Closer} on JDK6 to close one or - * more {@code Closeable} objects. This method is deprecated because it is easy to misuse and - * may swallow IO exceptions that really should be thrown and handled. See - * <a href="https://code.google.com/p/guava-libraries/issues/detail?id=1118">Guava issue - * 1118</a> for a more detailed explanation of the reasons for deprecation and see - * <a href="https://code.google.com/p/guava-libraries/wiki/ClosingResourcesExplained"> - * Closing Resources</a> for more information on the problems with closing {@code Closeable} - * objects and some of the preferred solutions for handling it correctly. This method is - * scheduled to be removed in Guava 16.0. + * Equivalent to calling {@code close(closeable, true)}, but with no + * IOException in the signature. + * @param closeable the {@code Closeable} object to be closed, or null, in + * which case this method does nothing */ - @Deprecated public static void closeQuietly(@Nullable Closeable closeable) { try { close(closeable, true); diff --git a/guava/src/com/google/common/io/Closer.java b/guava/src/com/google/common/io/Closer.java deleted file mode 100644 index 49141c2..0000000 --- a/guava/src/com/google/common/io/Closer.java +++ /dev/null @@ -1,285 +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.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Throwables; - -import java.io.Closeable; -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.logging.Level; - -/** - * A {@link Closeable} that collects {@code Closeable} resources and closes them all when it is - * {@linkplain #close closed}. This is intended to approximately emulate the behavior of Java 7's - * <a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html"> - * try-with-resources</a> statement in JDK6-compatible code. Running on Java 7, code using this - * should be approximately equivalent in behavior to the same code written with try-with-resources. - * Running on Java 6, exceptions that cannot be thrown must be logged rather than being added to the - * thrown exception as a suppressed exception. - * - * <p>This class is intended to to be used in the following pattern: - * - * <pre>{@code - * Closer closer = Closer.create(); - * try { - * InputStream in = closer.register(openInputStream()); - * OutputStream out = closer.register(openOutputStream()); - * // do stuff - * } catch (Throwable e) { - * // ensure that any checked exception types other than IOException that could be thrown are - * // provided here, e.g. throw closer.rethrow(e, CheckedException.class); - * throw closer.rethrow(e); - * } finally { - * closer.close(); - * } - * }</pre> - * - * <p>Note that this try-catch-finally block is not equivalent to a try-catch-finally block using - * try-with-resources. To get the equivalent of that, you must wrap the above code in <i>another</i> - * try block in order to catch any exception that may be thrown (including from the call to - * {@code close()}). - * - * <p>This pattern ensures the following: - * <ul> - * <li>Each {@code Closeable} resource that is successfully registered will be closed later.</li> - * <li>If a {@code Throwable} is thrown in the try block, no exceptions that occur when attempting - * to close resources will be thrown from the finally block. The throwable from the try block will - * be thrown.</li> - * <li>If no exceptions or errors were thrown in the try block, the <i>first</i> exception thrown - * by an attempt to close a resource will be thrown.</li> - * <li>Any exception caught when attempting to close a resource that is <i>not</i> thrown - * (because another exception is already being thrown) is <i>suppressed</i>.</li> - * </ul> - * - * An exception that is suppressed is not thrown. The method of suppression used depends on the - * version of Java the code is running on: - * - * <ul> - * <li><b>Java 7+:</b> Exceptions are suppressed by adding them to the exception that <i>will</i> - * be thrown using {@code Throwable.addSuppressed(Throwable)}.</li> - * <li><b>Java 6:</b> Exceptions are suppressed by logging them instead.</li> - * </ul> - * - * @author Colin Decker - * @since 14.0 - */ -// Coffee's for {@link Closer closers} only. -@Beta -public final class Closer implements Closeable { - - /** - * The suppressor implementation to use for the current Java version. - */ - private static final Suppressor SUPPRESSOR = SuppressingSuppressor.isAvailable() - ? SuppressingSuppressor.INSTANCE - : LoggingSuppressor.INSTANCE; - - /** - * Creates a new {@link Closer}. - */ - public static Closer create() { - return new Closer(SUPPRESSOR); - } - - @VisibleForTesting final Suppressor suppressor; - - // only need space for 2 elements in most cases, so try to use the smallest array possible - private final Deque<Closeable> stack = new ArrayDeque<Closeable>(4); - private Throwable thrown; - - @VisibleForTesting Closer(Suppressor suppressor) { - this.suppressor = checkNotNull(suppressor); // checkNotNull to satisfy null tests - } - - /** - * Registers the given {@code closeable} to be closed when this {@code Closer} is - * {@linkplain #close closed}. - * - * @return the given {@code closeable} - */ - // close. this word no longer has any meaning to me. - public <C extends Closeable> C register(C closeable) { - stack.push(closeable); - return closeable; - } - - /** - * Stores the given throwable and rethrows it. It will be rethrown as is if it is an - * {@code IOException}, {@code RuntimeException} or {@code Error}. Otherwise, it will be rethrown - * wrapped in a {@code RuntimeException}. <b>Note:</b> Be sure to declare all of the checked - * exception types your try block can throw when calling an overload of this method so as to avoid - * losing the original exception type. - * - * <p>This method always throws, and as such should be called as - * {@code throw closer.rethrow(e);} to ensure the compiler knows that it will throw. - * - * @return this method does not return; it always throws - * @throws IOException when the given throwable is an IOException - */ - public RuntimeException rethrow(Throwable e) throws IOException { - thrown = e; - Throwables.propagateIfPossible(e, IOException.class); - throw Throwables.propagate(e); - } - - /** - * Stores the given throwable and rethrows it. It will be rethrown as is if it is an - * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of the - * given type. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. <b>Note:</b> - * Be sure to declare all of the checked exception types your try block can throw when calling an - * overload of this method so as to avoid losing the original exception type. - * - * <p>This method always throws, and as such should be called as - * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw. - * - * @return this method does not return; it always throws - * @throws IOException when the given throwable is an IOException - * @throws X when the given throwable is of the declared type X - */ - public <X extends Exception> RuntimeException rethrow(Throwable e, - Class<X> declaredType) throws IOException, X { - thrown = e; - Throwables.propagateIfPossible(e, IOException.class); - Throwables.propagateIfPossible(e, declaredType); - throw Throwables.propagate(e); - } - - /** - * Stores the given throwable and rethrows it. It will be rethrown as is if it is an - * {@code IOException}, {@code RuntimeException}, {@code Error} or a checked exception of either - * of the given types. Otherwise, it will be rethrown wrapped in a {@code RuntimeException}. - * <b>Note:</b> Be sure to declare all of the checked exception types your try block can throw - * when calling an overload of this method so as to avoid losing the original exception type. - * - * <p>This method always throws, and as such should be called as - * {@code throw closer.rethrow(e, ...);} to ensure the compiler knows that it will throw. - * - * @return this method does not return; it always throws - * @throws IOException when the given throwable is an IOException - * @throws X1 when the given throwable is of the declared type X1 - * @throws X2 when the given throwable is of the declared type X2 - */ - public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow( - Throwable e, Class<X1> declaredType1, Class<X2> declaredType2) throws IOException, X1, X2 { - thrown = e; - Throwables.propagateIfPossible(e, IOException.class); - Throwables.propagateIfPossible(e, declaredType1, declaredType2); - throw Throwables.propagate(e); - } - - /** - * Closes all {@code Closeable} instances that have been added to this {@code Closer}. If an - * exception was thrown in the try block and passed to one of the {@code exceptionThrown} methods, - * any exceptions thrown when attempting to close a closeable will be suppressed. Otherwise, the - * <i>first</i> exception to be thrown from an attempt to close a closeable will be thrown and any - * additional exceptions that are thrown after that will be suppressed. - */ - @Override - public void close() throws IOException { - Throwable throwable = thrown; - - // close closeables in LIFO order - while (!stack.isEmpty()) { - Closeable closeable = stack.pop(); - try { - closeable.close(); - } catch (Throwable e) { - if (throwable == null) { - throwable = e; - } else { - suppressor.suppress(closeable, throwable, e); - } - } - } - - if (thrown == null && throwable != null) { - Throwables.propagateIfPossible(throwable, IOException.class); - throw new AssertionError(throwable); // not possible - } - } - - /** - * Suppression strategy interface. - */ - @VisibleForTesting interface Suppressor { - /** - * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close - * the given closeable. {@code thrown} is the exception that is actually being thrown from the - * method. Implementations of this method should not throw under any circumstances. - */ - void suppress(Closeable closeable, Throwable thrown, Throwable suppressed); - } - - /** - * Suppresses exceptions by logging them. - */ - @VisibleForTesting static final class LoggingSuppressor implements Suppressor { - - static final LoggingSuppressor INSTANCE = new LoggingSuppressor(); - - @Override - public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { - // log to the same place as Closeables - Closeables.logger.log(Level.WARNING, - "Suppressing exception thrown when closing " + closeable, suppressed); - } - } - - /** - * Suppresses exceptions by adding them to the exception that will be thrown using JDK7's - * addSuppressed(Throwable) mechanism. - */ - @VisibleForTesting static final class SuppressingSuppressor implements Suppressor { - - static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor(); - - static boolean isAvailable() { - return addSuppressed != null; - } - - static final Method addSuppressed = getAddSuppressed(); - - private static Method getAddSuppressed() { - try { - return Throwable.class.getMethod("addSuppressed", Throwable.class); - } catch (Throwable e) { - return null; - } - } - - @Override - public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { - // ensure no exceptions from addSuppressed - if (thrown == suppressed) { - return; - } - try { - addSuppressed.invoke(thrown, suppressed); - } catch (Throwable e) { - // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging - LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed); - } - } - } -} diff --git a/guava/src/com/google/common/io/CountingInputStream.java b/guava/src/com/google/common/io/CountingInputStream.java index 573099a..d11c8ec 100644 --- a/guava/src/com/google/common/io/CountingInputStream.java +++ b/guava/src/com/google/common/io/CountingInputStream.java @@ -22,8 +22,6 @@ import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import javax.annotation.Nullable; - /** * An {@link InputStream} that counts the number of bytes read. * @@ -41,7 +39,7 @@ public final class CountingInputStream extends FilterInputStream { * * @param in the input stream to be wrapped */ - public CountingInputStream(@Nullable InputStream in) { + public CountingInputStream(InputStream in) { super(in); } diff --git a/guava/src/com/google/common/io/CountingOutputStream.java b/guava/src/com/google/common/io/CountingOutputStream.java index 4013f9c..5f57714 100644 --- a/guava/src/com/google/common/io/CountingOutputStream.java +++ b/guava/src/com/google/common/io/CountingOutputStream.java @@ -22,8 +22,6 @@ import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; -import javax.annotation.Nullable; - /** * An OutputStream that counts the number of bytes written. * @@ -40,7 +38,7 @@ public final class CountingOutputStream extends FilterOutputStream { * * @param out the output stream to be wrapped */ - public CountingOutputStream(@Nullable OutputStream out) { + public CountingOutputStream(OutputStream out) { super(out); } diff --git a/guava/src/com/google/common/io/FileWriteMode.java b/guava/src/com/google/common/io/FileWriteMode.java deleted file mode 100644 index d246740..0000000 --- a/guava/src/com/google/common/io/FileWriteMode.java +++ /dev/null @@ -1,28 +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; - -/** - * Modes for opening a file for writing. The default when mode when none is specified is to - * truncate the file before writing. - * - * @author Colin Decker - */ -public enum FileWriteMode { - /** Specifies that writes to the opened file should append to the end of the file. */ - APPEND -} diff --git a/guava/src/com/google/common/io/Files.java b/guava/src/com/google/common/io/Files.java index c3dd506..1d3ce1a 100644 --- a/guava/src/com/google/common/io/Files.java +++ b/guava/src/com/google/common/io/Files.java @@ -16,21 +16,15 @@ 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.io.FileWriteMode.APPEND; import com.google.common.annotations.Beta; -import com.google.common.base.Charsets; import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableSet; -import com.google.common.hash.HashCode; -import com.google.common.hash.HashFunction; import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; @@ -46,8 +40,8 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.Charset; +import java.security.MessageDigest; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.zip.Checksum; @@ -57,7 +51,6 @@ import java.util.zip.Checksum; * <p>All method parameters must be non-null unless documented otherwise. * * @author Chris Nokleberg - * @author Colin Decker * @since 1.0 */ @Beta @@ -73,14 +66,11 @@ public final class Files { * character set. * * @param file the file to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @return the buffered reader */ public static BufferedReader newReader(File file, Charset charset) throws FileNotFoundException { - checkNotNull(file); - checkNotNull(charset); return new BufferedReader( new InputStreamReader(new FileInputStream(file), charset)); } @@ -90,171 +80,16 @@ public final class Files { * character set. * * @param file the file to write to - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @return the buffered writer */ public static BufferedWriter newWriter(File file, Charset charset) throws FileNotFoundException { - checkNotNull(file); - checkNotNull(charset); return new BufferedWriter( new OutputStreamWriter(new FileOutputStream(file), charset)); } /** - * Returns a new {@link ByteSource} for reading bytes from the given file. - * - * @since 14.0 - */ - public static ByteSource asByteSource(File file) { - return new FileByteSource(file); - } - - private static final class FileByteSource extends ByteSource { - - private final File file; - - private FileByteSource(File file) { - this.file = checkNotNull(file); - } - - @Override - public FileInputStream openStream() throws IOException { - return new FileInputStream(file); - } - - @Override - public long size() throws IOException { - if (!file.isFile()) { - throw new FileNotFoundException(file.toString()); - } - return file.length(); - } - - @Override - public byte[] read() throws IOException { - long size = file.length(); - // some special files may return size 0 but have content - // read normally to be sure - if (size == 0) { - return super.read(); - } - - // can't initialize a large enough array - // technically, this could probably be Integer.MAX_VALUE - 5 - if (size > Integer.MAX_VALUE) { - // OOME is what would be thrown if we tried to initialize the array - throw new OutOfMemoryError("file is too large to fit in a byte array: " - + size + " bytes"); - } - - // initialize the array to the current size of the file - byte[] bytes = new byte[(int) size]; - - Closer closer = Closer.create(); - try { - InputStream in = closer.register(openStream()); - int off = 0; - int read = 0; - - // read until we've read size bytes or reached EOF - while (off < size - && ((read = in.read(bytes, off, (int) size - off)) != -1)) { - off += read; - } - - byte[] result = bytes; - - if (off < size) { - // encountered EOF early; truncate the result - result = Arrays.copyOf(bytes, off); - } else if (read != -1) { - // we read size bytes... if the last read didn't return -1, the file got larger - // so we just read the rest normally and then create a new array - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ByteStreams.copy(in, out); - byte[] moreBytes = out.toByteArray(); - result = new byte[bytes.length + moreBytes.length]; - System.arraycopy(bytes, 0, result, 0, bytes.length); - System.arraycopy(moreBytes, 0, result, bytes.length, moreBytes.length); - } - // normally, off should == size and read should == -1 - // in that case, the array is just returned as is - return result; - } catch (Throwable e) { - throw closer.rethrow(e); - } finally { - closer.close(); - } - } - - @Override - public String toString() { - return "Files.asByteSource(" + file + ")"; - } - } - - /** - * Returns a new {@link ByteSink} for writing bytes to the given file. The - * given {@code modes} control how the file is opened for writing. When no - * mode is provided, the file will be truncated before writing. When the - * {@link FileWriteMode#APPEND APPEND} mode is provided, writes will - * append to the end of the file without truncating it. - * - * @since 14.0 - */ - public static ByteSink asByteSink(File file, FileWriteMode... modes) { - return new FileByteSink(file, modes); - } - - private static final class FileByteSink extends ByteSink { - - private final File file; - private final ImmutableSet<FileWriteMode> modes; - - private FileByteSink(File file, FileWriteMode... modes) { - this.file = checkNotNull(file); - this.modes = ImmutableSet.copyOf(modes); - } - - @Override - public FileOutputStream openStream() throws IOException { - return new FileOutputStream(file, modes.contains(APPEND)); - } - - @Override - public String toString() { - return "Files.asByteSink(" + file + ", " + modes + ")"; - } - } - - /** - * Returns a new {@link CharSource} for reading character data from the given - * file using the given character set. - * - * @since 14.0 - */ - public static CharSource asCharSource(File file, Charset charset) { - return asByteSource(file).asCharSource(charset); - } - - /** - * Returns a new {@link CharSink} for writing character data to the given - * file using the given character set. The given {@code modes} control how - * the file is opened for writing. When no mode is provided, the file - * will be truncated before writing. When the - * {@link FileWriteMode#APPEND APPEND} mode is provided, writes will - * append to the end of the file without truncating it. - * - * @since 14.0 - */ - public static CharSink asCharSink(File file, Charset charset, - FileWriteMode... modes) { - return asByteSink(file, modes).asCharSink(charset); - } - - /** * Returns a factory that will supply instances of {@link FileInputStream} * that read from a file. * @@ -263,7 +98,13 @@ public final class Files { */ public static InputSupplier<FileInputStream> newInputStreamSupplier( final File file) { - return ByteStreams.asInputSupplier(asByteSource(file)); + Preconditions.checkNotNull(file); + return new InputSupplier<FileInputStream>() { + @Override + public FileInputStream getInput() throws IOException { + return new FileInputStream(file); + } + }; } /** @@ -289,13 +130,13 @@ public final class Files { */ public static OutputSupplier<FileOutputStream> newOutputStreamSupplier( final File file, final boolean append) { - return ByteStreams.asOutputSupplier(asByteSink(file, modes(append))); - } - - private static FileWriteMode[] modes(boolean append) { - return append - ? new FileWriteMode[]{ FileWriteMode.APPEND } - : new FileWriteMode[0]; + Preconditions.checkNotNull(file); + return new OutputSupplier<FileOutputStream>() { + @Override + public FileOutputStream getOutput() throws IOException { + return new FileOutputStream(file, append); + } + }; } /** @@ -303,13 +144,12 @@ public final class Files { * {@link InputStreamReader} that read a file using the given character set. * * @param file the file to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when reading the file * @return the factory */ public static InputSupplier<InputStreamReader> newReaderSupplier(File file, Charset charset) { - return CharStreams.asInputSupplier(asCharSource(file, charset)); + return CharStreams.newReaderSupplier(newInputStreamSupplier(file), charset); } /** @@ -317,8 +157,7 @@ public final class Files { * that write to a file using the given character set. * * @param file the file to write to - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @return the factory */ public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file, @@ -331,15 +170,15 @@ public final class Files { * that write to or append to a file using the given character set. * * @param file the file to write to - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @param append if true, the encoded characters will be appended to the file; * otherwise the file is overwritten * @return the factory */ public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file, Charset charset, boolean append) { - return CharStreams.asOutputSupplier(asCharSink(file, charset, modes(append))); + return CharStreams.newWriterSupplier(newOutputStreamSupplier(file, append), + charset); } /** @@ -352,7 +191,23 @@ public final class Files { * @throws IOException if an I/O error occurs */ public static byte[] toByteArray(File file) throws IOException { - return asByteSource(file).read(); + Preconditions.checkArgument(file.length() <= Integer.MAX_VALUE); + if (file.length() == 0) { + // Some special files are length 0 but have content nonetheless. + return ByteStreams.toByteArray(newInputStreamSupplier(file)); + } else { + // Avoid an extra allocation and copy. + byte[] b = new byte[(int) file.length()]; + boolean threw = true; + InputStream in = new FileInputStream(file); + try { + ByteStreams.readFully(in, b); + threw = false; + } finally { + Closeables.close(in, threw); + } + return b; + } } /** @@ -360,13 +215,12 @@ public final class Files { * character set. * * @param file the file to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when reading the file * @return a string containing all the characters from the file * @throws IOException if an I/O error occurs */ public static String toString(File file, Charset charset) throws IOException { - return asCharSource(file, charset).read(); + return new String(toByteArray(file), charset.name()); } /** @@ -379,7 +233,7 @@ public final class Files { */ public static void copy(InputSupplier<? extends InputStream> from, File to) throws IOException { - ByteStreams.asByteSource(from).copyTo(asByteSink(to)); + ByteStreams.copy(from, newOutputStreamSupplier(to)); } /** @@ -390,7 +244,7 @@ public final class Files { * @throws IOException if an I/O error occurs */ public static void write(byte[] from, File to) throws IOException { - asByteSink(to).write(from); + ByteStreams.write(from, newOutputStreamSupplier(to)); } /** @@ -403,7 +257,7 @@ public final class Files { */ public static void copy(File from, OutputSupplier<? extends OutputStream> to) throws IOException { - asByteSource(from).copyTo(ByteStreams.asByteSink(to)); + ByteStreams.copy(newInputStreamSupplier(from), to); } /** @@ -414,26 +268,21 @@ public final class Files { * @throws IOException if an I/O error occurs */ public static void copy(File from, OutputStream to) throws IOException { - asByteSource(from).copyTo(to); + ByteStreams.copy(newInputStreamSupplier(from), to); } /** * Copies all the bytes from one file to another. - * - * <p><b>Warning:</b> If {@code to} represents an existing file, that file - * will be overwritten with the contents of {@code from}. If {@code to} and - * {@code from} refer to the <i>same</i> file, the contents of that file - * will be deleted. - * + *. * @param from the source file * @param to the destination file * @throws IOException if an I/O error occurs * @throws IllegalArgumentException if {@code from.equals(to)} */ public static void copy(File from, File to) throws IOException { - checkArgument(!from.equals(to), + Preconditions.checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); - asByteSource(from).copyTo(asByteSink(to)); + copy(newInputStreamSupplier(from), to); } /** @@ -443,13 +292,12 @@ public final class Files { * * @param from the readable supplier * @param to the destination file - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @throws IOException if an I/O error occurs */ public static <R extends Readable & Closeable> void copy( InputSupplier<R> from, File to, Charset charset) throws IOException { - CharStreams.asCharSource(from).copyTo(asCharSink(to, charset)); + CharStreams.copy(from, newWriterSupplier(to, charset)); } /** @@ -458,13 +306,12 @@ public final class Files { * * @param from the character sequence to write * @param to the destination file - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @throws IOException if an I/O error occurs */ public static void write(CharSequence from, File to, Charset charset) throws IOException { - asCharSink(to, charset).write(from); + write(from, to, charset, false); } /** @@ -473,8 +320,7 @@ public final class Files { * * @param from the character sequence to append * @param to the destination file - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @throws IOException if an I/O error occurs */ public static void append(CharSequence from, File to, Charset charset) @@ -488,14 +334,13 @@ public final class Files { * * @param from the character sequence to append * @param to the destination file - * @param charset the charset used to encode the output stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @param append true to append, false to overwrite * @throws IOException if an I/O error occurs */ private static void write(CharSequence from, File to, Charset charset, boolean append) throws IOException { - asCharSink(to, charset, modes(append)).write(from); + CharStreams.write(from, newWriterSupplier(to, charset, append)); } /** @@ -504,14 +349,13 @@ public final class Files { * character set. * * @param from the source file - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when reading the file * @param to the appendable supplier * @throws IOException if an I/O error occurs */ public static <W extends Appendable & Closeable> void copy(File from, Charset charset, OutputSupplier<W> to) throws IOException { - asCharSource(from, charset).copyTo(CharStreams.asCharSink(to)); + CharStreams.copy(newReaderSupplier(from, charset), to); } /** @@ -519,14 +363,13 @@ public final class Files { * using the given character set. * * @param from the source file - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when reading the file * @param to the appendable object * @throws IOException if an I/O error occurs */ public static void copy(File from, Charset charset, Appendable to) throws IOException { - asCharSource(from, charset).copyTo(to); + CharStreams.copy(newReaderSupplier(from, charset), to); } /** @@ -535,8 +378,6 @@ public final class Files { * @throws IOException if an I/O error occurs */ public static boolean equal(File file1, File file2) throws IOException { - checkNotNull(file1); - checkNotNull(file2); if (file1 == file2 || file1.equals(file2)) { return true; } @@ -551,7 +392,8 @@ public final class Files { if (len1 != 0 && len2 != 0 && len1 != len2) { return false; } - return asByteSource(file1).contentEquals(asByteSource(file2)); + return ByteStreams.equal(newInputStreamSupplier(file1), + newInputStreamSupplier(file2)); } /** @@ -596,7 +438,6 @@ public final class Files { * @throws IOException if an I/O error occurs */ public static void touch(File file) throws IOException { - checkNotNull(file); if (!file.createNewFile() && !file.setLastModified(System.currentTimeMillis())) { throw new IOException("Unable to update modification time of " + file); @@ -614,7 +455,6 @@ public final class Files { * @since 4.0 */ public static void createParentDirs(File file) throws IOException { - checkNotNull(file); File parent = file.getCanonicalFile().getParentFile(); if (parent == null) { /* @@ -642,9 +482,8 @@ public final class Files { * @throws IllegalArgumentException if {@code from.equals(to)} */ public static void move(File from, File to) throws IOException { - checkNotNull(from); - checkNotNull(to); - checkArgument(!from.equals(to), + Preconditions.checkNotNull(to); + Preconditions.checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); if (!from.renameTo(to)) { @@ -664,14 +503,13 @@ public final class Files { * trailing whitespace. * * @param file the file to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @return the first line, or null if the file is empty * @throws IOException if an I/O error occurs */ public static String readFirstLine(File file, Charset charset) throws IOException { - return asCharSource(file, charset).readFirstLine(); + return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset)); } /** @@ -680,8 +518,7 @@ public final class Files { * trailing whitespace. * * @param file the file to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @return a mutable {@link List} containing all the lines * @throws IOException if an I/O error occurs */ @@ -695,15 +532,15 @@ public final class Files { * false, or we have read all of the lines. * * @param file the file to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @param callback the {@link LineProcessor} to use to handle the lines * @return the output of processing the lines * @throws IOException if an I/O error occurs */ public static <T> T readLines(File file, Charset charset, LineProcessor<T> callback) throws IOException { - return CharStreams.readLines(newReaderSupplier(file, charset), callback); + return CharStreams.readLines(Files.newReaderSupplier(file, charset), + callback); } /** @@ -731,28 +568,25 @@ public final class Files { * @return the result of {@link Checksum#getValue} after updating the * checksum object with all of the bytes in the file * @throws IOException if an I/O error occurs - * @deprecated Use {@code hash} with the {@code Hashing.crc32()} or - * {@code Hashing.adler32()} hash functions. This method is scheduled - * to be removed in Guava 15.0. */ - @Deprecated public static long getChecksum(File file, Checksum checksum) throws IOException { return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum); } /** - * Computes the hash code of the {@code file} using {@code hashFunction}. + * Computes and returns the digest value for a file. + * The digest object is reset when this method returns successfully. * * @param file the file to read - * @param hashFunction the hash function to use to hash the data - * @return the {@link HashCode} of all of the bytes in the file + * @param md the digest object + * @return the result of {@link MessageDigest#digest()} after updating the + * digest object with all of the bytes in this file * @throws IOException if an I/O error occurs - * @since 12.0 */ - public static HashCode hash(File file, HashFunction hashFunction) + public static byte[] getDigest(File file, MessageDigest md) throws IOException { - return asByteSource(file).hash(hashFunction); + return ByteStreams.getDigest(newInputStreamSupplier(file), md); } /** @@ -772,7 +606,6 @@ public final class Files { * @since 2.0 */ public static MappedByteBuffer map(File file) throws IOException { - checkNotNull(file); return map(file, MapMode.READ_ONLY); } @@ -796,8 +629,6 @@ public final class Files { */ public static MappedByteBuffer map(File file, MapMode mode) throws IOException { - checkNotNull(file); - checkNotNull(mode); if (!file.exists()) { throw new FileNotFoundException(file.toString()); } @@ -827,31 +658,30 @@ public final class Files { */ public static MappedByteBuffer map(File file, MapMode mode, long size) throws FileNotFoundException, IOException { - checkNotNull(file); - checkNotNull(mode); + RandomAccessFile raf = + new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw"); - Closer closer = Closer.create(); + boolean threw = true; try { - RandomAccessFile raf = closer.register( - new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw")); - return map(raf, mode, size); - } catch (Throwable e) { - throw closer.rethrow(e); + MappedByteBuffer mbb = map(raf, mode, size); + threw = false; + return mbb; } finally { - closer.close(); + Closeables.close(raf, threw); } } private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode, long size) throws IOException { - Closer closer = Closer.create(); + FileChannel channel = raf.getChannel(); + + boolean threw = true; try { - FileChannel channel = closer.register(raf.getChannel()); - return channel.map(mode, 0, size); - } catch (Throwable e) { - throw closer.rethrow(e); + MappedByteBuffer mbb = channel.map(mode, 0, size); + threw = false; + return mbb; } finally { - closer.close(); + Closeables.close(channel, threw); } } @@ -877,7 +707,6 @@ public final class Files { * @since 11.0 */ public static String simplifyPath(String pathname) { - checkNotNull(pathname); if (pathname.length() == 0) { return "."; } @@ -927,27 +756,9 @@ public final class Files { * * @since 11.0 */ - public static String getFileExtension(String fullName) { - checkNotNull(fullName); - String fileName = new File(fullName).getName(); + public static String getFileExtension(String fileName) { + checkNotNull(fileName); int dotIndex = fileName.lastIndexOf('.'); return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); } - - /** - * Returns the file name without its - * <a href="http://en.wikipedia.org/wiki/Filename_extension">file extension</a> or path. This is - * similar to the {@code basename} unix command. The result does not include the '{@code .}'. - * - * @param file The name of the file to trim the extension from. This can be either a fully - * qualified file name (including a path) or just a file name. - * @return The file name without its path or extension. - * @since 14.0 - */ - public static String getNameWithoutExtension(String file) { - checkNotNull(file); - String fileName = new File(file).getName(); - int dotIndex = fileName.lastIndexOf('.'); - return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); - } } diff --git a/guava/src/com/google/common/io/GwtWorkarounds.java b/guava/src/com/google/common/io/GwtWorkarounds.java deleted file mode 100644 index 285df59..0000000 --- a/guava/src/com/google/common/io/GwtWorkarounds.java +++ /dev/null @@ -1,231 +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 static com.google.common.base.Preconditions.checkPositionIndexes; - -import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; - -/** - * 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 Reader} as a {@code CharInput}. - */ - @GwtIncompatible("Reader") - static CharInput asCharInput(final Reader reader) { - checkNotNull(reader); - return new CharInput() { - @Override - public int read() throws IOException { - return reader.read(); - } - - @Override - public void close() throws IOException { - reader.close(); - } - }; - } - - /** - * 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; - } - - /** - * Views a {@code ByteInput} as an {@code InputStream}. - */ - @GwtIncompatible("InputStream") - static InputStream asInputStream(final ByteInput input) { - checkNotNull(input); - return new InputStream() { - @Override - public int read() throws IOException { - return input.read(); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - checkNotNull(b); - checkPositionIndexes(off, off + len, b.length); - if (len == 0) { - return 0; - } - int firstByte = read(); - if (firstByte == -1) { - return -1; - } - b[off] = (byte) firstByte; - for (int dst = 1; dst < len; dst++) { - int readByte = read(); - if (readByte == -1) { - return dst; - } - b[off + dst] = (byte) readByte; - } - return len; - } - - @Override - public void close() throws IOException { - input.close(); - } - }; - } - - /** - * A GWT-compatible substitute for an {@code OutputStream}. - */ - interface ByteOutput { - void write(byte b) throws IOException; - void flush() throws IOException; - void close() throws IOException; - } - - /** - * Views a {@code ByteOutput} as an {@code OutputStream}. - */ - @GwtIncompatible("OutputStream") - static OutputStream asOutputStream(final ByteOutput output) { - checkNotNull(output); - return new OutputStream() { - @Override - public void write(int b) throws IOException { - output.write((byte) b); - } - - @Override - public void flush() throws IOException { - output.flush(); - } - - @Override - public void close() throws IOException { - output.close(); - } - }; - } - - /** - * A GWT-compatible substitute for a {@code Writer}. - */ - interface CharOutput { - void write(char c) throws IOException; - void flush() throws IOException; - void close() throws IOException; - } - - /** - * Views a {@code Writer} as a {@code CharOutput}. - */ - @GwtIncompatible("Writer") - static CharOutput asCharOutput(final Writer writer) { - checkNotNull(writer); - return new CharOutput() { - @Override - public void write(char c) throws IOException { - writer.append(c); - } - - @Override - public void flush() throws IOException { - writer.flush(); - } - - @Override - public void close() throws IOException { - writer.close(); - } - }; - } - - /** - * 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/src/com/google/common/io/LimitInputStream.java b/guava/src/com/google/common/io/LimitInputStream.java index c8bd6cf..e9d963d 100644 --- a/guava/src/com/google/common/io/LimitInputStream.java +++ b/guava/src/com/google/common/io/LimitInputStream.java @@ -28,11 +28,8 @@ import java.io.InputStream; * * @author Charles Fry * @since 1.0 - * @deprecated Use {@link ByteStreams#limit} instead. This class is scheduled - * to be removed in Guava release 15.0. */ @Beta -@Deprecated public final class LimitInputStream extends FilterInputStream { private long left; diff --git a/guava/src/com/google/common/io/LineBuffer.java b/guava/src/com/google/common/io/LineBuffer.java index 119516b..1f1c8dc 100644 --- a/guava/src/com/google/common/io/LineBuffer.java +++ b/guava/src/com/google/common/io/LineBuffer.java @@ -75,9 +75,6 @@ abstract class LineBuffer { finishLine(true); start = pos + 1; break; - - default: - // do nothing } } line.append(cbuf, start, off + len - start); diff --git a/guava/src/com/google/common/io/MultiInputStream.java b/guava/src/com/google/common/io/MultiInputStream.java index 1902baf..f8f1a0b 100644 --- a/guava/src/com/google/common/io/MultiInputStream.java +++ b/guava/src/com/google/common/io/MultiInputStream.java @@ -16,14 +16,10 @@ package com.google.common.io; -import static com.google.common.base.Preconditions.checkNotNull; - import java.io.IOException; import java.io.InputStream; import java.util.Iterator; -import javax.annotation.Nullable; - /** * An {@link InputStream} that concatenates multiple substreams. At most * one stream will be open at a time. @@ -44,7 +40,7 @@ final class MultiInputStream extends InputStream { public MultiInputStream( Iterator<? extends InputSupplier<? extends InputStream>> it) throws IOException { - this.it = checkNotNull(it); + this.it = it; advance(); } @@ -91,7 +87,7 @@ final class MultiInputStream extends InputStream { return result; } - @Override public int read(@Nullable byte[] b, int off, int len) throws IOException { + @Override public int read(byte[] b, int off, int len) throws IOException { if (in == null) { return -1; } diff --git a/guava/src/com/google/common/io/MultiReader.java b/guava/src/com/google/common/io/MultiReader.java index fc0e7fb..6757a26 100644 --- a/guava/src/com/google/common/io/MultiReader.java +++ b/guava/src/com/google/common/io/MultiReader.java @@ -22,8 +22,6 @@ import java.io.IOException; import java.io.Reader; import java.util.Iterator; -import javax.annotation.Nullable; - /** * A {@link Reader} that concatenates multiple readers. * @@ -50,7 +48,7 @@ class MultiReader extends Reader { } } - @Override public int read(@Nullable char cbuf[], int off, int len) throws IOException { + @Override public int read(char cbuf[], int off, int len) throws IOException { if (current == null) { return -1; } diff --git a/guava/src/com/google/common/io/NullOutputStream.java b/guava/src/com/google/common/io/NullOutputStream.java index d1dda67..1c1e98e 100644 --- a/guava/src/com/google/common/io/NullOutputStream.java +++ b/guava/src/com/google/common/io/NullOutputStream.java @@ -16,8 +16,6 @@ package com.google.common.io; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.Beta; import java.io.OutputStream; @@ -27,11 +25,8 @@ import java.io.OutputStream; * * @author Spencer Kimball * @since 1.0 - * @deprecated Use {@link ByteStreams#nullOutputStream} instead. This class is - * scheduled to be removed in Guava release 15.0. */ @Beta -@Deprecated public final class NullOutputStream extends OutputStream { /** Discards the specified byte. */ @Override public void write(int b) { @@ -39,6 +34,5 @@ public final class NullOutputStream extends OutputStream { /** Discards the specified byte array. */ @Override public void write(byte[] b, int off, int len) { - checkNotNull(b); } } diff --git a/guava/src/com/google/common/io/PatternFilenameFilter.java b/guava/src/com/google/common/io/PatternFilenameFilter.java index 2859277..065566f 100644 --- a/guava/src/com/google/common/io/PatternFilenameFilter.java +++ b/guava/src/com/google/common/io/PatternFilenameFilter.java @@ -27,8 +27,8 @@ import java.util.regex.PatternSyntaxException; import javax.annotation.Nullable; /** - * File name filter that only accepts files matching a regular expression. This - * class is thread-safe and immutable. + * File name filter that only accepts files matching a regular expression. This class is thread-safe + * and immutable. * * @author Apple Chow * @since 1.0 diff --git a/guava/src/com/google/common/io/Resources.java b/guava/src/com/google/common/io/Resources.java index 3d81491..c57a95f 100644 --- a/guava/src/com/google/common/io/Resources.java +++ b/guava/src/com/google/common/io/Resources.java @@ -20,7 +20,6 @@ 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.base.Charsets; import java.io.IOException; import java.io.InputStream; @@ -39,7 +38,6 @@ import java.util.List; * * @author Chris Nokleberg * @author Ben Yu - * @author Colin Decker * @since 1.0 */ @Beta @@ -53,39 +51,15 @@ public final class Resources { * @param url the URL to read from * @return the factory */ - public static InputSupplier<InputStream> newInputStreamSupplier(URL url) { - return ByteStreams.asInputSupplier(asByteSource(url)); - } - - /** - * Returns a {@link ByteSource} that reads from the given URL. - * - * @since 14.0 - */ - public static ByteSource asByteSource(URL url) { - return new UrlByteSource(url); - } - - /** - * A byte source that reads from a URL using {@link URL#openStream()}. - */ - private static final class UrlByteSource extends ByteSource { - - private final URL url; - - private UrlByteSource(URL url) { - this.url = checkNotNull(url); - } - - @Override - public InputStream openStream() throws IOException { - return url.openStream(); - } - - @Override - public String toString() { - return "Resources.newByteSource(" + url + ")"; - } + public static InputSupplier<InputStream> newInputStreamSupplier( + final URL url) { + checkNotNull(url); + return new InputSupplier<InputStream>() { + @Override + public InputStream getInput() throws IOException { + return url.openStream(); + } + }; } /** @@ -93,22 +67,12 @@ public final class Resources { * {@link InputStreamReader} that read a URL using the given character set. * * @param url the URL to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when reading the URL contents * @return the factory */ public static InputSupplier<InputStreamReader> newReaderSupplier( URL url, Charset charset) { - return CharStreams.asInputSupplier(asCharSource(url, charset)); - } - - /** - * Returns a {@link CharSource} that reads from the given URL using the given character set. - * - * @since 14.0 - */ - public static CharSource asCharSource(URL url, Charset charset) { - return asByteSource(url).asCharSource(charset); + return CharStreams.newReaderSupplier(newInputStreamSupplier(url), charset); } /** @@ -119,7 +83,7 @@ public final class Resources { * @throws IOException if an I/O error occurs */ public static byte[] toByteArray(URL url) throws IOException { - return asByteSource(url).read(); + return ByteStreams.toByteArray(newInputStreamSupplier(url)); } /** @@ -127,13 +91,12 @@ public final class Resources { * character set. * * @param url the URL to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when reading the URL * @return a string containing all the characters from the URL * @throws IOException if an I/O error occurs. */ public static String toString(URL url, Charset charset) throws IOException { - return asCharSource(url, charset).read(); + return CharStreams.toString(newReaderSupplier(url, charset)); } /** @@ -141,8 +104,7 @@ public final class Resources { * have read all of the lines. * * @param url the URL to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when reading the URL * @param callback the LineProcessor to use to handle the lines * @return the output of processing the lines * @throws IOException if an I/O error occurs @@ -158,8 +120,7 @@ public final class Resources { * whitespace. * * @param url the URL to read from - * @param charset the charset used to decode the input stream; see {@link - * Charsets} for helpful predefined constants + * @param charset the character set used when writing the file * @return a mutable {@link List} containing all the lines * @throws IOException if an I/O error occurs */ @@ -176,7 +137,7 @@ public final class Resources { * @throws IOException if an I/O error occurs */ public static void copy(URL from, OutputStream to) throws IOException { - asByteSource(from).copyTo(to); + ByteStreams.copy(newInputStreamSupplier(from), to); } /** diff --git a/guava/src/com/google/common/io/package-info.java b/guava/src/com/google/common/io/package-info.java index f5f83ad..9f08e64 100644 --- a/guava/src/com/google/common/io/package-info.java +++ b/guava/src/com/google/common/io/package-info.java @@ -38,4 +38,3 @@ package com.google.common.io; import javax.annotation.ParametersAreNonnullByDefault; - diff --git a/guava/src/com/google/common/math/BigIntegerMath.java b/guava/src/com/google/common/math/BigIntegerMath.java index 6b40b6b..99e69fe 100644 --- a/guava/src/com/google/common/math/BigIntegerMath.java +++ b/guava/src/com/google/common/math/BigIntegerMath.java @@ -25,8 +25,7 @@ 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.GwtIncompatible; +import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import java.math.BigDecimal; @@ -47,7 +46,7 @@ import java.util.List; * @author Louis Wasserman * @since 11.0 */ -@GwtCompatible(emulated = true) +@Beta public final class BigIntegerMath { /** * Returns {@code true} if {@code x} represents a power of two. @@ -65,7 +64,6 @@ public final class BigIntegerMath { * 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; @@ -124,7 +122,6 @@ public final class BigIntegerMath { * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} * is not a power of ten */ - @GwtIncompatible("TODO") @SuppressWarnings("fallthrough") public static int log10(BigInteger x, RoundingMode mode) { checkPositive("x", x); @@ -132,45 +129,27 @@ public final class BigIntegerMath { return LongMath.log10(x.longValue(), mode); } - int approxLog10 = (int) (log2(x, FLOOR) * LN_2 / LN_10); - BigInteger approxPow = BigInteger.TEN.pow(approxLog10); - int approxCmp = approxPow.compareTo(x); - - /* - * We adjust approxLog10 and approxPow until they're equal to floor(log10(x)) and - * 10^floor(log10(x)). - */ - - if (approxCmp > 0) { - /* - * The code is written so that even completely incorrect approximations will still yield the - * correct answer eventually, but in practice this branch should almost never be entered, - * and even then the loop should not run more than once. - */ - do { - approxLog10--; - approxPow = approxPow.divide(BigInteger.TEN); - approxCmp = approxPow.compareTo(x); - } while (approxCmp > 0); - } else { - BigInteger nextPow = BigInteger.TEN.multiply(approxPow); - int nextCmp = nextPow.compareTo(x); - while (nextCmp <= 0) { - approxLog10++; - approxPow = nextPow; - approxCmp = nextCmp; - nextPow = BigInteger.TEN.multiply(approxPow); - nextCmp = nextPow.compareTo(x); + // capacity of 10 suffices for all x <= 10^(2^10). + List<BigInteger> powersOf10 = new ArrayList<BigInteger>(10); + BigInteger powerOf10 = BigInteger.TEN; + while (x.compareTo(powerOf10) >= 0) { + powersOf10.add(powerOf10); + powerOf10 = powerOf10.pow(2); + } + BigInteger floorPow = BigInteger.ONE; + int floorLog = 0; + for (int i = powersOf10.size() - 1; i >= 0; i--) { + BigInteger powOf10 = powersOf10.get(i); + floorLog *= 2; + BigInteger tenPow = powOf10.multiply(floorPow); + if (x.compareTo(tenPow) >= 0) { + floorPow = tenPow; + floorLog++; } } - - int floorLog = approxLog10; - BigInteger floorPow = approxPow; - int floorCmp = approxCmp; - switch (mode) { case UNNECESSARY: - checkRoundingUnnecessary(floorCmp == 0); + checkRoundingUnnecessary(floorPow.equals(x)); // fall through case FLOOR: case DOWN: @@ -192,9 +171,6 @@ public final class BigIntegerMath { } } - private static final double LN_10 = Math.log(10); - private static final double LN_2 = Math.log(2); - /** * Returns the square root of {@code x}, rounded with the specified rounding mode. * @@ -202,7 +178,6 @@ public final class BigIntegerMath { * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and * {@code sqrt(x)} is not an integer */ - @GwtIncompatible("TODO") @SuppressWarnings("fallthrough") public static BigInteger sqrt(BigInteger x, RoundingMode mode) { checkNonNegative("x", x); @@ -234,7 +209,6 @@ public final class BigIntegerMath { } } - @GwtIncompatible("TODO") private static BigInteger sqrtFloor(BigInteger x) { /* * Adapted from Hacker's Delight, Figure 11-1. @@ -257,7 +231,7 @@ public final class BigIntegerMath { */ BigInteger sqrt0; int log2 = log2(x, FLOOR); - if(log2 < Double.MAX_EXPONENT) { + if(log2 < DoubleUtils.MAX_DOUBLE_EXPONENT) { sqrt0 = sqrtApproxWithDoubles(x); } else { int shift = (log2 - DoubleUtils.SIGNIFICAND_BITS) & ~1; // even! @@ -278,7 +252,6 @@ public final class BigIntegerMath { return sqrt0; } - @GwtIncompatible("TODO") private static BigInteger sqrtApproxWithDoubles(BigInteger x) { return DoubleMath.roundToBigInteger(Math.sqrt(DoubleUtils.bigToDouble(x)), HALF_EVEN); } @@ -290,7 +263,6 @@ public final class BigIntegerMath { * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a} * is not an integer multiple of {@code b} */ - @GwtIncompatible("TODO") public static BigInteger divide(BigInteger p, BigInteger q, RoundingMode mode){ BigDecimal pDec = new BigDecimal(p); BigDecimal qDec = new BigDecimal(q); @@ -313,8 +285,8 @@ public final class BigIntegerMath { 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]); + if (n < LongMath.FACTORIALS.length) { + return BigInteger.valueOf(LongMath.FACTORIALS[n]); } // Pre-allocate space for our list of intermediate BigIntegers. @@ -322,8 +294,8 @@ public final class BigIntegerMath { 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]; + int startingNumber = LongMath.FACTORIALS.length; + long product = LongMath.FACTORIALS[startingNumber - 1]; // Strip off 2s from this value. int shift = Long.numberOfTrailingZeros(product); product >>= shift; @@ -400,48 +372,18 @@ public final class BigIntegerMath { if (k > (n >> 1)) { k = n - k; } - if (k < LongMath.biggestBinomials.length && n <= LongMath.biggestBinomials[k]) { + if (k < LongMath.BIGGEST_BINOMIALS.length && n <= LongMath.BIGGEST_BINOMIALS[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; - } + BigInteger result = BigInteger.ONE; + for (int i = 0; i < k; i++) { + result = result.multiply(BigInteger.valueOf(n - i)); + result = result.divide(BigInteger.valueOf(i + 1)); } - return accum - .multiply(BigInteger.valueOf(numeratorAccum)) - .divide(BigInteger.valueOf(denominatorAccum)); + return result; } // Returns true if BigInteger.valueOf(x.longValue()).equals(x). - @GwtIncompatible("TODO") static boolean fitsInLong(BigInteger x) { return x.bitLength() <= Long.SIZE - 1; } diff --git a/guava/src/com/google/common/math/DoubleMath.java b/guava/src/com/google/common/math/DoubleMath.java index ded38b2..35365a7 100644 --- a/guava/src/com/google/common/math/DoubleMath.java +++ b/guava/src/com/google/common/math/DoubleMath.java @@ -19,31 +19,29 @@ package com.google.common.math; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.math.DoubleUtils.IMPLICIT_BIT; import static com.google.common.math.DoubleUtils.SIGNIFICAND_BITS; +import static com.google.common.math.DoubleUtils.getExponent; import static com.google.common.math.DoubleUtils.getSignificand; import static com.google.common.math.DoubleUtils.isFinite; import static com.google.common.math.DoubleUtils.isNormal; +import static com.google.common.math.DoubleUtils.next; import static com.google.common.math.DoubleUtils.scaleNormalize; import static com.google.common.math.MathPreconditions.checkInRange; import static com.google.common.math.MathPreconditions.checkNonNegative; import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary; -import static java.lang.Math.abs; -import static java.lang.Math.copySign; -import static java.lang.Math.getExponent; -import static java.lang.Math.log; -import static java.lang.Math.rint; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.primitives.Booleans; import java.math.BigInteger; import java.math.RoundingMode; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.annotations.Beta; + /** * A class for arithmetic on doubles that is not covered by {@link java.lang.Math}. * * @author Louis Wasserman * @since 11.0 */ +@Beta public final class DoubleMath { /* * This method returns a value y such that rounding y DOWN (towards zero) gives the same result @@ -59,55 +57,43 @@ public final class DoubleMath { return x; case FLOOR: - if (x >= 0.0 || isMathematicalInteger(x)) { - return x; - } else { - return x - 1.0; - } + return (x >= 0.0) ? x : Math.floor(x); case CEILING: - if (x <= 0.0 || isMathematicalInteger(x)) { - return x; - } else { - return x + 1.0; - } + return (x >= 0.0) ? Math.ceil(x) : x; case DOWN: return x; case UP: - if (isMathematicalInteger(x)) { - return x; - } else { - return x + Math.copySign(1.0, x); - } + return (x >= 0.0) ? Math.ceil(x) : Math.floor(x); case HALF_EVEN: - return rint(x); + return Math.rint(x); - case HALF_UP: { - double z = rint(x); - if (abs(x - z) == 0.5) { - return x + copySign(0.5, x); + case HALF_UP: + if (isMathematicalInteger(x)) { + return x; } else { - return z; + return (x >= 0.0) ? x + 0.5 : x - 0.5; } - } - case HALF_DOWN: { - double z = rint(x); - if (abs(x - z) == 0.5) { + case HALF_DOWN: + if (isMathematicalInteger(x)) { return x; + } else if (x >= 0.0) { + double z = x + 0.5; + return (z == x) ? x : next(z, false); // x + 0.5 - epsilon } else { - return z; + double z = x - 0.5; + return (z == x) ? x : next(z, true); // x - 0.5 + epsilon } - } default: throw new AssertionError(); } } - + /** * Returns the {@code int} value that is equal to {@code x} rounded with the specified rounding * mode, if possible. @@ -175,6 +161,9 @@ public final class DoubleMath { return BigInteger.valueOf((long) x); } int exponent = getExponent(x); + if (exponent < 0) { + return BigInteger.ZERO; + } long significand = getSignificand(x); BigInteger result = BigInteger.valueOf(significand).shiftLeft(exponent - SIGNIFICAND_BITS); return (x < 0) ? result.negate() : result; @@ -198,16 +187,16 @@ public final class DoubleMath { * <li>If {@code x} is positive or negative zero, the result is negative infinity. * </ul> * - * <p>The computed result is within 1 ulp of the exact result. + * <p>The computed result must be within 1 ulp of the exact result. * * <p>If the result of this method will be immediately rounded to an {@code int}, * {@link #log2(double, RoundingMode)} is faster. */ public static double log2(double x) { - return log(x) / LN_2; // surprisingly within 1 ulp according to tests + return Math.log(x) / LN_2; // surprisingly within 1 ulp according to tests } - private static final double LN_2 = log(2); + private static final double LN_2 = Math.log(2); /** * Returns the base 2 logarithm of a double value, rounded with the specified rounding mode to an @@ -260,14 +249,14 @@ public final class DoubleMath { /** * Returns {@code true} if {@code x} represents a mathematical integer. - * + * * <p>This is equivalent to, but not necessarily implemented as, the expression {@code * !Double.isNaN(x) && !Double.isInfinite(x) && x == Math.rint(x)}. */ public static boolean isMathematicalInteger(double x) { return isFinite(x) - && (x == 0.0 || - SIGNIFICAND_BITS - Long.numberOfTrailingZeros(getSignificand(x)) <= getExponent(x)); + && (x == 0.0 || SIGNIFICAND_BITS + - Long.numberOfTrailingZeros(getSignificand(x)) <= getExponent(x)); } /** @@ -285,12 +274,12 @@ public final class DoubleMath { return Double.POSITIVE_INFINITY; } else { // Multiplying the last (n & 0xf) values into their own accumulator gives a more accurate - // result than multiplying by everySixteenthFactorial[n >> 4] directly. + // result than multiplying by EVERY_SIXTEENTH_FACTORIAL[n >> 4] directly. double accum = 1.0; for (int i = 1 + (n & ~0xf); i <= n; i++) { accum *= i; } - return accum * everySixteenthFactorial[n >> 4]; + return accum * EVERY_SIXTEENTH_FACTORIAL[n >> 4]; } } @@ -298,7 +287,7 @@ public final class DoubleMath { static final int MAX_FACTORIAL = 170; @VisibleForTesting - static final double[] everySixteenthFactorial = { + static final double[] EVERY_SIXTEENTH_FACTORIAL = { 0x1.0p0, 0x1.30777758p44, 0x1.956ad0aae33a4p117, @@ -310,66 +299,4 @@ public final class DoubleMath { 0x1.1e5dfc140e1e5p716, 0x1.8ce85fadb707ep829, 0x1.95d5f3d928edep945}; - - /** - * Returns {@code true} if {@code a} and {@code b} are within {@code tolerance} of each other. - * - * <p>Technically speaking, this is equivalent to - * {@code Math.abs(a - b) <= tolerance || Double.valueOf(a).equals(Double.valueOf(b))}. - * - * <p>Notable special cases include: - * <ul> - * <li>All NaNs are fuzzily equal. - * <li>If {@code a == b}, then {@code a} and {@code b} are always fuzzily equal. - * <li>Positive and negative zero are always fuzzily equal. - * <li>If {@code tolerance} is zero, and neither {@code a} nor {@code b} is NaN, then - * {@code a} and {@code b} are fuzzily equal if and only if {@code a == b}. - * <li>With {@link Double#POSITIVE_INFINITY} tolerance, all non-NaN values are fuzzily equal. - * <li>With finite tolerance, {@code Double.POSITIVE_INFINITY} and {@code - * Double.NEGATIVE_INFINITY} are fuzzily equal only to themselves. - * </li> - * - * <p>This is reflexive and symmetric, but <em>not</em> transitive, so it is <em>not</em> an - * equivalence relation and <em>not</em> suitable for use in {@link Object#equals} - * implementations. - * - * @throws IllegalArgumentException if {@code tolerance} is {@code < 0} or NaN - * @since 13.0 - */ - public static boolean fuzzyEquals(double a, double b, double tolerance) { - MathPreconditions.checkNonNegative("tolerance", tolerance); - return - Math.copySign(a - b, 1.0) <= tolerance - // copySign(x, 1.0) is a branch-free version of abs(x), but with different NaN semantics - || (a == b) // needed to ensure that infinities equal themselves - || ((a != a) && (b != b)); // x != x is equivalent to Double.isNaN(x), but faster - } - - /** - * Compares {@code a} and {@code b} "fuzzily," with a tolerance for nearly-equal values. - * - * <p>This method is equivalent to - * {@code fuzzyEquals(a, b, tolerance) ? 0 : Double.compare(a, b)}. In particular, like - * {@link Double#compare(double, double)}, it treats all NaN values as equal and greater than all - * other values (including {@link Double#POSITIVE_INFINITY}). - * - * <p>This is <em>not</em> a total ordering and is <em>not</em> suitable for use in - * {@link Comparable#compareTo} implementations. In particular, it is not transitive. - * - * @throws IllegalArgumentException if {@code tolerance} is {@code < 0} or NaN - * @since 13.0 - */ - public static int fuzzyCompare(double a, double b, double tolerance) { - if (fuzzyEquals(a, b, tolerance)) { - return 0; - } else if (a < b) { - return -1; - } else if (a > b) { - return 1; - } else { - return Booleans.compare(Double.isNaN(a), Double.isNaN(b)); - } - } - - private DoubleMath() {} } diff --git a/guava/src/com/google/common/math/DoubleUtils.java b/guava/src/com/google/common/math/DoubleUtils.java index 6cd94e3..5a6fb3e 100644 --- a/guava/src/com/google/common/math/DoubleUtils.java +++ b/guava/src/com/google/common/math/DoubleUtils.java @@ -17,27 +17,35 @@ package com.google.common.math; import static com.google.common.base.Preconditions.checkArgument; -import static java.lang.Double.MAX_EXPONENT; -import static java.lang.Double.MIN_EXPONENT; -import static java.lang.Double.POSITIVE_INFINITY; -import static java.lang.Double.doubleToRawLongBits; -import static java.lang.Double.isNaN; -import static java.lang.Double.longBitsToDouble; -import static java.lang.Math.getExponent; import java.math.BigInteger; +import com.google.common.annotations.VisibleForTesting; + /** - * Utilities for {@code double} primitives. + * Utilities for {@code double} primitives. Some of these are exposed in JDK 6, + * but we can't depend on them there. * * @author Louis Wasserman */ final class DoubleUtils { + // TODO(user): replace with appropriate calls when we move to JDK 6 + private DoubleUtils() { } - static double nextDown(double d) { - return -Math.nextUp(-d); + static double next(double x, boolean up) { + // Math.nextAfter is JDK 6. + if (x == 0.0) { + return up ? Double.MIN_VALUE : -Double.MIN_VALUE; + } + long bits = Double.doubleToRawLongBits(x); + if ((x < 0.0) == up) { + bits--; + } else { + bits++; + } + return Double.longBitsToDouble(bits); } // The mask for the significand, according to the {@link @@ -56,27 +64,64 @@ final class DoubleUtils { static final int EXPONENT_BIAS = 1023; + static final int MIN_DOUBLE_EXPONENT = -1022; + + static final int MAX_DOUBLE_EXPONENT = 1023; + /** * The implicit 1 bit that is omitted in significands of normal doubles. */ static final long IMPLICIT_BIT = SIGNIFICAND_MASK + 1; + @VisibleForTesting + static int getExponent(double d) { + // TODO: replace with Math.getExponent in JDK 6 + long bits = Double.doubleToRawLongBits(d); + int exponent = (int) ((bits & EXPONENT_MASK) >> SIGNIFICAND_BITS); + exponent -= EXPONENT_BIAS; + return exponent; + } + + /** + * Returns {@code d * 2^scale}. + */ + static strictfp double scalb(double d, int scale) { + // TODO: replace with Math.scalb in JDK 6 + int exponent = getExponent(d); + switch (exponent) { + case MAX_DOUBLE_EXPONENT + 1: // NaN, infinity + return d; + case MIN_DOUBLE_EXPONENT - 1: + return d * StrictMath.pow(2.0, scale); + default: + int newExponent = exponent + scale; + if (MIN_DOUBLE_EXPONENT <= newExponent + & newExponent <= MAX_DOUBLE_EXPONENT) { + long bits = Double.doubleToRawLongBits(d); + bits &= ~EXPONENT_MASK; + bits |= ((long) (newExponent + EXPONENT_BIAS)) << SIGNIFICAND_BITS; + return Double.longBitsToDouble(bits); + } + return d * StrictMath.pow(2.0, scale); + } + } + static long getSignificand(double d) { checkArgument(isFinite(d), "not a normal value"); int exponent = getExponent(d); - long bits = doubleToRawLongBits(d); + long bits = Double.doubleToRawLongBits(d); bits &= SIGNIFICAND_MASK; - return (exponent == MIN_EXPONENT - 1) + return (exponent == MIN_DOUBLE_EXPONENT - 1) ? bits << 1 : bits | IMPLICIT_BIT; } static boolean isFinite(double d) { - return getExponent(d) <= MAX_EXPONENT; + return getExponent(d) <= MAX_DOUBLE_EXPONENT; } static boolean isNormal(double d) { - return getExponent(d) >= MIN_EXPONENT; + return getExponent(d) >= MIN_DOUBLE_EXPONENT; } /* @@ -84,8 +129,8 @@ final class DoubleUtils { * normal, and finite. */ static double scaleNormalize(double x) { - long significand = doubleToRawLongBits(x) & SIGNIFICAND_MASK; - return longBitsToDouble(significand | ONE_BITS); + long significand = Double.doubleToRawLongBits(x) & SIGNIFICAND_MASK; + return Double.longBitsToDouble(significand | ONE_BITS); } static double bigToDouble(BigInteger x) { @@ -95,8 +140,8 @@ final class DoubleUtils { // exponent == floor(log2(abs(x))) if (exponent < Long.SIZE - 1) { return x.longValue(); - } else if (exponent > MAX_EXPONENT) { - return x.signum() * POSITIVE_INFINITY; + } else if (exponent > MAX_DOUBLE_EXPONENT) { + return x.signum() * Double.POSITIVE_INFINITY; } /* @@ -129,20 +174,8 @@ final class DoubleUtils { * Double.POSITIVE_INFINITY. */ bits |= x.signum() & SIGN_MASK; - return longBitsToDouble(bits); - } - - /** - * Returns its argument if it is non-negative, zero if it is negative. - */ - static double ensureNonNegative(double value) { - checkArgument(!isNaN(value)); - if (value > 0.0) { - return value; - } else { - return 0.0; - } + return Double.longBitsToDouble(bits); } - private static final long ONE_BITS = doubleToRawLongBits(1.0); + private static final long ONE_BITS = Double.doubleToRawLongBits(1.0); } diff --git a/guava/src/com/google/common/math/IntMath.java b/guava/src/com/google/common/math/IntMath.java index 33ed400..8fc7cd8 100644 --- a/guava/src/com/google/common/math/IntMath.java +++ b/guava/src/com/google/common/math/IntMath.java @@ -23,10 +23,10 @@ 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.GwtIncompatible; import com.google.common.annotations.VisibleForTesting; @@ -48,6 +48,7 @@ import java.math.RoundingMode; * @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 &&, || @@ -70,8 +71,8 @@ public final class IntMath { * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} * is not a power of two */ + @GwtIncompatible("need BigIntegerMath to adequately test") @SuppressWarnings("fallthrough") - // TODO(kevinb): remove after this warning is disabled globally public static int log2(int x, RoundingMode mode) { checkPositive("x", x); switch (mode) { @@ -116,7 +117,7 @@ public final class IntMath { public static int log10(int x, RoundingMode mode) { checkPositive("x", x); int logFloor = log10Floor(x); - int floorPow = powersOf10[logFloor]; + int floorPow = POWERS_OF_10[logFloor]; switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(x == floorPow); @@ -131,40 +132,26 @@ public final class IntMath { case HALF_UP: case HALF_EVEN: // sqrt(10) is irrational, so log10(x) - logFloor is never exactly 0.5 - return (x <= halfPowersOf10[logFloor]) ? logFloor : logFloor + 1; + return (x <= HALF_POWERS_OF_10[logFloor]) ? logFloor : logFloor + 1; default: throw new AssertionError(); } } 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[] POWERS_OF_10 = + {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; - @VisibleForTesting static final int[] powersOf10 = {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}; /** @@ -194,8 +181,6 @@ public final class IntMath { } else { return 0; } - default: - // continue below to handle the general case } for (int accum = 1;; k >>= 1) { switch (k) { @@ -259,6 +244,7 @@ public final class IntMath { * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a} * is not an integer multiple of {@code b} */ + @GwtIncompatible("failing tests") @SuppressWarnings("fallthrough") public static int divide(int p, int q, RoundingMode mode) { checkNotNull(mode); @@ -352,41 +338,13 @@ 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; } /** @@ -430,6 +388,7 @@ public final class IntMath { * @throws ArithmeticException if {@code b} to the {@code k}th power overflows in signed * {@code int} arithmetic */ + @GwtIncompatible("failing tests") public static int checkedPow(int b, int k) { checkNonNegative("exponent", k); switch (b) { @@ -445,8 +404,6 @@ public final class IntMath { 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) { @@ -477,12 +434,13 @@ public final class IntMath { * * @throws IllegalArgumentException if {@code n < 0} */ + @GwtIncompatible("need BigIntegerMath to adequately test") public static int factorial(int n) { checkNonNegative("n", n); - return (n < factorials.length) ? factorials[n] : Integer.MAX_VALUE; + return (n < FACTORIALS.length) ? FACTORIALS[n] : Integer.MAX_VALUE; } - private static final int[] factorials = { + static final int[] FACTORIALS = { 1, 1, 1 * 2, @@ -511,7 +469,7 @@ public final class IntMath { if (k > (n >> 1)) { k = n - k; } - if (k >= biggestBinomials.length || n > biggestBinomials[k]) { + if (k >= BIGGEST_BINOMIALS.length || n > BIGGEST_BINOMIALS[k]) { return Integer.MAX_VALUE; } switch (k) { @@ -529,8 +487,8 @@ public final class IntMath { } } - // 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, @@ -550,18 +508,5 @@ public final class IntMath { 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/src/com/google/common/math/LongMath.java b/guava/src/com/google/common/math/LongMath.java index 6e00e61..07bd534 100644 --- a/guava/src/com/google/common/math/LongMath.java +++ b/guava/src/com/google/common/math/LongMath.java @@ -27,8 +27,7 @@ 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.GwtIncompatible; +import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import java.math.BigInteger; @@ -48,7 +47,7 @@ import java.math.RoundingMode; * @author Louis Wasserman * @since 11.0 */ -@GwtCompatible(emulated = true) +@Beta public final class LongMath { // NOTE: Whenever both tests are cheap and functional, it's faster to use &, | instead of &&, || @@ -70,7 +69,6 @@ public final class LongMath { * 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) { @@ -110,16 +108,14 @@ public final class LongMath { * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x} * is not a power of ten */ - @GwtIncompatible("TODO") @SuppressWarnings("fallthrough") - // TODO(kevinb): remove after this warning is disabled globally public static int log10(long x, RoundingMode mode) { checkPositive("x", x); if (fitsInInt(x)) { return IntMath.log10((int) x, mode); } int logFloor = log10Floor(x); - long floorPow = powersOf10[logFloor]; + long floorPow = POWERS_OF_10[logFloor]; switch (mode) { case UNNECESSARY: checkRoundingUnnecessary(x == floorPow); @@ -134,41 +130,23 @@ public final class LongMath { case HALF_UP: case HALF_EVEN: // sqrt(10) is irrational, so log10(x)-logFloor is never exactly 0.5 - return (x <= halfPowersOf10[logFloor]) ? logFloor : logFloor + 1; + return (x <= HALF_POWERS_OF_10[logFloor]) ? logFloor : logFloor + 1; default: throw new AssertionError(); } } - @GwtIncompatible("TODO") static int log10Floor(long 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[Long.numberOfLeadingZeros(x)]; - // y is the higher of the two possible values of floor(log10(x)) - - long sgn = (x - powersOf10[y]) >>> (Long.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 - (int) 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 = { - 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 }; - - @GwtIncompatible("TODO") @VisibleForTesting - static final long[] powersOf10 = { + static final long[] POWERS_OF_10 = { 1L, 10L, 100L, @@ -190,10 +168,9 @@ public final class LongMath { 1000000000000000000L }; - // halfPowersOf10[i] = largest long less than 10^(i + 0.5) - @GwtIncompatible("TODO") + // HALF_POWERS_OF_10[i] = largest long less than 10^(i + 0.5) @VisibleForTesting - static final long[] halfPowersOf10 = { + static final long[] HALF_POWERS_OF_10 = { 3L, 31L, 316L, @@ -222,7 +199,6 @@ public final class LongMath { * * @throws IllegalArgumentException if {@code k < 0} */ - @GwtIncompatible("TODO") public static long pow(long b, int k) { checkNonNegative("exponent", k); if (-2 <= b && b <= 2) { @@ -241,8 +217,6 @@ public final class LongMath { } else { return 0; } - default: - throw new AssertionError(); } } for (long accum = 1;; k >>= 1) { @@ -265,7 +239,6 @@ public final class LongMath { * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and * {@code sqrt(x)} is not an integer */ - @GwtIncompatible("TODO") @SuppressWarnings("fallthrough") public static long sqrt(long x, RoundingMode mode) { checkNonNegative("x", x); @@ -297,60 +270,19 @@ public final class LongMath { } } - @GwtIncompatible("TODO") private static long sqrtFloor(long x) { - long guess = (long) Math.sqrt(x); - /* - * Lemma: For all a, b, if |a - b| <= 1, then |floor(a) - floor(b)| <= 1. - * - * Proof: - * -1 <= a - b <= 1 - * b - 1 <= a <= b + 1 - * floor(b - 1) <= floor(a) <= floor(b + 1) - * floor(b) - 1 <= floor(a) <= floor(b) + 1 - * -1 <= floor(a) - floor(b) <= 1 - * - * Theorem: |floor(sqrt(x)) - guess| <= 1. - * - * Proof: By the lemma, it suffices to show that |sqrt(x) - Math.sqrt(x)| <= 1. - * We consider two cases: x <= 2^53 and x > 2^53. - * - * If x <= 2^53, then x is exactly representable as a double, so the only error is in rounding - * sqrt(x) to a double, which introduces at most 2^-52 relative error. Since sqrt(x) < 2^27, - * the absolute error is at most 2^(27-52) = 2^-25 < 1. - * - * Otherwise, x > 2^53. The rounding error introduced by casting x to a double is at most - * 2^63 * 2^-52 = 2^11. Noting that sqrt(x) > 2^26, - * - * sqrt(x) - 0.5 = sqrt((sqrt(x) - 0.5)^2) - * = sqrt(x - sqrt(x) + 0.25) - * < sqrt(x - 2^26 + 0.25) - * < sqrt(x - 2^11) - * <= sqrt((double) x) - * sqrt(x) + 0.5 = sqrt((sqrt(x) + 0.5)^2) - * = sqrt(x + sqrt(x) + 0.25) - * > sqrt(x + 2^26 + 0.25) - * > sqrt(x + 2^11) - * >= sqrt((double) x) - * sqrt(x) - 0.5 < sqrt((double) x) < sqrt(x) + 0.5 - * - * Math.sqrt((double) x) is obtained by rounding sqrt((double) x) to a double, increasing the - * error by at most 2^-52 * sqrt(x) <= 2^(32 - 52) <= 2^-20, so clearly - * - * sqrt(x) - 0.5 - 2^-20 <= Math.sqrt((double) x) <= sqrt(x) + 0.5 + 2^-20 - * - * Therefore, |sqrt(x) - Math.sqrt((double) x)| <= 1, so - * |floor(sqrt(x)) - (long) Math.sqrt((double) x)| <= 1 - * as desired. - */ - long guessSquared = guess * guess; - if (x - guessSquared >= guess + guess + 1) { - // The condition is equivalent to x >= (guess + 1) * (guess + 1), but doesn't overflow. - guess++; - } else if (x < guessSquared) { - guess--; + // Hackers's Delight, Figure 11-1 + long sqrt0 = (long) Math.sqrt(x); + // Precision can be lost in the cast to double, so we use this as a starting estimate. + long sqrt1 = (sqrt0 + (x / sqrt0)) >> 1; + if (sqrt1 == sqrt0) { + return sqrt0; } - return guess; + do { + sqrt0 = sqrt1; + sqrt1 = (sqrt0 + (x / sqrt0)) >> 1; + } while (sqrt1 < sqrt0); + return sqrt0; } /** @@ -360,7 +292,6 @@ public final class LongMath { * @throws ArithmeticException if {@code q == 0}, or if {@code mode == UNNECESSARY} and {@code a} * is not an integer multiple of {@code b} */ - @GwtIncompatible("TODO") @SuppressWarnings("fallthrough") public static long divide(long p, long q, RoundingMode mode) { checkNotNull(mode); @@ -431,7 +362,6 @@ public final class LongMath { * * @throws ArithmeticException if {@code m <= 0} */ - @GwtIncompatible("TODO") public static int mod(long x, int m) { // Cast is safe because the result is guaranteed in the range [0, m) return (int) mod(x, (long) m); @@ -453,7 +383,6 @@ public final class LongMath { * * @throws ArithmeticException if {@code m <= 0} */ - @GwtIncompatible("TODO") public static long mod(long x, long m) { if (m <= 0) { throw new ArithmeticException("Modulus " + m + " must be > 0"); @@ -476,38 +405,24 @@ public final class LongMath { */ 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 + if (a == 0 | b == 0) { + return a | b; } /* * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm. - * This is >60% faster than the Euclidean algorithm in benchmarks. + * This is over 40% 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) + if (a < b) { // swap a, b + long t = b; + b = a; + a = t; + } + a -= b; // a is now positive and even a >>= Long.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b } return a << min(aTwos, bTwos); @@ -518,7 +433,6 @@ public final class LongMath { * * @throws ArithmeticException if {@code a + b} overflows in signed {@code long} arithmetic */ - @GwtIncompatible("TODO") public static long checkedAdd(long a, long b) { long result = a + b; checkNoOverflow((a ^ b) < 0 | (a ^ result) >= 0); @@ -530,7 +444,6 @@ public final class LongMath { * * @throws ArithmeticException if {@code a - b} overflows in signed {@code long} arithmetic */ - @GwtIncompatible("TODO") public static long checkedSubtract(long a, long b) { long result = a - b; checkNoOverflow((a ^ b) >= 0 | (a ^ result) >= 0); @@ -542,7 +455,6 @@ public final class LongMath { * * @throws ArithmeticException if {@code a * b} overflows in signed {@code long} arithmetic */ - @GwtIncompatible("TODO") public static long checkedMultiply(long a, long b) { // Hacker's Delight, Section 2-12 int leadingZeros = Long.numberOfLeadingZeros(a) + Long.numberOfLeadingZeros(~a) @@ -573,7 +485,6 @@ public final class LongMath { * @throws ArithmeticException if {@code b} to the {@code k}th power overflows in signed * {@code long} arithmetic */ - @GwtIncompatible("TODO") public static long checkedPow(long b, int k) { checkNonNegative("exponent", k); if (b >= -2 & b <= 2) { @@ -590,8 +501,6 @@ public final class LongMath { case (-2): checkNoOverflow(k < Long.SIZE); return ((k & 1) == 0) ? (1L << k) : (-1L << k); - default: - throw new AssertionError(); } } long accum = 1; @@ -614,7 +523,6 @@ public final class LongMath { } } - @GwtIncompatible("TODO") @VisibleForTesting static final long FLOOR_SQRT_MAX_LONG = 3037000499L; /** @@ -624,13 +532,12 @@ public final class LongMath { * * @throws IllegalArgumentException if {@code n < 0} */ - @GwtIncompatible("TODO") public static long factorial(int n) { checkNonNegative("n", n); - return (n < factorials.length) ? factorials[n] : Long.MAX_VALUE; + return (n < FACTORIALS.length) ? FACTORIALS[n] : Long.MAX_VALUE; } - static final long[] factorials = { + static final long[] FACTORIALS = { 1L, 1L, 1L * 2, @@ -667,111 +574,51 @@ public final class LongMath { 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); - } + if (k >= BIGGEST_BINOMIALS.length || n > BIGGEST_BINOMIALS[k]) { + return Long.MAX_VALUE; } - } - - /** - * 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 result = 1; + if (k < BIGGEST_SIMPLE_BINOMIALS.length && n <= BIGGEST_SIMPLE_BINOMIALS[k]) { + // guaranteed not to overflow + for (int i = 0; i < k; i++) { + result *= n - i; + result /= i + 1; + } + } else { + // We want to do this in long math for speed, but want to avoid overflow. + // Dividing by the GCD suffices to avoid overflow in all the remaining cases. + for (int i = 1; i <= k; i++, n--) { + int d = IntMath.gcd(n, i); + result /= i / d; // (i/d) is guaranteed to divide result + result *= n / d; + } } - 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); + return result; } /* - * binomial(biggestBinomials[k], k) fits in a long, but not - * binomial(biggestBinomials[k] + 1, k). + * binomial(BIGGEST_BINOMIALS[k], k) fits in a long, but not + * binomial(BIGGEST_BINOMIALS[k] + 1, k). */ - static final int[] biggestBinomials = + static final int[] BIGGEST_BINOMIALS = {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. + * binomial(BIGGEST_SIMPLE_BINOMIALS[k], k) doesn't need to use the slower GCD-based impl, + * but binomial(BIGGEST_SIMPLE_BINOMIALS[k] + 1, k) does. */ - @VisibleForTesting static final int[] biggestSimpleBinomials = + @VisibleForTesting static final int[] BIGGEST_SIMPLE_BINOMIALS = {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. - @GwtIncompatible("TODO") static boolean fitsInInt(long x) { return (int) x == x; } - /** - * 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/src/com/google/common/math/MathPreconditions.java b/guava/src/com/google/common/math/MathPreconditions.java index afaee3b..98a6d38 100644 --- a/guava/src/com/google/common/math/MathPreconditions.java +++ b/guava/src/com/google/common/math/MathPreconditions.java @@ -1,76 +1,71 @@ /* * 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 + * 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. + * 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.checkNotNull; + import com.google.common.annotations.GwtCompatible; import java.math.BigInteger; -import javax.annotation.Nullable; - /** * A collection of preconditions for math functions. - * + * * @author Louis Wasserman */ @GwtCompatible final class MathPreconditions { - static int checkPositive(@Nullable String role, int x) { + static int checkPositive(String role, int x) { if (x <= 0) { throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); } return x; } - static long checkPositive(@Nullable String role, long x) { + static long checkPositive(String role, long x) { if (x <= 0) { throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); } return x; } - static BigInteger checkPositive(@Nullable String role, BigInteger x) { + static BigInteger checkPositive(String role, BigInteger x) { if (x.signum() <= 0) { throw new IllegalArgumentException(role + " (" + x + ") must be > 0"); } return x; } - static int checkNonNegative(@Nullable String role, int x) { + static int checkNonNegative(String role, int x) { if (x < 0) { throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); } return x; } - static long checkNonNegative(@Nullable String role, long x) { + static long checkNonNegative(String role, long x) { if (x < 0) { throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); } return x; } - static BigInteger checkNonNegative(@Nullable String role, BigInteger x) { - if (x.signum() < 0) { - throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); - } - return x; - } - - static double checkNonNegative(@Nullable String role, double x) { - if (!(x >= 0)) { // not x < 0, to work with NaN. + static BigInteger checkNonNegative(String role, BigInteger x) { + if (checkNotNull(x).signum() < 0) { throw new IllegalArgumentException(role + " (" + x + ") must be >= 0"); } return x; diff --git a/guava/src/com/google/common/math/package-info.java b/guava/src/com/google/common/math/package-info.java index bd17e52..6bbd4f2 100644 --- a/guava/src/com/google/common/math/package-info.java +++ b/guava/src/com/google/common/math/package-info.java @@ -19,13 +19,8 @@ * * <p>This package is a part of the open-source * <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/MathExplained"> - * math utilities</a>. */ @ParametersAreNonnullByDefault package com.google.common.math; import javax.annotation.ParametersAreNonnullByDefault; - diff --git a/guava/src/com/google/common/net/HostAndPort.java b/guava/src/com/google/common/net/HostAndPort.java index a0cd3bf..eacc858 100644 --- a/guava/src/com/google/common/net/HostAndPort.java +++ b/guava/src/com/google/common/net/HostAndPort.java @@ -22,13 +22,10 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.Beta; import com.google.common.base.Objects; -import com.google.common.base.Strings; -import java.io.Serializable; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** @@ -63,7 +60,7 @@ import javax.annotation.concurrent.Immutable; * @since 10.0 */ @Beta @Immutable -public final class HostAndPort implements Serializable { +public final class HostAndPort { /** Magic value indicating the absence of a port number. */ private static final int NO_PORT = -1; @@ -86,7 +83,7 @@ public final class HostAndPort implements Serializable { * Returns the portion of this {@code HostAndPort} instance that should * represent the hostname or IPv4/IPv6 literal. * - * <p>A successful parse does not imply any degree of sanity in this field. + * A successful parse does not imply any degree of sanity in this field. * For additional validation, see the {@link HostSpecifier} class. */ public String getHostText() { @@ -174,7 +171,7 @@ public final class HostAndPort implements Serializable { } int port = NO_PORT; - if (!Strings.isNullOrEmpty(portString)) { + if (portString != null) { // Try to parse the whole port string as a number. // JDK7 accepts leading plus signs. We don't want to. checkArgument(!portString.startsWith("+"), "Unparseable port number: %s", hostPortString); @@ -228,7 +225,7 @@ public final class HostAndPort implements Serializable { } @Override - public boolean equals(@Nullable Object other) { + public boolean equals(Object other) { if (this == other) { return true; } @@ -265,6 +262,4 @@ public final class HostAndPort implements Serializable { private static boolean isValidPort(int port) { return port >= 0 && port <= 65535; } - - private static final long serialVersionUID = 0; } diff --git a/guava/src/com/google/common/net/HttpHeaders.java b/guava/src/com/google/common/net/HttpHeaders.java index ace1773..4bb6f9f 100644 --- a/guava/src/com/google/common/net/HttpHeaders.java +++ b/guava/src/com/google/common/net/HttpHeaders.java @@ -16,6 +16,7 @@ package com.google.common.net; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** @@ -31,6 +32,7 @@ import com.google.common.annotations.GwtCompatible; * @author Kurt Alfred Kluever * @since 11.0 */ +@Beta @GwtCompatible public final class HttpHeaders { private HttpHeaders() {} diff --git a/guava/src/com/google/common/net/InetAddresses.java b/guava/src/com/google/common/net/InetAddresses.java index 8eddd0d..2cd9472 100644 --- a/guava/src/com/google/common/net/InetAddresses.java +++ b/guava/src/com/google/common/net/InetAddresses.java @@ -17,9 +17,8 @@ package com.google.common.net; import com.google.common.annotations.Beta; -import com.google.common.base.Objects; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.common.hash.Hashing; import com.google.common.io.ByteStreams; import com.google.common.primitives.Ints; @@ -42,6 +41,12 @@ import javax.annotation.Nullable; * IP address string literals -- there is no blocking DNS penalty for a * malformed string. * + * <p>This class hooks into the {@code sun.net.util.IPAddressUtil} class + * to make use of the {@code textToNumericFormatV4} and + * {@code textToNumericFormatV6} methods directly as a means to avoid + * accidentally traversing all nameservices (it can be vitally important + * to avoid, say, blocking on DNS at times). + * * <p>When dealing with {@link Inet4Address} and {@link Inet6Address} * objects as byte arrays (vis. {@code InetAddress.getAddress()}) they * are 4 and 16 bytes in length, respectively, and represent the address @@ -114,36 +119,68 @@ import javax.annotation.Nullable; public final class InetAddresses { private static final int IPV4_PART_COUNT = 4; private static final int IPV6_PART_COUNT = 8; - private static final Inet4Address LOOPBACK4 = (Inet4Address) forString("127.0.0.1"); - private static final Inet4Address ANY4 = (Inet4Address) forString("0.0.0.0"); + private static final Inet4Address LOOPBACK4 = + (Inet4Address) forString("127.0.0.1"); + private static final Inet4Address ANY4 = + (Inet4Address) forString("0.0.0.0"); private InetAddresses() {} /** - * Returns an {@link Inet4Address}, given a byte array representation of the IPv4 address. + * Returns an {@link Inet4Address}, given a byte array representation + * of the IPv4 address. * - * @param bytes byte array representing an IPv4 address (should be of length 4) - * @return {@link Inet4Address} corresponding to the supplied byte array - * @throws IllegalArgumentException if a valid {@link Inet4Address} can not be created + * @param bytes byte array representing an IPv4 address (should be + * of length 4). + * @return {@link Inet4Address} corresponding to the supplied byte + * array. + * @throws IllegalArgumentException if a valid {@link Inet4Address} + * can not be created. */ private static Inet4Address getInet4Address(byte[] bytes) { Preconditions.checkArgument(bytes.length == 4, "Byte array has invalid length for an IPv4 address: %s != 4.", bytes.length); - // Given a 4-byte array, this cast should always succeed. - return (Inet4Address) bytesToInetAddress(bytes); + try { + InetAddress ipv4 = InetAddress.getByAddress(bytes); + if (!(ipv4 instanceof Inet4Address)) { + throw new UnknownHostException( + String.format("'%s' is not an IPv4 address.", + ipv4.getHostAddress())); + } + + return (Inet4Address) ipv4; + } catch (UnknownHostException e) { + + /* + * This really shouldn't happen in practice since all our byte + * sequences should be valid IP addresses. + * + * However {@link InetAddress#getByAddress} is documented as + * potentially throwing this "if IP address is of illegal length". + * + * This is mapped to IllegalArgumentException since, presumably, + * the argument triggered some bizarre processing bug. + */ + throw new IllegalArgumentException( + String.format("Host address '%s' is not a valid IPv4 address.", + Arrays.toString(bytes)), + e); + } } /** - * Returns the {@link InetAddress} having the given string representation. + * Returns the {@link InetAddress} having the given string + * representation. * * <p>This deliberately avoids all nameservice lookups (e.g. no DNS). * - * @param ipString {@code String} containing an IPv4 or IPv6 string literal, e.g. - * {@code "192.168.0.1"} or {@code "2001:db8::1"} + * @param ipString {@code String} containing an IPv4 or IPv6 string literal, + * e.g. {@code "192.168.0.1"} or {@code "2001:db8::1"} * @return {@link InetAddress} representing the argument - * @throws IllegalArgumentException if the argument is not a valid IP string literal + * @throws IllegalArgumentException if the argument is not a valid + * IP string literal */ public static InetAddress forString(String ipString) { byte[] addr = ipStringToBytes(ipString); @@ -154,7 +191,25 @@ public final class InetAddresses { String.format("'%s' is not an IP string literal.", ipString)); } - return bytesToInetAddress(addr); + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException e) { + + /* + * This really shouldn't happen in practice since all our byte + * sequences should be valid IP addresses. + * + * However {@link InetAddress#getByAddress} is documented as + * potentially throwing this "if IP address is of illegal length". + * + * This is mapped to IllegalArgumentException since, presumably, + * the argument triggered some processing bug in either + * {@link IPAddressUtil#textToNumericFormatV4} or + * {@link IPAddressUtil#textToNumericFormatV6}. + */ + throw new IllegalArgumentException( + String.format("'%s' is extremely broken.", ipString), e); + } } /** @@ -316,25 +371,6 @@ public final class InetAddresses { } /** - * Convert a byte array into an InetAddress. - * - * {@link InetAddress#getByAddress} is documented as throwing a checked - * exception "if IP address if of illegal length." We replace it with - * an unchecked exception, for use by callers who already know that addr - * is an array of length 4 or 16. - * - * @param addr the raw 4-byte or 16-byte IP address in big-endian order - * @return an InetAddress object created from the raw IP address - */ - private static InetAddress bytesToInetAddress(byte[] addr) { - try { - return InetAddress.getByAddress(addr); - } catch (UnknownHostException e) { - throw new AssertionError(e); - } - } - - /** * Returns the string representation of an {@link InetAddress}. * * <p>For IPv4 addresses, this is identical to @@ -375,7 +411,7 @@ public final class InetAddresses { * leftmost run wins. If a qualifying run is found, its hextets are replaced * by the sentinel value -1. * - * @param hextets {@code int[]} mutable array of eight 16-bit hextets + * @param hextets {@code int[]} mutable array of eight 16-bit hextets. */ private static void compressLongestRunOfZeroes(int[] hextets) { int bestRunStart = -1; @@ -400,13 +436,13 @@ public final class InetAddresses { } } - /** + /** * Convert a list of hextets into a human-readable IPv6 address. * * <p>In order for "::" compression to work, the input should contain negative * sentinel values in place of the elided zeroes. * - * @param hextets {@code int[]} array of eight 16-bit hextets, or -1s + * @param hextets {@code int[]} array of eight 16-bit hextets, or -1s. */ private static String hextetsToIPv6String(int[] hextets) { /* @@ -483,26 +519,30 @@ public final class InetAddresses { */ public static InetAddress forUriString(String hostAddr) { Preconditions.checkNotNull(hostAddr); + Preconditions.checkArgument(hostAddr.length() > 0, "host string is empty"); + InetAddress retval = null; - // Decide if this should be an IPv6 or IPv4 address. - String ipString; - int expectBytes; - if (hostAddr.startsWith("[") && hostAddr.endsWith("]")) { - ipString = hostAddr.substring(1, hostAddr.length() - 1); - expectBytes = 16; - } else { - ipString = hostAddr; - expectBytes = 4; + // IPv4 address? + try { + retval = forString(hostAddr); + if (retval instanceof Inet4Address) { + return retval; + } + } catch (IllegalArgumentException e) { + // Not a valid IP address, fall through. } - // Parse the address, and make sure the length/version is correct. - byte[] addr = ipStringToBytes(ipString); - if (addr == null || addr.length != expectBytes) { - throw new IllegalArgumentException( - String.format("Not a valid URI IP literal: '%s'", hostAddr)); + // IPv6 address + if (!(hostAddr.startsWith("[") && hostAddr.endsWith("]"))) { + throw new IllegalArgumentException("Not a valid address: \"" + hostAddr + '"'); + } + + retval = forString(hostAddr.substring(1, hostAddr.length() - 1)); + if (retval instanceof Inet6Address) { + return retval; } - return bytesToInetAddress(addr); + throw new IllegalArgumentException("Not a valid address: \"" + hostAddr + '"'); } /** @@ -542,7 +582,8 @@ public final class InetAddresses { * proper IPv6 addresses (which they are), NOT IPv4 compatible * addresses (which they are generally NOT considered to be). * - * @param ip {@link Inet6Address} to be examined for embedded IPv4 compatible address format + * @param ip {@link Inet6Address} to be examined for embedded IPv4 + * compatible address format * @return {@code true} if the argument is a valid "compat" address */ public static boolean isCompatIPv4Address(Inet6Address ip) { @@ -552,7 +593,7 @@ public final class InetAddresses { byte[] bytes = ip.getAddress(); if ((bytes[12] == 0) && (bytes[13] == 0) && (bytes[14] == 0) - && ((bytes[15] == 0) || (bytes[15] == 1))) { + && ((bytes[15] == 0) || (bytes[15] == 1))) { return false; } @@ -562,15 +603,17 @@ public final class InetAddresses { /** * Returns the IPv4 address embedded in an IPv4 compatible address. * - * @param ip {@link Inet6Address} to be examined for an embedded IPv4 address + * @param ip {@link Inet6Address} to be examined for an embedded + * IPv4 address * @return {@link Inet4Address} of the embedded IPv4 address - * @throws IllegalArgumentException if the argument is not a valid IPv4 compatible address + * @throws IllegalArgumentException if the argument is not a valid + * IPv4 compatible address */ public static Inet4Address getCompatIPv4Address(Inet6Address ip) { Preconditions.checkArgument(isCompatIPv4Address(ip), "Address '%s' is not IPv4-compatible.", toAddrString(ip)); - return getInet4Address(Arrays.copyOfRange(ip.getAddress(), 12, 16)); + return getInet4Address(copyOfRange(ip.getAddress(), 12, 16)); } /** @@ -584,7 +627,8 @@ public final class InetAddresses { * <a target="_parent" href="http://tools.ietf.org/html/rfc3056#section-2" * >http://tools.ietf.org/html/rfc3056</a> * - * @param ip {@link Inet6Address} to be examined for 6to4 address format + * @param ip {@link Inet6Address} to be examined for 6to4 address + * format * @return {@code true} if the argument is a 6to4 address */ public static boolean is6to4Address(Inet6Address ip) { @@ -595,19 +639,21 @@ public final class InetAddresses { /** * Returns the IPv4 address embedded in a 6to4 address. * - * @param ip {@link Inet6Address} to be examined for embedded IPv4 in 6to4 address - * @return {@link Inet4Address} of embedded IPv4 in 6to4 address - * @throws IllegalArgumentException if the argument is not a valid IPv6 6to4 address + * @param ip {@link Inet6Address} to be examined for embedded IPv4 + * in 6to4 address. + * @return {@link Inet4Address} of embedded IPv4 in 6to4 address. + * @throws IllegalArgumentException if the argument is not a valid + * IPv6 6to4 address. */ public static Inet4Address get6to4IPv4Address(Inet6Address ip) { Preconditions.checkArgument(is6to4Address(ip), "Address '%s' is not a 6to4 address.", toAddrString(ip)); - return getInet4Address(Arrays.copyOfRange(ip.getAddress(), 2, 6)); + return getInet4Address(copyOfRange(ip.getAddress(), 2, 6)); } /** - * A simple immutable data class to encapsulate the information to be found in a + * A simple data class to encapsulate the information to be found in a * Teredo address. * * <p>All of the fields in this class are encoded in various portions @@ -635,19 +681,31 @@ public final class InetAddresses { * <p>Both server and client can be {@code null}, in which case the * value {@code "0.0.0.0"} will be assumed. * - * @throws IllegalArgumentException if either of the {@code port} or the {@code flags} - * arguments are out of range of an unsigned short + * @throws IllegalArgumentException if either of the {@code port} + * or the {@code flags} arguments are out of range of an + * unsigned short */ // TODO: why is this public? - public TeredoInfo( - @Nullable Inet4Address server, @Nullable Inet4Address client, int port, int flags) { + public TeredoInfo(@Nullable Inet4Address server, + @Nullable Inet4Address client, + int port, int flags) { Preconditions.checkArgument((port >= 0) && (port <= 0xffff), "port '%s' is out of range (0 <= port <= 0xffff)", port); Preconditions.checkArgument((flags >= 0) && (flags <= 0xffff), "flags '%s' is out of range (0 <= flags <= 0xffff)", flags); - - this.server = Objects.firstNonNull(server, ANY4); - this.client = Objects.firstNonNull(client, ANY4); + + if (server != null) { + this.server = server; + } else { + this.server = ANY4; + } + + if (client != null) { + this.client = client; + } else { + this.client = ANY4; + } + this.port = port; this.flags = flags; } @@ -674,7 +732,8 @@ public final class InetAddresses { * * <p>Teredo addresses begin with the {@code "2001::/32"} prefix. * - * @param ip {@link Inet6Address} to be examined for Teredo address format + * @param ip {@link Inet6Address} to be examined for Teredo address + * format. * @return {@code true} if the argument is a Teredo address */ public static boolean isTeredoAddress(Inet6Address ip) { @@ -686,23 +745,25 @@ public final class InetAddresses { /** * Returns the Teredo information embedded in a Teredo address. * - * @param ip {@link Inet6Address} to be examined for embedded Teredo information + * @param ip {@link Inet6Address} to be examined for embedded Teredo + * information * @return extracted {@code TeredoInfo} - * @throws IllegalArgumentException if the argument is not a valid IPv6 Teredo address + * @throws IllegalArgumentException if the argument is not a valid + * IPv6 Teredo address */ public static TeredoInfo getTeredoInfo(Inet6Address ip) { Preconditions.checkArgument(isTeredoAddress(ip), "Address '%s' is not a Teredo address.", toAddrString(ip)); byte[] bytes = ip.getAddress(); - Inet4Address server = getInet4Address(Arrays.copyOfRange(bytes, 4, 8)); + Inet4Address server = getInet4Address(copyOfRange(bytes, 4, 8)); int flags = ByteStreams.newDataInput(bytes, 8).readShort() & 0xffff; // Teredo obfuscates the mapped client port, per section 4 of the RFC. int port = ~ByteStreams.newDataInput(bytes, 10).readShort() & 0xffff; - byte[] clientBytes = Arrays.copyOfRange(bytes, 12, 16); + byte[] clientBytes = copyOfRange(bytes, 12, 16); for (int i = 0; i < clientBytes.length; i++) { // Teredo obfuscates the mapped client IP, per section 4 of the RFC. clientBytes[i] = (byte) ~clientBytes[i]; @@ -724,7 +785,8 @@ public final class InetAddresses { * <a target="_parent" href="http://tools.ietf.org/html/rfc5214#section-6.1" * >http://tools.ietf.org/html/rfc5214</a> * - * @param ip {@link Inet6Address} to be examined for ISATAP address format + * @param ip {@link Inet6Address} to be examined for ISATAP address + * format. * @return {@code true} if the argument is an ISATAP address */ public static boolean isIsatapAddress(Inet6Address ip) { @@ -751,15 +813,17 @@ public final class InetAddresses { /** * Returns the IPv4 address embedded in an ISATAP address. * - * @param ip {@link Inet6Address} to be examined for embedded IPv4 in ISATAP address + * @param ip {@link Inet6Address} to be examined for embedded IPv4 + * in ISATAP address * @return {@link Inet4Address} of embedded IPv4 in an ISATAP address - * @throws IllegalArgumentException if the argument is not a valid IPv6 ISATAP address + * @throws IllegalArgumentException if the argument is not a valid + * IPv6 ISATAP address */ public static Inet4Address getIsatapIPv4Address(Inet6Address ip) { Preconditions.checkArgument(isIsatapAddress(ip), "Address '%s' is not an ISATAP address.", toAddrString(ip)); - return getInet4Address(Arrays.copyOfRange(ip.getAddress(), 12, 16)); + return getInet4Address(copyOfRange(ip.getAddress(), 12, 16)); } /** @@ -770,12 +834,14 @@ public final class InetAddresses { * due to their trivial spoofability. With other transition addresses * spoofing involves (at least) infection of one's BGP routing table. * - * @param ip {@link Inet6Address} to be examined for embedded IPv4 client address - * @return {@code true} if there is an embedded IPv4 client address + * @param ip {@link Inet6Address} to be examined for embedded IPv4 + * client address. + * @return {@code true} if there is an embedded IPv4 client address. * @since 7.0 */ public static boolean hasEmbeddedIPv4ClientAddress(Inet6Address ip) { - return isCompatIPv4Address(ip) || is6to4Address(ip) || isTeredoAddress(ip); + return isCompatIPv4Address(ip) || is6to4Address(ip) || + isTeredoAddress(ip); } /** @@ -787,9 +853,11 @@ public final class InetAddresses { * due to their trivial spoofability. With other transition addresses * spoofing involves (at least) infection of one's BGP routing table. * - * @param ip {@link Inet6Address} to be examined for embedded IPv4 client address - * @return {@link Inet4Address} of embedded IPv4 client address - * @throws IllegalArgumentException if the argument does not have a valid embedded IPv4 address + * @param ip {@link Inet6Address} to be examined for embedded IPv4 + * client address. + * @return {@link Inet4Address} of embedded IPv4 client address. + * @throws IllegalArgumentException if the argument does not have a valid + * embedded IPv4 address. */ public static Inet4Address getEmbeddedIPv4ClientAddress(Inet6Address ip) { if (isCompatIPv4Address(ip)) { @@ -805,7 +873,8 @@ public final class InetAddresses { } throw new IllegalArgumentException( - String.format("'%s' has no embedded IPv4 address.", toAddrString(ip))); + String.format("'%s' has no embedded IPv4 address.", + toAddrString(ip))); } /** @@ -826,7 +895,8 @@ public final class InetAddresses { * {@link Inet6Address} methods, but it would be unwise to depend on such * a poorly-documented feature.) * - * @param ipString {@code String} to be examined for embedded IPv4-mapped IPv6 address format + * @param ipString {@code String} to be examined for embedded IPv4-mapped + * IPv6 address format * @return {@code true} if the argument is a valid "mapped" address * @since 10.0 */ @@ -899,7 +969,7 @@ public final class InetAddresses { } // Many strategies for hashing are possible. This might suffice for now. - int coercedHash = Hashing.murmur3_32().hashLong(addressAsLong).asInt(); + int coercedHash = hash64To32(addressAsLong); // Squash into 224/4 Multicast and 240/4 Reserved space (i.e. 224/3). coercedHash |= 0xe0000000; @@ -914,6 +984,27 @@ public final class InetAddresses { } /** + * Returns an {@code int} hash of a 64-bit long. + * + * This comes from http://www.concentric.net/~ttwang/tech/inthash.htm + * + * This hash gives no guarantees on the cryptographic suitability nor the + * quality of randomness produced, and the mapping may change in the future. + * + * @param key A 64-bit number to hash + * @return {@code int} the input hashed into 32 bits + */ + @VisibleForTesting static int hash64To32(long key) { + key = (~key) + (key << 18); + key = key ^ (key >>> 31); + key = key * 21; + key = key ^ (key >>> 11); + key = key + (key << 6); + key = key ^ (key >>> 22); + return (int) key; + } + + /** * Returns an integer representing an IPv4 address regardless of * whether the supplied argument is an IPv4 address or not. * @@ -960,7 +1051,8 @@ public final class InetAddresses { * @return an InetAddress object created from the raw IP address * @throws UnknownHostException if IP address is of illegal length */ - public static InetAddress fromLittleEndianByteArray(byte[] addr) throws UnknownHostException { + public static InetAddress fromLittleEndianByteArray(byte[] addr) + throws UnknownHostException { byte[] reversed = new byte[addr.length]; for (int i = 0; i < addr.length; i++) { reversed[i] = addr[addr.length - i - 1]; @@ -973,8 +1065,9 @@ public final class InetAddresses { * This method works for both IPv4 and IPv6 addresses. * * @param address the InetAddress to increment - * @return a new InetAddress that is one more than the passed in address - * @throws IllegalArgumentException if InetAddress is at the end of its range + * @return a new InetAddress that is one more than the passed in address. + * @throws IllegalArgumentException if InetAddress is at the end of its + * range. * @since 10.0 */ public static InetAddress increment(InetAddress address) { @@ -988,7 +1081,11 @@ public final class InetAddresses { Preconditions.checkArgument(i >= 0, "Incrementing %s would wrap.", address); addr[i]++; - return bytesToInetAddress(addr); + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException e) { + throw new AssertionError(e); + } } /** @@ -996,7 +1093,7 @@ public final class InetAddresses { * ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff for IPv6. * * @return true if the InetAddress is either 255.255.255.255 for IPv4 or - * ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff for IPv6 + * ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff for IPv6. * @since 10.0 */ public static boolean isMaximum(InetAddress address) { @@ -1008,4 +1105,19 @@ public final class InetAddresses { } return true; } + + /** + * This method emulates the Java 6 method + * {@code Arrays.copyOfRange(byte, int, int)}, which is not available in + * Java 5, and thus cannot be used in Guava code. + */ + private static byte[] copyOfRange(byte[] original, int from, int to) { + Preconditions.checkNotNull(original); + + int end = Math.min(to, original.length); + byte[] result = new byte[to - from]; + + System.arraycopy(original, from, result, 0, end - from); + return result; + } } diff --git a/guava/src/com/google/common/net/InternetDomainName.java b/guava/src/com/google/common/net/InternetDomainName.java index 4ca6581..7537271 100644 --- a/guava/src/com/google/common/net/InternetDomainName.java +++ b/guava/src/com/google/common/net/InternetDomainName.java @@ -73,7 +73,7 @@ import javax.annotation.Nullable; * @since 5.0 */ @Beta -@GwtCompatible +@GwtCompatible(emulated = true) public final class InternetDomainName { private static final CharMatcher DOTS_MATCHER = @@ -146,13 +146,11 @@ public final class InternetDomainName { name = name.substring(0, name.length() - 1); } - checkArgument(name.length() <= MAX_LENGTH, - "Domain name too long: '%s':", name); + checkArgument(name.length() <= MAX_LENGTH, "Domain name too long: '%s':", name); this.name = name; this.parts = ImmutableList.copyOf(DOT_SPLITTER.split(name)); - checkArgument(parts.size() <= MAX_PARTS, - "Domain has too many parts: '%s'", name); + checkArgument(parts.size() <= MAX_PARTS, "Domain has too many parts: '%s'", name); checkArgument(validateSyntax(parts), "Not a valid domain name: '%s'", name); this.publicSuffixIndex = findPublicSuffix(); @@ -194,7 +192,7 @@ public final class InternetDomainName { * * @param domain A domain name (not IP address) * @throws IllegalArgumentException if {@code name} is not syntactically valid - * according to {@link #isValid} + * according to {@link #isValidLenient} * @since 8.0 (previously named {@code from}) * @deprecated Use {@link #from(String)} */ diff --git a/guava/src/com/google/common/net/MediaType.java b/guava/src/com/google/common/net/MediaType.java deleted file mode 100644 index bef306c..0000000 --- a/guava/src/com/google/common/net/MediaType.java +++ /dev/null @@ -1,703 +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.net; - -import static com.google.common.base.CharMatcher.ASCII; -import static com.google.common.base.CharMatcher.JAVA_ISO_CONTROL; -import static com.google.common.base.Charsets.UTF_8; -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.Ascii; -import com.google.common.base.CharMatcher; -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.Optional; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultiset; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; - -import java.nio.charset.Charset; -import java.nio.charset.IllegalCharsetNameException; -import java.nio.charset.UnsupportedCharsetException; -import java.util.Collection; -import java.util.Map; -import java.util.Map.Entry; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; - -/** - * Represents an <a href="http://en.wikipedia.org/wiki/Internet_media_type">Internet Media Type</a> - * (also known as a MIME Type or Content Type). This class also supports the concept of media ranges - * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">defined by HTTP/1.1</a>. - * As such, the {@code *} character is treated as a wildcard and is used to represent any acceptable - * type or subtype value. A media type may not have wildcard type with a declared subtype. The - * {@code *} character has no special meaning as part of a parameter. All values for type, subtype, - * parameter attributes or parameter values must be valid according to RFCs - * <a href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and - * <a href="http://www.ietf.org/rfc/rfc2046.txt">2046</a>. - * - * <p>All portions of the media type that are case-insensitive (type, subtype, parameter attributes) - * are normalized to lowercase. The value of the {@code charset} parameter is normalized to - * lowercase, but all others are left as-is. - * - * <p>Note that this specifically does <strong>not</strong> represent the value of the MIME - * {@code Content-Type} header and as such has no support for header-specific considerations such as - * line folding and comments. - * - * <p>For media types that take a charset the predefined constants default to UTF-8 and have a - * "_UTF_8" suffix. To get a version without a character set, use {@link #withoutParameters}. - * - * @since 12.0 - * - * @author Gregory Kick - */ -@Beta -@GwtCompatible -@Immutable -public final class MediaType { - private static final String CHARSET_ATTRIBUTE = "charset"; - private static final ImmutableListMultimap<String, String> UTF_8_CONSTANT_PARAMETERS = - ImmutableListMultimap.of(CHARSET_ATTRIBUTE, Ascii.toLowerCase(UTF_8.name())); - - /** Matcher for type, subtype and attributes. */ - private static final CharMatcher TOKEN_MATCHER = ASCII.and(JAVA_ISO_CONTROL.negate()) - .and(CharMatcher.isNot(' ')) - .and(CharMatcher.noneOf("()<>@,;:\\\"/[]?=")); - private static final CharMatcher QUOTED_TEXT_MATCHER = ASCII - .and(CharMatcher.noneOf("\"\\\r")); - /* - * This matches the same characters as linear-white-space from RFC 822, but we make no effort to - * enforce any particular rules with regards to line folding as stated in the class docs. - */ - private static final CharMatcher LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n"); - - // TODO(gak): make these public? - private static final String APPLICATION_TYPE = "application"; - private static final String AUDIO_TYPE = "audio"; - private static final String IMAGE_TYPE = "image"; - private static final String TEXT_TYPE = "text"; - private static final String VIDEO_TYPE = "video"; - - private static final String WILDCARD = "*"; - - /* - * The following constants are grouped by their type and ordered alphabetically by the constant - * name within that type. The constant name should be a sensible identifier that is closest to the - * "common name" of the media. This is often, but not necessarily the same as the subtype. - * - * Be sure to declare all constants with the type and subtype in all lowercase. - * - * When adding constants, be sure to add an entry into the KNOWN_TYPES map. For types that - * take a charset (e.g. all text/* types), default to UTF-8 and suffix with "_UTF_8". - */ - - public static final MediaType ANY_TYPE = createConstant(WILDCARD, WILDCARD); - public static final MediaType ANY_TEXT_TYPE = createConstant(TEXT_TYPE, WILDCARD); - public static final MediaType ANY_IMAGE_TYPE = createConstant(IMAGE_TYPE, WILDCARD); - public static final MediaType ANY_AUDIO_TYPE = createConstant(AUDIO_TYPE, WILDCARD); - public static final MediaType ANY_VIDEO_TYPE = createConstant(VIDEO_TYPE, WILDCARD); - public static final MediaType ANY_APPLICATION_TYPE = createConstant(APPLICATION_TYPE, WILDCARD); - - /* text types */ - public static final MediaType CACHE_MANIFEST_UTF_8 = - createConstantUtf8(TEXT_TYPE, "cache-manifest"); - public static final MediaType CSS_UTF_8 = createConstantUtf8(TEXT_TYPE, "css"); - public static final MediaType CSV_UTF_8 = createConstantUtf8(TEXT_TYPE, "csv"); - public static final MediaType HTML_UTF_8 = createConstantUtf8(TEXT_TYPE, "html"); - public static final MediaType I_CALENDAR_UTF_8 = createConstantUtf8(TEXT_TYPE, "calendar"); - public static final MediaType PLAIN_TEXT_UTF_8 = createConstantUtf8(TEXT_TYPE, "plain"); - /** - * <a href="http://www.rfc-editor.org/rfc/rfc4329.txt">RFC 4329</a> declares - * {@link #JAVASCRIPT_UTF_8 application/javascript} to be the correct media type for JavaScript, - * but this may be necessary in certain situations for compatibility. - */ - public static final MediaType TEXT_JAVASCRIPT_UTF_8 = createConstantUtf8(TEXT_TYPE, "javascript"); - public static final MediaType VCARD_UTF_8 = createConstantUtf8(TEXT_TYPE, "vcard"); - public static final MediaType WML_UTF_8 = createConstantUtf8(TEXT_TYPE, "vnd.wap.wml"); - /** - * As described in <a href="http://www.ietf.org/rfc/rfc3023.txt">RFC 3023</a>, this constant - * ({@code text/xml}) is used for XML documents that are "readable by casual users." - * {@link #APPLICATION_XML_UTF_8} is provided for documents that are intended for applications. - */ - public static final MediaType XML_UTF_8 = createConstantUtf8(TEXT_TYPE, "xml"); - - /* image types */ - public static final MediaType BMP = createConstant(IMAGE_TYPE, "bmp"); - public static final MediaType GIF = createConstant(IMAGE_TYPE, "gif"); - public static final MediaType ICO = createConstant(IMAGE_TYPE, "vnd.microsoft.icon"); - public static final MediaType JPEG = createConstant(IMAGE_TYPE, "jpeg"); - public static final MediaType PNG = createConstant(IMAGE_TYPE, "png"); - public static final MediaType SVG_UTF_8 = createConstantUtf8(IMAGE_TYPE, "svg+xml"); - public static final MediaType TIFF = createConstant(IMAGE_TYPE, "tiff"); - public static final MediaType WEBP = createConstant(IMAGE_TYPE, "webp"); - - /* audio types */ - public static final MediaType MP4_AUDIO = createConstant(AUDIO_TYPE, "mp4"); - public static final MediaType MPEG_AUDIO = createConstant(AUDIO_TYPE, "mpeg"); - public static final MediaType OGG_AUDIO = createConstant(AUDIO_TYPE, "ogg"); - public static final MediaType WEBM_AUDIO = createConstant(AUDIO_TYPE, "webm"); - - /* video types */ - public static final MediaType MP4_VIDEO = createConstant(VIDEO_TYPE, "mp4"); - public static final MediaType MPEG_VIDEO = createConstant(VIDEO_TYPE, "mpeg"); - public static final MediaType OGG_VIDEO = createConstant(VIDEO_TYPE, "ogg"); - public static final MediaType QUICKTIME = createConstant(VIDEO_TYPE, "quicktime"); - public static final MediaType WEBM_VIDEO = createConstant(VIDEO_TYPE, "webm"); - public static final MediaType WMV = createConstant(VIDEO_TYPE, "x-ms-wmv"); - - /* application types */ - /** - * As described in <a href="http://www.ietf.org/rfc/rfc3023.txt">RFC 3023</a>, this constant - * ({@code application/xml}) is used for XML documents that are "unreadable by casual users." - * {@link #XML_UTF_8} is provided for documents that may be read by users. - */ - public static final MediaType APPLICATION_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xml"); - public static final MediaType ATOM_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "atom+xml"); - public static final MediaType BZIP2 = createConstant(APPLICATION_TYPE, "x-bzip2"); - public static final MediaType FORM_DATA = createConstant(APPLICATION_TYPE, - "x-www-form-urlencoded"); - /** - * This is a non-standard media type, but is commonly used in serving hosted binary files as it is - * <a href="http://code.google.com/p/browsersec/wiki/Part2#Survey_of_content_sniffing_behaviors"> - * known not to trigger content sniffing in current browsers</a>. It <i>should not</i> be used in - * other situations as it is not specified by any RFC and does not appear in the <a href= - * "http://www.iana.org/assignments/media-types">/IANA MIME Media Types</a> list. Consider - * {@link #OCTET_STREAM} for binary data that is not being served to a browser. - * - * - * @since 14.0 - */ - public static final MediaType APPLICATION_BINARY = createConstant(APPLICATION_TYPE, "binary"); - public static final MediaType GZIP = createConstant(APPLICATION_TYPE, "x-gzip"); - /** - * <a href="http://www.rfc-editor.org/rfc/rfc4329.txt">RFC 4329</a> declares this to be the - * correct media type for JavaScript, but {@link #TEXT_JAVASCRIPT_UTF_8 text/javascript} may be - * necessary in certain situations for compatibility. - */ - public static final MediaType JAVASCRIPT_UTF_8 = - createConstantUtf8(APPLICATION_TYPE, "javascript"); - public static final MediaType JSON_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "json"); - public static final MediaType KML = createConstant(APPLICATION_TYPE, "vnd.google-earth.kml+xml"); - public static final MediaType KMZ = createConstant(APPLICATION_TYPE, "vnd.google-earth.kmz"); - public static final MediaType MBOX = createConstant(APPLICATION_TYPE, "mbox"); - public static final MediaType MICROSOFT_EXCEL = createConstant(APPLICATION_TYPE, "vnd.ms-excel"); - public static final MediaType MICROSOFT_POWERPOINT = - createConstant(APPLICATION_TYPE, "vnd.ms-powerpoint"); - public static final MediaType MICROSOFT_WORD = createConstant(APPLICATION_TYPE, "msword"); - public static final MediaType OCTET_STREAM = createConstant(APPLICATION_TYPE, "octet-stream"); - public static final MediaType OGG_CONTAINER = createConstant(APPLICATION_TYPE, "ogg"); - public static final MediaType OOXML_DOCUMENT = createConstant(APPLICATION_TYPE, - "vnd.openxmlformats-officedocument.wordprocessingml.document"); - public static final MediaType OOXML_PRESENTATION = createConstant(APPLICATION_TYPE, - "vnd.openxmlformats-officedocument.presentationml.presentation"); - public static final MediaType OOXML_SHEET = - createConstant(APPLICATION_TYPE, "vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - public static final MediaType OPENDOCUMENT_GRAPHICS = - createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.graphics"); - public static final MediaType OPENDOCUMENT_PRESENTATION = - createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.presentation"); - public static final MediaType OPENDOCUMENT_SPREADSHEET = - createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.spreadsheet"); - public static final MediaType OPENDOCUMENT_TEXT = - createConstant(APPLICATION_TYPE, "vnd.oasis.opendocument.text"); - public static final MediaType PDF = createConstant(APPLICATION_TYPE, "pdf"); - public static final MediaType POSTSCRIPT = createConstant(APPLICATION_TYPE, "postscript"); - public static final MediaType RDF_XML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rdf+xml"); - public static final MediaType RTF_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "rtf"); - public static final MediaType SHOCKWAVE_FLASH = createConstant(APPLICATION_TYPE, - "x-shockwave-flash"); - public static final MediaType SKETCHUP = createConstant(APPLICATION_TYPE, "vnd.sketchup.skp"); - public static final MediaType TAR = createConstant(APPLICATION_TYPE, "x-tar"); - public static final MediaType XHTML_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xhtml+xml"); - /** - * Media type for Extensible Resource Descriptors. This is not yet registered with the IANA, but - * it is specified by OASIS in the - * <a href="http://docs.oasis-open.org/xri/xrd/v1.0/cd02/xrd-1.0-cd02.html"> XRD definition</a> - * and implemented in projects such as - * <a href="http://code.google.com/p/webfinger/">WebFinger</a>. - */ - public static final MediaType XRD_UTF_8 = createConstantUtf8(APPLICATION_TYPE, "xrd+xml"); - public static final MediaType ZIP = createConstant(APPLICATION_TYPE, "zip"); - - private static final ImmutableMap<MediaType, MediaType> KNOWN_TYPES = - new ImmutableMap.Builder<MediaType, MediaType>() - .put(ANY_TYPE, ANY_TYPE) - .put(ANY_TEXT_TYPE, ANY_TEXT_TYPE) - .put(ANY_IMAGE_TYPE, ANY_IMAGE_TYPE) - .put(ANY_AUDIO_TYPE, ANY_AUDIO_TYPE) - .put(ANY_VIDEO_TYPE, ANY_VIDEO_TYPE) - .put(ANY_APPLICATION_TYPE, ANY_APPLICATION_TYPE) - /* text types */ - .put(CACHE_MANIFEST_UTF_8, CACHE_MANIFEST_UTF_8) - .put(CSS_UTF_8, CSS_UTF_8) - .put(CSV_UTF_8, CSV_UTF_8) - .put(HTML_UTF_8, HTML_UTF_8) - .put(I_CALENDAR_UTF_8, I_CALENDAR_UTF_8) - .put(PLAIN_TEXT_UTF_8, PLAIN_TEXT_UTF_8) - .put(TEXT_JAVASCRIPT_UTF_8, TEXT_JAVASCRIPT_UTF_8) - .put(VCARD_UTF_8, VCARD_UTF_8) - .put(WML_UTF_8, WML_UTF_8) - .put(XML_UTF_8, XML_UTF_8) - /* image types */ - .put(BMP, BMP) - .put(GIF, GIF) - .put(ICO, ICO) - .put(JPEG, JPEG) - .put(PNG, PNG) - .put(SVG_UTF_8, SVG_UTF_8) - .put(TIFF, TIFF) - .put(WEBP, WEBP) - /* audio types */ - .put(MP4_AUDIO, MP4_AUDIO) - .put(MPEG_AUDIO, MPEG_AUDIO) - .put(OGG_AUDIO, OGG_AUDIO) - .put(WEBM_AUDIO, WEBM_AUDIO) - /* video types */ - .put(MP4_VIDEO, MP4_VIDEO) - .put(MPEG_VIDEO, MPEG_VIDEO) - .put(OGG_VIDEO, OGG_VIDEO) - .put(QUICKTIME, QUICKTIME) - .put(WEBM_VIDEO, WEBM_VIDEO) - .put(WMV, WMV) - /* application types */ - .put(APPLICATION_XML_UTF_8, APPLICATION_XML_UTF_8) - .put(ATOM_UTF_8, ATOM_UTF_8) - .put(BZIP2, BZIP2) - .put(FORM_DATA, FORM_DATA) - .put(APPLICATION_BINARY, APPLICATION_BINARY) - .put(GZIP, GZIP) - .put(JAVASCRIPT_UTF_8, JAVASCRIPT_UTF_8) - .put(JSON_UTF_8, JSON_UTF_8) - .put(KML, KML) - .put(KMZ, KMZ) - .put(MBOX, MBOX) - .put(MICROSOFT_EXCEL, MICROSOFT_EXCEL) - .put(MICROSOFT_POWERPOINT, MICROSOFT_POWERPOINT) - .put(MICROSOFT_WORD, MICROSOFT_WORD) - .put(OCTET_STREAM, OCTET_STREAM) - .put(OGG_CONTAINER, OGG_CONTAINER) - .put(OOXML_DOCUMENT, OOXML_DOCUMENT) - .put(OOXML_PRESENTATION, OOXML_PRESENTATION) - .put(OOXML_SHEET, OOXML_SHEET) - .put(OPENDOCUMENT_GRAPHICS, OPENDOCUMENT_GRAPHICS) - .put(OPENDOCUMENT_PRESENTATION, OPENDOCUMENT_PRESENTATION) - .put(OPENDOCUMENT_SPREADSHEET, OPENDOCUMENT_SPREADSHEET) - .put(OPENDOCUMENT_TEXT, OPENDOCUMENT_TEXT) - .put(PDF, PDF) - .put(POSTSCRIPT, POSTSCRIPT) - .put(RDF_XML_UTF_8, RDF_XML_UTF_8) - .put(RTF_UTF_8, RTF_UTF_8) - .put(SHOCKWAVE_FLASH, SHOCKWAVE_FLASH) - .put(SKETCHUP, SKETCHUP) - .put(TAR, TAR) - .put(XHTML_UTF_8, XHTML_UTF_8) - .put(XRD_UTF_8, XRD_UTF_8) - .put(ZIP, ZIP) - .build(); - - private final String type; - private final String subtype; - private final ImmutableListMultimap<String, String> parameters; - - private MediaType(String type, String subtype, - ImmutableListMultimap<String, String> parameters) { - this.type = type; - this.subtype = subtype; - this.parameters = parameters; - } - - private static MediaType createConstant(String type, String subtype) { - return new MediaType(type, subtype, ImmutableListMultimap.<String, String>of()); - } - - private static MediaType createConstantUtf8(String type, String subtype) { - return new MediaType(type, subtype, UTF_8_CONSTANT_PARAMETERS); - } - - /** Returns the top-level media type. For example, {@code "text"} in {@code "text/plain"}. */ - public String type() { - return type; - } - - /** Returns the media subtype. For example, {@code "plain"} in {@code "text/plain"}. */ - public String subtype() { - return subtype; - } - - /** Returns a multimap containing the parameters of this media type. */ - public ImmutableListMultimap<String, String> parameters() { - return parameters; - } - - private Map<String, ImmutableMultiset<String>> parametersAsMap() { - return Maps.transformValues(parameters.asMap(), - new Function<Collection<String>, ImmutableMultiset<String>>() { - @Override public ImmutableMultiset<String> apply(Collection<String> input) { - return ImmutableMultiset.copyOf(input); - } - }); - } - - /** - * Returns an optional charset for the value of the charset parameter if it is specified. - * - * @throws IllegalStateException if multiple charset values have been set for this media type - * @throws IllegalCharsetNameException if a charset value is present, but illegal - * @throws UnsupportedCharsetException if a charset value is present, but no support is available - * in this instance of the Java virtual machine - */ - public Optional<Charset> charset() { - ImmutableSet<String> charsetValues = ImmutableSet.copyOf(parameters.get(CHARSET_ATTRIBUTE)); - switch (charsetValues.size()) { - case 0: - return Optional.absent(); - case 1: - return Optional.of(Charset.forName(Iterables.getOnlyElement(charsetValues))); - default: - throw new IllegalStateException("Multiple charset values defined: " + charsetValues); - } - } - - /** - * Returns a new instance with the same type and subtype as this instance, but without any - * parameters. - */ - public MediaType withoutParameters() { - return parameters.isEmpty() ? this : create(type, subtype); - } - - /** - * <em>Replaces</em> all parameters with the given parameters. - * - * @throws IllegalArgumentException if any parameter or value is invalid - */ - public MediaType withParameters(Multimap<String, String> parameters) { - return create(type, subtype, parameters); - } - - /** - * <em>Replaces</em> all parameters with the given attribute with a single parameter with the - * given value. If multiple parameters with the same attributes are necessary use - * {@link #withParameters}. Prefer {@link #withCharset} for setting the {@code charset} parameter - * when using a {@link Charset} object. - * - * @throws IllegalArgumentException if either {@code attribute} or {@code value} is invalid - */ - public MediaType withParameter(String attribute, String value) { - checkNotNull(attribute); - checkNotNull(value); - String normalizedAttribute = normalizeToken(attribute); - ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder(); - for (Entry<String, String> entry : parameters.entries()) { - String key = entry.getKey(); - if (!normalizedAttribute.equals(key)) { - builder.put(key, entry.getValue()); - } - } - builder.put(normalizedAttribute, normalizeParameterValue(normalizedAttribute, value)); - MediaType mediaType = new MediaType(type, subtype, builder.build()); - // Return one of the constants if the media type is a known type. - return Objects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); - } - - /** - * Returns a new instance with the same type and subtype as this instance, with the - * {@code charset} parameter set to the {@link Charset#name name} of the given charset. Only one - * {@code charset} parameter will be present on the new instance regardless of the number set on - * this one. - * - * <p>If a charset must be specified that is not supported on this JVM (and thus is not - * representable as a {@link Charset} instance, use {@link #withParameter}. - */ - public MediaType withCharset(Charset charset) { - checkNotNull(charset); - return withParameter(CHARSET_ATTRIBUTE, charset.name()); - } - - /** Returns true if either the type or subtype is the wildcard. */ - public boolean hasWildcard() { - return WILDCARD.equals(type) || WILDCARD.equals(subtype); - } - - /** - * Returns {@code true} if this instance falls within the range (as defined by - * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">the HTTP Accept header</a>) - * given by the argument according to three criteria: - * - * <ol> - * <li>The type of the argument is the wildcard or equal to the type of this instance. - * <li>The subtype of the argument is the wildcard or equal to the subtype of this instance. - * <li>All of the parameters present in the argument are present in this instance. - * </ol> - * - * For example: <pre> {@code - * PLAIN_TEXT_UTF_8.is(PLAIN_TEXT_UTF_8) // true - * PLAIN_TEXT_UTF_8.is(HTML_UTF_8) // false - * PLAIN_TEXT_UTF_8.is(ANY_TYPE) // true - * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE) // true - * PLAIN_TEXT_UTF_8.is(ANY_IMAGE_TYPE) // false - * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_8)) // true - * PLAIN_TEXT_UTF_8.withoutParameters().is(ANY_TEXT_TYPE.withCharset(UTF_8)) // false - * PLAIN_TEXT_UTF_8.is(ANY_TEXT_TYPE.withCharset(UTF_16)) // false}</pre> - * - * <p>Note that while it is possible to have the same parameter declared multiple times within a - * media type this method does not consider the number of occurrences of a parameter. For - * example, {@code "text/plain; charset=UTF-8"} satisfies - * {@code "text/plain; charset=UTF-8; charset=UTF-8"}. - */ - public boolean is(MediaType mediaTypeRange) { - return (mediaTypeRange.type.equals(WILDCARD) || mediaTypeRange.type.equals(this.type)) - && (mediaTypeRange.subtype.equals(WILDCARD) || mediaTypeRange.subtype.equals(this.subtype)) - && this.parameters.entries().containsAll(mediaTypeRange.parameters.entries()); - } - - /** - * Creates a new media type with the given type and subtype. - * - * @throws IllegalArgumentException if type or subtype is invalid or if a wildcard is used for the - * type, but not the subtype. - */ - public static MediaType create(String type, String subtype) { - return create(type, subtype, ImmutableListMultimap.<String, String>of()); - } - - /** - * Creates a media type with the "application" type and the given subtype. - * - * @throws IllegalArgumentException if subtype is invalid - */ - static MediaType createApplicationType(String subtype) { - return create(APPLICATION_TYPE, subtype); - } - - /** - * Creates a media type with the "audio" type and the given subtype. - * - * @throws IllegalArgumentException if subtype is invalid - */ - static MediaType createAudioType(String subtype) { - return create(AUDIO_TYPE, subtype); - } - - /** - * Creates a media type with the "image" type and the given subtype. - * - * @throws IllegalArgumentException if subtype is invalid - */ - static MediaType createImageType(String subtype) { - return create(IMAGE_TYPE, subtype); - } - - /** - * Creates a media type with the "text" type and the given subtype. - * - * @throws IllegalArgumentException if subtype is invalid - */ - static MediaType createTextType(String subtype) { - return create(TEXT_TYPE, subtype); - } - - /** - * Creates a media type with the "video" type and the given subtype. - * - * @throws IllegalArgumentException if subtype is invalid - */ - static MediaType createVideoType(String subtype) { - return create(VIDEO_TYPE, subtype); - } - - private static MediaType create(String type, String subtype, - Multimap<String, String> parameters) { - checkNotNull(type); - checkNotNull(subtype); - checkNotNull(parameters); - String normalizedType = normalizeToken(type); - String normalizedSubtype = normalizeToken(subtype); - checkArgument(!WILDCARD.equals(normalizedType) || WILDCARD.equals(normalizedSubtype), - "A wildcard type cannot be used with a non-wildcard subtype"); - ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder(); - for (Entry<String, String> entry : parameters.entries()) { - String attribute = normalizeToken(entry.getKey()); - builder.put(attribute, normalizeParameterValue(attribute, entry.getValue())); - } - MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build()); - // Return one of the constants if the media type is a known type. - return Objects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); - } - - private static String normalizeToken(String token) { - checkArgument(TOKEN_MATCHER.matchesAllOf(token)); - return Ascii.toLowerCase(token); - } - - private static String normalizeParameterValue(String attribute, String value) { - return CHARSET_ATTRIBUTE.equals(attribute) ? Ascii.toLowerCase(value) : value; - } - - /** - * Parses a media type from its string representation. - * - * @throws IllegalArgumentException if the input is not parsable - */ - public static MediaType parse(String input) { - checkNotNull(input); - Tokenizer tokenizer = new Tokenizer(input); - try { - String type = tokenizer.consumeToken(TOKEN_MATCHER); - tokenizer.consumeCharacter('/'); - String subtype = tokenizer.consumeToken(TOKEN_MATCHER); - ImmutableListMultimap.Builder<String, String> parameters = ImmutableListMultimap.builder(); - while (tokenizer.hasMore()) { - tokenizer.consumeCharacter(';'); - tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); - String attribute = tokenizer.consumeToken(TOKEN_MATCHER); - tokenizer.consumeCharacter('='); - final String value; - if ('"' == tokenizer.previewChar()) { - tokenizer.consumeCharacter('"'); - StringBuilder valueBuilder = new StringBuilder(); - while ('"' != tokenizer.previewChar()) { - if ('\\' == tokenizer.previewChar()) { - tokenizer.consumeCharacter('\\'); - valueBuilder.append(tokenizer.consumeCharacter(ASCII)); - } else { - valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); - } - } - value = valueBuilder.toString(); - tokenizer.consumeCharacter('"'); - } else { - value = tokenizer.consumeToken(TOKEN_MATCHER); - } - parameters.put(attribute, value); - } - return create(type, subtype, parameters.build()); - } catch (IllegalStateException e) { - throw new IllegalArgumentException(e); - } - } - - private static final class Tokenizer { - final String input; - int position = 0; - - Tokenizer(String input) { - this.input = input; - } - - String consumeTokenIfPresent(CharMatcher matcher) { - checkState(hasMore()); - int startPosition = position; - position = matcher.negate().indexIn(input, startPosition); - return hasMore() ? input.substring(startPosition, position) : input.substring(startPosition); - } - - String consumeToken(CharMatcher matcher) { - int startPosition = position; - String token = consumeTokenIfPresent(matcher); - checkState(position != startPosition); - return token; - } - - char consumeCharacter(CharMatcher matcher) { - checkState(hasMore()); - char c = previewChar(); - checkState(matcher.matches(c)); - position++; - return c; - } - - char consumeCharacter(char c) { - checkState(hasMore()); - checkState(previewChar() == c); - position++; - return c; - } - - char previewChar() { - checkState(hasMore()); - return input.charAt(position); - } - - boolean hasMore() { - return (position >= 0) && (position < input.length()); - } - } - - @Override public boolean equals(@Nullable Object obj) { - if (obj == this) { - return true; - } else if (obj instanceof MediaType) { - MediaType that = (MediaType) obj; - return this.type.equals(that.type) - && this.subtype.equals(that.subtype) - // compare parameters regardless of order - && this.parametersAsMap().equals(that.parametersAsMap()); - } else { - return false; - } - } - - @Override public int hashCode() { - return Objects.hashCode(type, subtype, parametersAsMap()); - } - - private static final MapJoiner PARAMETER_JOINER = Joiner.on("; ").withKeyValueSeparator("="); - - /** - * Returns the string representation of this media type in the format described in <a - * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>. - */ - @Override public String toString() { - StringBuilder builder = new StringBuilder().append(type).append('/').append(subtype); - if (!parameters.isEmpty()) { - builder.append("; "); - Multimap<String, String> quotedParameters = Multimaps.transformValues(parameters, - new Function<String, String>() { - @Override public String apply(String value) { - return TOKEN_MATCHER.matchesAllOf(value) ? value : escapeAndQuote(value); - } - }); - PARAMETER_JOINER.appendTo(builder, quotedParameters.entries()); - } - return builder.toString(); - } - - private static String escapeAndQuote(String value) { - StringBuilder escaped = new StringBuilder(value.length() + 16).append('"'); - for (char ch : value.toCharArray()) { - if (ch == '\r' || ch == '\\' || ch == '"') { - escaped.append('\\'); - } - escaped.append(ch); - } - return escaped.append('"').toString(); - } - -} diff --git a/guava/src/com/google/common/net/TldPatterns.java b/guava/src/com/google/common/net/TldPatterns.java index 6bc5bb2..879b34d 100644 --- a/guava/src/com/google/common/net/TldPatterns.java +++ b/guava/src/com/google/common/net/TldPatterns.java @@ -1,26 +1,11 @@ // GENERATED FILE - DO NOT EDIT -/* - * 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.net; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableSet; +import java.util.Set; /** * A generated static class containing public members which provide domain @@ -28,13 +13,15 @@ import com.google.common.collect.ImmutableSet; * effective top-level domain (TLD). */ @GwtCompatible -final class TldPatterns { - private TldPatterns() {} +class TldPatterns { + private TldPatterns() { + // Prevent instantiation. + } /** * If a hostname is contained in this set, it is a TLD. */ - static final ImmutableSet<String> EXACT = ImmutableSet.of( + static final Set<String> EXACT = ImmutableSet.of( "ac", "com.ac", "edu.ac", @@ -194,25 +181,9 @@ final class TldPatterns { "co.at", "gv.at", "or.at", - "com.au", - "net.au", - "org.au", - "edu.au", - "gov.au", - "csiro.au", - "asn.au", - "id.au", - "info.au", - "conf.au", - "oz.au", - "act.au", - "nsw.au", - "nt.au", - "qld.au", - "sa.au", - "tas.au", - "vic.au", - "wa.au", + "biz.at", + "info.at", + "priv.at", "act.edu.au", "nsw.edu.au", "nt.edu.au", @@ -228,6 +199,14 @@ final class TldPatterns { "tas.gov.au", "vic.gov.au", "wa.gov.au", + "act.au", + "nsw.au", + "nt.au", + "qld.au", + "sa.au", + "tas.au", + "vic.au", + "wa.au", "aw", "com.aw", "ax", @@ -350,13 +329,13 @@ final class TldPatterns { "bio.br", "blog.br", "bmd.br", + "can.br", "cim.br", "cng.br", "cnt.br", "com.br", "coop.br", "ecn.br", - "eco.br", "edu.br", "emp.br", "eng.br", @@ -377,7 +356,6 @@ final class TldPatterns { "inf.br", "jor.br", "jus.br", - "leg.br", "lel.br", "mat.br", "med.br", @@ -477,8 +455,6 @@ final class TldPatterns { "cl", "gov.cl", "gob.cl", - "co.cl", - "mil.cl", "cm", "gov.cm", "cn", @@ -544,6 +520,26 @@ final class TldPatterns { "rec.co", "web.co", "com", + "ar.com", + "br.com", + "cn.com", + "de.com", + "eu.com", + "gb.com", + "hu.com", + "jpn.com", + "kr.com", + "no.com", + "qc.com", + "ru.com", + "sa.com", + "se.com", + "uk.com", + "us.com", + "uy.com", + "za.com", + "operaunite.com", + "appspot.com", "coop", "cr", "ac.cr", @@ -637,6 +633,7 @@ final class TldPatterns { "eu", "fi", "aland.fi", + "iki.fi", "fm", "fo", "fr", @@ -1190,1732 +1187,6 @@ final class TldPatterns { "lg.jp", "ne.jp", "or.jp", - "aichi.jp", - "akita.jp", - "aomori.jp", - "chiba.jp", - "ehime.jp", - "fukui.jp", - "fukuoka.jp", - "fukushima.jp", - "gifu.jp", - "gunma.jp", - "hiroshima.jp", - "hokkaido.jp", - "hyogo.jp", - "ibaraki.jp", - "ishikawa.jp", - "iwate.jp", - "kagawa.jp", - "kagoshima.jp", - "kanagawa.jp", - "kochi.jp", - "kumamoto.jp", - "kyoto.jp", - "mie.jp", - "miyagi.jp", - "miyazaki.jp", - "nagano.jp", - "nagasaki.jp", - "nara.jp", - "niigata.jp", - "oita.jp", - "okayama.jp", - "okinawa.jp", - "osaka.jp", - "saga.jp", - "saitama.jp", - "shiga.jp", - "shimane.jp", - "shizuoka.jp", - "tochigi.jp", - "tokushima.jp", - "tokyo.jp", - "tottori.jp", - "toyama.jp", - "wakayama.jp", - "yamagata.jp", - "yamaguchi.jp", - "yamanashi.jp", - "aisai.aichi.jp", - "ama.aichi.jp", - "anjo.aichi.jp", - "asuke.aichi.jp", - "chiryu.aichi.jp", - "chita.aichi.jp", - "fuso.aichi.jp", - "gamagori.aichi.jp", - "handa.aichi.jp", - "hazu.aichi.jp", - "hekinan.aichi.jp", - "higashiura.aichi.jp", - "ichinomiya.aichi.jp", - "inazawa.aichi.jp", - "inuyama.aichi.jp", - "isshiki.aichi.jp", - "iwakura.aichi.jp", - "kanie.aichi.jp", - "kariya.aichi.jp", - "kasugai.aichi.jp", - "kira.aichi.jp", - "kiyosu.aichi.jp", - "komaki.aichi.jp", - "konan.aichi.jp", - "kota.aichi.jp", - "mihama.aichi.jp", - "miyoshi.aichi.jp", - "nagakute.aichi.jp", - "nishio.aichi.jp", - "nisshin.aichi.jp", - "obu.aichi.jp", - "oguchi.aichi.jp", - "oharu.aichi.jp", - "okazaki.aichi.jp", - "owariasahi.aichi.jp", - "seto.aichi.jp", - "shikatsu.aichi.jp", - "shinshiro.aichi.jp", - "shitara.aichi.jp", - "tahara.aichi.jp", - "takahama.aichi.jp", - "tobishima.aichi.jp", - "toei.aichi.jp", - "togo.aichi.jp", - "tokai.aichi.jp", - "tokoname.aichi.jp", - "toyoake.aichi.jp", - "toyohashi.aichi.jp", - "toyokawa.aichi.jp", - "toyone.aichi.jp", - "toyota.aichi.jp", - "tsushima.aichi.jp", - "yatomi.aichi.jp", - "akita.akita.jp", - "daisen.akita.jp", - "fujisato.akita.jp", - "gojome.akita.jp", - "hachirogata.akita.jp", - "happou.akita.jp", - "higashinaruse.akita.jp", - "honjo.akita.jp", - "honjyo.akita.jp", - "ikawa.akita.jp", - "kamikoani.akita.jp", - "kamioka.akita.jp", - "katagami.akita.jp", - "kazuno.akita.jp", - "kitaakita.akita.jp", - "kosaka.akita.jp", - "kyowa.akita.jp", - "misato.akita.jp", - "mitane.akita.jp", - "moriyoshi.akita.jp", - "nikaho.akita.jp", - "noshiro.akita.jp", - "odate.akita.jp", - "oga.akita.jp", - "ogata.akita.jp", - "semboku.akita.jp", - "yokote.akita.jp", - "yurihonjo.akita.jp", - "aomori.aomori.jp", - "gonohe.aomori.jp", - "hachinohe.aomori.jp", - "hashikami.aomori.jp", - "hiranai.aomori.jp", - "hirosaki.aomori.jp", - "itayanagi.aomori.jp", - "kuroishi.aomori.jp", - "misawa.aomori.jp", - "mutsu.aomori.jp", - "nakadomari.aomori.jp", - "noheji.aomori.jp", - "oirase.aomori.jp", - "owani.aomori.jp", - "rokunohe.aomori.jp", - "sannohe.aomori.jp", - "shichinohe.aomori.jp", - "shingo.aomori.jp", - "takko.aomori.jp", - "towada.aomori.jp", - "tsugaru.aomori.jp", - "tsuruta.aomori.jp", - "abiko.chiba.jp", - "asahi.chiba.jp", - "chonan.chiba.jp", - "chosei.chiba.jp", - "choshi.chiba.jp", - "chuo.chiba.jp", - "funabashi.chiba.jp", - "futtsu.chiba.jp", - "hanamigawa.chiba.jp", - "ichihara.chiba.jp", - "ichikawa.chiba.jp", - "ichinomiya.chiba.jp", - "inzai.chiba.jp", - "isumi.chiba.jp", - "kamagaya.chiba.jp", - "kamogawa.chiba.jp", - "kashiwa.chiba.jp", - "katori.chiba.jp", - "katsuura.chiba.jp", - "kimitsu.chiba.jp", - "kisarazu.chiba.jp", - "kozaki.chiba.jp", - "kujukuri.chiba.jp", - "kyonan.chiba.jp", - "matsudo.chiba.jp", - "midori.chiba.jp", - "mihama.chiba.jp", - "minamiboso.chiba.jp", - "mobara.chiba.jp", - "mutsuzawa.chiba.jp", - "nagara.chiba.jp", - "nagareyama.chiba.jp", - "narashino.chiba.jp", - "narita.chiba.jp", - "noda.chiba.jp", - "oamishirasato.chiba.jp", - "omigawa.chiba.jp", - "onjuku.chiba.jp", - "otaki.chiba.jp", - "sakae.chiba.jp", - "sakura.chiba.jp", - "shimofusa.chiba.jp", - "shirako.chiba.jp", - "shiroi.chiba.jp", - "shisui.chiba.jp", - "sodegaura.chiba.jp", - "sosa.chiba.jp", - "tako.chiba.jp", - "tateyama.chiba.jp", - "togane.chiba.jp", - "tohnosho.chiba.jp", - "tomisato.chiba.jp", - "urayasu.chiba.jp", - "yachimata.chiba.jp", - "yachiyo.chiba.jp", - "yokaichiba.chiba.jp", - "yokoshibahikari.chiba.jp", - "yotsukaido.chiba.jp", - "ainan.ehime.jp", - "honai.ehime.jp", - "ikata.ehime.jp", - "imabari.ehime.jp", - "iyo.ehime.jp", - "kamijima.ehime.jp", - "kihoku.ehime.jp", - "kumakogen.ehime.jp", - "masaki.ehime.jp", - "matsuno.ehime.jp", - "matsuyama.ehime.jp", - "namikata.ehime.jp", - "niihama.ehime.jp", - "ozu.ehime.jp", - "saijo.ehime.jp", - "seiyo.ehime.jp", - "shikokuchuo.ehime.jp", - "tobe.ehime.jp", - "toon.ehime.jp", - "uchiko.ehime.jp", - "uwajima.ehime.jp", - "yawatahama.ehime.jp", - "echizen.fukui.jp", - "eiheiji.fukui.jp", - "fukui.fukui.jp", - "ikeda.fukui.jp", - "katsuyama.fukui.jp", - "mihama.fukui.jp", - "minamiechizen.fukui.jp", - "obama.fukui.jp", - "ohi.fukui.jp", - "ono.fukui.jp", - "sabae.fukui.jp", - "sakai.fukui.jp", - "takahama.fukui.jp", - "tsuruga.fukui.jp", - "wakasa.fukui.jp", - "ashiya.fukuoka.jp", - "buzen.fukuoka.jp", - "chikugo.fukuoka.jp", - "chikuho.fukuoka.jp", - "chikujo.fukuoka.jp", - "chikushino.fukuoka.jp", - "chikuzen.fukuoka.jp", - "chuo.fukuoka.jp", - "dazaifu.fukuoka.jp", - "fukuchi.fukuoka.jp", - "hakata.fukuoka.jp", - "higashi.fukuoka.jp", - "hirokawa.fukuoka.jp", - "hisayama.fukuoka.jp", - "iizuka.fukuoka.jp", - "inatsuki.fukuoka.jp", - "kaho.fukuoka.jp", - "kasuga.fukuoka.jp", - "kasuya.fukuoka.jp", - "kawara.fukuoka.jp", - "keisen.fukuoka.jp", - "koga.fukuoka.jp", - "kurate.fukuoka.jp", - "kurogi.fukuoka.jp", - "kurume.fukuoka.jp", - "minami.fukuoka.jp", - "miyako.fukuoka.jp", - "miyama.fukuoka.jp", - "miyawaka.fukuoka.jp", - "mizumaki.fukuoka.jp", - "munakata.fukuoka.jp", - "nakagawa.fukuoka.jp", - "nakama.fukuoka.jp", - "nishi.fukuoka.jp", - "nogata.fukuoka.jp", - "ogori.fukuoka.jp", - "okagaki.fukuoka.jp", - "okawa.fukuoka.jp", - "oki.fukuoka.jp", - "omuta.fukuoka.jp", - "onga.fukuoka.jp", - "onojo.fukuoka.jp", - "oto.fukuoka.jp", - "saigawa.fukuoka.jp", - "sasaguri.fukuoka.jp", - "shingu.fukuoka.jp", - "shinyoshitomi.fukuoka.jp", - "shonai.fukuoka.jp", - "soeda.fukuoka.jp", - "sue.fukuoka.jp", - "tachiarai.fukuoka.jp", - "tagawa.fukuoka.jp", - "takata.fukuoka.jp", - "toho.fukuoka.jp", - "toyotsu.fukuoka.jp", - "tsuiki.fukuoka.jp", - "ukiha.fukuoka.jp", - "umi.fukuoka.jp", - "usui.fukuoka.jp", - "yamada.fukuoka.jp", - "yame.fukuoka.jp", - "yanagawa.fukuoka.jp", - "yukuhashi.fukuoka.jp", - "aizubange.fukushima.jp", - "aizumisato.fukushima.jp", - "aizuwakamatsu.fukushima.jp", - "asakawa.fukushima.jp", - "bandai.fukushima.jp", - "date.fukushima.jp", - "fukushima.fukushima.jp", - "furudono.fukushima.jp", - "futaba.fukushima.jp", - "hanawa.fukushima.jp", - "higashi.fukushima.jp", - "hirata.fukushima.jp", - "hirono.fukushima.jp", - "iitate.fukushima.jp", - "inawashiro.fukushima.jp", - "ishikawa.fukushima.jp", - "iwaki.fukushima.jp", - "izumizaki.fukushima.jp", - "kagamiishi.fukushima.jp", - "kaneyama.fukushima.jp", - "kawamata.fukushima.jp", - "kitakata.fukushima.jp", - "kitashiobara.fukushima.jp", - "koori.fukushima.jp", - "koriyama.fukushima.jp", - "kunimi.fukushima.jp", - "miharu.fukushima.jp", - "mishima.fukushima.jp", - "namie.fukushima.jp", - "nango.fukushima.jp", - "nishiaizu.fukushima.jp", - "nishigo.fukushima.jp", - "okuma.fukushima.jp", - "omotego.fukushima.jp", - "ono.fukushima.jp", - "otama.fukushima.jp", - "samegawa.fukushima.jp", - "shimogo.fukushima.jp", - "shirakawa.fukushima.jp", - "showa.fukushima.jp", - "soma.fukushima.jp", - "sukagawa.fukushima.jp", - "taishin.fukushima.jp", - "tamakawa.fukushima.jp", - "tanagura.fukushima.jp", - "tenei.fukushima.jp", - "yabuki.fukushima.jp", - "yamato.fukushima.jp", - "yamatsuri.fukushima.jp", - "yanaizu.fukushima.jp", - "yugawa.fukushima.jp", - "anpachi.gifu.jp", - "ena.gifu.jp", - "gifu.gifu.jp", - "ginan.gifu.jp", - "godo.gifu.jp", - "gujo.gifu.jp", - "hashima.gifu.jp", - "hichiso.gifu.jp", - "hida.gifu.jp", - "higashishirakawa.gifu.jp", - "ibigawa.gifu.jp", - "ikeda.gifu.jp", - "kakamigahara.gifu.jp", - "kani.gifu.jp", - "kasahara.gifu.jp", - "kasamatsu.gifu.jp", - "kawaue.gifu.jp", - "kitagata.gifu.jp", - "mino.gifu.jp", - "minokamo.gifu.jp", - "mitake.gifu.jp", - "mizunami.gifu.jp", - "motosu.gifu.jp", - "nakatsugawa.gifu.jp", - "ogaki.gifu.jp", - "sakahogi.gifu.jp", - "seki.gifu.jp", - "sekigahara.gifu.jp", - "shirakawa.gifu.jp", - "tajimi.gifu.jp", - "takayama.gifu.jp", - "tarui.gifu.jp", - "toki.gifu.jp", - "tomika.gifu.jp", - "wanouchi.gifu.jp", - "yamagata.gifu.jp", - "yaotsu.gifu.jp", - "yoro.gifu.jp", - "annaka.gunma.jp", - "chiyoda.gunma.jp", - "fujioka.gunma.jp", - "higashiagatsuma.gunma.jp", - "isesaki.gunma.jp", - "itakura.gunma.jp", - "kanna.gunma.jp", - "kanra.gunma.jp", - "katashina.gunma.jp", - "kawaba.gunma.jp", - "kiryu.gunma.jp", - "kusatsu.gunma.jp", - "maebashi.gunma.jp", - "meiwa.gunma.jp", - "midori.gunma.jp", - "minakami.gunma.jp", - "naganohara.gunma.jp", - "nakanojo.gunma.jp", - "nanmoku.gunma.jp", - "numata.gunma.jp", - "oizumi.gunma.jp", - "ora.gunma.jp", - "ota.gunma.jp", - "shibukawa.gunma.jp", - "shimonita.gunma.jp", - "shinto.gunma.jp", - "showa.gunma.jp", - "takasaki.gunma.jp", - "takayama.gunma.jp", - "tamamura.gunma.jp", - "tatebayashi.gunma.jp", - "tomioka.gunma.jp", - "tsukiyono.gunma.jp", - "tsumagoi.gunma.jp", - "ueno.gunma.jp", - "yoshioka.gunma.jp", - "asaminami.hiroshima.jp", - "daiwa.hiroshima.jp", - "etajima.hiroshima.jp", - "fuchu.hiroshima.jp", - "fukuyama.hiroshima.jp", - "hatsukaichi.hiroshima.jp", - "higashihiroshima.hiroshima.jp", - "hongo.hiroshima.jp", - "jinsekikogen.hiroshima.jp", - "kaita.hiroshima.jp", - "kui.hiroshima.jp", - "kumano.hiroshima.jp", - "kure.hiroshima.jp", - "mihara.hiroshima.jp", - "miyoshi.hiroshima.jp", - "naka.hiroshima.jp", - "onomichi.hiroshima.jp", - "osakikamijima.hiroshima.jp", - "otake.hiroshima.jp", - "saka.hiroshima.jp", - "sera.hiroshima.jp", - "seranishi.hiroshima.jp", - "shinichi.hiroshima.jp", - "shobara.hiroshima.jp", - "takehara.hiroshima.jp", - "abashiri.hokkaido.jp", - "abira.hokkaido.jp", - "aibetsu.hokkaido.jp", - "akabira.hokkaido.jp", - "akkeshi.hokkaido.jp", - "asahikawa.hokkaido.jp", - "ashibetsu.hokkaido.jp", - "ashoro.hokkaido.jp", - "assabu.hokkaido.jp", - "atsuma.hokkaido.jp", - "bibai.hokkaido.jp", - "biei.hokkaido.jp", - "bifuka.hokkaido.jp", - "bihoro.hokkaido.jp", - "biratori.hokkaido.jp", - "chippubetsu.hokkaido.jp", - "chitose.hokkaido.jp", - "date.hokkaido.jp", - "ebetsu.hokkaido.jp", - "embetsu.hokkaido.jp", - "eniwa.hokkaido.jp", - "erimo.hokkaido.jp", - "esan.hokkaido.jp", - "esashi.hokkaido.jp", - "fukagawa.hokkaido.jp", - "fukushima.hokkaido.jp", - "furano.hokkaido.jp", - "furubira.hokkaido.jp", - "haboro.hokkaido.jp", - "hakodate.hokkaido.jp", - "hamatonbetsu.hokkaido.jp", - "hidaka.hokkaido.jp", - "higashikagura.hokkaido.jp", - "higashikawa.hokkaido.jp", - "hiroo.hokkaido.jp", - "hokuryu.hokkaido.jp", - "hokuto.hokkaido.jp", - "honbetsu.hokkaido.jp", - "horokanai.hokkaido.jp", - "horonobe.hokkaido.jp", - "ikeda.hokkaido.jp", - "imakane.hokkaido.jp", - "ishikari.hokkaido.jp", - "iwamizawa.hokkaido.jp", - "iwanai.hokkaido.jp", - "kamifurano.hokkaido.jp", - "kamikawa.hokkaido.jp", - "kamishihoro.hokkaido.jp", - "kamisunagawa.hokkaido.jp", - "kamoenai.hokkaido.jp", - "kayabe.hokkaido.jp", - "kembuchi.hokkaido.jp", - "kikonai.hokkaido.jp", - "kimobetsu.hokkaido.jp", - "kitahiroshima.hokkaido.jp", - "kitami.hokkaido.jp", - "kiyosato.hokkaido.jp", - "koshimizu.hokkaido.jp", - "kunneppu.hokkaido.jp", - "kuriyama.hokkaido.jp", - "kuromatsunai.hokkaido.jp", - "kushiro.hokkaido.jp", - "kutchan.hokkaido.jp", - "kyowa.hokkaido.jp", - "mashike.hokkaido.jp", - "matsumae.hokkaido.jp", - "mikasa.hokkaido.jp", - "minamifurano.hokkaido.jp", - "mombetsu.hokkaido.jp", - "moseushi.hokkaido.jp", - "mukawa.hokkaido.jp", - "muroran.hokkaido.jp", - "naie.hokkaido.jp", - "nakagawa.hokkaido.jp", - "nakasatsunai.hokkaido.jp", - "nakatombetsu.hokkaido.jp", - "nanae.hokkaido.jp", - "nanporo.hokkaido.jp", - "nayoro.hokkaido.jp", - "nemuro.hokkaido.jp", - "niikappu.hokkaido.jp", - "niki.hokkaido.jp", - "nishiokoppe.hokkaido.jp", - "noboribetsu.hokkaido.jp", - "numata.hokkaido.jp", - "obihiro.hokkaido.jp", - "obira.hokkaido.jp", - "oketo.hokkaido.jp", - "okoppe.hokkaido.jp", - "otaru.hokkaido.jp", - "otobe.hokkaido.jp", - "otofuke.hokkaido.jp", - "otoineppu.hokkaido.jp", - "oumu.hokkaido.jp", - "ozora.hokkaido.jp", - "pippu.hokkaido.jp", - "rankoshi.hokkaido.jp", - "rebun.hokkaido.jp", - "rikubetsu.hokkaido.jp", - "rishiri.hokkaido.jp", - "rishirifuji.hokkaido.jp", - "saroma.hokkaido.jp", - "sarufutsu.hokkaido.jp", - "shakotan.hokkaido.jp", - "shari.hokkaido.jp", - "shibecha.hokkaido.jp", - "shibetsu.hokkaido.jp", - "shikabe.hokkaido.jp", - "shikaoi.hokkaido.jp", - "shimamaki.hokkaido.jp", - "shimizu.hokkaido.jp", - "shimokawa.hokkaido.jp", - "shinshinotsu.hokkaido.jp", - "shintoku.hokkaido.jp", - "shiranuka.hokkaido.jp", - "shiraoi.hokkaido.jp", - "shiriuchi.hokkaido.jp", - "sobetsu.hokkaido.jp", - "sunagawa.hokkaido.jp", - "taiki.hokkaido.jp", - "takasu.hokkaido.jp", - "takikawa.hokkaido.jp", - "takinoue.hokkaido.jp", - "teshikaga.hokkaido.jp", - "tobetsu.hokkaido.jp", - "tohma.hokkaido.jp", - "tomakomai.hokkaido.jp", - "tomari.hokkaido.jp", - "toya.hokkaido.jp", - "toyako.hokkaido.jp", - "toyotomi.hokkaido.jp", - "toyoura.hokkaido.jp", - "tsubetsu.hokkaido.jp", - "tsukigata.hokkaido.jp", - "urakawa.hokkaido.jp", - "urausu.hokkaido.jp", - "uryu.hokkaido.jp", - "utashinai.hokkaido.jp", - "wakkanai.hokkaido.jp", - "wassamu.hokkaido.jp", - "yakumo.hokkaido.jp", - "yoichi.hokkaido.jp", - "aioi.hyogo.jp", - "akashi.hyogo.jp", - "ako.hyogo.jp", - "amagasaki.hyogo.jp", - "aogaki.hyogo.jp", - "asago.hyogo.jp", - "ashiya.hyogo.jp", - "awaji.hyogo.jp", - "fukusaki.hyogo.jp", - "goshiki.hyogo.jp", - "harima.hyogo.jp", - "himeji.hyogo.jp", - "ichikawa.hyogo.jp", - "inagawa.hyogo.jp", - "itami.hyogo.jp", - "kakogawa.hyogo.jp", - "kamigori.hyogo.jp", - "kamikawa.hyogo.jp", - "kasai.hyogo.jp", - "kasuga.hyogo.jp", - "kawanishi.hyogo.jp", - "miki.hyogo.jp", - "minamiawaji.hyogo.jp", - "nishinomiya.hyogo.jp", - "nishiwaki.hyogo.jp", - "ono.hyogo.jp", - "sanda.hyogo.jp", - "sannan.hyogo.jp", - "sasayama.hyogo.jp", - "sayo.hyogo.jp", - "shingu.hyogo.jp", - "shinonsen.hyogo.jp", - "shiso.hyogo.jp", - "sumoto.hyogo.jp", - "taishi.hyogo.jp", - "taka.hyogo.jp", - "takarazuka.hyogo.jp", - "takasago.hyogo.jp", - "takino.hyogo.jp", - "tamba.hyogo.jp", - "tatsuno.hyogo.jp", - "toyooka.hyogo.jp", - "yabu.hyogo.jp", - "yashiro.hyogo.jp", - "yoka.hyogo.jp", - "yokawa.hyogo.jp", - "ami.ibaraki.jp", - "asahi.ibaraki.jp", - "bando.ibaraki.jp", - "chikusei.ibaraki.jp", - "daigo.ibaraki.jp", - "fujishiro.ibaraki.jp", - "hitachi.ibaraki.jp", - "hitachinaka.ibaraki.jp", - "hitachiomiya.ibaraki.jp", - "hitachiota.ibaraki.jp", - "ibaraki.ibaraki.jp", - "ina.ibaraki.jp", - "inashiki.ibaraki.jp", - "itako.ibaraki.jp", - "iwama.ibaraki.jp", - "joso.ibaraki.jp", - "kamisu.ibaraki.jp", - "kasama.ibaraki.jp", - "kashima.ibaraki.jp", - "kasumigaura.ibaraki.jp", - "koga.ibaraki.jp", - "miho.ibaraki.jp", - "mito.ibaraki.jp", - "moriya.ibaraki.jp", - "naka.ibaraki.jp", - "namegata.ibaraki.jp", - "oarai.ibaraki.jp", - "ogawa.ibaraki.jp", - "omitama.ibaraki.jp", - "ryugasaki.ibaraki.jp", - "sakai.ibaraki.jp", - "sakuragawa.ibaraki.jp", - "shimodate.ibaraki.jp", - "shimotsuma.ibaraki.jp", - "shirosato.ibaraki.jp", - "sowa.ibaraki.jp", - "suifu.ibaraki.jp", - "takahagi.ibaraki.jp", - "tamatsukuri.ibaraki.jp", - "tokai.ibaraki.jp", - "tomobe.ibaraki.jp", - "tone.ibaraki.jp", - "toride.ibaraki.jp", - "tsuchiura.ibaraki.jp", - "tsukuba.ibaraki.jp", - "uchihara.ibaraki.jp", - "ushiku.ibaraki.jp", - "yachiyo.ibaraki.jp", - "yamagata.ibaraki.jp", - "yawara.ibaraki.jp", - "yuki.ibaraki.jp", - "anamizu.ishikawa.jp", - "hakui.ishikawa.jp", - "hakusan.ishikawa.jp", - "kaga.ishikawa.jp", - "kahoku.ishikawa.jp", - "kanazawa.ishikawa.jp", - "kawakita.ishikawa.jp", - "komatsu.ishikawa.jp", - "nakanoto.ishikawa.jp", - "nanao.ishikawa.jp", - "nomi.ishikawa.jp", - "nonoichi.ishikawa.jp", - "noto.ishikawa.jp", - "shika.ishikawa.jp", - "suzu.ishikawa.jp", - "tsubata.ishikawa.jp", - "tsurugi.ishikawa.jp", - "uchinada.ishikawa.jp", - "wajima.ishikawa.jp", - "fudai.iwate.jp", - "fujisawa.iwate.jp", - "hanamaki.iwate.jp", - "hiraizumi.iwate.jp", - "hirono.iwate.jp", - "ichinohe.iwate.jp", - "ichinoseki.iwate.jp", - "iwaizumi.iwate.jp", - "iwate.iwate.jp", - "joboji.iwate.jp", - "kamaishi.iwate.jp", - "kanegasaki.iwate.jp", - "karumai.iwate.jp", - "kawai.iwate.jp", - "kitakami.iwate.jp", - "kuji.iwate.jp", - "kunohe.iwate.jp", - "kuzumaki.iwate.jp", - "miyako.iwate.jp", - "mizusawa.iwate.jp", - "morioka.iwate.jp", - "ninohe.iwate.jp", - "noda.iwate.jp", - "ofunato.iwate.jp", - "oshu.iwate.jp", - "otsuchi.iwate.jp", - "rikuzentakata.iwate.jp", - "shiwa.iwate.jp", - "shizukuishi.iwate.jp", - "sumita.iwate.jp", - "takizawa.iwate.jp", - "tanohata.iwate.jp", - "tono.iwate.jp", - "yahaba.iwate.jp", - "yamada.iwate.jp", - "ayagawa.kagawa.jp", - "higashikagawa.kagawa.jp", - "kanonji.kagawa.jp", - "kotohira.kagawa.jp", - "manno.kagawa.jp", - "marugame.kagawa.jp", - "mitoyo.kagawa.jp", - "naoshima.kagawa.jp", - "sanuki.kagawa.jp", - "tadotsu.kagawa.jp", - "takamatsu.kagawa.jp", - "tonosho.kagawa.jp", - "uchinomi.kagawa.jp", - "utazu.kagawa.jp", - "zentsuji.kagawa.jp", - "akune.kagoshima.jp", - "amami.kagoshima.jp", - "hioki.kagoshima.jp", - "isa.kagoshima.jp", - "isen.kagoshima.jp", - "izumi.kagoshima.jp", - "kagoshima.kagoshima.jp", - "kanoya.kagoshima.jp", - "kawanabe.kagoshima.jp", - "kinko.kagoshima.jp", - "kouyama.kagoshima.jp", - "makurazaki.kagoshima.jp", - "matsumoto.kagoshima.jp", - "minamitane.kagoshima.jp", - "nakatane.kagoshima.jp", - "nishinoomote.kagoshima.jp", - "satsumasendai.kagoshima.jp", - "soo.kagoshima.jp", - "tarumizu.kagoshima.jp", - "yusui.kagoshima.jp", - "aikawa.kanagawa.jp", - "atsugi.kanagawa.jp", - "ayase.kanagawa.jp", - "chigasaki.kanagawa.jp", - "ebina.kanagawa.jp", - "fujisawa.kanagawa.jp", - "hadano.kanagawa.jp", - "hakone.kanagawa.jp", - "hiratsuka.kanagawa.jp", - "isehara.kanagawa.jp", - "kaisei.kanagawa.jp", - "kamakura.kanagawa.jp", - "kiyokawa.kanagawa.jp", - "matsuda.kanagawa.jp", - "minamiashigara.kanagawa.jp", - "miura.kanagawa.jp", - "nakai.kanagawa.jp", - "ninomiya.kanagawa.jp", - "odawara.kanagawa.jp", - "oi.kanagawa.jp", - "oiso.kanagawa.jp", - "sagamihara.kanagawa.jp", - "samukawa.kanagawa.jp", - "tsukui.kanagawa.jp", - "yamakita.kanagawa.jp", - "yamato.kanagawa.jp", - "yokosuka.kanagawa.jp", - "yugawara.kanagawa.jp", - "zama.kanagawa.jp", - "zushi.kanagawa.jp", - "aki.kochi.jp", - "geisei.kochi.jp", - "hidaka.kochi.jp", - "higashitsuno.kochi.jp", - "ino.kochi.jp", - "kagami.kochi.jp", - "kami.kochi.jp", - "kitagawa.kochi.jp", - "kochi.kochi.jp", - "mihara.kochi.jp", - "motoyama.kochi.jp", - "muroto.kochi.jp", - "nahari.kochi.jp", - "nakamura.kochi.jp", - "nankoku.kochi.jp", - "nishitosa.kochi.jp", - "niyodogawa.kochi.jp", - "ochi.kochi.jp", - "okawa.kochi.jp", - "otoyo.kochi.jp", - "otsuki.kochi.jp", - "sakawa.kochi.jp", - "sukumo.kochi.jp", - "susaki.kochi.jp", - "tosa.kochi.jp", - "tosashimizu.kochi.jp", - "toyo.kochi.jp", - "tsuno.kochi.jp", - "umaji.kochi.jp", - "yasuda.kochi.jp", - "yusuhara.kochi.jp", - "amakusa.kumamoto.jp", - "arao.kumamoto.jp", - "aso.kumamoto.jp", - "choyo.kumamoto.jp", - "gyokuto.kumamoto.jp", - "hitoyoshi.kumamoto.jp", - "kamiamakusa.kumamoto.jp", - "kashima.kumamoto.jp", - "kikuchi.kumamoto.jp", - "kosa.kumamoto.jp", - "kumamoto.kumamoto.jp", - "mashiki.kumamoto.jp", - "mifune.kumamoto.jp", - "minamata.kumamoto.jp", - "minamioguni.kumamoto.jp", - "nagasu.kumamoto.jp", - "nishihara.kumamoto.jp", - "oguni.kumamoto.jp", - "ozu.kumamoto.jp", - "sumoto.kumamoto.jp", - "takamori.kumamoto.jp", - "uki.kumamoto.jp", - "uto.kumamoto.jp", - "yamaga.kumamoto.jp", - "yamato.kumamoto.jp", - "yatsushiro.kumamoto.jp", - "ayabe.kyoto.jp", - "fukuchiyama.kyoto.jp", - "higashiyama.kyoto.jp", - "ide.kyoto.jp", - "ine.kyoto.jp", - "joyo.kyoto.jp", - "kameoka.kyoto.jp", - "kamo.kyoto.jp", - "kita.kyoto.jp", - "kizu.kyoto.jp", - "kumiyama.kyoto.jp", - "kyotamba.kyoto.jp", - "kyotanabe.kyoto.jp", - "kyotango.kyoto.jp", - "maizuru.kyoto.jp", - "minami.kyoto.jp", - "minamiyamashiro.kyoto.jp", - "miyazu.kyoto.jp", - "muko.kyoto.jp", - "nagaokakyo.kyoto.jp", - "nakagyo.kyoto.jp", - "nantan.kyoto.jp", - "oyamazaki.kyoto.jp", - "sakyo.kyoto.jp", - "seika.kyoto.jp", - "tanabe.kyoto.jp", - "uji.kyoto.jp", - "ujitawara.kyoto.jp", - "wazuka.kyoto.jp", - "yamashina.kyoto.jp", - "yawata.kyoto.jp", - "asahi.mie.jp", - "inabe.mie.jp", - "ise.mie.jp", - "kameyama.mie.jp", - "kawagoe.mie.jp", - "kiho.mie.jp", - "kisosaki.mie.jp", - "kiwa.mie.jp", - "komono.mie.jp", - "kumano.mie.jp", - "kuwana.mie.jp", - "matsusaka.mie.jp", - "meiwa.mie.jp", - "mihama.mie.jp", - "minamiise.mie.jp", - "misugi.mie.jp", - "miyama.mie.jp", - "nabari.mie.jp", - "shima.mie.jp", - "suzuka.mie.jp", - "tado.mie.jp", - "taiki.mie.jp", - "taki.mie.jp", - "tamaki.mie.jp", - "toba.mie.jp", - "tsu.mie.jp", - "udono.mie.jp", - "ureshino.mie.jp", - "watarai.mie.jp", - "yokkaichi.mie.jp", - "furukawa.miyagi.jp", - "higashimatsushima.miyagi.jp", - "ishinomaki.miyagi.jp", - "iwanuma.miyagi.jp", - "kakuda.miyagi.jp", - "kami.miyagi.jp", - "kawasaki.miyagi.jp", - "kesennuma.miyagi.jp", - "marumori.miyagi.jp", - "matsushima.miyagi.jp", - "minamisanriku.miyagi.jp", - "misato.miyagi.jp", - "murata.miyagi.jp", - "natori.miyagi.jp", - "ogawara.miyagi.jp", - "ohira.miyagi.jp", - "onagawa.miyagi.jp", - "osaki.miyagi.jp", - "rifu.miyagi.jp", - "semine.miyagi.jp", - "shibata.miyagi.jp", - "shichikashuku.miyagi.jp", - "shikama.miyagi.jp", - "shiogama.miyagi.jp", - "shiroishi.miyagi.jp", - "tagajo.miyagi.jp", - "taiwa.miyagi.jp", - "tome.miyagi.jp", - "tomiya.miyagi.jp", - "wakuya.miyagi.jp", - "watari.miyagi.jp", - "yamamoto.miyagi.jp", - "zao.miyagi.jp", - "aya.miyazaki.jp", - "ebino.miyazaki.jp", - "gokase.miyazaki.jp", - "hyuga.miyazaki.jp", - "kadogawa.miyazaki.jp", - "kawaminami.miyazaki.jp", - "kijo.miyazaki.jp", - "kitagawa.miyazaki.jp", - "kitakata.miyazaki.jp", - "kitaura.miyazaki.jp", - "kobayashi.miyazaki.jp", - "kunitomi.miyazaki.jp", - "kushima.miyazaki.jp", - "mimata.miyazaki.jp", - "miyakonojo.miyazaki.jp", - "miyazaki.miyazaki.jp", - "morotsuka.miyazaki.jp", - "nichinan.miyazaki.jp", - "nishimera.miyazaki.jp", - "nobeoka.miyazaki.jp", - "saito.miyazaki.jp", - "shiiba.miyazaki.jp", - "shintomi.miyazaki.jp", - "takaharu.miyazaki.jp", - "takanabe.miyazaki.jp", - "takazaki.miyazaki.jp", - "tsuno.miyazaki.jp", - "achi.nagano.jp", - "agematsu.nagano.jp", - "anan.nagano.jp", - "aoki.nagano.jp", - "asahi.nagano.jp", - "azumino.nagano.jp", - "chikuhoku.nagano.jp", - "chikuma.nagano.jp", - "chino.nagano.jp", - "fujimi.nagano.jp", - "hakuba.nagano.jp", - "hara.nagano.jp", - "hiraya.nagano.jp", - "iida.nagano.jp", - "iijima.nagano.jp", - "iiyama.nagano.jp", - "iizuna.nagano.jp", - "ikeda.nagano.jp", - "ikusaka.nagano.jp", - "ina.nagano.jp", - "karuizawa.nagano.jp", - "kawakami.nagano.jp", - "kiso.nagano.jp", - "kisofukushima.nagano.jp", - "kitaaiki.nagano.jp", - "komagane.nagano.jp", - "komoro.nagano.jp", - "matsukawa.nagano.jp", - "matsumoto.nagano.jp", - "miasa.nagano.jp", - "minamiaiki.nagano.jp", - "minamimaki.nagano.jp", - "minamiminowa.nagano.jp", - "minowa.nagano.jp", - "miyada.nagano.jp", - "miyota.nagano.jp", - "mochizuki.nagano.jp", - "nagano.nagano.jp", - "nagawa.nagano.jp", - "nagiso.nagano.jp", - "nakagawa.nagano.jp", - "nakano.nagano.jp", - "nozawaonsen.nagano.jp", - "obuse.nagano.jp", - "ogawa.nagano.jp", - "okaya.nagano.jp", - "omachi.nagano.jp", - "omi.nagano.jp", - "ookuwa.nagano.jp", - "ooshika.nagano.jp", - "otaki.nagano.jp", - "otari.nagano.jp", - "sakae.nagano.jp", - "sakaki.nagano.jp", - "saku.nagano.jp", - "sakuho.nagano.jp", - "shimosuwa.nagano.jp", - "shinanomachi.nagano.jp", - "shiojiri.nagano.jp", - "suwa.nagano.jp", - "suzaka.nagano.jp", - "takagi.nagano.jp", - "takamori.nagano.jp", - "takayama.nagano.jp", - "tateshina.nagano.jp", - "tatsuno.nagano.jp", - "togakushi.nagano.jp", - "togura.nagano.jp", - "tomi.nagano.jp", - "ueda.nagano.jp", - "wada.nagano.jp", - "yamagata.nagano.jp", - "yamanouchi.nagano.jp", - "yasaka.nagano.jp", - "yasuoka.nagano.jp", - "chijiwa.nagasaki.jp", - "futsu.nagasaki.jp", - "goto.nagasaki.jp", - "hasami.nagasaki.jp", - "hirado.nagasaki.jp", - "iki.nagasaki.jp", - "isahaya.nagasaki.jp", - "kawatana.nagasaki.jp", - "kuchinotsu.nagasaki.jp", - "matsuura.nagasaki.jp", - "nagasaki.nagasaki.jp", - "obama.nagasaki.jp", - "omura.nagasaki.jp", - "oseto.nagasaki.jp", - "saikai.nagasaki.jp", - "sasebo.nagasaki.jp", - "seihi.nagasaki.jp", - "shimabara.nagasaki.jp", - "shinkamigoto.nagasaki.jp", - "togitsu.nagasaki.jp", - "tsushima.nagasaki.jp", - "unzen.nagasaki.jp", - "ando.nara.jp", - "gose.nara.jp", - "heguri.nara.jp", - "higashiyoshino.nara.jp", - "ikaruga.nara.jp", - "ikoma.nara.jp", - "kamikitayama.nara.jp", - "kanmaki.nara.jp", - "kashiba.nara.jp", - "kashihara.nara.jp", - "katsuragi.nara.jp", - "kawai.nara.jp", - "kawakami.nara.jp", - "kawanishi.nara.jp", - "koryo.nara.jp", - "kurotaki.nara.jp", - "mitsue.nara.jp", - "miyake.nara.jp", - "nara.nara.jp", - "nosegawa.nara.jp", - "oji.nara.jp", - "ouda.nara.jp", - "oyodo.nara.jp", - "sakurai.nara.jp", - "sango.nara.jp", - "shimoichi.nara.jp", - "shimokitayama.nara.jp", - "shinjo.nara.jp", - "soni.nara.jp", - "takatori.nara.jp", - "tawaramoto.nara.jp", - "tenkawa.nara.jp", - "tenri.nara.jp", - "uda.nara.jp", - "yamatokoriyama.nara.jp", - "yamatotakada.nara.jp", - "yamazoe.nara.jp", - "yoshino.nara.jp", - "aga.niigata.jp", - "agano.niigata.jp", - "gosen.niigata.jp", - "itoigawa.niigata.jp", - "izumozaki.niigata.jp", - "joetsu.niigata.jp", - "kamo.niigata.jp", - "kariwa.niigata.jp", - "kashiwazaki.niigata.jp", - "minamiuonuma.niigata.jp", - "mitsuke.niigata.jp", - "muika.niigata.jp", - "murakami.niigata.jp", - "myoko.niigata.jp", - "nagaoka.niigata.jp", - "niigata.niigata.jp", - "ojiya.niigata.jp", - "omi.niigata.jp", - "sado.niigata.jp", - "sanjo.niigata.jp", - "seiro.niigata.jp", - "seirou.niigata.jp", - "sekikawa.niigata.jp", - "shibata.niigata.jp", - "tagami.niigata.jp", - "tainai.niigata.jp", - "tochio.niigata.jp", - "tokamachi.niigata.jp", - "tsubame.niigata.jp", - "tsunan.niigata.jp", - "uonuma.niigata.jp", - "yahiko.niigata.jp", - "yoita.niigata.jp", - "yuzawa.niigata.jp", - "beppu.oita.jp", - "bungoono.oita.jp", - "bungotakada.oita.jp", - "hasama.oita.jp", - "hiji.oita.jp", - "himeshima.oita.jp", - "hita.oita.jp", - "kamitsue.oita.jp", - "kokonoe.oita.jp", - "kuju.oita.jp", - "kunisaki.oita.jp", - "kusu.oita.jp", - "oita.oita.jp", - "saiki.oita.jp", - "taketa.oita.jp", - "tsukumi.oita.jp", - "usa.oita.jp", - "usuki.oita.jp", - "yufu.oita.jp", - "akaiwa.okayama.jp", - "asakuchi.okayama.jp", - "bizen.okayama.jp", - "hayashima.okayama.jp", - "ibara.okayama.jp", - "kagamino.okayama.jp", - "kasaoka.okayama.jp", - "kibichuo.okayama.jp", - "kumenan.okayama.jp", - "kurashiki.okayama.jp", - "maniwa.okayama.jp", - "misaki.okayama.jp", - "nagi.okayama.jp", - "niimi.okayama.jp", - "nishiawakura.okayama.jp", - "okayama.okayama.jp", - "satosho.okayama.jp", - "setouchi.okayama.jp", - "shinjo.okayama.jp", - "shoo.okayama.jp", - "soja.okayama.jp", - "takahashi.okayama.jp", - "tamano.okayama.jp", - "tsuyama.okayama.jp", - "wake.okayama.jp", - "yakage.okayama.jp", - "aguni.okinawa.jp", - "ginowan.okinawa.jp", - "ginoza.okinawa.jp", - "gushikami.okinawa.jp", - "haebaru.okinawa.jp", - "higashi.okinawa.jp", - "hirara.okinawa.jp", - "iheya.okinawa.jp", - "ishigaki.okinawa.jp", - "ishikawa.okinawa.jp", - "itoman.okinawa.jp", - "izena.okinawa.jp", - "kadena.okinawa.jp", - "kin.okinawa.jp", - "kitadaito.okinawa.jp", - "kitanakagusuku.okinawa.jp", - "kumejima.okinawa.jp", - "kunigami.okinawa.jp", - "minamidaito.okinawa.jp", - "motobu.okinawa.jp", - "nago.okinawa.jp", - "naha.okinawa.jp", - "nakagusuku.okinawa.jp", - "nakijin.okinawa.jp", - "nanjo.okinawa.jp", - "nishihara.okinawa.jp", - "ogimi.okinawa.jp", - "okinawa.okinawa.jp", - "onna.okinawa.jp", - "shimoji.okinawa.jp", - "taketomi.okinawa.jp", - "tarama.okinawa.jp", - "tokashiki.okinawa.jp", - "tomigusuku.okinawa.jp", - "tonaki.okinawa.jp", - "urasoe.okinawa.jp", - "uruma.okinawa.jp", - "yaese.okinawa.jp", - "yomitan.okinawa.jp", - "yonabaru.okinawa.jp", - "yonaguni.okinawa.jp", - "zamami.okinawa.jp", - "abeno.osaka.jp", - "chihayaakasaka.osaka.jp", - "chuo.osaka.jp", - "daito.osaka.jp", - "fujiidera.osaka.jp", - "habikino.osaka.jp", - "hannan.osaka.jp", - "higashiosaka.osaka.jp", - "higashisumiyoshi.osaka.jp", - "higashiyodogawa.osaka.jp", - "hirakata.osaka.jp", - "ibaraki.osaka.jp", - "ikeda.osaka.jp", - "izumi.osaka.jp", - "izumiotsu.osaka.jp", - "izumisano.osaka.jp", - "kadoma.osaka.jp", - "kaizuka.osaka.jp", - "kanan.osaka.jp", - "kashiwara.osaka.jp", - "katano.osaka.jp", - "kawachinagano.osaka.jp", - "kishiwada.osaka.jp", - "kita.osaka.jp", - "kumatori.osaka.jp", - "matsubara.osaka.jp", - "minato.osaka.jp", - "minoh.osaka.jp", - "misaki.osaka.jp", - "moriguchi.osaka.jp", - "neyagawa.osaka.jp", - "nishi.osaka.jp", - "nose.osaka.jp", - "osakasayama.osaka.jp", - "sakai.osaka.jp", - "sayama.osaka.jp", - "sennan.osaka.jp", - "settsu.osaka.jp", - "shijonawate.osaka.jp", - "shimamoto.osaka.jp", - "suita.osaka.jp", - "tadaoka.osaka.jp", - "taishi.osaka.jp", - "tajiri.osaka.jp", - "takaishi.osaka.jp", - "takatsuki.osaka.jp", - "tondabayashi.osaka.jp", - "toyonaka.osaka.jp", - "toyono.osaka.jp", - "yao.osaka.jp", - "ariake.saga.jp", - "arita.saga.jp", - "fukudomi.saga.jp", - "genkai.saga.jp", - "hamatama.saga.jp", - "hizen.saga.jp", - "imari.saga.jp", - "kamimine.saga.jp", - "kanzaki.saga.jp", - "karatsu.saga.jp", - "kashima.saga.jp", - "kitagata.saga.jp", - "kitahata.saga.jp", - "kiyama.saga.jp", - "kouhoku.saga.jp", - "kyuragi.saga.jp", - "nishiarita.saga.jp", - "ogi.saga.jp", - "omachi.saga.jp", - "ouchi.saga.jp", - "saga.saga.jp", - "shiroishi.saga.jp", - "taku.saga.jp", - "tara.saga.jp", - "tosu.saga.jp", - "yoshinogari.saga.jp", - "arakawa.saitama.jp", - "asaka.saitama.jp", - "chichibu.saitama.jp", - "fujimi.saitama.jp", - "fujimino.saitama.jp", - "fukaya.saitama.jp", - "hanno.saitama.jp", - "hanyu.saitama.jp", - "hasuda.saitama.jp", - "hatogaya.saitama.jp", - "hatoyama.saitama.jp", - "hidaka.saitama.jp", - "higashichichibu.saitama.jp", - "higashimatsuyama.saitama.jp", - "honjo.saitama.jp", - "ina.saitama.jp", - "iruma.saitama.jp", - "iwatsuki.saitama.jp", - "kamiizumi.saitama.jp", - "kamikawa.saitama.jp", - "kamisato.saitama.jp", - "kasukabe.saitama.jp", - "kawagoe.saitama.jp", - "kawaguchi.saitama.jp", - "kawajima.saitama.jp", - "kazo.saitama.jp", - "kitamoto.saitama.jp", - "koshigaya.saitama.jp", - "kounosu.saitama.jp", - "kuki.saitama.jp", - "kumagaya.saitama.jp", - "matsubushi.saitama.jp", - "minano.saitama.jp", - "misato.saitama.jp", - "miyashiro.saitama.jp", - "miyoshi.saitama.jp", - "moroyama.saitama.jp", - "nagatoro.saitama.jp", - "namegawa.saitama.jp", - "niiza.saitama.jp", - "ogano.saitama.jp", - "ogawa.saitama.jp", - "ogose.saitama.jp", - "okegawa.saitama.jp", - "omiya.saitama.jp", - "otaki.saitama.jp", - "ranzan.saitama.jp", - "ryokami.saitama.jp", - "saitama.saitama.jp", - "sakado.saitama.jp", - "satte.saitama.jp", - "sayama.saitama.jp", - "shiki.saitama.jp", - "shiraoka.saitama.jp", - "soka.saitama.jp", - "sugito.saitama.jp", - "toda.saitama.jp", - "tokigawa.saitama.jp", - "tokorozawa.saitama.jp", - "tsurugashima.saitama.jp", - "urawa.saitama.jp", - "warabi.saitama.jp", - "yashio.saitama.jp", - "yokoze.saitama.jp", - "yono.saitama.jp", - "yorii.saitama.jp", - "yoshida.saitama.jp", - "yoshikawa.saitama.jp", - "yoshimi.saitama.jp", - "aisho.shiga.jp", - "gamo.shiga.jp", - "higashiomi.shiga.jp", - "hikone.shiga.jp", - "koka.shiga.jp", - "konan.shiga.jp", - "kosei.shiga.jp", - "koto.shiga.jp", - "kusatsu.shiga.jp", - "maibara.shiga.jp", - "moriyama.shiga.jp", - "nagahama.shiga.jp", - "nishiazai.shiga.jp", - "notogawa.shiga.jp", - "omihachiman.shiga.jp", - "otsu.shiga.jp", - "ritto.shiga.jp", - "ryuoh.shiga.jp", - "takashima.shiga.jp", - "takatsuki.shiga.jp", - "torahime.shiga.jp", - "toyosato.shiga.jp", - "yasu.shiga.jp", - "akagi.shimane.jp", - "ama.shimane.jp", - "gotsu.shimane.jp", - "hamada.shimane.jp", - "higashiizumo.shimane.jp", - "hikawa.shimane.jp", - "hikimi.shimane.jp", - "izumo.shimane.jp", - "kakinoki.shimane.jp", - "masuda.shimane.jp", - "matsue.shimane.jp", - "misato.shimane.jp", - "nishinoshima.shimane.jp", - "ohda.shimane.jp", - "okinoshima.shimane.jp", - "okuizumo.shimane.jp", - "shimane.shimane.jp", - "tamayu.shimane.jp", - "tsuwano.shimane.jp", - "unnan.shimane.jp", - "yakumo.shimane.jp", - "yasugi.shimane.jp", - "yatsuka.shimane.jp", - "arai.shizuoka.jp", - "atami.shizuoka.jp", - "fuji.shizuoka.jp", - "fujieda.shizuoka.jp", - "fujikawa.shizuoka.jp", - "fujinomiya.shizuoka.jp", - "fukuroi.shizuoka.jp", - "gotemba.shizuoka.jp", - "haibara.shizuoka.jp", - "hamamatsu.shizuoka.jp", - "higashiizu.shizuoka.jp", - "ito.shizuoka.jp", - "iwata.shizuoka.jp", - "izu.shizuoka.jp", - "izunokuni.shizuoka.jp", - "kakegawa.shizuoka.jp", - "kannami.shizuoka.jp", - "kawanehon.shizuoka.jp", - "kawazu.shizuoka.jp", - "kikugawa.shizuoka.jp", - "kosai.shizuoka.jp", - "makinohara.shizuoka.jp", - "matsuzaki.shizuoka.jp", - "minamiizu.shizuoka.jp", - "mishima.shizuoka.jp", - "morimachi.shizuoka.jp", - "nishiizu.shizuoka.jp", - "numazu.shizuoka.jp", - "omaezaki.shizuoka.jp", - "shimada.shizuoka.jp", - "shimizu.shizuoka.jp", - "shimoda.shizuoka.jp", - "shizuoka.shizuoka.jp", - "susono.shizuoka.jp", - "yaizu.shizuoka.jp", - "yoshida.shizuoka.jp", - "ashikaga.tochigi.jp", - "bato.tochigi.jp", - "haga.tochigi.jp", - "ichikai.tochigi.jp", - "iwafune.tochigi.jp", - "kaminokawa.tochigi.jp", - "kanuma.tochigi.jp", - "karasuyama.tochigi.jp", - "kuroiso.tochigi.jp", - "mashiko.tochigi.jp", - "mibu.tochigi.jp", - "moka.tochigi.jp", - "motegi.tochigi.jp", - "nasu.tochigi.jp", - "nasushiobara.tochigi.jp", - "nikko.tochigi.jp", - "nishikata.tochigi.jp", - "nogi.tochigi.jp", - "ohira.tochigi.jp", - "ohtawara.tochigi.jp", - "oyama.tochigi.jp", - "sakura.tochigi.jp", - "sano.tochigi.jp", - "shimotsuke.tochigi.jp", - "shioya.tochigi.jp", - "takanezawa.tochigi.jp", - "tochigi.tochigi.jp", - "tsuga.tochigi.jp", - "ujiie.tochigi.jp", - "utsunomiya.tochigi.jp", - "yaita.tochigi.jp", - "aizumi.tokushima.jp", - "anan.tokushima.jp", - "ichiba.tokushima.jp", - "itano.tokushima.jp", - "kainan.tokushima.jp", - "komatsushima.tokushima.jp", - "matsushige.tokushima.jp", - "mima.tokushima.jp", - "minami.tokushima.jp", - "miyoshi.tokushima.jp", - "mugi.tokushima.jp", - "nakagawa.tokushima.jp", - "naruto.tokushima.jp", - "sanagochi.tokushima.jp", - "shishikui.tokushima.jp", - "tokushima.tokushima.jp", - "wajiki.tokushima.jp", - "adachi.tokyo.jp", - "akiruno.tokyo.jp", - "akishima.tokyo.jp", - "aogashima.tokyo.jp", - "arakawa.tokyo.jp", - "bunkyo.tokyo.jp", - "chiyoda.tokyo.jp", - "chofu.tokyo.jp", - "chuo.tokyo.jp", - "edogawa.tokyo.jp", - "fuchu.tokyo.jp", - "fussa.tokyo.jp", - "hachijo.tokyo.jp", - "hachioji.tokyo.jp", - "hamura.tokyo.jp", - "higashikurume.tokyo.jp", - "higashimurayama.tokyo.jp", - "higashiyamato.tokyo.jp", - "hino.tokyo.jp", - "hinode.tokyo.jp", - "hinohara.tokyo.jp", - "inagi.tokyo.jp", - "itabashi.tokyo.jp", - "katsushika.tokyo.jp", - "kita.tokyo.jp", - "kiyose.tokyo.jp", - "kodaira.tokyo.jp", - "koganei.tokyo.jp", - "kokubunji.tokyo.jp", - "komae.tokyo.jp", - "koto.tokyo.jp", - "kouzushima.tokyo.jp", - "kunitachi.tokyo.jp", - "machida.tokyo.jp", - "meguro.tokyo.jp", - "minato.tokyo.jp", - "mitaka.tokyo.jp", - "mizuho.tokyo.jp", - "musashimurayama.tokyo.jp", - "musashino.tokyo.jp", - "nakano.tokyo.jp", - "nerima.tokyo.jp", - "ogasawara.tokyo.jp", - "okutama.tokyo.jp", - "ome.tokyo.jp", - "oshima.tokyo.jp", - "ota.tokyo.jp", - "setagaya.tokyo.jp", - "shibuya.tokyo.jp", - "shinagawa.tokyo.jp", - "shinjuku.tokyo.jp", - "suginami.tokyo.jp", - "sumida.tokyo.jp", - "tachikawa.tokyo.jp", - "taito.tokyo.jp", - "tama.tokyo.jp", - "toshima.tokyo.jp", - "chizu.tottori.jp", - "hino.tottori.jp", - "kawahara.tottori.jp", - "koge.tottori.jp", - "kotoura.tottori.jp", - "misasa.tottori.jp", - "nanbu.tottori.jp", - "nichinan.tottori.jp", - "sakaiminato.tottori.jp", - "tottori.tottori.jp", - "wakasa.tottori.jp", - "yazu.tottori.jp", - "yonago.tottori.jp", - "asahi.toyama.jp", - "fuchu.toyama.jp", - "fukumitsu.toyama.jp", - "funahashi.toyama.jp", - "himi.toyama.jp", - "imizu.toyama.jp", - "inami.toyama.jp", - "johana.toyama.jp", - "kamiichi.toyama.jp", - "kurobe.toyama.jp", - "nakaniikawa.toyama.jp", - "namerikawa.toyama.jp", - "nanto.toyama.jp", - "nyuzen.toyama.jp", - "oyabe.toyama.jp", - "taira.toyama.jp", - "takaoka.toyama.jp", - "tateyama.toyama.jp", - "toga.toyama.jp", - "tonami.toyama.jp", - "toyama.toyama.jp", - "unazuki.toyama.jp", - "uozu.toyama.jp", - "yamada.toyama.jp", - "arida.wakayama.jp", - "aridagawa.wakayama.jp", - "gobo.wakayama.jp", - "hashimoto.wakayama.jp", - "hidaka.wakayama.jp", - "hirogawa.wakayama.jp", - "inami.wakayama.jp", - "iwade.wakayama.jp", - "kainan.wakayama.jp", - "kamitonda.wakayama.jp", - "katsuragi.wakayama.jp", - "kimino.wakayama.jp", - "kinokawa.wakayama.jp", - "kitayama.wakayama.jp", - "koya.wakayama.jp", - "koza.wakayama.jp", - "kozagawa.wakayama.jp", - "kudoyama.wakayama.jp", - "kushimoto.wakayama.jp", - "mihama.wakayama.jp", - "misato.wakayama.jp", - "nachikatsuura.wakayama.jp", - "shingu.wakayama.jp", - "shirahama.wakayama.jp", - "taiji.wakayama.jp", - "tanabe.wakayama.jp", - "wakayama.wakayama.jp", - "yuasa.wakayama.jp", - "yura.wakayama.jp", - "asahi.yamagata.jp", - "funagata.yamagata.jp", - "higashine.yamagata.jp", - "iide.yamagata.jp", - "kahoku.yamagata.jp", - "kaminoyama.yamagata.jp", - "kaneyama.yamagata.jp", - "kawanishi.yamagata.jp", - "mamurogawa.yamagata.jp", - "mikawa.yamagata.jp", - "murayama.yamagata.jp", - "nagai.yamagata.jp", - "nakayama.yamagata.jp", - "nanyo.yamagata.jp", - "nishikawa.yamagata.jp", - "obanazawa.yamagata.jp", - "oe.yamagata.jp", - "oguni.yamagata.jp", - "ohkura.yamagata.jp", - "oishida.yamagata.jp", - "sagae.yamagata.jp", - "sakata.yamagata.jp", - "sakegawa.yamagata.jp", - "shinjo.yamagata.jp", - "shirataka.yamagata.jp", - "shonai.yamagata.jp", - "takahata.yamagata.jp", - "tendo.yamagata.jp", - "tozawa.yamagata.jp", - "tsuruoka.yamagata.jp", - "yamagata.yamagata.jp", - "yamanobe.yamagata.jp", - "yonezawa.yamagata.jp", - "yuza.yamagata.jp", - "abu.yamaguchi.jp", - "hagi.yamaguchi.jp", - "hikari.yamaguchi.jp", - "hofu.yamaguchi.jp", - "iwakuni.yamaguchi.jp", - "kudamatsu.yamaguchi.jp", - "mitou.yamaguchi.jp", - "nagato.yamaguchi.jp", - "oshima.yamaguchi.jp", - "shimonoseki.yamaguchi.jp", - "shunan.yamaguchi.jp", - "tabuse.yamaguchi.jp", - "tokuyama.yamaguchi.jp", - "toyota.yamaguchi.jp", - "ube.yamaguchi.jp", - "yuu.yamaguchi.jp", - "chuo.yamanashi.jp", - "doshi.yamanashi.jp", - "fuefuki.yamanashi.jp", - "fujikawa.yamanashi.jp", - "fujikawaguchiko.yamanashi.jp", - "fujiyoshida.yamanashi.jp", - "hayakawa.yamanashi.jp", - "hokuto.yamanashi.jp", - "ichikawamisato.yamanashi.jp", - "kai.yamanashi.jp", - "kofu.yamanashi.jp", - "koshu.yamanashi.jp", - "kosuge.yamanashi.jp", - "minami-alps.yamanashi.jp", - "minobu.yamanashi.jp", - "nakamichi.yamanashi.jp", - "nanbu.yamanashi.jp", - "narusawa.yamanashi.jp", - "nirasaki.yamanashi.jp", - "nishikatsura.yamanashi.jp", - "oshino.yamanashi.jp", - "otsuki.yamanashi.jp", - "showa.yamanashi.jp", - "tabayama.yamanashi.jp", - "tsuru.yamanashi.jp", - "uenohara.yamanashi.jp", - "yamanakako.yamanashi.jp", - "yamanashi.yamanashi.jp", "kg", "org.kg", "net.kg", @@ -3012,6 +1283,7 @@ final class TldPatterns { "per.la", "com.la", "org.la", + "c.la", "com.lb", "edu.lb", "gov.lb", @@ -3040,6 +1312,7 @@ final class TldPatterns { "assn.lk", "grp.lk", "hotel.lk", + "local", "com.lr", "edu.lr", "gov.lr", @@ -3760,6 +2033,10 @@ final class TldPatterns { "asso.nc", "ne", "net", + "gb.net", + "se.net", + "uk.net", + "za.net", "nf", "com.nf", "net.nf", @@ -4716,6 +2993,8 @@ final class TldPatterns { "com.nr", "nu", "org", + "ae.org", + "za.org", "pa", "ac.pa", "gob.pa", @@ -4944,7 +3223,7 @@ final class TldPatterns { "poznan.pl", "wroc.pl", "zakopane.pl", - "pm", + "co.pl", "pn", "gov.pn", "co.pn", @@ -4997,15 +3276,6 @@ final class TldPatterns { "ed.pw", "go.pw", "belau.pw", - "qa", - "com.qa", - "edu.qa", - "gov.qa", - "mil.qa", - "name.qa", - "net.qa", - "org.qa", - "sch.qa", "re", "com.re", "asso.re", @@ -5200,7 +3470,6 @@ final class TldPatterns { "org.sd", "edu.sd", "med.sd", - "tv.sd", "gov.sd", "info.sd", "se", @@ -5252,11 +3521,6 @@ final class TldPatterns { "edu.sg", "per.sg", "sh", - "com.sh", - "net.sh", - "gov.sh", - "org.sh", - "mil.sh", "si", "sk", "sl", @@ -5293,8 +3557,6 @@ final class TldPatterns { "saotome.st", "store.st", "su", - "sx", - "gov.sx", "sy", "edu.sy", "gov.sy", @@ -5339,14 +3601,6 @@ final class TldPatterns { "tl", "gov.tl", "tm", - "com.tm", - "co.tm", - "org.tm", - "net.tm", - "nom.tm", - "gov.tm", - "mil.tm", - "edu.tm", "tn", "com.tn", "ens.tn", @@ -5428,86 +3682,58 @@ final class TldPatterns { "net.ua", "org.ua", "cherkassy.ua", - "cherkasy.ua", "chernigov.ua", - "chernihiv.ua", - "chernivtsi.ua", "chernovtsy.ua", "ck.ua", "cn.ua", - "cr.ua", "crimea.ua", "cv.ua", "dn.ua", "dnepropetrovsk.ua", - "dnipropetrovsk.ua", - "dominic.ua", "donetsk.ua", "dp.ua", "if.ua", "ivano-frankivsk.ua", "kh.ua", - "kharkiv.ua", "kharkov.ua", "kherson.ua", "khmelnitskiy.ua", - "khmelnytskyi.ua", "kiev.ua", "kirovograd.ua", "km.ua", "kr.ua", - "krym.ua", "ks.ua", "kv.ua", - "kyiv.ua", "lg.ua", - "lt.ua", "lugansk.ua", "lutsk.ua", - "lv.ua", "lviv.ua", "mk.ua", - "mykolaiv.ua", "nikolaev.ua", "od.ua", - "odesa.ua", "odessa.ua", "pl.ua", "poltava.ua", - "rivne.ua", "rovno.ua", "rv.ua", - "sb.ua", "sebastopol.ua", - "sevastopol.ua", - "sm.ua", "sumy.ua", "te.ua", "ternopil.ua", - "uz.ua", "uzhgorod.ua", "vinnica.ua", - "vinnytsia.ua", "vn.ua", - "volyn.ua", - "yalta.ua", "zaporizhzhe.ua", - "zaporizhzhia.ua", - "zhitomir.ua", - "zhytomyr.ua", "zp.ua", + "zhitomir.ua", "zt.ua", - "co.ua", - "pp.ua", "ug", "co.ug", - "or.ug", "ac.ug", "sc.ug", "go.ug", "ne.ug", - "com.ug", - "org.ug", + "or.ug", "us", "dni.us", "fed.us", @@ -5736,18 +3962,9 @@ final class TldPatterns { "pvt.k12.ma.us", "chtr.k12.ma.us", "paroch.k12.ma.us", - "uy", - "com.uy", - "edu.uy", - "gub.uy", - "mil.uy", - "net.uy", - "org.uy", "uz", - "co.uz", "com.uz", - "net.uz", - "org.uz", + "co.uz", "va", "vc", "com.vc", @@ -5756,17 +3973,6 @@ final class TldPatterns { "gov.vc", "mil.vc", "edu.vc", - "ve", - "co.ve", - "com.ve", - "e12.ve", - "edu.ve", - "gov.ve", - "info.ve", - "mil.ve", - "net.ve", - "org.ve", - "web.ve", "vg", "vi", "co.vi", @@ -5788,14 +3994,12 @@ final class TldPatterns { "pro.vn", "health.vn", "vu", - "wf", "ws", "com.ws", "net.ws", "org.ws", "gov.ws", "edu.ws", - "yt", "\u0627\u0645\u0627\u0631\u0627\u062a", "xn--mgbaam7a8h", "\u09ac\u09be\u0982\u09b2\u09be", @@ -5881,330 +4085,9 @@ final class TldPatterns { "\u0627\u0644\u064a\u0645\u0646", "xn--mgb2ddes", "xxx", - "biz.at", - "info.at", - "priv.at", - "co.ca", - "ae.org", - "ar.com", - "br.com", - "cn.com", - "com.de", - "de.com", - "eu.com", - "gb.com", - "gb.net", - "gr.com", - "hu.com", - "hu.net", - "jp.net", - "jpn.com", - "kr.com", - "no.com", - "qc.com", - "ru.com", - "sa.com", - "se.com", - "se.net", - "uk.com", - "uk.net", - "us.com", - "us.org", - "uy.com", - "za.com", - "operaunite.com", - "appspot.com", - "dreamhosters.com", - "iki.fi", - "c.la", - "za.net", - "za.org", - "co.nl", - "co.no", - "co.pl", - "dyndns-at-home.com", - "dyndns-at-work.com", - "dyndns-blog.com", - "dyndns-free.com", - "dyndns-home.com", - "dyndns-ip.com", - "dyndns-mail.com", - "dyndns-office.com", - "dyndns-pics.com", - "dyndns-remote.com", - "dyndns-server.com", - "dyndns-web.com", - "dyndns-wiki.com", - "dyndns-work.com", - "dyndns.biz", - "dyndns.info", - "dyndns.org", - "dyndns.tv", - "at-band-camp.net", - "ath.cx", - "barrel-of-knowledge.info", - "barrell-of-knowledge.info", - "better-than.tv", - "blogdns.com", - "blogdns.net", - "blogdns.org", - "blogsite.org", - "boldlygoingnowhere.org", - "broke-it.net", - "buyshouses.net", - "cechire.com", - "dnsalias.com", - "dnsalias.net", - "dnsalias.org", - "dnsdojo.com", - "dnsdojo.net", - "dnsdojo.org", - "does-it.net", - "doesntexist.com", - "doesntexist.org", - "dontexist.com", - "dontexist.net", - "dontexist.org", - "doomdns.com", - "doomdns.org", - "dvrdns.org", - "dyn-o-saur.com", - "dynalias.com", - "dynalias.net", - "dynalias.org", - "dynathome.net", - "dyndns.ws", - "endofinternet.net", - "endofinternet.org", - "endoftheinternet.org", - "est-a-la-maison.com", - "est-a-la-masion.com", - "est-le-patron.com", - "est-mon-blogueur.com", - "for-better.biz", - "for-more.biz", - "for-our.info", - "for-some.biz", - "for-the.biz", - "forgot.her.name", - "forgot.his.name", - "from-ak.com", - "from-al.com", - "from-ar.com", - "from-az.net", - "from-ca.com", - "from-co.net", - "from-ct.com", - "from-dc.com", - "from-de.com", - "from-fl.com", - "from-ga.com", - "from-hi.com", - "from-ia.com", - "from-id.com", - "from-il.com", - "from-in.com", - "from-ks.com", - "from-ky.com", - "from-la.net", - "from-ma.com", - "from-md.com", - "from-me.org", - "from-mi.com", - "from-mn.com", - "from-mo.com", - "from-ms.com", - "from-mt.com", - "from-nc.com", - "from-nd.com", - "from-ne.com", - "from-nh.com", - "from-nj.com", - "from-nm.com", - "from-nv.com", - "from-ny.net", - "from-oh.com", - "from-ok.com", - "from-or.com", - "from-pa.com", - "from-pr.com", - "from-ri.com", - "from-sc.com", - "from-sd.com", - "from-tn.com", - "from-tx.com", - "from-ut.com", - "from-va.com", - "from-vt.com", - "from-wa.com", - "from-wi.com", - "from-wv.com", - "from-wy.com", - "ftpaccess.cc", - "fuettertdasnetz.de", - "game-host.org", - "game-server.cc", - "getmyip.com", - "gets-it.net", - "go.dyndns.org", - "gotdns.com", - "gotdns.org", - "groks-the.info", - "groks-this.info", - "ham-radio-op.net", - "here-for-more.info", - "hobby-site.com", - "hobby-site.org", - "home.dyndns.org", - "homedns.org", - "homeftp.net", - "homeftp.org", - "homeip.net", - "homelinux.com", - "homelinux.net", - "homelinux.org", - "homeunix.com", - "homeunix.net", - "homeunix.org", - "iamallama.com", - "in-the-band.net", - "is-a-anarchist.com", - "is-a-blogger.com", - "is-a-bookkeeper.com", - "is-a-bruinsfan.org", - "is-a-bulls-fan.com", - "is-a-candidate.org", - "is-a-caterer.com", - "is-a-celticsfan.org", - "is-a-chef.com", - "is-a-chef.net", - "is-a-chef.org", - "is-a-conservative.com", - "is-a-cpa.com", - "is-a-cubicle-slave.com", - "is-a-democrat.com", - "is-a-designer.com", - "is-a-doctor.com", - "is-a-financialadvisor.com", - "is-a-geek.com", - "is-a-geek.net", - "is-a-geek.org", - "is-a-green.com", - "is-a-guru.com", - "is-a-hard-worker.com", - "is-a-hunter.com", - "is-a-knight.org", - "is-a-landscaper.com", - "is-a-lawyer.com", - "is-a-liberal.com", - "is-a-libertarian.com", - "is-a-linux-user.org", - "is-a-llama.com", - "is-a-musician.com", - "is-a-nascarfan.com", - "is-a-nurse.com", - "is-a-painter.com", - "is-a-patsfan.org", - "is-a-personaltrainer.com", - "is-a-photographer.com", - "is-a-player.com", - "is-a-republican.com", - "is-a-rockstar.com", - "is-a-socialist.com", - "is-a-soxfan.org", - "is-a-student.com", - "is-a-teacher.com", - "is-a-techie.com", - "is-a-therapist.com", - "is-an-accountant.com", - "is-an-actor.com", - "is-an-actress.com", - "is-an-anarchist.com", - "is-an-artist.com", - "is-an-engineer.com", - "is-an-entertainer.com", - "is-by.us", - "is-certified.com", - "is-found.org", - "is-gone.com", - "is-into-anime.com", - "is-into-cars.com", - "is-into-cartoons.com", - "is-into-games.com", - "is-leet.com", - "is-lost.org", - "is-not-certified.com", - "is-saved.org", - "is-slick.com", - "is-uberleet.com", - "is-very-bad.org", - "is-very-evil.org", - "is-very-good.org", - "is-very-nice.org", - "is-very-sweet.org", - "is-with-theband.com", - "isa-geek.com", - "isa-geek.net", - "isa-geek.org", - "isa-hockeynut.com", - "issmarterthanyou.com", - "isteingeek.de", - "istmein.de", - "kicks-ass.net", - "kicks-ass.org", - "knowsitall.info", - "land-4-sale.us", - "lebtimnetz.de", - "leitungsen.de", - "likes-pie.com", - "likescandy.com", - "merseine.nu", - "mine.nu", - "misconfused.org", - "mypets.ws", - "myphotos.cc", - "neat-url.com", - "office-on-the.net", - "on-the-web.tv", - "podzone.net", - "podzone.org", - "readmyblog.org", - "saves-the-whales.com", - "scrapper-site.net", - "scrapping.cc", - "selfip.biz", - "selfip.com", - "selfip.info", - "selfip.net", - "selfip.org", - "sells-for-less.com", - "sells-for-u.com", - "sells-it.net", - "sellsyourhome.org", - "servebbs.com", - "servebbs.net", - "servebbs.org", - "serveftp.net", - "serveftp.org", - "servegame.org", - "shacknet.nu", - "simple-url.com", - "space-to-rent.com", - "stuff-4-sale.org", - "stuff-4-sale.us", - "teaches-yoga.com", - "thruhere.net", - "traeumtgerade.de", - "webhop.biz", - "webhop.info", - "webhop.net", - "webhop.org", - "worse-than.tv", - "writesthisblog.com", "tp", - "ng", - "\u049b\u0430\u0437", - "xn--80ao21a" + "yt", + "ng" ); /** @@ -6212,8 +4095,9 @@ final class TldPatterns { * leftmost component results in a name which is contained in this * set, it is a TLD. */ - static final ImmutableSet<String> UNDER = ImmutableSet.of( + static final Set<String> UNDER = ImmutableSet.of( "ar", + "au", "bd", "bn", "ck", @@ -6226,12 +4110,59 @@ final class TldPatterns { "gu", "il", "jm", + "aichi.jp", + "akita.jp", + "aomori.jp", + "chiba.jp", + "ehime.jp", + "fukui.jp", + "fukuoka.jp", + "fukushima.jp", + "gifu.jp", + "gunma.jp", + "hiroshima.jp", + "hokkaido.jp", + "hyogo.jp", + "ibaraki.jp", + "ishikawa.jp", + "iwate.jp", + "kagawa.jp", + "kagoshima.jp", + "kanagawa.jp", "kawasaki.jp", "kitakyushu.jp", "kobe.jp", + "kochi.jp", + "kumamoto.jp", + "kyoto.jp", + "mie.jp", + "miyagi.jp", + "miyazaki.jp", + "nagano.jp", + "nagasaki.jp", "nagoya.jp", + "nara.jp", + "niigata.jp", + "oita.jp", + "okayama.jp", + "okinawa.jp", + "osaka.jp", + "saga.jp", + "saitama.jp", "sapporo.jp", "sendai.jp", + "shiga.jp", + "shimane.jp", + "shizuoka.jp", + "tochigi.jp", + "tokushima.jp", + "tokyo.jp", + "tottori.jp", + "toyama.jp", + "wakayama.jp", + "yamagata.jp", + "yamaguchi.jp", + "yamanashi.jp", "yokohama.jp", "ke", "kh", @@ -6245,13 +4176,15 @@ final class TldPatterns { "om", "pg", "py", + "qa", "sv", "tr", "uk", - "nhs.uk", - "police.uk", "sch.uk", + "uy", + "ve", "ye", + "yu", "za", "zm", "zw" @@ -6261,7 +4194,7 @@ final class TldPatterns { * The elements in this set would pass the UNDER test, but are * known not to be TLDs and are thus excluded from consideration. */ - static final ImmutableSet<String> EXCLUDED = ImmutableSet.of( + static final Set<String> EXCLUDED = ImmutableSet.of( "congresodelalengua3.ar", "educ.ar", "gobiernoelectronico.ar", @@ -6271,14 +4204,68 @@ final class TldPatterns { "promocion.ar", "retina.ar", "uba.ar", - "www.ck", - "www.gt", + "metro.tokyo.jp", + "pref.aichi.jp", + "pref.akita.jp", + "pref.aomori.jp", + "pref.chiba.jp", + "pref.ehime.jp", + "pref.fukui.jp", + "pref.fukuoka.jp", + "pref.fukushima.jp", + "pref.gifu.jp", + "pref.gunma.jp", + "pref.hiroshima.jp", + "pref.hokkaido.jp", + "pref.hyogo.jp", + "pref.ibaraki.jp", + "pref.ishikawa.jp", + "pref.iwate.jp", + "pref.kagawa.jp", + "pref.kagoshima.jp", + "pref.kanagawa.jp", + "pref.kochi.jp", + "pref.kumamoto.jp", + "pref.kyoto.jp", + "pref.mie.jp", + "pref.miyagi.jp", + "pref.miyazaki.jp", + "pref.nagano.jp", + "pref.nagasaki.jp", + "pref.nara.jp", + "pref.niigata.jp", + "pref.oita.jp", + "pref.okayama.jp", + "pref.okinawa.jp", + "pref.osaka.jp", + "pref.saga.jp", + "pref.saitama.jp", + "pref.shiga.jp", + "pref.shimane.jp", + "pref.shizuoka.jp", + "pref.tochigi.jp", + "pref.tokushima.jp", + "pref.tottori.jp", + "pref.toyama.jp", + "pref.wakayama.jp", + "pref.yamagata.jp", + "pref.yamaguchi.jp", + "pref.yamanashi.jp", + "city.chiba.jp", + "city.fukuoka.jp", + "city.hiroshima.jp", "city.kawasaki.jp", "city.kitakyushu.jp", "city.kobe.jp", + "city.kyoto.jp", "city.nagoya.jp", + "city.niigata.jp", + "city.okayama.jp", + "city.osaka.jp", + "city.saitama.jp", "city.sapporo.jp", "city.sendai.jp", + "city.shizuoka.jp", "city.yokohama.jp", "mediaphone.om", "nawrastelecom.om", @@ -6291,15 +4278,17 @@ final class TldPatterns { "songfest.om", "statecouncil.om", "nic.tr", + "tsk.tr", "bl.uk", "british-library.uk", + "icnet.uk", "jet.uk", - "mod.uk", - "national-library-scotland.uk", "nel.uk", - "nic.uk", + "nhs.uk", "nls.uk", - "parliament.uk" + "national-library-scotland.uk", + "parliament.uk", + "police.uk" ); } diff --git a/guava/src/com/google/common/net/package-info.java b/guava/src/com/google/common/net/package-info.java index 090a231..1cb9958 100644 --- a/guava/src/com/google/common/net/package-info.java +++ b/guava/src/com/google/common/net/package-info.java @@ -27,4 +27,3 @@ package com.google.common.net; import javax.annotation.ParametersAreNonnullByDefault; - diff --git a/guava/src/com/google/common/primitives/Booleans.java b/guava/src/com/google/common/primitives/Booleans.java index 3601a8a..ab333f1 100644 --- a/guava/src/com/google/common/primitives/Booleans.java +++ b/guava/src/com/google/common/primitives/Booleans.java @@ -37,10 +37,6 @@ import java.util.RandomAccess; * Static utility methods pertaining to {@code boolean} primitives, that are not * already found in either {@link Boolean} 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 */ @@ -407,8 +403,7 @@ public final class Booleans { @Override public Boolean set(int index, Boolean element) { checkElementIndex(index, size()); boolean 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; } @@ -459,7 +454,7 @@ public final class Booleans { } boolean[] toBooleanArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 int size = size(); boolean[] result = new boolean[size]; System.arraycopy(array, start, result, 0, size); diff --git a/guava/src/com/google/common/primitives/Bytes.java b/guava/src/com/google/common/primitives/Bytes.java index d9c5e1f..92f7805 100644 --- a/guava/src/com/google/common/primitives/Bytes.java +++ b/guava/src/com/google/common/primitives/Bytes.java @@ -38,10 +38,6 @@ import java.util.RandomAccess; * treat bytes as signed or unsigned are found in {@link SignedBytes} and {@link * UnsignedBytes}. * - * <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 */ @@ -214,21 +210,20 @@ public final class Bytes { } /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code byte} value in the manner of {@link Number#byteValue}. + * Copies a collection of {@code Byte} instances into a new array of + * primitive {@code byte} 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 Byte} 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<Byte>} before 12.0) */ - public static byte[] toArray(Collection<? extends Number> collection) { + public static byte[] toArray(Collection<Byte> collection) { if (collection instanceof ByteArrayAsList) { return ((ByteArrayAsList) collection).toByteArray(); } @@ -238,7 +233,7 @@ public final class Bytes { byte[] array = new byte[len]; for (int i = 0; i < len; i++) { // checkNotNull for GWT (do not optimize) - array[i] = ((Number) checkNotNull(boxedArray[i])).byteValue(); + array[i] = (Byte) checkNotNull(boxedArray[i]); } return array; } @@ -325,8 +320,7 @@ public final class Bytes { @Override public Byte set(int index, Byte element) { checkElementIndex(index, size()); byte 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; } @@ -377,7 +371,7 @@ public final class Bytes { } byte[] toByteArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 int size = size(); byte[] result = new byte[size]; System.arraycopy(array, start, result, 0, size); diff --git a/guava/src/com/google/common/primitives/Chars.java b/guava/src/com/google/common/primitives/Chars.java index 241814a..5ac57d1 100644 --- a/guava/src/com/google/common/primitives/Chars.java +++ b/guava/src/com/google/common/primitives/Chars.java @@ -40,10 +40,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 */ @@ -523,8 +519,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; } @@ -575,7 +570,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/src/com/google/common/primitives/Doubles.java b/guava/src/com/google/common/primitives/Doubles.java index 068fc9d..dc3ea64 100644 --- a/guava/src/com/google/common/primitives/Doubles.java +++ b/guava/src/com/google/common/primitives/Doubles.java @@ -23,9 +23,7 @@ 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.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.io.Serializable; import java.util.AbstractList; @@ -35,22 +33,15 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; /** * Static utility methods pertaining to {@code double} primitives, that are not * already found in either {@link Double} or {@link Arrays}. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained"> - * primitive utilities</a>. - * * @author Kevin Bourrillion * @since 1.0 */ -@GwtCompatible(emulated = true) +@GwtCompatible public final class Doubles { private Doubles() {} @@ -302,8 +293,8 @@ public final class Doubles { * 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"}. + * differently in GWT sometimes. In the previous example, it returns the string + * {@code "1-2-3"}. * * @param separator the text that should appear between consecutive values in * the resulting string (but not at the start or end) @@ -361,21 +352,20 @@ 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 Double} instances into a new array of + * primitive {@code double} 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 Double} 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) { + public static double[] toArray(Collection<Double> collection) { if (collection instanceof DoubleArrayAsList) { return ((DoubleArrayAsList) collection).toDoubleArray(); } @@ -385,7 +375,7 @@ public final class Doubles { double[] array = new double[len]; for (int i = 0; i < len; i++) { // checkNotNull for GWT (do not optimize) - array[i] = ((Number) checkNotNull(boxedArray[i])).doubleValue(); + array[i] = (Double) checkNotNull(boxedArray[i]); } return array; } @@ -475,8 +465,7 @@ public final class Doubles { @Override public Double set(int index, Double element) { checkElementIndex(index, size()); double 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 +516,7 @@ public final class Doubles { } double[] toDoubleArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 int size = size(); double[] result = new double[size]; System.arraycopy(array, start, result, 0, size); @@ -536,59 +525,4 @@ public final class Doubles { private static final long serialVersionUID = 0; } - - /** - * This is adapted from the regex suggested by {@link Double#valueOf(String)} - * for prevalidating inputs. All valid inputs must pass this regex, but it's - * semantically fine if not all inputs that pass this regex are valid -- - * only a performance hit is incurred, not a semantics bug. - */ - @GwtIncompatible("regular expressions") - static final Pattern FLOATING_POINT_PATTERN = fpPattern(); - - @GwtIncompatible("regular expressions") - private static Pattern fpPattern() { - String decimal = "(?:\\d++(?:\\.\\d*+)?|\\.\\d++)"; - String completeDec = decimal + "(?:[eE][+-]?\\d++)?[fFdD]?"; - String hex = "(?:\\p{XDigit}++(?:\\.\\p{XDigit}*+)?|\\.\\p{XDigit}++)"; - String completeHex = "0[xX]" + hex + "[pP][+-]?\\d++[fFdD]?"; - String fpPattern = "[+-]?(?:NaN|Infinity|" + completeDec + "|" + completeHex + ")"; - return Pattern.compile(fpPattern); - } - - /** - * Parses the specified string as a double-precision floating point value. - * The ASCII character {@code '-'} (<code>'\u002D'</code>) is recognized - * as the minus sign. - * - * <p>Unlike {@link Double#parseDouble(String)}, this method returns - * {@code null} instead of throwing an exception if parsing fails. - * Valid inputs are exactly those accepted by {@link Double#valueOf(String)}, - * except that leading and trailing whitespace is not permitted. - * - * <p>This implementation is likely to be faster than {@code - * Double.parseDouble} if many failures are expected. - * - * @param string the string representation of a {@code double} value - * @return the floating point value represented by {@code string}, or - * {@code null} if {@code string} has a length of zero or cannot be - * parsed as a {@code double} value - * @since 14.0 - */ - @GwtIncompatible("regular expressions") - @Nullable - @Beta - public static Double tryParse(String string) { - if (FLOATING_POINT_PATTERN.matcher(string).matches()) { - // TODO(user): could be potentially optimized, but only with - // extensive testing - try { - return Double.parseDouble(string); - } catch (NumberFormatException e) { - // Double.parseDouble has changed specs several times, so fall through - // gracefully - } - } - return null; - } } diff --git a/guava/src/com/google/common/primitives/Floats.java b/guava/src/com/google/common/primitives/Floats.java index 03a1ed1..ffc932f 100644 --- a/guava/src/com/google/common/primitives/Floats.java +++ b/guava/src/com/google/common/primitives/Floats.java @@ -23,9 +23,7 @@ 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.Beta; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.GwtIncompatible; import java.io.Serializable; import java.util.AbstractList; @@ -36,20 +34,14 @@ import java.util.Comparator; import java.util.List; import java.util.RandomAccess; -import javax.annotation.Nullable; - /** * 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) +@GwtCompatible public final class Floats { private Floats() {} @@ -357,21 +349,20 @@ public final class Floats { } /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code float} value in the manner of {@link Number#floatValue}. + * Copies a collection of {@code Float} instances into a new array of + * primitive {@code float} 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 Float} 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<Float>} before 12.0) */ - public static float[] toArray(Collection<? extends Number> collection) { + public static float[] toArray(Collection<Float> collection) { if (collection instanceof FloatArrayAsList) { return ((FloatArrayAsList) collection).toFloatArray(); } @@ -381,7 +372,7 @@ public final class Floats { 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(); + array[i] = (Float) checkNotNull(boxedArray[i]); } return array; } @@ -471,8 +462,7 @@ public final class Floats { @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); + array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize) return oldValue; } @@ -523,7 +513,7 @@ public final class Floats { } float[] toFloatArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 int size = size(); float[] result = new float[size]; System.arraycopy(array, start, result, 0, size); @@ -532,40 +522,4 @@ public final class Floats { private static final long serialVersionUID = 0; } - - /** - * Parses the specified string as a single-precision floating point value. - * The ASCII character {@code '-'} (<code>'\u002D'</code>) is recognized - * as the minus sign. - * - * <p>Unlike {@link Float#parseFloat(String)}, this method returns - * {@code null} instead of throwing an exception if parsing fails. - * Valid inputs are exactly those accepted by {@link Float#valueOf(String)}, - * except that leading and trailing whitespace is not permitted. - * - * <p>This implementation is likely to be faster than {@code - * Float.parseFloat} if many failures are expected. - * - * @param string the string representation of a {@code float} value - * @return the floating point value represented by {@code string}, or - * {@code null} if {@code string} has a length of zero or cannot be - * parsed as a {@code float} value - * @since 14.0 - */ - @GwtIncompatible("regular expressions") - @Nullable - @Beta - public static Float tryParse(String string) { - if (Doubles.FLOATING_POINT_PATTERN.matcher(string).matches()) { - // TODO(user): could be potentially optimized, but only with - // extensive testing - try { - return Float.parseFloat(string); - } catch (NumberFormatException e) { - // Float.parseFloat has changed specs several times, so fall through - // gracefully - } - } - return null; - } } diff --git a/guava/src/com/google/common/primitives/Ints.java b/guava/src/com/google/common/primitives/Ints.java index 966066d..bc7acb8 100644 --- a/guava/src/com/google/common/primitives/Ints.java +++ b/guava/src/com/google/common/primitives/Ints.java @@ -40,10 +40,6 @@ import javax.annotation.CheckForNull; * 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 */ @@ -420,21 +416,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(); } @@ -444,7 +439,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; } @@ -531,8 +526,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; } @@ -583,7 +577,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/src/com/google/common/primitives/Longs.java b/guava/src/com/google/common/primitives/Longs.java index 9460316..99c7033 100644 --- a/guava/src/com/google/common/primitives/Longs.java +++ b/guava/src/com/google/common/primitives/Longs.java @@ -21,8 +21,8 @@ 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 com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; import java.io.Serializable; import java.util.AbstractList; @@ -37,14 +37,10 @@ import java.util.RandomAccess; * Static utility methods pertaining to {@code long} primitives, that are not * already found in either {@link Long} 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 +@GwtCompatible(emulated = true) public final class Longs { private Longs() {} @@ -258,15 +254,17 @@ public final class Longs { * {@link com.google.common.io.ByteStreams#newDataOutput()} to get a growable * buffer. */ + @GwtIncompatible("doesn't work") public static byte[] toByteArray(long value) { - // Note that this code needs to stay compatible with GWT, which has known - // bugs when narrowing byte casts of long values occur. - byte[] result = new byte[8]; - for (int i = 7; i >= 0; i--) { - result[i] = (byte) (value & 0xffL); - value >>= 8; - } - return result; + return new byte[] { + (byte) (value >> 56), + (byte) (value >> 48), + (byte) (value >> 40), + (byte) (value >> 32), + (byte) (value >> 24), + (byte) (value >> 16), + (byte) (value >> 8), + (byte) value}; } /** @@ -282,6 +280,7 @@ public final class Longs { * @throws IllegalArgumentException if {@code bytes} has fewer than 8 * elements */ + @GwtIncompatible("doesn't work") public static long fromByteArray(byte[] bytes) { checkArgument(bytes.length >= BYTES, "array too small: %s < %s", bytes.length, BYTES); @@ -296,6 +295,7 @@ public final class Longs { * * @since 7.0 */ + @GwtIncompatible("doesn't work") public static long fromBytes(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7, byte b8) { return (b1 & 0xFFL) << 56 @@ -309,60 +309,6 @@ public final class Longs { } /** - * Parses the specified string as a signed decimal long value. The ASCII - * character {@code '-'} (<code>'\u002D'</code>) is recognized as the - * minus sign. - * - * <p>Unlike {@link Long#parseLong(String)}, this method returns - * {@code null} instead of throwing an exception if parsing fails. - * - * <p>Note that strings prefixed with ASCII {@code '+'} are rejected, even - * under JDK 7, despite the change to {@link Long#parseLong(String)} for - * that version. - * - * @param string the string representation of a long value - * @return the long value represented by {@code string}, or {@code null} if - * {@code string} has a length of zero or cannot be parsed as a long - * value - * @since 14.0 - */ - @Beta - public static Long tryParse(String string) { - if (checkNotNull(string).isEmpty()) { - return null; - } - boolean negative = string.charAt(0) == '-'; - int index = negative ? 1 : 0; - if (index == string.length()) { - return null; - } - int digit = string.charAt(index++) - '0'; - if (digit < 0 || digit > 9) { - return null; - } - long accum = -digit; - while (index < string.length()) { - digit = string.charAt(index++) - '0'; - if (digit < 0 || digit > 9 || accum < Long.MIN_VALUE / 10) { - return null; - } - accum *= 10; - if (accum < Long.MIN_VALUE + digit) { - return null; - } - accum -= digit; - } - - if (negative) { - return accum; - } else if (accum == Long.MIN_VALUE) { - return null; - } else { - return -accum; - } - } - - /** * 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. @@ -455,21 +401,20 @@ public final class Longs { } /** - * Returns an array containing each value of {@code collection}, converted to - * a {@code long} value in the manner of {@link Number#longValue}. + * 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<Long>} before 12.0) */ - public static long[] toArray(Collection<? extends Number> collection) { + public static long[] toArray(Collection<Long> collection) { if (collection instanceof LongArrayAsList) { return ((LongArrayAsList) collection).toLongArray(); } @@ -479,7 +424,7 @@ public final class Longs { long[] array = new long[len]; for (int i = 0; i < len; i++) { // checkNotNull for GWT (do not optimize) - array[i] = ((Number) checkNotNull(boxedArray[i])).longValue(); + array[i] = (Long) checkNotNull(boxedArray[i]); } return array; } @@ -566,8 +511,7 @@ public final class Longs { @Override public Long set(int index, Long element) { checkElementIndex(index, size()); long 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; } @@ -618,7 +562,7 @@ public final class Longs { } long[] toLongArray() { - // Arrays.copyOfRange() is not available under GWT + // Arrays.copyOfRange() requires Java 6 int size = size(); long[] result = new long[size]; System.arraycopy(array, start, result, 0, size); diff --git a/guava/src/com/google/common/primitives/ParseRequest.java b/guava/src/com/google/common/primitives/ParseRequest.java deleted file mode 100644 index 98f29b4..0000000 --- a/guava/src/com/google/common/primitives/ParseRequest.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.primitives; - -import com.google.common.annotations.GwtCompatible; - -/** - * A string to be parsed as a number and the radix to interpret it in. - */ -@GwtCompatible -final class ParseRequest { - final String rawValue; - final int radix; - - private ParseRequest(String rawValue, int radix) { - this.rawValue = rawValue; - this.radix = radix; - } - - static ParseRequest fromString(String stringValue) { - if (stringValue.length() == 0) { - throw new NumberFormatException("empty string"); - } - - // Handle radix specifier if present - String rawValue; - int radix; - char firstChar = stringValue.charAt(0); - if (stringValue.startsWith("0x") || stringValue.startsWith("0X")) { - rawValue = stringValue.substring(2); - radix = 16; - } else if (firstChar == '#') { - rawValue = stringValue.substring(1); - radix = 16; - } else if (firstChar == '0' && stringValue.length() > 1) { - rawValue = stringValue.substring(1); - radix = 8; - } else { - rawValue = stringValue; - radix = 10; - } - - return new ParseRequest(rawValue, radix); - } -} diff --git a/guava/src/com/google/common/primitives/Shorts.java b/guava/src/com/google/common/primitives/Shorts.java index db3d206..d5859b3 100644 --- a/guava/src/com/google/common/primitives/Shorts.java +++ b/guava/src/com/google/common/primitives/Shorts.java @@ -37,10 +37,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 */ @@ -418,21 +414,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(); } @@ -442,7 +437,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; } @@ -529,8 +524,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; } @@ -581,7 +575,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/src/com/google/common/primitives/SignedBytes.java b/guava/src/com/google/common/primitives/SignedBytes.java index 07e3bda..00a36d8 100644 --- a/guava/src/com/google/common/primitives/SignedBytes.java +++ b/guava/src/com/google/common/primitives/SignedBytes.java @@ -28,10 +28,6 @@ import java.util.Comparator; * interpret values as signed. The corresponding methods that treat the values * as unsigned are found in {@link UnsignedBytes}, and the methods for which * signedness is not an issue are in {@link Bytes}. - * - * <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 diff --git a/guava/src/com/google/common/primitives/UnsignedBytes.java b/guava/src/com/google/common/primitives/UnsignedBytes.java index c503f99..1651295 100644 --- a/guava/src/com/google/common/primitives/UnsignedBytes.java +++ b/guava/src/com/google/common/primitives/UnsignedBytes.java @@ -19,12 +19,16 @@ package com.google.common.primitives; 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.VisibleForTesting; -import sun.misc.Unsafe; +// BEGIN android-changed +//import sun.misc.Unsafe; +// END android-changed +import java.lang.reflect.Field; import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Comparator; /** @@ -34,35 +38,20 @@ import java.util.Comparator; * the values as signed are found in {@link SignedBytes}, and the methods for * which signedness is not an issue are in {@link Bytes}. * - * <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 * @author Martin Buchholz * @author Hiroshi Yamauchi - * @author Louis Wasserman * @since 1.0 */ public final class UnsignedBytes { private UnsignedBytes() {} /** - * The largest power of two that can be represented as an unsigned {@code - * byte}. + * The largest power of two that can be represented as an unsigned {@code byte}. * * @since 10.0 */ - public static final byte MAX_POWER_OF_TWO = (byte) 0x80; - - /** - * The largest value that fits into an unsigned byte. - * - * @since 13.0 - */ - public static final byte MAX_VALUE = (byte) 0xFF; - - private static final int UNSIGNED_MASK = 0xFF; + public static final byte MAX_POWER_OF_TWO = (byte) (1 << 7); /** * Returns the value of the given byte as an integer, when treated as @@ -72,7 +61,7 @@ public final class UnsignedBytes { * @since 6.0 */ public static int toInt(byte value) { - return value & UNSIGNED_MASK; + return value & 0xFF; } /** @@ -86,7 +75,7 @@ public final class UnsignedBytes { * than 255 */ public static byte checkedCast(long value) { - checkArgument(value >> Byte.SIZE == 0, "out of range: %s", value); + checkArgument(value >> 8 == 0, "out of range: %s", value); return (byte) value; } @@ -99,8 +88,8 @@ public final class UnsignedBytes { * {@code value <= 0}, and {@code value} cast to {@code byte} otherwise */ public static byte saturatedCast(long value) { - if (value > toInt(MAX_VALUE)) { - return MAX_VALUE; // -1 + if (value > 255) { + return (byte) 255; // -1 } if (value < 0) { return (byte) 0; @@ -164,71 +153,6 @@ public final class UnsignedBytes { } /** - * Returns a string representation of x, where x is treated as unsigned. - * - * @since 13.0 - */ - @Beta - public static String toString(byte x) { - return toString(x, 10); - } - - /** - * Returns a string representation of {@code x} for the given radix, where {@code x} is treated - * as unsigned. - * - * @param x the value to convert to a string. - * @param radix the radix to use while working with {@code x} - * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} - * and {@link Character#MAX_RADIX}. - * @since 13.0 - */ - @Beta - public static String toString(byte x, int radix) { - checkArgument(radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX, - "radix (%s) must be between Character.MIN_RADIX and Character.MAX_RADIX", radix); - // Benchmarks indicate this is probably not worth optimizing. - return Integer.toString(toInt(x), radix); - } - - /** - * Returns the unsigned {@code byte} value represented by the given decimal string. - * - * @throws NumberFormatException if the string does not contain a valid unsigned {@code byte} - * value - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Byte#parseByte(String)}) - * @since 13.0 - */ - @Beta - public static byte parseUnsignedByte(String string) { - return parseUnsignedByte(string, 10); - } - - /** - * Returns the unsigned {@code byte} value represented by a string with the given radix. - * - * @param string the string containing the unsigned {@code byte} representation to be parsed. - * @param radix the radix to use while parsing {@code string} - * @throws NumberFormatException if the string does not contain a valid unsigned {@code byte} - * with the given radix, or if {@code radix} is not between {@link Character#MIN_RADIX} - * and {@link Character#MAX_RADIX}. - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Byte#parseByte(String)}) - * @since 13.0 - */ - @Beta - public static byte parseUnsignedByte(String string, int radix) { - int parse = Integer.parseInt(checkNotNull(string), radix); - // We need to throw a NumberFormatException, so we have to duplicate checkedCast. =( - if (parse >> Byte.SIZE == 0) { - return (byte) parse; - } else { - throw new NumberFormatException("out of range: " + parse); - } - } - - /** * Returns a string containing the supplied {@code byte} values separated by * {@code separator}. For example, {@code join(":", (byte) 1, (byte) 2, * (byte) 255)} returns the string {@code "1:2:255"}. @@ -244,10 +168,10 @@ public final class UnsignedBytes { } // For pre-sizing a builder, just get the right order of magnitude - StringBuilder builder = new StringBuilder(array.length * (3 + separator.length())); + StringBuilder builder = new StringBuilder(array.length * 5); builder.append(toInt(array[0])); for (int i = 1; i < array.length; i++) { - builder.append(separator).append(toString(array[i])); + builder.append(separator).append(toInt(array[i])); } return builder.toString(); } @@ -289,133 +213,123 @@ public final class UnsignedBytes { static final String UNSAFE_COMPARATOR_NAME = LexicographicalComparatorHolder.class.getName() + "$UnsafeComparator"; - static final Comparator<byte[]> BEST_COMPARATOR = getBestComparator(); - - @VisibleForTesting - enum UnsafeComparator implements Comparator<byte[]> { - INSTANCE; - - static final boolean littleEndian = - ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); - - /* - * The following static final fields exist for performance reasons. - * - * In UnsignedBytesBenchmark, accessing the following objects via static - * final fields is the fastest (more than twice as fast as the Java - * implementation, vs ~1.5x with non-final static fields, on x86_32) - * under the Hotspot server compiler. The reason is obviously that the - * non-final fields need to be reloaded inside the loop. - * - * And, no, defining (final or not) local variables out of the loop still - * isn't as good because the null check on the theUnsafe object remains - * inside the loop and BYTE_ARRAY_BASE_OFFSET doesn't get - * constant-folded. - * - * The compiler can treat static final fields as compile-time constants - * and can constant-fold them while (final or not) local variables are - * run time values. - */ - - static final Unsafe theUnsafe; - - /** The offset to the first element in a byte array. */ - static final int BYTE_ARRAY_BASE_OFFSET; - - static { - theUnsafe = getUnsafe(); - - BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); - - // sanity check - this should never fail - if (theUnsafe.arrayIndexScale(byte[].class) != 1) { - throw new AssertionError(); - } - } - - /** - * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. - * Replace with a simple call to Unsafe.getUnsafe when integrating - * into a jdk. - * - * @return a sun.misc.Unsafe - */ - private static sun.misc.Unsafe getUnsafe() { - try { - return sun.misc.Unsafe.getUnsafe(); - } catch (SecurityException tryReflectionInstead) {} - try { - return java.security.AccessController.doPrivileged - (new java.security.PrivilegedExceptionAction<sun.misc.Unsafe>() { - public sun.misc.Unsafe run() throws Exception { - Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class; - for (java.lang.reflect.Field f : k.getDeclaredFields()) { - f.setAccessible(true); - Object x = f.get(null); - if (k.isInstance(x)) - return k.cast(x); - } - throw new NoSuchFieldError("the Unsafe"); - }}); - } catch (java.security.PrivilegedActionException e) { - throw new RuntimeException("Could not initialize intrinsics", - e.getCause()); - } - } - - @Override public int compare(byte[] left, byte[] right) { - int minLength = Math.min(left.length, right.length); - int minWords = minLength / Longs.BYTES; - - /* - * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a - * time is no slower than comparing 4 bytes at a time even on 32-bit. - * On the other hand, it is substantially faster on 64-bit. - */ - for (int i = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) { - long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i); - long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); - long diff = lw ^ rw; - - if (diff != 0) { - if (!littleEndian) { - return UnsignedLongs.compare(lw, rw); - } - - // Use binary search - int n = 0; - int y; - int x = (int) diff; - if (x == 0) { - x = (int) (diff >>> 32); - n = 32; - } - - y = x << 16; - if (y == 0) { - n += 16; - } else { - x = y; - } - - y = x << 8; - if (y == 0) { - n += 8; - } - return (int) (((lw >>> n) & UNSIGNED_MASK) - ((rw >>> n) & UNSIGNED_MASK)); - } - } - - // The epilogue to cover the last (minLength % 8) elements. - for (int i = minWords * Longs.BYTES; i < minLength; i++) { - int result = UnsignedBytes.compare(left[i], right[i]); - if (result != 0) { - return result; - } - } - return left.length - right.length; - } - } + // BEGIN android-changed + + static final Comparator<byte[]> BEST_COMPARATOR = lexicographicalComparatorJavaImpl(); + + // @VisibleForTesting + // enum UnsafeComparator implements Comparator<byte[]> { + // INSTANCE; + + // static final boolean littleEndian = + // ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); + + // /* + // * The following static final fields exist for performance reasons. + // * + // * In UnsignedBytesBenchmark, accessing the following objects via static + // * final fields is the fastest (more than twice as fast as the Java + // * implementation, vs ~1.5x with non-final static fields, on x86_32) + // * under the Hotspot server compiler. The reason is obviously that the + // * non-final fields need to be reloaded inside the loop. + // * + // * And, no, defining (final or not) local variables out of the loop still + // * isn't as good because the null check on the theUnsafe object remains + // * inside the loop and BYTE_ARRAY_BASE_OFFSET doesn't get + // * constant-folded. + // * + // * The compiler can treat static final fields as compile-time constants + // * and can constant-fold them while (final or not) local variables are + // * run time values. + // */ + + // static final Unsafe theUnsafe; + + // /** The offset to the first element in a byte array. */ + // static final int BYTE_ARRAY_BASE_OFFSET; + + // static { + // theUnsafe = (Unsafe) AccessController.doPrivileged( + // new PrivilegedAction<Object>() { + // @Override + // public Object run() { + // try { + // Field f = Unsafe.class.getDeclaredField("theUnsafe"); + // f.setAccessible(true); + // return f.get(null); + // } catch (NoSuchFieldException e) { + // // It doesn't matter what we throw; + // // it's swallowed in getBestComparator(). + // throw new Error(); + // } catch (IllegalAccessException e) { + // throw new Error(); + // } + // } + // }); + + // BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + + // // sanity check - this should never fail + // if (theUnsafe.arrayIndexScale(byte[].class) != 1) { + // throw new AssertionError(); + // } + // } + + // @Override public int compare(byte[] left, byte[] right) { + // int minLength = Math.min(left.length, right.length); + // int minWords = minLength / Longs.BYTES; + + // /* + // * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a + // * time is no slower than comparing 4 bytes at a time even on 32-bit. + // * On the other hand, it is substantially faster on 64-bit. + // */ + // for (int i = 0; i < minWords * Longs.BYTES; i += Longs.BYTES) { + // long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i); + // long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i); + // long diff = lw ^ rw; + + // if (diff != 0) { + // if (!littleEndian) { + // return UnsignedLongs.compare(lw, rw); + // } + + // // Use binary search + // int n = 0; + // int y; + // int x = (int) diff; + // if (x == 0) { + // x = (int) (diff >>> 32); + // n = 32; + // } + + // y = x << 16; + // if (y == 0) { + // n += 16; + // } else { + // x = y; + // } + + // y = x << 8; + // if (y == 0) { + // n += 8; + // } + // return (int) (((lw >>> n) & 0xFFL) - ((rw >>> n) & 0xFFL)); + // } + // } + + // // The epilogue to cover the last (minLength % 8) elements. + // for (int i = minWords * Longs.BYTES; i < minLength; i++) { + // int result = UnsignedBytes.compare(left[i], right[i]); + // if (result != 0) { + // return result; + // } + // } + // return left.length - right.length; + // } + // } + + // END android-changed enum PureJavaComparator implements Comparator<byte[]> { INSTANCE; @@ -432,22 +346,28 @@ public final class UnsignedBytes { } } - /** - * Returns the Unsafe-using Comparator, or falls back to the pure-Java - * implementation if unable to do so. - */ - static Comparator<byte[]> getBestComparator() { - try { - Class<?> theClass = Class.forName(UNSAFE_COMPARATOR_NAME); - - // yes, UnsafeComparator does implement Comparator<byte[]> - @SuppressWarnings("unchecked") - Comparator<byte[]> comparator = - (Comparator<byte[]>) theClass.getEnumConstants()[0]; - return comparator; - } catch (Throwable t) { // ensure we really catch *everything* - return lexicographicalComparatorJavaImpl(); - } - } + // BEGIN android-changed + + // /** + // * Returns the Unsafe-using Comparator, or falls back to the pure-Java + // * implementation if unable to do so. + // */ + // static Comparator<byte[]> getBestComparator() { + // try { + // Class<?> theClass = Class.forName(UNSAFE_COMPARATOR_NAME); + + // // yes, UnsafeComparator does implement Comparator<byte[]> + // @SuppressWarnings("unchecked") + // Comparator<byte[]> comparator = + // (Comparator<byte[]>) theClass.getEnumConstants()[0]; + // return comparator; + // } catch (Throwable t) { // ensure we really catch *everything* + // return lexicographicalComparatorJavaImpl(); + // } + // } + + // END android-changed + } } + diff --git a/guava/src/com/google/common/primitives/UnsignedInteger.java b/guava/src/com/google/common/primitives/UnsignedInteger.java index 88d89b5..d64ff88 100644 --- a/guava/src/com/google/common/primitives/UnsignedInteger.java +++ b/guava/src/com/google/common/primitives/UnsignedInteger.java @@ -1,11 +1,11 @@ /* * 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 @@ -20,28 +20,24 @@ 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 com.google.common.annotations.GwtIncompatible; - import java.math.BigInteger; -import javax.annotation.CheckReturnValue; import javax.annotation.Nullable; +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + /** * 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 */ +@Beta @GwtCompatible(emulated = true) public final class UnsignedInteger extends Number implements Comparable<UnsignedInteger> { public static final UnsignedInteger ZERO = asUnsigned(0); @@ -51,37 +47,15 @@ public final class UnsignedInteger extends Number implements Comparable<Unsigned 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); + return new UnsignedInteger(value); } /** @@ -91,26 +65,26 @@ public final class UnsignedInteger extends Number implements Comparable<Unsigned 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); + return asUnsigned((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()); + return asUnsigned(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 */ @@ -121,139 +95,62 @@ public final class UnsignedInteger extends Number implements Comparable<Unsigned /** * 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)); + return asUnsigned(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); + checkNotNull(val); + return asUnsigned(this.value + 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); + checkNotNull(val); + return asUnsigned(this.value - val.value); } /** * Returns the result of multiplying this and {@code val}. If the result would have more than 32 * bits, returns the low 32 bits of the result. - * - * @deprecated Use {@link #times(UnsignedInteger)}. This method is scheduled to be removed in - * Guava release 15.0. */ - @Deprecated - @Beta @GwtIncompatible("Does not truncate correctly") public UnsignedInteger multiply(UnsignedInteger val) { - return times(val); - } - - /** - * Returns the result of multiplying 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 - @GwtIncompatible("Does not truncate correctly") - public UnsignedInteger times(UnsignedInteger val) { - // TODO(user): make this GWT-compatible - return fromIntBits(value * checkNotNull(val).value); + checkNotNull(val); + return asUnsigned(value * 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)); + checkNotNull(val); + return asUnsigned(UnsignedInts.divide(value, 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)); + checkNotNull(val); + return asUnsigned(UnsignedInts.remainder(value, val.value)); } /** * Returns the value of this {@code UnsignedInteger} as an {@code int}. This is an inverse - * operation to {@link #fromIntBits}. - * + * operation to {@link #asUnsigned}. + * * <p>Note that if this {@code UnsignedInteger} holds a value {@code >= 2^31}, the returned value * will be equal to {@code this - 2^32}. */ diff --git a/guava/src/com/google/common/primitives/UnsignedInts.java b/guava/src/com/google/common/primitives/UnsignedInts.java index 762b841..222d9f3 100644 --- a/guava/src/com/google/common/primitives/UnsignedInts.java +++ b/guava/src/com/google/common/primitives/UnsignedInts.java @@ -1,11 +1,11 @@ /* * 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 @@ -17,30 +17,26 @@ package com.google.common.primitives; 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 java.util.Comparator; +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + /** * Static utility methods pertaining to {@code int} primitives that interpret values as * <i>unsigned</i> (that is, any negative value {@code x} is treated as the positive value * {@code 2^32 + x}). The methods for which signedness is not an issue are in {@link Ints}, as well * as signed versions of methods for which signedness is an issue. - * + * * <p>In addition, this class provides several static methods for converting an {@code int} to a * {@code String} and a {@code String} to an {@code int} that treat the {@code int} as an unsigned * number. - * + * * <p>Users of these utilities must be <i>extremely careful</i> not to mix up signed and unsigned * {@code int} values. When possible, it is recommended that the {@link UnsignedInteger} wrapper * class be used, at a small efficiency penalty, to enforce the distinction in the type system. - * - * <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 */ @@ -58,7 +54,7 @@ public final class UnsignedInts { /** * Compares the two specified {@code int} values, treating them as unsigned values between * {@code 0} and {@code 2^32 - 1} inclusive. - * + * * @param a the first unsigned {@code int} to compare * @param b the second unsigned {@code int} to compare * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is @@ -77,7 +73,7 @@ public final class UnsignedInts { /** * Returns the least value present in {@code array}, treating values as unsigned. - * + * * @param array a <i>nonempty</i> array of unsigned {@code int} values * @return the value present in {@code array} that is less than or equal to every other value in * the array according to {@link #compare} @@ -97,7 +93,7 @@ public final class UnsignedInts { /** * Returns the greatest value present in {@code array}, treating values as unsigned. - * + * * @param array a <i>nonempty</i> array of unsigned {@code int} values * @return the value present in {@code array} that is greater than or equal to every other value * in the array according to {@link #compare} @@ -118,7 +114,7 @@ public final class UnsignedInts { /** * Returns a string containing the supplied unsigned {@code int} values separated by * {@code separator}. For example, {@code join("-", 1, 2, 3)} 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 unsigned {@code int} values, possibly empty @@ -131,7 +127,7 @@ public final class UnsignedInts { // For pre-sizing a builder, just get the right order of magnitude StringBuilder builder = new StringBuilder(array.length * 5); - builder.append(toString(array[0])); + builder.append(array[0]); for (int i = 1; i < array.length; i++) { builder.append(separator).append(toString(array[i])); } @@ -143,10 +139,10 @@ public final class UnsignedInts { * That is, it compares, using {@link #compare(int, int)}), 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] < [1, 2] < [2] < [1 << 31]}. - * + * * <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(int[], int[])}. - * + * * @see <a href="http://en.wikipedia.org/wiki/Lexicographical_order"> Lexicographical order * article at Wikipedia</a> */ @@ -172,7 +168,7 @@ public final class UnsignedInts { /** * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 32-bit * quantities. - * + * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) * @throws ArithmeticException if divisor is 0 @@ -184,7 +180,7 @@ public final class UnsignedInts { /** * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 32-bit * quantities. - * + * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) * @throws ArithmeticException if divisor is 0 @@ -194,39 +190,11 @@ public final class UnsignedInts { } /** - * Returns the unsigned {@code int} value represented by the given string. - * - * Accepts a decimal, hexadecimal, or octal number given by specifying the following prefix: - * - * <ul> - * <li>{@code 0x}<i>HexDigits</i> - * <li>{@code 0X}<i>HexDigits</i> - * <li>{@code #}<i>HexDigits</i> - * <li>{@code 0}<i>OctalDigits</i> - * </ul> - * - * @throws NumberFormatException if the string does not contain a valid unsigned {@code int} value - * @since 13.0 - */ - public static int decode(String stringValue) { - ParseRequest request = ParseRequest.fromString(stringValue); - - try { - return parseUnsignedInt(request.rawValue, request.radix); - } catch (NumberFormatException e) { - NumberFormatException decodeException = - new NumberFormatException("Error parsing value: " + stringValue); - decodeException.initCause(e); - throw decodeException; - } - } - - /** * Returns the unsigned {@code int} value represented by the given decimal string. - * - * @throws NumberFormatException if the string does not contain a valid unsigned {@code int} value - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Integer#parseInt(String)}) + * + * @throws NumberFormatException if the string does not contain a valid unsigned integer, or if + * the value represented is too large to fit in an unsigned {@code int}. + * @throws NullPointerException if {@code s} is null */ public static int parseUnsignedInt(String s) { return parseUnsignedInt(s, 10); @@ -234,14 +202,12 @@ public final class UnsignedInts { /** * Returns the unsigned {@code int} value represented by a string with the given radix. - * + * * @param string the string containing the unsigned integer representation to be parsed. * @param radix the radix to use while parsing {@code s}; must be between * {@link Character#MIN_RADIX} and {@link Character#MAX_RADIX}. * @throws NumberFormatException if the string does not contain a valid unsigned {@code int}, or * if supplied radix is invalid. - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Integer#parseInt(String)}) */ public static int parseUnsignedInt(String string, int radix) { checkNotNull(string); @@ -263,7 +229,7 @@ public final class UnsignedInts { /** * Returns a string representation of {@code x} for the given radix, where {@code x} is treated * as unsigned. - * + * * @param x the value to convert to a string. * @param radix the radix to use while working with {@code x} * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} diff --git a/guava/src/com/google/common/primitives/UnsignedLong.java b/guava/src/com/google/common/primitives/UnsignedLong.java index a48de6b..62f7f81 100644 --- a/guava/src/com/google/common/primitives/UnsignedLong.java +++ b/guava/src/com/google/common/primitives/UnsignedLong.java @@ -17,31 +17,30 @@ package com.google.common.primitives; 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.io.Serializable; import java.math.BigInteger; -import javax.annotation.CheckReturnValue; import javax.annotation.Nullable; +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + /** * A wrapper class for unsigned {@code long} 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 long} values as unsigned, using the methods from {@link UnsignedLongs}. * - * <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>. + * <p><b>Please do not extend this class; it will be made final in the near future.</b> * * @author Louis Wasserman * @author Colin Evans * @since 11.0 */ +@Beta @GwtCompatible(serializable = true) -public final class UnsignedLong extends Number implements Comparable<UnsignedLong>, Serializable { +public class UnsignedLong extends Number implements Comparable<UnsignedLong>, Serializable { + // TODO(user): make final as soon as util.UnsignedLong is migrated over private static final long UNSIGNED_MASK = 0x7fffffffffffffffL; @@ -51,7 +50,7 @@ public final class UnsignedLong extends Number implements Comparable<UnsignedLon private final long value; - private UnsignedLong(long value) { + protected UnsignedLong(long value) { this.value = value; } @@ -61,49 +60,14 @@ public final class UnsignedLong extends Number implements Comparable<UnsignedLon * * <p>Put another way, if {@code value} is negative, the returned result will be equal to * {@code 2^64 + value}; otherwise, the returned result will be equal to {@code value}. - * - * @deprecated Use {@link #fromLongBits(long)}. This method is scheduled to be removed in Guava - * release 15.0. */ - @Deprecated - @Beta public static UnsignedLong asUnsigned(long value) { - return fromLongBits(value); - } - - /** - * Returns an {@code UnsignedLong} corresponding to a given bit representation. - * The argument is interpreted as an unsigned 64-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^64 + bits}. - * - * <p>To represent decimal constants less than {@code 2^63}, consider {@link #valueOf(long)} - * instead. - * - * @since 14.0 - */ - public static UnsignedLong fromLongBits(long bits) { - // TODO(user): consider caching small values, like Long.valueOf - return new UnsignedLong(bits); + return new UnsignedLong(value); } /** - * Returns an {@code UnsignedLong} representing the same value as the specified {@code long}. - * - * @throws IllegalArgumentException if {@code value} is negative - * @since 14.0 - */ - public static UnsignedLong valueOf(long value) { - checkArgument(value >= 0, - "value (%s) is outside the range for an unsigned long value", value); - return fromLongBits(value); - } - - /** - * Returns a {@code UnsignedLong} representing the same value as the specified - * {@code BigInteger}. This is the inverse operation of {@link #bigIntegerValue()}. + * Returns a {@code UnsignedLong} representing the same value as the specified {@code BigInteger} + * . This is the inverse operation of {@link #bigIntegerValue()}. * * @throws IllegalArgumentException if {@code value} is negative or {@code value >= 2^64} */ @@ -111,7 +75,7 @@ public final class UnsignedLong extends Number implements Comparable<UnsignedLon checkNotNull(value); checkArgument(value.signum() >= 0 && value.bitLength() <= Long.SIZE, "value (%s) is outside the range for an unsigned long value", value); - return fromLongBits(value.longValue()); + return asUnsigned(value.longValue()); } /** @@ -134,121 +98,50 @@ public final class UnsignedLong extends Number implements Comparable<UnsignedLon * {@link Character#MAX_RADIX} */ public static UnsignedLong valueOf(String string, int radix) { - return fromLongBits(UnsignedLongs.parseUnsignedLong(string, radix)); + return asUnsigned(UnsignedLongs.parseUnsignedLong(string, radix)); } /** * Returns the result of adding this and {@code val}. If the result would have more than 64 bits, * returns the low 64 bits of the result. - * - * @deprecated Use {@link #plus(UnsignedLong)}. This method is scheduled to be removed in Guava - * release 15.0. */ - @Deprecated - @Beta public UnsignedLong add(UnsignedLong val) { - return plus(val); - } - - /** - * Returns the result of adding this and {@code val}. If the result would have more than 64 bits, - * returns the low 64 bits of the result. - * - * @since 14.0 - */ - public UnsignedLong plus(UnsignedLong val) { - return fromLongBits(this.value + checkNotNull(val).value); + checkNotNull(val); + return asUnsigned(this.value + val.value); } /** * Returns the result of subtracting this and {@code val}. If the result would be negative, * returns the low 64 bits of the result. - * - * @deprecated Use {@link #minus(UnsignedLong)}. This method is scheduled to be removed in Guava - * release 15.0. */ - @Deprecated - @Beta public UnsignedLong subtract(UnsignedLong val) { - return minus(val); - } - - /** - * Returns the result of subtracting this and {@code val}. If the result would have more than 64 - * bits, returns the low 64 bits of the result. - * - * @since 14.0 - */ - public UnsignedLong minus(UnsignedLong val) { - return fromLongBits(this.value - checkNotNull(val).value); + checkNotNull(val); + return asUnsigned(this.value - val.value); } /** * Returns the result of multiplying this and {@code val}. If the result would have more than 64 * bits, returns the low 64 bits of the result. - * - * @deprecated Use {@link #times(UnsignedLong)}. This method is scheduled to be removed in Guava - * release 15.0. */ - @Deprecated - @Beta public UnsignedLong multiply(UnsignedLong val) { - return times(val); - } - - /** - * Returns the result of multiplying this and {@code val}. If the result would have more than 64 - * bits, returns the low 64 bits of the result. - * - * @since 14.0 - */ - @CheckReturnValue - public UnsignedLong times(UnsignedLong val) { - return fromLongBits(value * checkNotNull(val).value); + checkNotNull(val); + return asUnsigned(value * val.value); } /** * Returns the result of dividing this by {@code val}. - * - * @deprecated Use {@link #dividedBy(UnsignedLong)}. This method is scheduled to be removed in - * Guava release 15.0. */ - @Deprecated - @Beta public UnsignedLong divide(UnsignedLong val) { - return dividedBy(val); - } - - /** - * Returns the result of dividing this by {@code val}. - * - * @since 14.0 - */ - @CheckReturnValue - public UnsignedLong dividedBy(UnsignedLong val) { - return fromLongBits(UnsignedLongs.divide(value, checkNotNull(val).value)); + checkNotNull(val); + return asUnsigned(UnsignedLongs.divide(value, val.value)); } /** * Returns the remainder of dividing this by {@code val}. - * - * @deprecated Use {@link #mod(UnsignedLong)}. This method is scheduled to be removed in Guava - * release 15.0. */ - @Deprecated - @Beta public UnsignedLong remainder(UnsignedLong val) { - return mod(val); - } - - /** - * Returns this modulo {@code val}. - * - * @since 14.0 - */ - @CheckReturnValue - public UnsignedLong mod(UnsignedLong val) { - return fromLongBits(UnsignedLongs.remainder(value, checkNotNull(val).value)); + checkNotNull(val); + return asUnsigned(UnsignedLongs.remainder(value, val.value)); } /** diff --git a/guava/src/com/google/common/primitives/UnsignedLongs.java b/guava/src/com/google/common/primitives/UnsignedLongs.java index cb9923f..b723a1b 100644 --- a/guava/src/com/google/common/primitives/UnsignedLongs.java +++ b/guava/src/com/google/common/primitives/UnsignedLongs.java @@ -1,11 +1,11 @@ /* * 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 @@ -17,31 +17,27 @@ package com.google.common.primitives; 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.math.BigInteger; import java.util.Arrays; import java.util.Comparator; +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + /** * Static utility methods pertaining to {@code long} primitives that interpret values as * <i>unsigned</i> (that is, any negative value {@code x} is treated as the positive value * {@code 2^64 + x}). The methods for which signedness is not an issue are in {@link Longs}, as * well as signed versions of methods for which signedness is an issue. - * + * * <p>In addition, this class provides several static methods for converting a {@code long} to a * {@code String} and a {@code String} to a {@code long} that treat the {@code long} as an unsigned * number. - * + * * <p>Users of these utilities must be <i>extremely careful</i> not to mix up signed and unsigned - * {@code long} values. When possible, it is recommended that the {@link UnsignedLong} wrapper + * {@code long} values. When possible, it is recommended that the {@link UnsignedLong} wrapper * class be used, at a small efficiency penalty, to enforce the distinction in the type system. - * - * <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 * @author Brian Milch * @author Colin Evans @@ -56,7 +52,7 @@ public final class UnsignedLongs { /** * A (self-inverse) bijection which converts the ordering on unsigned longs to the ordering on - * longs, that is, {@code a <= b} as unsigned longs if and only if {@code flip(a) <= flip(b)} + * longs, that is, {@code a <= b} as unsigned longs if and only if {@code rotate(a) <= rotate(b)} * as signed longs. */ private static long flip(long a) { @@ -66,7 +62,7 @@ public final class UnsignedLongs { /** * Compares the two specified {@code long} values, treating them as unsigned values between * {@code 0} and {@code 2^64 - 1} inclusive. - * + * * @param a the first unsigned {@code long} to compare * @param b the second unsigned {@code long} to compare * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is @@ -78,7 +74,7 @@ public final class UnsignedLongs { /** * Returns the least value present in {@code array}, treating values as unsigned. - * + * * @param array a <i>nonempty</i> array of unsigned {@code long} values * @return the value present in {@code array} that is less than or equal to every other value in * the array according to {@link #compare} @@ -98,7 +94,7 @@ public final class UnsignedLongs { /** * Returns the greatest value present in {@code array}, treating values as unsigned. - * + * * @param array a <i>nonempty</i> array of unsigned {@code long} values * @return the value present in {@code array} that is greater than or equal to every other value * in the array according to {@link #compare} @@ -119,7 +115,7 @@ public final class UnsignedLongs { /** * Returns a string containing the supplied unsigned {@code long} values separated by * {@code separator}. For example, {@code join("-", 1, 2, 3)} 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 unsigned {@code long} values, possibly empty @@ -132,7 +128,7 @@ public final class UnsignedLongs { // For pre-sizing a builder, just get the right order of magnitude StringBuilder builder = new StringBuilder(array.length * 5); - builder.append(toString(array[0])); + builder.append(array[0]); for (int i = 1; i < array.length; i++) { builder.append(separator).append(toString(array[i])); } @@ -144,11 +140,11 @@ public final class UnsignedLongs { * lexicographically. That is, it compares, using {@link #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 [] < [1L] < [1L, 2L] < [2L] < [1L << 63]}. - * + * * <p>The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays * support only identity equality), but it is consistent with * {@link Arrays#equals(long[], long[])}. - * + * * @see <a href="http://en.wikipedia.org/wiki/Lexicographical_order">Lexicographical order * article at Wikipedia</a> */ @@ -174,7 +170,7 @@ public final class UnsignedLongs { /** * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 64-bit * quantities. - * + * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) * @throws ArithmeticException if divisor is 0 @@ -207,7 +203,7 @@ public final class UnsignedLongs { /** * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 64-bit * quantities. - * + * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) * @throws ArithmeticException if divisor is 0 @@ -240,55 +236,22 @@ public final class UnsignedLongs { /** * Returns the unsigned {@code long} value represented by the given decimal string. - * + * * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} * value - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Long#parseLong(String)}) */ public static long parseUnsignedLong(String s) { return parseUnsignedLong(s, 10); } /** - * Returns the unsigned {@code long} value represented by the given string. - * - * Accepts a decimal, hexadecimal, or octal number given by specifying the following prefix: - * - * <ul> - * <li>{@code 0x}<i>HexDigits</i> - * <li>{@code 0X}<i>HexDigits</i> - * <li>{@code #}<i>HexDigits</i> - * <li>{@code 0}<i>OctalDigits</i> - * </ul> - * - * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} - * value - * @since 13.0 - */ - public static long decode(String stringValue) { - ParseRequest request = ParseRequest.fromString(stringValue); - - try { - return parseUnsignedLong(request.rawValue, request.radix); - } catch (NumberFormatException e) { - NumberFormatException decodeException = - new NumberFormatException("Error parsing value: " + stringValue); - decodeException.initCause(e); - throw decodeException; - } - } - - /** * Returns the unsigned {@code long} value represented by a string with the given radix. - * + * * @param s the string containing the unsigned {@code long} representation to be parsed. * @param radix the radix to use while parsing {@code s} * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} * with the given radix, or if {@code radix} is not between {@link Character#MIN_RADIX} * and {@link Character#MAX_RADIX}. - * @throws NullPointerException if {@code s} is null - * (in contrast to {@link Long#parseLong(String)}) */ public static long parseUnsignedLong(String s, int radix) { checkNotNull(s); @@ -296,7 +259,7 @@ public final class UnsignedLongs { throw new NumberFormatException("empty string"); } if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { - throw new NumberFormatException("illegal radix: " + radix); + throw new NumberFormatException("illegal radix:" + radix); } int max_safe_pos = maxSafeDigits[radix] - 1; @@ -347,7 +310,7 @@ public final class UnsignedLongs { /** * Returns a string representation of {@code x} for the given radix, where {@code x} is treated * as unsigned. - * + * * @param x the value to convert to a string. * @param radix the radix to use while working with {@code x} * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX} @@ -363,17 +326,23 @@ public final class UnsignedLongs { char[] buf = new char[64]; int i = buf.length; if (x < 0) { - // Separate off the last digit using unsigned division. That will leave - // a number that is nonnegative as a signed integer. - long quotient = divide(x, radix); - long rem = x - quotient * radix; - buf[--i] = Character.forDigit((int) rem, radix); - x = quotient; - } - // Simple modulo/division approach - while (x > 0) { - buf[--i] = Character.forDigit((int) (x % radix), radix); - x /= radix; + // Split x into high-order and low-order halves. + // Individual digits are generated from the bottom half into which + // bits are moved continously from the top half. + long top = x >>> 32; + long bot = (x & 0xffffffffl) + ((top % radix) << 32); + top /= radix; + while ((bot > 0) || (top > 0)) { + buf[--i] = Character.forDigit((int) (bot % radix), radix); + bot = (bot / radix) + ((top % radix) << 32); + top /= radix; + } + } else { + // Simple modulo/division approach + while (x > 0) { + buf[--i] = Character.forDigit((int) (x % radix), radix); + x /= radix; + } } // Generate string return new String(buf, i, buf.length - i); diff --git a/guava/src/com/google/common/primitives/generate.sh b/guava/src/com/google/common/primitives/generate.sh new file mode 100644 index 0000000..5d594a2 --- /dev/null +++ b/guava/src/com/google/common/primitives/generate.sh @@ -0,0 +1,604 @@ +#!/bin/sh +# +# Usage example: ./generate.sh int Int Integer" +# Args are: primitive type, capitalized primitive type, wrapper type +# +# To make changes to the .java files in this package, +# 1. run this script to generate the templates, move the .gen files +# somewhere else +# 2. modify the template with your intended changes, then rerun the +# script +# 3. use any three-way merge tool to edit the checked-in source files, +# using the before-and-after generated files as the bases. +# + +if [ "$#" -ne "3" ] +then + echo "Usage example: ./generate.sh int Int Integer" + exit 1 +fi + +# Note: using the strange strings 'primtyp' and 'WrapperCl' so that they match +# the maximum length of the real strings ('boolean' and 'Character'). + +perl -pe "s/primtyp/$1/g; s/PrimTyp/$2/g; s/WrapperCl/$3/g" << "--EOF--" > $2s.java.gen +/* + * 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 com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Arrays; +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 primtyp} primitives, that are not + * already found in either {@link WrapperCl} or {@link Arrays}. + * + * @author Kevin Bourrillion + * @since 1.0 + */ +@GwtCompatible +public final class PrimTyps { + private PrimTyps() {} + + /** + * The number of bytes required to represent a primitive {@code primtyp} + * value. + */ + public static final int BYTES = WrapperCl.SIZE / Byte.SIZE; + + /** + * Returns a hash code for {@code value}; equal to the result of invoking + * {@code ((WrapperCl) value).hashCode()}. + * + * @param value a primitive {@code primtyp} value + * @return a hash code for the value + */ + public static int hashCode(primtyp value) { + return ?? + } + + /** + * Returns the {@code primtyp} value that is equal to {@code value}, if + * possible. + * + * @param value any value in the range of the {@code primtyp} type + * @return the {@code primtyp} value that equals {@code value} + * @throws IllegalArgumentException if {@code value} is greater than {@link + * WrapperCl#MAX_VALUE} or less than {@link WrapperCl#MIN_VALUE} + */ + public static primtyp checkedCast(long value) { + primtyp result = (primtyp) value; + checkArgument(result == value, "Out of range: %s", value); + return result; + } + + /** + * Returns the {@code primtyp} nearest in value to {@code value}. + * + * @param value any {@code long} value + * @return the same value cast to {@code primtyp} if it is in the range of the + * {@code primtyp} type, {@link WrapperCl#MAX_VALUE} if it is too large, + * or {@link WrapperCl#MIN_VALUE} if it is too small + */ + public static primtyp saturatedCast(long value) { + if (value > WrapperCl.MAX_VALUE) { + return WrapperCl.MAX_VALUE; + } + if (value < WrapperCl.MIN_VALUE) { + return WrapperCl.MIN_VALUE; + } + return (primtyp) value; + } + + /** + * Compares the two specified {@code primtyp} values. The sign of the value + * returned is the same as that of {@code ((WrapperCl) a).compareTo(b)}. + * + * @param a the first {@code primtyp} to compare + * @param b the second {@code primtyp} 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(primtyp a, primtyp b) { + return (a < b) ? -1 : ((a > b) ? 1 : 0); + } + + /** + * Returns {@code true} if {@code target} is present as an element anywhere in + * {@code array}. + * + * @param array an array of {@code primtyp} values, possibly empty + * @param target a primitive {@code primtyp} value + * @return {@code true} if {@code array[i] == target} for some value of {@code + * i} + */ + public static boolean contains(primtyp[] array, primtyp target) { + for (primtyp value : array) { + if (value == target) { + return true; + } + } + return false; + } + + /** + * Returns the index of the first appearance of the value {@code target} in + * {@code array}. + * + * @param array an array of {@code primtyp} values, possibly empty + * @param target a primitive {@code primtyp} 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(primtyp[] array, primtyp target) { + return indexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int indexOf( + primtyp[] array, primtyp 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}. + * + * @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(primtyp[] array, primtyp[] 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}. + * + * @param array an array of {@code primtyp} values, possibly empty + * @param target a primitive {@code primtyp} 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(primtyp[] array, primtyp target) { + return lastIndexOf(array, target, 0, array.length); + } + + // TODO(kevinb): consider making this public + private static int lastIndexOf( + primtyp[] array, primtyp 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}. + * + * @param array a <i>nonempty</i> array of {@code primtyp} 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 primtyp min(primtyp... array) { + checkArgument(array.length > 0); + primtyp min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + return min; + } + + /** + * Returns the greatest value present in {@code array}. + * + * @param array a <i>nonempty</i> array of {@code primtyp} 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 primtyp max(primtyp... array) { + checkArgument(array.length > 0); + primtyp max = array[0]; + for (int i = 1; i < array.length; 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 primtyp[] {a, b}, new primtyp[] {}, new + * primtyp[] {c}} returns the array {@code {a, b, c}}. + * + * @param arrays zero or more {@code primtyp} arrays + * @return a single array containing all the values from the source arrays, in + * order + */ + public static primtyp[] concat(primtyp[]... arrays) { + int length = 0; + for (primtyp[] array : arrays) { + length += array.length; + } + primtyp[] result = new primtyp[length]; + int pos = 0; + for (primtyp[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + + /** + * Returns a big-endian representation of {@code value} in a ?-element byte + * array; equivalent to {@code + * ByteBuffer.allocate(?).putPrimTyp(value).array()}. For example, the input + * value {@code ?} would yield the byte array {@code {?}}. + * + * <p>If you need to convert and concatenate several values (possibly even of + * different types), use a shared {@link java.nio.ByteBuffer} instance, or use + * {@link com.google.common.io.ByteStreams#newDataOutput()} to get a growable + * buffer. + */ + @GwtIncompatible("doesn't work") + public static byte[] toByteArray(primtyp value) { + return new byte[] { + ? + }; + } + + /** + * Returns the {@code primtyp} value whose big-endian representation is + * stored in the first ? bytes of {@code bytes}; equivalent to {@code + * ByteBuffer.wrap(bytes).getPrimTyp()}. For example, the input byte array + * {@code {?}} would yield the {@code primtyp} value {@code ?}. + * + * <p>Arguably, it's preferable to use {@link java.nio.ByteBuffer}; that + * library exposes much more flexibility at little cost in readability. + * + * @throws IllegalArgumentException if {@code bytes} has fewer than ? + * elements + */ + @GwtIncompatible("doesn't work") + public static primtyp fromByteArray(byte[] bytes) { + checkArgument(bytes.length >= BYTES, + "array too small: %s < %s", bytes.length, BYTES); + return fromBytes(bytes[0], bytes[1], bytes[2], bytes[3]); + } + + /** + * Returns the {@code primtyp} value whose byte representation is the given ? + * bytes, in big-endian order; equivalent to {@code + * PrimTyps.fromByteArray(new byte[] { ? })}. + * + * @since 7.0 + */ + @GwtIncompatible("doesn't work") + public static primtyp fromBytes(byte b1, byte b2, byte b3, byte b4) { + return ? + } + + /** + * 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 primtyp[] ensureCapacity( + primtyp[] 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 primtyp[] copyOf(primtyp[] original, int length) { + primtyp[] copy = new primtyp[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + /** + * Returns a string containing the supplied {@code primtyp} values separated + * by {@code separator}. For example, {@code join("-", 1?, 2?, 3?)} 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 primtyp} values, possibly empty + */ + public static String join(String separator, primtyp... 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 * ??); + 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 primtyp} arrays + * lexicographically. That is, it compares, using {@link + * #compare(primtyp, primtyp)}), 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] < [1, 2] < [2]}. + * + * <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(primtyp[], primtyp[])}. + * + * @see <a href="http://en.wikipedia.org/wiki/Lexicographical_order"> + * Lexicographical order article at Wikipedia</a> + * @since 2.0 + */ + public static Comparator<primtyp[]> lexicographicalComparator() { + return LexicographicalComparator.INSTANCE; + } + + private enum LexicographicalComparator implements Comparator<primtyp[]> { + INSTANCE; + + @Override + public int compare(primtyp[] left, primtyp[] right) { + int minLength = Math.min(left.length, right.length); + for (int i = 0; i < minLength; i++) { + int result = PrimTyps.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + return left.length - right.length; + } + } + + /** + * Copies a collection of {@code WrapperCl} instances into a new array of + * primitive {@code primtyp} 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 WrapperCl} 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 + */ + public static primtyp[] toArray(Collection<WrapperCl> collection) { + if (collection instanceof PrimTypArrayAsList) { + return ((PrimTypArrayAsList) collection).toPrimTypArray(); + } + + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + primtyp[] array = new primtyp[len]; + for (int i = 0; i < len; i++) { + // checkNotNull for GWT (do not optimize) + array[i] = (WrapperCl) checkNotNull(boxedArray[i]); + } + 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 WrapperCl} 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. + * + * @param backingArray the array to back the list + * @return a list view of the array + */ + public static List<WrapperCl> asList(primtyp... backingArray) { + if (backingArray.length == 0) { + return Collections.emptyList(); + } + return new PrimTypArrayAsList(backingArray); + } + + @GwtCompatible + private static class PrimTypArrayAsList extends AbstractList<WrapperCl> + implements RandomAccess, Serializable { + final primtyp[] array; + final int start; + final int end; + + PrimTypArrayAsList(primtyp[] array) { + this(array, 0, array.length); + } + + PrimTypArrayAsList(primtyp[] 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 WrapperCl 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 WrapperCl) + && PrimTyps.indexOf(array, (WrapperCl) target, start, end) != -1; + } + + @Override public int indexOf(Object target) { + // Overridden to prevent a ton of boxing + if (target instanceof WrapperCl) { + int i = PrimTyps.indexOf(array, (WrapperCl) 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 WrapperCl) { + int i = PrimTyps.lastIndexOf(array, (WrapperCl) target, start, end); + if (i >= 0) { + return i - start; + } + } + return -1; + } + + @Override public WrapperCl set(int index, WrapperCl element) { + checkElementIndex(index, size()); + primtyp oldValue = array[start + index]; + array[start + index] = checkNotNull(element); // checkNotNull for GWT (do not optimize) + return oldValue; + } + + @Override public List<WrapperCl> subList(int fromIndex, int toIndex) { + int size = size(); + checkPositionIndexes(fromIndex, toIndex, size); + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new PrimTypArrayAsList(array, start + fromIndex, start + toIndex); + } + + @Override public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof PrimTypArrayAsList) { + PrimTypArrayAsList that = (PrimTypArrayAsList) 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 + PrimTyps.hashCode(array[i]); + } + return result; + } + + @Override public String toString() { + StringBuilder builder = new StringBuilder(size() * ??); + builder.append('[').append(array[start]); + for (int i = start + 1; i < end; i++) { + builder.append(", ").append(array[i]); + } + return builder.append(']').toString(); + } + + primtyp[] toPrimTypArray() { + // Arrays.copyOfRange() requires Java 6 + int size = size(); + primtyp[] result = new primtyp[size]; + System.arraycopy(array, start, result, 0, size); + return result; + } + + private static final long serialVersionUID = 0; + } +} +--EOF-- + diff --git a/guava/src/com/google/common/primitives/package-info.java b/guava/src/com/google/common/primitives/package-info.java index 205183f..a3b3a01 100644 --- a/guava/src/com/google/common/primitives/package-info.java +++ b/guava/src/com/google/common/primitives/package-info.java @@ -15,15 +15,10 @@ */ /** - * Static utilities for working with the eight primitive types and {@code void}, - * and value types for treating them as unsigned. + * Static utilities for working with the eight primitive types and {@code void}. * * <p>This package is a part of the open-source * <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/PrimitivesExplained"> - * primitive utilities</a>. * * <h2>Contents</h2> * @@ -66,4 +61,3 @@ package com.google.common.primitives; import javax.annotation.ParametersAreNonnullByDefault; - diff --git a/guava/src/com/google/common/reflect/AbstractInvocationHandler.java b/guava/src/com/google/common/reflect/AbstractInvocationHandler.java deleted file mode 100644 index 489dcff..0000000 --- a/guava/src/com/google/common/reflect/AbstractInvocationHandler.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.reflect; - -import com.google.common.annotations.Beta; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import javax.annotation.Nullable; - -/** - * Abstract implementation of {@link InvocationHandler} that handles {@link Object#equals}, - * {@link Object#hashCode} and {@link Object#toString}. - * - * @author Ben Yu - * @since 12.0 - */ -@Beta -public abstract class AbstractInvocationHandler implements InvocationHandler { - - private static final Object[] NO_ARGS = {}; - - /** - * {@inheritDoc} - * - * <p><ul> - * <li>{@code proxy.hashCode()} delegates to {@link AbstractInvocationHandler#hashCode} - * <li>{@code proxy.toString()} delegates to {@link AbstractInvocationHandler#toString} - * <li>{@code proxy.equals(argument)} returns true if: <ul> - * <li>{@code proxy} and {@code argument} are of the same type - * <li>and {@link AbstractInvocationHandler#equals} returns true for the {@link - * InvocationHandler} of {@code argument} - * </ul> - * <li>other method calls are dispatched to {@link #handleInvocation}. - * </ul> - */ - @Override public final Object invoke(Object proxy, Method method, @Nullable Object[] args) - throws Throwable { - if (args == null) { - args = NO_ARGS; - } - if (args.length == 0 && method.getName().equals("hashCode")) { - return hashCode(); - } - if (args.length == 1 - && method.getName().equals("equals") - && method.getParameterTypes()[0] == Object.class) { - Object arg = args[0]; - return proxy.getClass().isInstance(arg) && equals(Proxy.getInvocationHandler(arg)); - } - if (args.length == 0 && method.getName().equals("toString")) { - return toString(); - } - return handleInvocation(proxy, method, args); - } - - /** - * {@link #invoke} delegates to this method upon any method invocation on the proxy instance, - * except {@link Object#equals}, {@link Object#hashCode} and {@link Object#toString}. The result - * will be returned as the proxied method's return value. - * - * <p>Unlike {@link #invoke}, {@code args} will never be null. When the method has no parameter, - * an empty array is passed in. - */ - protected abstract Object handleInvocation(Object proxy, Method method, Object[] args) - throws Throwable; - - /** - * By default delegates to {@link Object#equals} so instances are only equal if they are - * identical. {@code proxy.equals(argument)} returns true if: <ul> - * <li>{@code proxy} and {@code argument} are of the same type - * <li>and this method returns true for the {@link InvocationHandler} of {@code argument} - * </ul> - * Subclasses can override this method to provide custom equality. - */ - @Override public boolean equals(Object obj) { - return super.equals(obj); - } - - /** - * By default delegates to {@link Object#hashCode}. The dynamic proxies' {@code hashCode()} will - * delegate to this method. Subclasses can override this method to provide custom equality. - */ - @Override public int hashCode() { - return super.hashCode(); - } - - /** - * By default delegates to {@link Object#toString}. The dynamic proxies' {@code toString()} will - * delegate to this method. Subclasses can override this method to provide custom string - * representation for the proxies. - */ - @Override public String toString() { - return super.toString(); - } -} diff --git a/guava/src/com/google/common/reflect/ClassPath.java b/guava/src/com/google/common/reflect/ClassPath.java deleted file mode 100644 index cfbc479..0000000 --- a/guava/src/com/google/common/reflect/ClassPath.java +++ /dev/null @@ -1,379 +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.reflect; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Enumeration; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.logging.Logger; - -import javax.annotation.Nullable; - -/** - * Scans the source of a {@link ClassLoader} and finds all the classes loadable. - * - * @author Ben Yu - * @since 14.0 - */ -@Beta -public final class ClassPath { - - private static final Logger logger = Logger.getLogger(ClassPath.class.getName()); - - /** Separator for the Class-Path manifest attribute value in jar files. */ - private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR = - Splitter.on(" ").omitEmptyStrings(); - - private static final String CLASS_FILE_NAME_EXTENSION = ".class"; - - private final ImmutableSet<ResourceInfo> resources; - - private ClassPath(ImmutableSet<ResourceInfo> resources) { - this.resources = resources; - } - - /** - * Returns a {@code ClassPath} representing all classes and resources loadable from {@code - * classloader} and its parent class loaders. - * - * <p>Currently only {@link URLClassLoader} and only {@code file://} urls are supported. - * - * @throws IOException if the attempt to read class path resources (jar files or directories) - * failed. - */ - public static ClassPath from(ClassLoader classloader) throws IOException { - ImmutableSortedSet.Builder<ResourceInfo> resources = - new ImmutableSortedSet.Builder<ResourceInfo>(Ordering.usingToString()); - for (Map.Entry<URI, ClassLoader> entry : getClassPathEntries(classloader).entrySet()) { - browse(entry.getKey(), entry.getValue(), resources); - } - return new ClassPath(resources.build()); - } - - /** - * Returns all resources loadable from the current class path, including the class files of all - * loadable classes. - */ - public ImmutableSet<ResourceInfo> getResources() { - return resources; - } - - /** Returns all top level classes loadable from the current class path. */ - public ImmutableSet<ClassInfo> getTopLevelClasses() { - ImmutableSet.Builder<ClassInfo> builder = ImmutableSet.builder(); - for (ResourceInfo resource : resources) { - if (resource instanceof ClassInfo) { - builder.add((ClassInfo) resource); - } - } - return builder.build(); - } - - /** Returns all top level classes whose package name is {@code packageName}. */ - public ImmutableSet<ClassInfo> getTopLevelClasses(String packageName) { - checkNotNull(packageName); - ImmutableSet.Builder<ClassInfo> builder = ImmutableSet.builder(); - for (ClassInfo classInfo : getTopLevelClasses()) { - if (classInfo.getPackageName().equals(packageName)) { - builder.add(classInfo); - } - } - return builder.build(); - } - - /** - * Returns all top level classes whose package name is {@code packageName} or starts with - * {@code packageName} followed by a '.'. - */ - public ImmutableSet<ClassInfo> getTopLevelClassesRecursive(String packageName) { - checkNotNull(packageName); - String packagePrefix = packageName + '.'; - ImmutableSet.Builder<ClassInfo> builder = ImmutableSet.builder(); - for (ClassInfo classInfo : getTopLevelClasses()) { - if (classInfo.getName().startsWith(packagePrefix)) { - builder.add(classInfo); - } - } - return builder.build(); - } - - /** - * Represents a class path resource that can be either a class file or any other resource file - * loadable from the class path. - * - * @since 14.0 - */ - @Beta - public static class ResourceInfo { - private final String resourceName; - final ClassLoader loader; - - static ResourceInfo of(String resourceName, ClassLoader loader) { - if (resourceName.endsWith(CLASS_FILE_NAME_EXTENSION) && !resourceName.contains("$")) { - return new ClassInfo(resourceName, loader); - } else { - return new ResourceInfo(resourceName, loader); - } - } - - ResourceInfo(String resourceName, ClassLoader loader) { - this.resourceName = checkNotNull(resourceName); - this.loader = checkNotNull(loader); - } - - /** Returns the url identifying the resource. */ - public final URL url() { - return checkNotNull(loader.getResource(resourceName), - "Failed to load resource: %s", resourceName); - } - - /** Returns the fully qualified name of the resource. Such as "com/mycomp/foo/bar.txt". */ - public final String getResourceName() { - return resourceName; - } - - @Override public int hashCode() { - return resourceName.hashCode(); - } - - @Override public boolean equals(Object obj) { - if (obj instanceof ResourceInfo) { - ResourceInfo that = (ResourceInfo) obj; - return resourceName.equals(that.resourceName) - && loader == that.loader; - } - return false; - } - - @Override public String toString() { - return resourceName; - } - } - - /** - * Represents a class that can be loaded through {@link #load}. - * - * @since 14.0 - */ - @Beta - public static final class ClassInfo extends ResourceInfo { - private final String className; - - ClassInfo(String resourceName, ClassLoader loader) { - super(resourceName, loader); - this.className = getClassName(resourceName); - } - - /** Returns the package name of the class, without attempting to load the class. */ - public String getPackageName() { - return Reflection.getPackageName(className); - } - - /** Returns the simple name of the underlying class as given in the source code. */ - public String getSimpleName() { - String packageName = getPackageName(); - if (packageName.isEmpty()) { - return className; - } - // Since this is a top level class, its simple name is always the part after package name. - return className.substring(packageName.length() + 1); - } - - /** Returns the fully qualified name of the class. */ - public String getName() { - return className; - } - - /** Loads (but doesn't link or initialize) the class. */ - public Class<?> load() { - try { - return loader.loadClass(className); - } catch (ClassNotFoundException e) { - // Shouldn't happen, since the class name is read from the class path. - throw new IllegalStateException(e); - } - } - - @Override public String toString() { - return className; - } - } - - @VisibleForTesting static ImmutableMap<URI, ClassLoader> getClassPathEntries( - ClassLoader classloader) { - LinkedHashMap<URI, ClassLoader> entries = Maps.newLinkedHashMap(); - // Search parent first, since it's the order ClassLoader#loadClass() uses. - ClassLoader parent = classloader.getParent(); - if (parent != null) { - entries.putAll(getClassPathEntries(parent)); - } - if (classloader instanceof URLClassLoader) { - URLClassLoader urlClassLoader = (URLClassLoader) classloader; - for (URL entry : urlClassLoader.getURLs()) { - URI uri; - try { - uri = entry.toURI(); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e); - } - if (!entries.containsKey(uri)) { - entries.put(uri, classloader); - } - } - } - return ImmutableMap.copyOf(entries); - } - - private static void browse( - URI uri, ClassLoader classloader, ImmutableSet.Builder<ResourceInfo> resources) - throws IOException { - if (uri.getScheme().equals("file")) { - browseFrom(new File(uri), classloader, resources); - } - } - - @VisibleForTesting static void browseFrom( - File file, ClassLoader classloader, ImmutableSet.Builder<ResourceInfo> resources) - throws IOException { - if (!file.exists()) { - return; - } - if (file.isDirectory()) { - browseDirectory(file, classloader, resources); - } else { - browseJar(file, classloader, resources); - } - } - - private static void browseDirectory( - File directory, ClassLoader classloader, ImmutableSet.Builder<ResourceInfo> resources) { - browseDirectory(directory, classloader, "", resources); - } - - private static void browseDirectory( - File directory, ClassLoader classloader, String packagePrefix, - ImmutableSet.Builder<ResourceInfo> resources) { - for (File f : directory.listFiles()) { - String name = f.getName(); - if (f.isDirectory()) { - browseDirectory(f, classloader, packagePrefix + name + "/", resources); - } else { - String resourceName = packagePrefix + name; - resources.add(ResourceInfo.of(resourceName, classloader)); - } - } - } - - private static void browseJar( - File file, ClassLoader classloader, ImmutableSet.Builder<ResourceInfo> resources) - throws IOException { - JarFile jarFile; - try { - jarFile = new JarFile(file); - } catch (IOException e) { - // Not a jar file - return; - } - try { - for (URI uri : getClassPathFromManifest(file, jarFile.getManifest())) { - browse(uri, classloader, resources); - } - Enumeration<JarEntry> entries = jarFile.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - if (entry.isDirectory() || entry.getName().startsWith("META-INF/")) { - continue; - } - resources.add(ResourceInfo.of(entry.getName(), classloader)); - } - } finally { - try { - jarFile.close(); - } catch (IOException ignored) {} - } - } - - /** - * Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according - * to <a href="http://docs.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#Main%20Attributes"> - * JAR File Specification</a>. If {@code manifest} is null, it means the jar file has no manifest, - * and an empty set will be returned. - */ - @VisibleForTesting static ImmutableSet<URI> getClassPathFromManifest( - File jarFile, @Nullable Manifest manifest) { - if (manifest == null) { - return ImmutableSet.of(); - } - ImmutableSet.Builder<URI> builder = ImmutableSet.builder(); - String classpathAttribute = manifest.getMainAttributes().getValue("Class-Path"); - if (classpathAttribute != null) { - for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) { - URI uri; - try { - uri = getClassPathEntry(jarFile, path); - } catch (URISyntaxException e) { - // Ignore bad entry - logger.warning("Invalid Class-Path entry: " + path); - continue; - } - builder.add(uri); - } - } - return builder.build(); - } - - /** - * Returns the absolute uri of the Class-Path entry value as specified in - * <a href="http://docs.oracle.com/javase/1.4.2/docs/guide/jar/jar.html#Main%20Attributes"> - * JAR File Specification</a>. Even though the specification only talks about relative urls, - * absolute urls are actually supported too (for example, in Maven surefire plugin). - */ - @VisibleForTesting static URI getClassPathEntry(File jarFile, String path) - throws URISyntaxException { - URI uri = new URI(path); - if (uri.isAbsolute()) { - return uri; - } else { - return new File(jarFile.getParentFile(), path.replace('/', File.separatorChar)).toURI(); - } - } - - @VisibleForTesting static String getClassName(String filename) { - int classNameEnd = filename.length() - CLASS_FILE_NAME_EXTENSION.length(); - return filename.substring(0, classNameEnd).replace('/', '.'); - } -} diff --git a/guava/src/com/google/common/reflect/Element.java b/guava/src/com/google/common/reflect/Element.java deleted file mode 100644 index 14962b6..0000000 --- a/guava/src/com/google/common/reflect/Element.java +++ /dev/null @@ -1,164 +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.reflect; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.lang.annotation.Annotation; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -import javax.annotation.Nullable; - -/** - * Represents either a {@link Field}, a {@link Method} or a {@link Constructor}. - * Provides convenience methods such as {@link #isPublic} and {@link #isPackagePrivate}. - * - * @author Ben Yu - */ -class Element extends AccessibleObject implements Member { - - private final AccessibleObject accessibleObject; - private final Member member; - - <M extends AccessibleObject & Member> Element(M member) { - checkNotNull(member); - this.accessibleObject = member; - this.member = member; - } - - @Override public final boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { - return accessibleObject.isAnnotationPresent(annotationClass); - } - - @Override public final <A extends Annotation> A getAnnotation(Class<A> annotationClass) { - return accessibleObject.getAnnotation(annotationClass); - } - - @Override public final Annotation[] getAnnotations() { - return accessibleObject.getAnnotations(); - } - - @Override public final Annotation[] getDeclaredAnnotations() { - return accessibleObject.getDeclaredAnnotations(); - } - - @Override public final void setAccessible(boolean flag) throws SecurityException { - accessibleObject.setAccessible(flag); - } - - @Override public final boolean isAccessible() { - return accessibleObject.isAccessible(); - } - - @Override public Class<?> getDeclaringClass() { - return member.getDeclaringClass(); - } - - @Override public final String getName() { - return member.getName(); - } - - @Override public final int getModifiers() { - return member.getModifiers(); - } - - @Override public final boolean isSynthetic() { - return member.isSynthetic(); - } - - /** Returns true if the element is public. */ - public final boolean isPublic() { - return Modifier.isPublic(getModifiers()); - } - - /** Returns true if the element is protected. */ - public final boolean isProtected() { - return Modifier.isProtected(getModifiers()); - } - - /** Returns true if the element is package-private. */ - public final boolean isPackagePrivate() { - return !isPrivate() && !isPublic() && !isProtected(); - } - - /** Returns true if the element is private. */ - public final boolean isPrivate() { - return Modifier.isPrivate(getModifiers()); - } - - /** Returns true if the element is static. */ - public final boolean isStatic() { - return Modifier.isStatic(getModifiers()); - } - - /** - * Returns {@code true} if this method is final, per {@code Modifier.isFinal(getModifiers())}. - * - * <p>Note that a method may still be effectively "final", or non-overridable when it has no - * {@code final} keyword. For example, it could be private, or it could be declared by a final - * class. To tell whether a method is overridable, use {@link Invokable#isOverridable}. - */ - public final boolean isFinal() { - return Modifier.isFinal(getModifiers()); - } - - /** Returns true if the method is abstract. */ - public final boolean isAbstract() { - return Modifier.isAbstract(getModifiers()); - } - - /** Returns true if the element is native. */ - public final boolean isNative() { - return Modifier.isNative(getModifiers()); - } - - /** Returns true if the method is synchronized. */ - public final boolean isSynchronized() { - return Modifier.isSynchronized(getModifiers()); - } - - /** Returns true if the field is volatile. */ - final boolean isVolatile() { - return Modifier.isVolatile(getModifiers()); - } - - /** Returns true if the field is transient. */ - final boolean isTransient() { - return Modifier.isTransient(getModifiers()); - } - - @Override public boolean equals(@Nullable Object obj) { - if (obj instanceof Element) { - Element that = (Element) obj; - return member.equals(that.member); - } - return false; - } - - @Override public int hashCode() { - return member.hashCode(); - } - - @Override public String toString() { - return member.toString(); - } -} diff --git a/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java b/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java deleted file mode 100644 index 43e5e1e..0000000 --- a/guava/src/com/google/common/reflect/ImmutableTypeToInstanceMap.java +++ /dev/null @@ -1,138 +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.reflect; - -import com.google.common.annotations.Beta; -import com.google.common.collect.ForwardingMap; -import com.google.common.collect.ImmutableMap; - -import java.util.Map; - -/** - * A type-to-instance map backed by an {@link ImmutableMap}. See also {@link - * MutableTypeToInstanceMap}. - * - * @author Ben Yu - * @since 13.0 - */ -@Beta -public final class ImmutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B> - implements TypeToInstanceMap<B> { - - /** Returns an empty type to instance map. */ - public static <B> ImmutableTypeToInstanceMap<B> of() { - return new ImmutableTypeToInstanceMap<B>(ImmutableMap.<TypeToken<? extends B>, B>of()); - } - - /** Returns a new builder. */ - public static <B> Builder<B> builder() { - return new Builder<B>(); - } - - /** - * A builder for creating immutable type-to-instance maps. Example: - * <pre> {@code - * - * static final ImmutableTypeToInstanceMap<Handler<?>> HANDLERS = - * ImmutableTypeToInstanceMap.<Handler<?>>builder() - * .put(new TypeToken<Handler<Foo>>() {}, new FooHandler()) - * .put(new TypeToken<Handler<Bar>>() {}, new SubBarHandler()) - * .build();}</pre> - * - * After invoking {@link #build()} it is still possible to add more entries - * and build again. Thus each map generated by this builder will be a superset - * of any map generated before it. - * - * @since 13.0 - */ - @Beta - public static final class Builder<B> { - private final ImmutableMap.Builder<TypeToken<? extends B>, B> mapBuilder - = ImmutableMap.builder(); - - private Builder() {} - - /** - * Associates {@code key} with {@code value} in the built map. Duplicate - * keys are not allowed, and will cause {@link #build} to fail. - */ - public <T extends B> Builder<B> put(Class<T> key, T value) { - mapBuilder.put(TypeToken.of(key), value); - return this; - } - - /** - * Associates {@code key} with {@code value} in the built map. Duplicate - * keys are not allowed, and will cause {@link #build} to fail. - */ - public <T extends B> Builder<B> put(TypeToken<T> key, T value) { - mapBuilder.put(key.rejectTypeVariables(), value); - return this; - } - - /** - * Returns a new immutable type-to-instance map containing the entries - * provided to this builder. - * - * @throws IllegalArgumentException if duplicate keys were added - */ - public ImmutableTypeToInstanceMap<B> build() { - return new ImmutableTypeToInstanceMap<B>(mapBuilder.build()); - } - } - - private final ImmutableMap<TypeToken<? extends B>, B> delegate; - - private ImmutableTypeToInstanceMap(ImmutableMap<TypeToken<? extends B>, B> delegate) { - this.delegate = delegate; - } - - @Override public <T extends B> T getInstance(TypeToken<T> type) { - return trustedGet(type.rejectTypeVariables()); - } - - /** - * Guaranteed to throw an exception and leave the map unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override public <T extends B> T putInstance(TypeToken<T> type, T value) { - throw new UnsupportedOperationException(); - } - - @Override public <T extends B> T getInstance(Class<T> type) { - return trustedGet(TypeToken.of(type)); - } - - /** - * Guaranteed to throw an exception and leave the map unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override public <T extends B> T putInstance(Class<T> type, T value) { - throw new UnsupportedOperationException(); - } - - @Override protected Map<TypeToken<? extends B>, B> delegate() { - return delegate; - } - - @SuppressWarnings("unchecked") // value could not get in if not a T - private <T extends B> T trustedGet(TypeToken<T> type) { - return (T) delegate.get(type); - } -} diff --git a/guava/src/com/google/common/reflect/Invokable.java b/guava/src/com/google/common/reflect/Invokable.java deleted file mode 100644 index a8f9b77..0000000 --- a/guava/src/com/google/common/reflect/Invokable.java +++ /dev/null @@ -1,286 +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.reflect; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.collect.ImmutableList; - -import java.lang.annotation.Annotation; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Constructor; -import java.lang.reflect.GenericDeclaration; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.Arrays; - -import javax.annotation.Nullable; - -/** - * Wrapper around either a {@link Method} or a {@link Constructor}. - * Convenience API is provided to make common reflective operation easier to deal with, - * such as {@link #isPublic}, {@link #getParameters} etc. - * - * <p>In addition to convenience methods, {@link TypeToken#method} and {@link - * TypeToken#constructor} will resolve the type parameters of the method or constructor in the - * context of the owner type, which may be a subtype of the declaring class. For example: - * <pre> {@code - * - * Method getMethod = List.class.getMethod("get", int.class); - * Invokable<List<String>, ?> invokable = new TypeToken<List<String>>() {}.method(getMethod); - * assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class! - * assertEquals(new TypeToken<List<String>>() {}, invokable.getOwnerType());}</pre> - * - * @param <T> the type that owns this method or constructor. - * @param <R> the return type of (or supertype thereof) the method or the declaring type of the - * constructor. - * @author Ben Yu - * @since 14.0 - */ -@Beta -public abstract class Invokable<T, R> extends Element implements GenericDeclaration { - - <M extends AccessibleObject & Member> Invokable(M member) { - super(member); - } - - /** Returns {@link Invokable} of {@code method}. */ - public static Invokable<?, Object> from(Method method) { - return new MethodInvokable<Object>(method); - } - - /** Returns {@link Invokable} of {@code constructor}. */ - public static <T> Invokable<T, T> from(Constructor<T> constructor) { - return new ConstructorInvokable<T>(constructor); - } - - /** - * Returns {@code true} if this is an overridable method. Constructors, private, static or final - * methods, or methods declared by final classes are not overridable. - */ - public abstract boolean isOverridable(); - - /** Returns {@code true} if this was declared to take a variable number of arguments. */ - public abstract boolean isVarArgs(); - - /** - * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method - * and returns the return value; or calls the underlying constructor with {@code args} and returns - * the constructed instance. - * - * @throws IllegalAccessException if this {@code Constructor} object enforces Java language - * access control and the underlying method or constructor is inaccessible. - * @throws IllegalArgumentException if the number of actual and formal parameters differ; - * if an unwrapping conversion for primitive arguments fails; or if, after possible - * unwrapping, a parameter value cannot be converted to the corresponding formal - * parameter type by a method invocation conversion. - * @throws InvocationTargetException if the underlying method or constructor throws an exception. - */ - // All subclasses are owned by us and we'll make sure to get the R type right. - @SuppressWarnings("unchecked") - public final R invoke(@Nullable T receiver, Object... args) - throws InvocationTargetException, IllegalAccessException { - return (R) invokeInternal(receiver, checkNotNull(args)); - } - - /** Returns the return type of this {@code Invokable}. */ - // All subclasses are owned by us and we'll make sure to get the R type right. - @SuppressWarnings("unchecked") - public final TypeToken<? extends R> getReturnType() { - return (TypeToken<? extends R>) TypeToken.of(getGenericReturnType()); - } - - /** - * Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor - * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden - * {@code this} parameter of the enclosing class is excluded from the returned parameters. - */ - public final ImmutableList<Parameter> getParameters() { - Type[] parameterTypes = getGenericParameterTypes(); - Annotation[][] annotations = getParameterAnnotations(); - ImmutableList.Builder<Parameter> builder = ImmutableList.builder(); - for (int i = 0; i < parameterTypes.length; i++) { - builder.add(new Parameter( - this, i, TypeToken.of(parameterTypes[i]), annotations[i])); - } - return builder.build(); - } - - /** Returns all declared exception types of this {@code Invokable}. */ - public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes() { - ImmutableList.Builder<TypeToken<? extends Throwable>> builder = ImmutableList.builder(); - for (Type type : getGenericExceptionTypes()) { - // getGenericExceptionTypes() will never return a type that's not exception - @SuppressWarnings("unchecked") - TypeToken<? extends Throwable> exceptionType = (TypeToken<? extends Throwable>) - TypeToken.of(type); - builder.add(exceptionType); - } - return builder.build(); - } - - /** - * Explicitly specifies the return type of this {@code Invokable}. For example: - * <pre> {@code - * Method factoryMethod = Person.class.getMethod("create"); - * Invokable<?, Person> factory = Invokable.of(getNameMethod).returning(Person.class); - * }</pre> - */ - public final <R1 extends R> Invokable<T, R1> returning(Class<R1> returnType) { - return returning(TypeToken.of(returnType)); - } - - /** Explicitly specifies the return type of this {@code Invokable}. */ - public final <R1 extends R> Invokable<T, R1> returning(TypeToken<R1> returnType) { - if (!returnType.isAssignableFrom(getReturnType())) { - throw new IllegalArgumentException( - "Invokable is known to return " + getReturnType() + ", not " + returnType); - } - @SuppressWarnings("unchecked") // guarded by previous check - Invokable<T, R1> specialized = (Invokable<T, R1>) this; - return specialized; - } - - @SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes. - @Override public final Class<? super T> getDeclaringClass() { - return (Class<? super T>) super.getDeclaringClass(); - } - - /** Returns the type of {@code T}. */ - // Overridden in TypeToken#method() and TypeToken#constructor() - @SuppressWarnings("unchecked") // The declaring class is T. - public TypeToken<T> getOwnerType() { - return (TypeToken<T>) TypeToken.of(getDeclaringClass()); - } - - abstract Object invokeInternal(@Nullable Object receiver, Object[] args) - throws InvocationTargetException, IllegalAccessException; - - abstract Type[] getGenericParameterTypes(); - - /** This should never return a type that's not a subtype of Throwable. */ - abstract Type[] getGenericExceptionTypes(); - - abstract Annotation[][] getParameterAnnotations(); - - abstract Type getGenericReturnType(); - - static class MethodInvokable<T> extends Invokable<T, Object> { - - private final Method method; - - MethodInvokable(Method method) { - super(method); - this.method = method; - } - - @Override final Object invokeInternal(@Nullable Object receiver, Object[] args) - throws InvocationTargetException, IllegalAccessException { - return method.invoke(receiver, args); - } - - @Override Type getGenericReturnType() { - return method.getGenericReturnType(); - } - - @Override Type[] getGenericParameterTypes() { - return method.getGenericParameterTypes(); - } - - @Override Type[] getGenericExceptionTypes() { - return method.getGenericExceptionTypes(); - } - - @Override final Annotation[][] getParameterAnnotations() { - return method.getParameterAnnotations(); - } - - @Override public final TypeVariable<?>[] getTypeParameters() { - return method.getTypeParameters(); - } - - @Override public final boolean isOverridable() { - return !(isFinal() || isPrivate() || isStatic() - || Modifier.isFinal(getDeclaringClass().getModifiers())); - } - - @Override public final boolean isVarArgs() { - return method.isVarArgs(); - } - } - - static class ConstructorInvokable<T> extends Invokable<T, T> { - - private final Constructor<?> constructor; - - ConstructorInvokable(Constructor<?> constructor) { - super(constructor); - this.constructor = constructor; - } - - @Override final Object invokeInternal(@Nullable Object receiver, Object[] args) - throws InvocationTargetException, IllegalAccessException { - try { - return constructor.newInstance(args); - } catch (InstantiationException e) { - throw new RuntimeException(constructor + " failed.", e); - } - } - - @Override Type getGenericReturnType() { - return constructor.getDeclaringClass(); - } - - @Override Type[] getGenericParameterTypes() { - Type[] types = constructor.getGenericParameterTypes(); - Class<?> declaringClass = constructor.getDeclaringClass(); - if (!Modifier.isStatic(declaringClass.getModifiers()) - && declaringClass.getEnclosingClass() != null) { - if (types.length == constructor.getParameterTypes().length) { - // first parameter is the hidden 'this' - return Arrays.copyOfRange(types, 1, types.length); - } - } - return types; - } - - @Override Type[] getGenericExceptionTypes() { - return constructor.getGenericExceptionTypes(); - } - - @Override final Annotation[][] getParameterAnnotations() { - return constructor.getParameterAnnotations(); - } - - @Override public final TypeVariable<?>[] getTypeParameters() { - return constructor.getTypeParameters(); - } - - @Override public final boolean isOverridable() { - return false; - } - - @Override public final boolean isVarArgs() { - return constructor.isVarArgs(); - } - } -} diff --git a/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java b/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java deleted file mode 100644 index 5f1249d..0000000 --- a/guava/src/com/google/common/reflect/MutableTypeToInstanceMap.java +++ /dev/null @@ -1,89 +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.reflect; - -import com.google.common.annotations.Beta; -import com.google.common.collect.ForwardingMap; -import com.google.common.collect.Maps; - -import java.util.Map; - -import javax.annotation.Nullable; - -/** - * A mutable type-to-instance map. - * See also {@link ImmutableTypeToInstanceMap}. - * - * @author Ben Yu - * @since 13.0 - */ -@Beta -public final class MutableTypeToInstanceMap<B> extends ForwardingMap<TypeToken<? extends B>, B> - implements TypeToInstanceMap<B> { - - private final Map<TypeToken<? extends B>, B> backingMap = Maps.newHashMap(); - - @Nullable - @Override - public <T extends B> T getInstance(Class<T> type) { - return trustedGet(TypeToken.of(type)); - } - - @Nullable - @Override - public <T extends B> T putInstance(Class<T> type, @Nullable T value) { - return trustedPut(TypeToken.of(type), value); - } - - @Nullable - @Override - public <T extends B> T getInstance(TypeToken<T> type) { - return trustedGet(type.rejectTypeVariables()); - } - - @Nullable - @Override - public <T extends B> T putInstance(TypeToken<T> type, @Nullable T value) { - return trustedPut(type.rejectTypeVariables(), value); - } - - /** Not supported. Use {@link #putInstance} instead. */ - @Override public B put(TypeToken<? extends B> key, B value) { - throw new UnsupportedOperationException("Please use putInstance() instead."); - } - - /** Not supported. Use {@link #putInstance} instead. */ - @Override public void putAll(Map<? extends TypeToken<? extends B>, ? extends B> map) { - throw new UnsupportedOperationException("Please use putInstance() instead."); - } - - @Override protected Map<TypeToken<? extends B>, B> delegate() { - return backingMap; - } - - @SuppressWarnings("unchecked") // value could not get in if not a T - @Nullable - private <T extends B> T trustedPut(TypeToken<T> type, @Nullable T value) { - return (T) backingMap.put(type, value); - } - - @SuppressWarnings("unchecked") // value could not get in if not a T - @Nullable - private <T extends B> T trustedGet(TypeToken<T> type) { - return (T) backingMap.get(type); - } -} diff --git a/guava/src/com/google/common/reflect/Parameter.java b/guava/src/com/google/common/reflect/Parameter.java deleted file mode 100644 index 977bc8d..0000000 --- a/guava/src/com/google/common/reflect/Parameter.java +++ /dev/null @@ -1,103 +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.reflect; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.collect.ImmutableList; - -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; - -import javax.annotation.Nullable; - -/** - * Represents a method or constructor parameter. - * - * @author Ben Yu - * @since 14.0 - */ -@Beta -public final class Parameter implements AnnotatedElement { - - private final Invokable<?, ?> declaration; - private final int position; - private final TypeToken<?> type; - private final ImmutableList<Annotation> annotations; - - Parameter( - Invokable<?, ?> declaration, - int position, - TypeToken<?> type, - Annotation[] annotations) { - this.declaration = declaration; - this.position = position; - this.type = type; - this.annotations = ImmutableList.copyOf(annotations); - } - - /** Returns the type of the parameter. */ - public TypeToken<?> getType() { - return type; - } - - /** Returns the {@link Invokable} that declares this parameter. */ - public Invokable<?, ?> getDeclaringInvokable() { - return declaration; - } - - @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { - return getAnnotation(annotationType) != null; - } - - @Override - @Nullable - public <A extends Annotation> A getAnnotation(Class<A> annotationType) { - checkNotNull(annotationType); - for (Annotation annotation : annotations) { - if (annotationType.isInstance(annotation)) { - return annotationType.cast(annotation); - } - } - return null; - } - - @Override public Annotation[] getAnnotations() { - return getDeclaredAnnotations(); - } - - @Override public Annotation[] getDeclaredAnnotations() { - return annotations.toArray(new Annotation[annotations.size()]); - } - - @Override public boolean equals(@Nullable Object obj) { - if (obj instanceof Parameter) { - Parameter that = (Parameter) obj; - return position == that.position && declaration.equals(that.declaration); - } - return false; - } - - @Override public int hashCode() { - return position; - } - - @Override public String toString() { - return type + " arg" + position; - } -} diff --git a/guava/src/com/google/common/reflect/Reflection.java b/guava/src/com/google/common/reflect/Reflection.java deleted file mode 100644 index 028c8a9..0000000 --- a/guava/src/com/google/common/reflect/Reflection.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2005 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.reflect; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; - -/** - * Static utilities relating to Java reflection. - * - * @since 12.0 - */ -@Beta -public final class Reflection { - - /** - * Returns the package name of {@code clazz} according to the Java Language Specification (section - * 6.7). Unlike {@link Class#getPackage}, this method only parses the class name, without - * attempting to define the {@link Package} and hence load files. - */ - public static String getPackageName(Class<?> clazz) { - return getPackageName(clazz.getName()); - } - - /** - * Returns the package name of {@code classFullName} according to the Java Language Specification - * (section 6.7). Unlike {@link Class#getPackage}, this method only parses the class name, without - * attempting to define the {@link Package} and hence load files. - */ - public static String getPackageName(String classFullName) { - int lastDot = classFullName.lastIndexOf('.'); - return (lastDot < 0) ? "" : classFullName.substring(0, lastDot); - } - - /** - * Ensures that the given classes are initialized, as described in - * <a href="http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.4.2"> - * JLS Section 12.4.2</a>. - * - * <p>WARNING: Normally it's a smell if a class needs to be explicitly initialized, because static - * state hurts system maintainability and testability. In cases when you have no choice while - * inter-operating with a legacy framework, this method helps to keep the code less ugly. - * - * @throws ExceptionInInitializerError if an exception is thrown during - * initialization of a class - */ - public static void initialize(Class<?>... classes) { - for (Class<?> clazz : classes) { - try { - Class.forName(clazz.getName(), true, clazz.getClassLoader()); - } catch (ClassNotFoundException e) { - throw new AssertionError(e); - } - } - } - - /** - * Returns a proxy instance that implements {@code interfaceType} by - * dispatching method invocations to {@code handler}. The class loader of - * {@code interfaceType} will be used to define the proxy class. To implement - * multiple interfaces or specify a class loader, use - * {@link Proxy#newProxyInstance}. - * - * @throws IllegalArgumentException if {@code interfaceType} does not specify - * the type of a Java interface - */ - public static <T> T newProxy( - Class<T> interfaceType, InvocationHandler handler) { - checkNotNull(handler); - checkArgument(interfaceType.isInterface(), "%s is not an interface", interfaceType); - Object object = Proxy.newProxyInstance( - interfaceType.getClassLoader(), - new Class<?>[] { interfaceType }, - handler); - return interfaceType.cast(object); - } - - private Reflection() {} -} diff --git a/guava/src/com/google/common/reflect/TypeCapture.java b/guava/src/com/google/common/reflect/TypeCapture.java deleted file mode 100644 index c686661..0000000 --- a/guava/src/com/google/common/reflect/TypeCapture.java +++ /dev/null @@ -1,38 +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.reflect; - -import static com.google.common.base.Preconditions.checkArgument; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -/** - * Captures the actual type of {@code T}. - * - * @author Ben Yu - */ -abstract class TypeCapture<T> { - - /** Returns the captured type. */ - final Type capture() { - Type superclass = getClass().getGenericSuperclass(); - checkArgument(superclass instanceof ParameterizedType, - "%s isn't parameterized", superclass); - return ((ParameterizedType) superclass).getActualTypeArguments()[0]; - } -} diff --git a/guava/src/com/google/common/reflect/TypeParameter.java b/guava/src/com/google/common/reflect/TypeParameter.java deleted file mode 100644 index 79bf076..0000000 --- a/guava/src/com/google/common/reflect/TypeParameter.java +++ /dev/null @@ -1,67 +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.reflect; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.annotations.Beta; - -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; - -import javax.annotation.Nullable; - -/** - * Captures a free type variable that can be used in {@link TypeToken#where}. - * For example: <pre> {@code - * - * static <T> TypeToken<List<T>> listOf(Class<T> elementType) { - * return new TypeToken<List<T>>() {} - * .where(new TypeParameter<T>() {}, elementType); - * } - * }</pre> - * - * @author Ben Yu - * @since 12.0 - */ -@Beta -public abstract class TypeParameter<T> extends TypeCapture<T> { - - final TypeVariable<?> typeVariable; - - protected TypeParameter() { - Type type = capture(); - checkArgument(type instanceof TypeVariable, "%s should be a type variable.", type); - this.typeVariable = (TypeVariable<?>) type; - } - - @Override public final int hashCode() { - return typeVariable.hashCode(); - } - - @Override public final boolean equals(@Nullable Object o) { - if (o instanceof TypeParameter) { - TypeParameter<?> that = (TypeParameter<?>) o; - return typeVariable.equals(that.typeVariable); - } - return false; - } - - @Override public String toString() { - return typeVariable.toString(); - } -} diff --git a/guava/src/com/google/common/reflect/TypeResolver.java b/guava/src/com/google/common/reflect/TypeResolver.java deleted file mode 100644 index 8c2d582..0000000 --- a/guava/src/com/google/common/reflect/TypeResolver.java +++ /dev/null @@ -1,391 +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.reflect; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nullable; - -/** - * An object of this class encapsulates type mappings from type variables. Mappings are established - * with {@link #where} and types are resolved using {@link #resolveType}. - * - * <p>Note that usually type mappings are already implied by the static type hierarchy (for example, - * the {@code E} type variable declared by class {@code List} naturally maps to {@code String} in - * the context of {@code class MyStringList implements List<String>}. In such case, prefer to use - * {@link TypeToken#resolveType} since it's simpler and more type safe. This class should only be - * used when the type mapping isn't implied by the static type hierarchy, but provided through other - * means such as an annotation or external configuration file. - * - * @author Ben Yu - */ -class TypeResolver { - - private final ImmutableMap<TypeVariable<?>, Type> typeTable; - - public TypeResolver() { - this.typeTable = ImmutableMap.of(); - } - - private TypeResolver(ImmutableMap<TypeVariable<?>, Type> typeTable) { - this.typeTable = typeTable; - } - - static TypeResolver accordingTo(Type type) { - return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(type)); - } - - /** - * Returns a new {@code TypeResolver} with type variables in {@code formal} mapping to types in - * {@code actual}. - * - * <p>For example, if {@code formal} is a {@code TypeVariable T}, and {@code actual} is {@code - * String.class}, then {@code new TypeResolver().where(formal, actual)} will {@linkplain - * #resolveType resolve} {@code ParameterizedType List<T>} to {@code List<String>}, and resolve - * {@code Map<T, Something>} to {@code Map<String, Something>} etc. Similarly, {@code formal} and - * {@code actual} can be {@code Map<K, V>} and {@code Map<String, Integer>} respectively, or they - * can be {@code E[]} and {@code String[]} respectively, or even any arbitrary combination - * thereof. - * - * @param formal The type whose type variables or itself is mapped to other type(s). It's almost - * always a bug if {@code formal} isn't a type variable and contains no type variable. Make - * sure you are passing the two parameters in the right order. - * @param actual The type that the formal type variable(s) are mapped to. It can be or contain yet - * other type variables, in which case these type variables will be further resolved if - * corresponding mappings exist in the current {@code TypeResolver} instance. - */ - public final TypeResolver where(Type formal, Type actual) { - Map<TypeVariable<?>, Type> mappings = Maps.newHashMap(); - populateTypeMappings(mappings, checkNotNull(formal), checkNotNull(actual)); - return where(mappings); - } - - /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */ - final TypeResolver where(Map<? extends TypeVariable<?>, ? extends Type> mappings) { - ImmutableMap.Builder<TypeVariable<?>, Type> builder = ImmutableMap.builder(); - builder.putAll(typeTable); - for (Map.Entry<? extends TypeVariable<?>, ? extends Type> mapping : mappings.entrySet()) { - TypeVariable<?> variable = mapping.getKey(); - Type type = mapping.getValue(); - checkArgument(!variable.equals(type), "Type variable %s bound to itself", variable); - builder.put(variable, type); - } - return new TypeResolver(builder.build()); - } - - private static void populateTypeMappings( - Map<TypeVariable<?>, Type> mappings, Type from, Type to) { - if (from.equals(to)) { - return; - } - if (from instanceof TypeVariable) { - mappings.put((TypeVariable<?>) from, to); - } else if (from instanceof GenericArrayType) { - populateTypeMappings(mappings, - ((GenericArrayType) from).getGenericComponentType(), - checkNonNullArgument(Types.getComponentType(to), "%s is not an array type.", to)); - } else if (from instanceof ParameterizedType) { - ParameterizedType fromParameterizedType = (ParameterizedType) from; - ParameterizedType toParameterizedType = expectArgument(ParameterizedType.class, to); - checkArgument(fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()), - "Inconsistent raw type: %s vs. %s", from, to); - Type[] fromArgs = fromParameterizedType.getActualTypeArguments(); - Type[] toArgs = toParameterizedType.getActualTypeArguments(); - checkArgument(fromArgs.length == toArgs.length); - for (int i = 0; i < fromArgs.length; i++) { - populateTypeMappings(mappings, fromArgs[i], toArgs[i]); - } - } else if (from instanceof WildcardType) { - WildcardType fromWildcardType = (WildcardType) from; - WildcardType toWildcardType = expectArgument(WildcardType.class, to); - Type[] fromUpperBounds = fromWildcardType.getUpperBounds(); - Type[] toUpperBounds = toWildcardType.getUpperBounds(); - Type[] fromLowerBounds = fromWildcardType.getLowerBounds(); - Type[] toLowerBounds = toWildcardType.getLowerBounds(); - checkArgument( - fromUpperBounds.length == toUpperBounds.length - && fromLowerBounds.length == toLowerBounds.length, - "Incompatible type: %s vs. %s", from, to); - for (int i = 0; i < fromUpperBounds.length; i++) { - populateTypeMappings(mappings, fromUpperBounds[i], toUpperBounds[i]); - } - for (int i = 0; i < fromLowerBounds.length; i++) { - populateTypeMappings(mappings, fromLowerBounds[i], toLowerBounds[i]); - } - } else { - throw new IllegalArgumentException("No type mapping from " + from); - } - } - - /** - * Resolves all type variables in {@code type} and all downstream types and - * returns a corresponding type with type variables resolved. - */ - public final Type resolveType(Type type) { - checkNotNull(type); - if (type instanceof TypeVariable) { - return resolveTypeVariable((TypeVariable<?>) type); - } else if (type instanceof ParameterizedType) { - return resolveParameterizedType((ParameterizedType) type); - } else if (type instanceof GenericArrayType) { - return resolveGenericArrayType((GenericArrayType) type); - } else if (type instanceof WildcardType) { - WildcardType wildcardType = (WildcardType) type; - return new Types.WildcardTypeImpl( - resolveTypes(wildcardType.getLowerBounds()), - resolveTypes(wildcardType.getUpperBounds())); - } else { - // if Class<?>, no resolution needed, we are done. - return type; - } - } - - private Type[] resolveTypes(Type[] types) { - Type[] result = new Type[types.length]; - for (int i = 0; i < types.length; i++) { - result[i] = resolveType(types[i]); - } - return result; - } - - private Type resolveGenericArrayType(GenericArrayType type) { - Type componentType = resolveType(type.getGenericComponentType()); - return Types.newArrayType(componentType); - } - - private Type resolveTypeVariable(final TypeVariable<?> var) { - final TypeResolver unguarded = this; - TypeResolver guarded = new TypeResolver(typeTable) { - @Override Type resolveTypeVariable( - TypeVariable<?> intermediateVar, TypeResolver guardedResolver) { - if (intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration())) { - return intermediateVar; - } - return unguarded.resolveTypeVariable(intermediateVar, guardedResolver); - } - }; - return resolveTypeVariable(var, guarded); - } - - /** - * Resolves {@code var} using the encapsulated type mapping. If it maps to yet another - * non-reified type, {@code guardedResolver} is used to do further resolution, which doesn't try - * to resolve any type variable on generic declarations that are already being resolved. - */ - Type resolveTypeVariable(TypeVariable<?> var, TypeResolver guardedResolver) { - checkNotNull(guardedResolver); - Type type = typeTable.get(var); - if (type == null) { - Type[] bounds = var.getBounds(); - if (bounds.length == 0) { - return var; - } - return Types.newTypeVariable( - var.getGenericDeclaration(), - var.getName(), - guardedResolver.resolveTypes(bounds)); - } - return guardedResolver.resolveType(type); // in case the type is yet another type variable. - } - - private ParameterizedType resolveParameterizedType(ParameterizedType type) { - Type owner = type.getOwnerType(); - Type resolvedOwner = (owner == null) ? null : resolveType(owner); - Type resolvedRawType = resolveType(type.getRawType()); - - Type[] vars = type.getActualTypeArguments(); - Type[] resolvedArgs = new Type[vars.length]; - for (int i = 0; i < vars.length; i++) { - resolvedArgs[i] = resolveType(vars[i]); - } - return Types.newParameterizedTypeWithOwner( - resolvedOwner, (Class<?>) resolvedRawType, resolvedArgs); - } - - private static <T> T checkNonNullArgument(T arg, String format, Object... messageParams) { - checkArgument(arg != null, format, messageParams); - return arg; - } - - private static <T> T expectArgument(Class<T> type, Object arg) { - try { - return type.cast(arg); - } catch (ClassCastException e) { - throw new IllegalArgumentException(arg + " is not a " + type.getSimpleName()); - } - } - - private static final class TypeMappingIntrospector { - - private static final WildcardCapturer wildcardCapturer = new WildcardCapturer(); - - private final Map<TypeVariable<?>, Type> mappings = Maps.newHashMap(); - private final Set<Type> introspectedTypes = Sets.newHashSet(); - - /** - * Returns type mappings using type parameters and type arguments found in - * the generic superclass and the super interfaces of {@code contextClass}. - */ - static ImmutableMap<TypeVariable<?>, Type> getTypeMappings( - Type contextType) { - TypeMappingIntrospector introspector = new TypeMappingIntrospector(); - introspector.introspect(wildcardCapturer.capture(contextType)); - return ImmutableMap.copyOf(introspector.mappings); - } - - private void introspect(Type type) { - if (!introspectedTypes.add(type)) { - return; - } - if (type instanceof ParameterizedType) { - introspectParameterizedType((ParameterizedType) type); - } else if (type instanceof Class) { - introspectClass((Class<?>) type); - } else if (type instanceof TypeVariable) { - for (Type bound : ((TypeVariable<?>) type).getBounds()) { - introspect(bound); - } - } else if (type instanceof WildcardType) { - for (Type bound : ((WildcardType) type).getUpperBounds()) { - introspect(bound); - } - } - } - - private void introspectClass(Class<?> clazz) { - introspect(clazz.getGenericSuperclass()); - for (Type interfaceType : clazz.getGenericInterfaces()) { - introspect(interfaceType); - } - } - - private void introspectParameterizedType( - ParameterizedType parameterizedType) { - Class<?> rawClass = (Class<?>) parameterizedType.getRawType(); - TypeVariable<?>[] vars = rawClass.getTypeParameters(); - Type[] typeArgs = parameterizedType.getActualTypeArguments(); - checkState(vars.length == typeArgs.length); - for (int i = 0; i < vars.length; i++) { - map(vars[i], typeArgs[i]); - } - introspectClass(rawClass); - introspect(parameterizedType.getOwnerType()); - } - - private void map(final TypeVariable<?> var, final Type arg) { - if (mappings.containsKey(var)) { - // Mapping already established - // This is possible when following both superClass -> enclosingClass - // and enclosingclass -> superClass paths. - // Since we follow the path of superclass first, enclosing second, - // superclass mapping should take precedence. - return; - } - // First, check whether var -> arg forms a cycle - for (Type t = arg; t != null; t = mappings.get(t)) { - if (var.equals(t)) { - // cycle detected, remove the entire cycle from the mapping so that - // each type variable resolves deterministically to itself. - // Otherwise, a F -> T cycle will end up resolving both F and T - // nondeterministically to either F or T. - for (Type x = arg; x != null; x = mappings.remove(x)) {} - return; - } - } - mappings.put(var, arg); - } - } - - // This is needed when resolving types against a context with wildcards - // For example: - // class Holder<T> { - // void set(T data) {...} - // } - // Holder<List<?>> should *not* resolve the set() method to set(List<?> data). - // Instead, it should create a capture of the wildcard so that set() rejects any List<T>. - private static final class WildcardCapturer { - - private final AtomicInteger id = new AtomicInteger(); - - Type capture(Type type) { - checkNotNull(type); - if (type instanceof Class) { - return type; - } - if (type instanceof TypeVariable) { - return type; - } - if (type instanceof GenericArrayType) { - GenericArrayType arrayType = (GenericArrayType) type; - return Types.newArrayType(capture(arrayType.getGenericComponentType())); - } - if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - return Types.newParameterizedTypeWithOwner( - captureNullable(parameterizedType.getOwnerType()), - (Class<?>) parameterizedType.getRawType(), - capture(parameterizedType.getActualTypeArguments())); - } - if (type instanceof WildcardType) { - WildcardType wildcardType = (WildcardType) type; - Type[] lowerBounds = wildcardType.getLowerBounds(); - if (lowerBounds.length == 0) { // ? extends something changes to capture-of - Type[] upperBounds = wildcardType.getUpperBounds(); - String name = "capture#" + id.incrementAndGet() + "-of ? extends " - + Joiner.on('&').join(upperBounds); - return Types.newTypeVariable( - WildcardCapturer.class, name, wildcardType.getUpperBounds()); - } else { - // TODO(benyu): handle ? super T somehow. - return type; - } - } - throw new AssertionError("must have been one of the known types"); - } - - private Type captureNullable(@Nullable Type type) { - if (type == null) { - return null; - } - return capture(type); - } - - private Type[] capture(Type[] types) { - Type[] result = new Type[types.length]; - for (int i = 0; i < types.length; i++) { - result[i] = capture(types[i]); - } - return result; - } - } -} diff --git a/guava/src/com/google/common/reflect/TypeToInstanceMap.java b/guava/src/com/google/common/reflect/TypeToInstanceMap.java deleted file mode 100644 index 3b00820..0000000 --- a/guava/src/com/google/common/reflect/TypeToInstanceMap.java +++ /dev/null @@ -1,92 +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.reflect; - -import com.google.common.annotations.Beta; - -import java.util.Map; - -import javax.annotation.Nullable; - -/** - * A map, each entry of which maps a {@link TypeToken} to an instance of that type. - * In addition to implementing {@code Map}, the additional type-safe operations - * {@link #putInstance} and {@link #getInstance} are available. - * - * <p>Generally, implementations don't support {@link #put} and {@link #putAll} - * because there is no way to check an object at runtime to be an instance of a - * {@link TypeToken}. Instead, caller should use the type safe {@link #putInstance}. - * - * <p>Also, if caller suppresses unchecked warnings and passes in an {@code Iterable<String>} - * for type {@code Iterable<Integer>}, the map won't be able to detect and throw type error. - * - * <p>Like any other {@code Map<Class, Object>}, this map may contain entries - * for primitive types, and a primitive type and its corresponding wrapper type - * may map to different values. - * - * @param <B> the common supertype that all entries must share; often this is - * simply {@link Object} - * - * @author Ben Yu - * @since 13.0 - */ -@Beta -public interface TypeToInstanceMap<B> extends Map<TypeToken<? extends B>, B> { - - /** - * Returns the value the specified class is mapped to, or {@code null} if no - * entry for this class is present. This will only return a value that was - * bound to this specific class, not a value that may have been bound to a - * subtype. - * - * <p>{@code getInstance(Foo.class)} is equivalent to - * {@code getInstance(TypeToken.of(Foo.class))}. - */ - @Nullable - <T extends B> T getInstance(Class<T> type); - - /** - * Maps the specified class to the specified value. Does <i>not</i> associate - * this value with any of the class's supertypes. - * - * <p>{@code putInstance(Foo.class, foo)} is equivalent to - * {@code putInstance(TypeToken.of(Foo.class), foo)}. - * - * @return the value previously associated with this class (possibly {@code null}), - * or {@code null} if there was no previous entry. - */ - @Nullable - <T extends B> T putInstance(Class<T> type, @Nullable T value); - - /** - * Returns the value the specified type is mapped to, or {@code null} if no - * entry for this type is present. This will only return a value that was - * bound to this specific type, not a value that may have been bound to a subtype. - */ - @Nullable - <T extends B> T getInstance(TypeToken<T> type); - - /** - * Maps the specified type to the specified value. Does <i>not</i> associate - * this value with any of the type's supertypes. - * - * @return the value previously associated with this type (possibly {@code null}), - * or {@code null} if there was no previous entry. - */ - @Nullable - <T extends B> T putInstance(TypeToken<T> type, @Nullable T value); -} diff --git a/guava/src/com/google/common/reflect/TypeToken.java b/guava/src/com/google/common/reflect/TypeToken.java deleted file mode 100644 index 61469b5..0000000 --- a/guava/src/com/google/common/reflect/TypeToken.java +++ /dev/null @@ -1,1146 +0,0 @@ -/* - * Copyright (C) 2006 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.reflect; - -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.VisibleForTesting; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ForwardingSet; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; - -import java.io.Serializable; -import java.lang.reflect.Constructor; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Map; -import java.util.Set; - -import javax.annotation.Nullable; - -/** - * A {@link Type} with generics. - * - * <p>Operations that are otherwise only available in {@link Class} are implemented to support - * {@code Type}, for example {@link #isAssignableFrom}, {@link #isArray} and {@link - * #getComponentType}. It also provides additional utilities such as {@link #getTypes} and {@link - * #resolveType} etc. - * - * <p>There are three ways to get a {@code TypeToken} instance: <ul> - * <li>Wrap a {@code Type} obtained via reflection. For example: {@code - * TypeToken.of(method.getGenericReturnType())}. - * <li>Capture a generic type with a (usually anonymous) subclass. For example: <pre> {@code - * - * new TypeToken<List<String>>() {} - * }</pre> - * Note that it's critical that the actual type argument is carried by a subclass. - * The following code is wrong because it only captures the {@code <T>} type variable - * of the {@code listType()} method signature; while {@code <String>} is lost in erasure: - * <pre> {@code - * - * class Util { - * static <T> TypeToken<List<T>> listType() { - * return new TypeToken<List<T>>() {}; - * } - * } - * - * TypeToken<List<String>> stringListType = Util.<String>listType(); - * }</pre> - * <li>Capture a generic type with a (usually anonymous) subclass and resolve it against - * a context class that knows what the type parameters are. For example: <pre> {@code - * abstract class IKnowMyType<T> { - * TypeToken<T> type = new TypeToken<T>(getClass()) {}; - * } - * new IKnowMyType<String>() {}.type => String - * }</pre> - * </ul> - * - * <p>{@code TypeToken} is serializable when no type variable is contained in the type. - * - * <p>Note to Guice users: {@code} TypeToken is similar to Guice's {@code TypeLiteral} class, - * but with one important difference: it supports non-reified types such as {@code T}, - * {@code List<T>} or even {@code List<? extends Number>}; while TypeLiteral does not. - * TypeToken is also serializable and offers numerous additional utility methods. - * - * @author Bob Lee - * @author Sven Mawson - * @author Ben Yu - * @since 12.0 - */ -@Beta -@SuppressWarnings("serial") // SimpleTypeToken is the serialized form. -public abstract class TypeToken<T> extends TypeCapture<T> implements Serializable { - - private final Type runtimeType; - - /** Resolver for resolving types with {@link #runtimeType} as context. */ - private transient TypeResolver typeResolver; - - /** - * Constructs a new type token of {@code T}. - * - * <p>Clients create an empty anonymous subclass. Doing so embeds the type - * parameter in the anonymous class's type hierarchy so we can reconstitute - * it at runtime despite erasure. - * - * <p>For example: <pre> {@code - * - * TypeToken<List<String>> t = new TypeToken<List<String>>() {}; - * }</pre> - */ - protected TypeToken() { - this.runtimeType = capture(); - checkState(!(runtimeType instanceof TypeVariable), - "Cannot construct a TypeToken for a type variable.\n" + - "You probably meant to call new TypeToken<%s>(getClass()) " + - "that can resolve the type variable for you.\n" + - "If you do need to create a TypeToken of a type variable, " + - "please use TypeToken.of() instead.", runtimeType); - } - - /** - * Constructs a new type token of {@code T} while resolving free type variables in the context of - * {@code declaringClass}. - * - * <p>Clients create an empty anonymous subclass. Doing so embeds the type - * parameter in the anonymous class's type hierarchy so we can reconstitute - * it at runtime despite erasure. - * - * <p>For example: <pre> {@code - * - * abstract class IKnowMyType<T> { - * TypeToken<T> getMyType() { - * return new TypeToken<T>(getClass()) {}; - * } - * } - * - * new IKnowMyType<String>() {}.getMyType() => String - * }</pre> - */ - protected TypeToken(Class<?> declaringClass) { - Type captured = super.capture(); - if (captured instanceof Class) { - this.runtimeType = captured; - } else { - this.runtimeType = of(declaringClass).resolveType(captured).runtimeType; - } - } - - private TypeToken(Type type) { - this.runtimeType = checkNotNull(type); - } - - /** Returns an instance of type token that wraps {@code type}. */ - public static <T> TypeToken<T> of(Class<T> type) { - return new SimpleTypeToken<T>(type); - } - - /** Returns an instance of type token that wraps {@code type}. */ - public static TypeToken<?> of(Type type) { - return new SimpleTypeToken<Object>(type); - } - - /** - * Returns the raw type of {@code T}. Formally speaking, if {@code T} is returned by - * {@link java.lang.reflect.Method#getGenericReturnType}, the raw type is what's returned by - * {@link java.lang.reflect.Method#getReturnType} of the same method object. Specifically: - * <ul> - * <li>If {@code T} is a {@code Class} itself, {@code T} itself is returned. - * <li>If {@code T} is a {@link ParameterizedType}, the raw type of the parameterized type is - * returned. - * <li>If {@code T} is a {@link GenericArrayType}, the returned type is the corresponding array - * class. For example: {@code List<Integer>[] => List[]}. - * <li>If {@code T} is a type variable or a wildcard type, the raw type of the first upper bound - * is returned. For example: {@code <X extends Foo> => Foo}. - * </ul> - */ - public final Class<? super T> getRawType() { - Class<?> rawType = getRawType(runtimeType); - @SuppressWarnings("unchecked") // raw type is |T| - Class<? super T> result = (Class<? super T>) rawType; - return result; - } - - /** - * Returns the raw type of the class or parameterized type; if {@code T} is type variable or - * wildcard type, the raw types of all its upper bounds are returned. - */ - private ImmutableSet<Class<? super T>> getImmediateRawTypes() { - // Cast from ImmutableSet<Class<?>> to ImmutableSet<Class<? super T>> - @SuppressWarnings({"unchecked", "rawtypes"}) - ImmutableSet<Class<? super T>> result = (ImmutableSet) getRawTypes(runtimeType); - return result; - } - - /** Returns the represented type. */ - public final Type getType() { - return runtimeType; - } - - /** - * Returns a new {@code TypeToken} where type variables represented by {@code typeParam} - * are substituted by {@code typeArg}. For example, it can be used to construct - * {@code Map<K, V>} for any {@code K} and {@code V} type: <pre> {@code - * - * static <K, V> TypeToken<Map<K, V>> mapOf( - * TypeToken<K> keyType, TypeToken<V> valueType) { - * return new TypeToken<Map<K, V>>() {} - * .where(new TypeParameter<K>() {}, keyType) - * .where(new TypeParameter<V>() {}, valueType); - * } - * }</pre> - * - * @param <X> The parameter type - * @param typeParam the parameter type variable - * @param typeArg the actual type to substitute - */ - public final <X> TypeToken<T> where(TypeParameter<X> typeParam, TypeToken<X> typeArg) { - TypeResolver resolver = new TypeResolver() - .where(ImmutableMap.of(typeParam.typeVariable, typeArg.runtimeType)); - // If there's any type error, we'd report now rather than later. - return new SimpleTypeToken<T>(resolver.resolveType(runtimeType)); - } - - /** - * Returns a new {@code TypeToken} where type variables represented by {@code typeParam} - * are substituted by {@code typeArg}. For example, it can be used to construct - * {@code Map<K, V>} for any {@code K} and {@code V} type: <pre> {@code - * - * static <K, V> TypeToken<Map<K, V>> mapOf( - * Class<K> keyType, Class<V> valueType) { - * return new TypeToken<Map<K, V>>() {} - * .where(new TypeParameter<K>() {}, keyType) - * .where(new TypeParameter<V>() {}, valueType); - * } - * }</pre> - * - * @param <X> The parameter type - * @param typeParam the parameter type variable - * @param typeArg the actual type to substitute - */ - public final <X> TypeToken<T> where(TypeParameter<X> typeParam, Class<X> typeArg) { - return where(typeParam, of(typeArg)); - } - - /** - * Resolves the given {@code type} against the type context represented by this type. - * For example: <pre> {@code - * - * new TypeToken<List<String>>() {}.resolveType( - * List.class.getMethod("get", int.class).getGenericReturnType()) - * => String.class - * }</pre> - */ - public final TypeToken<?> resolveType(Type type) { - checkNotNull(type); - TypeResolver resolver = typeResolver; - if (resolver == null) { - resolver = (typeResolver = TypeResolver.accordingTo(runtimeType)); - } - return of(resolver.resolveType(type)); - } - - private Type[] resolveInPlace(Type[] types) { - for (int i = 0; i < types.length; i++) { - types[i] = resolveType(types[i]).getType(); - } - return types; - } - - private TypeToken<?> resolveSupertype(Type type) { - TypeToken<?> supertype = resolveType(type); - // super types' type mapping is a subset of type mapping of this type. - supertype.typeResolver = typeResolver; - return supertype; - } - - /** - * Returns the generic superclass of this type or {@code null} if the type represents - * {@link Object} or an interface. This method is similar but different from {@link - * Class#getGenericSuperclass}. For example, {@code - * new TypeToken<StringArrayList>() {}.getGenericSuperclass()} will return {@code - * new TypeToken<ArrayList<String>>() {}}; while {@code - * StringArrayList.class.getGenericSuperclass()} will return {@code ArrayList<E>}, where {@code E} - * is the type variable declared by class {@code ArrayList}. - * - * <p>If this type is a type variable or wildcard, its first upper bound is examined and returned - * if the bound is a class or extends from a class. This means that the returned type could be a - * type variable too. - */ - @Nullable - final TypeToken<? super T> getGenericSuperclass() { - if (runtimeType instanceof TypeVariable) { - // First bound is always the super class, if one exists. - return boundAsSuperclass(((TypeVariable<?>) runtimeType).getBounds()[0]); - } - if (runtimeType instanceof WildcardType) { - // wildcard has one and only one upper bound. - return boundAsSuperclass(((WildcardType) runtimeType).getUpperBounds()[0]); - } - Type superclass = getRawType().getGenericSuperclass(); - if (superclass == null) { - return null; - } - @SuppressWarnings("unchecked") // super class of T - TypeToken<? super T> superToken = (TypeToken<? super T>) resolveSupertype(superclass); - return superToken; - } - - @Nullable private TypeToken<? super T> boundAsSuperclass(Type bound) { - TypeToken<?> token = of(bound); - if (token.getRawType().isInterface()) { - return null; - } - @SuppressWarnings("unchecked") // only upper bound of T is passed in. - TypeToken<? super T> superclass = (TypeToken<? super T>) token; - return superclass; - } - - /** - * Returns the generic interfaces that this type directly {@code implements}. This method is - * similar but different from {@link Class#getGenericInterfaces()}. For example, {@code - * new TypeToken<List<String>>() {}.getGenericInterfaces()} will return a list that contains - * {@code new TypeToken<Iterable<String>>() {}}; while {@code List.class.getGenericInterfaces()} - * will return an array that contains {@code Iterable<T>}, where the {@code T} is the type - * variable declared by interface {@code Iterable}. - * - * <p>If this type is a type variable or wildcard, its upper bounds are examined and those that - * are either an interface or upper-bounded only by interfaces are returned. This means that the - * returned types could include type variables too. - */ - final ImmutableList<TypeToken<? super T>> getGenericInterfaces() { - if (runtimeType instanceof TypeVariable) { - return boundsAsInterfaces(((TypeVariable<?>) runtimeType).getBounds()); - } - if (runtimeType instanceof WildcardType) { - return boundsAsInterfaces(((WildcardType) runtimeType).getUpperBounds()); - } - ImmutableList.Builder<TypeToken<? super T>> builder = ImmutableList.builder(); - for (Type interfaceType : getRawType().getGenericInterfaces()) { - @SuppressWarnings("unchecked") // interface of T - TypeToken<? super T> resolvedInterface = (TypeToken<? super T>) - resolveSupertype(interfaceType); - builder.add(resolvedInterface); - } - return builder.build(); - } - - private ImmutableList<TypeToken<? super T>> boundsAsInterfaces(Type[] bounds) { - ImmutableList.Builder<TypeToken<? super T>> builder = ImmutableList.builder(); - for (Type bound : bounds) { - @SuppressWarnings("unchecked") // upper bound of T - TypeToken<? super T> boundType = (TypeToken<? super T>) of(bound); - if (boundType.getRawType().isInterface()) { - builder.add(boundType); - } - } - return builder.build(); - } - - /** - * Returns the set of interfaces and classes that this type is or is a subtype of. The returned - * types are parameterized with proper type arguments. - * - * <p>Subtypes are always listed before supertypes. But the reverse is not true. A type isn't - * necessarily a subtype of all the types following. Order between types without subtype - * relationship is arbitrary and not guaranteed. - * - * <p>If this type is a type variable or wildcard, upper bounds that are themselves type variables - * aren't included (their super interfaces and superclasses are). - */ - public final TypeSet getTypes() { - return new TypeSet(); - } - - /** - * Returns the generic form of {@code superclass}. For example, if this is - * {@code ArrayList<String>}, {@code Iterable<String>} is returned given the - * input {@code Iterable.class}. - */ - public final TypeToken<? super T> getSupertype(Class<? super T> superclass) { - checkArgument(superclass.isAssignableFrom(getRawType()), - "%s is not a super class of %s", superclass, this); - if (runtimeType instanceof TypeVariable) { - return getSupertypeFromUpperBounds(superclass, ((TypeVariable<?>) runtimeType).getBounds()); - } - if (runtimeType instanceof WildcardType) { - return getSupertypeFromUpperBounds(superclass, ((WildcardType) runtimeType).getUpperBounds()); - } - if (superclass.isArray()) { - return getArraySupertype(superclass); - } - @SuppressWarnings("unchecked") // resolved supertype - TypeToken<? super T> supertype = (TypeToken<? super T>) - resolveSupertype(toGenericType(superclass).runtimeType); - return supertype; - } - - /** - * Returns subtype of {@code this} with {@code subclass} as the raw class. - * For example, if this is {@code Iterable<String>} and {@code subclass} is {@code List}, - * {@code List<String>} is returned. - */ - public final TypeToken<? extends T> getSubtype(Class<?> subclass) { - checkArgument(!(runtimeType instanceof TypeVariable), - "Cannot get subtype of type variable <%s>", this); - if (runtimeType instanceof WildcardType) { - return getSubtypeFromLowerBounds(subclass, ((WildcardType) runtimeType).getLowerBounds()); - } - checkArgument(getRawType().isAssignableFrom(subclass), - "%s isn't a subclass of %s", subclass, this); - // unwrap array type if necessary - if (isArray()) { - return getArraySubtype(subclass); - } - @SuppressWarnings("unchecked") // guarded by the isAssignableFrom() statement above - TypeToken<? extends T> subtype = (TypeToken<? extends T>) - of(resolveTypeArgsForSubclass(subclass)); - return subtype; - } - - /** Returns true if this type is assignable from the given {@code type}. */ - public final boolean isAssignableFrom(TypeToken<?> type) { - return isAssignableFrom(type.runtimeType); - } - - /** Check if this type is assignable from the given {@code type}. */ - public final boolean isAssignableFrom(Type type) { - return isAssignable(checkNotNull(type), runtimeType); - } - - /** - * Returns true if this type is known to be an array type, such as {@code int[]}, {@code T[]}, - * {@code <? extends Map<String, Integer>[]>} etc. - */ - public final boolean isArray() { - return getComponentType() != null; - } - - /** - * Returns the array component type if this type represents an array ({@code int[]}, {@code T[]}, - * {@code <? extends Map<String, Integer>[]>} etc.), or else {@code null} is returned. - */ - @Nullable public final TypeToken<?> getComponentType() { - Type componentType = Types.getComponentType(runtimeType); - if (componentType == null) { - return null; - } - return of(componentType); - } - - /** - * Returns the {@link Invokable} for {@code method}, which must be a member of {@code T}. - * - * @since 14.0 - */ - public final Invokable<T, Object> method(Method method) { - checkArgument(of(method.getDeclaringClass()).isAssignableFrom(this), - "%s not declared by %s", method, this); - return new Invokable.MethodInvokable<T>(method) { - @Override Type getGenericReturnType() { - return resolveType(super.getGenericReturnType()).getType(); - } - @Override Type[] getGenericParameterTypes() { - return resolveInPlace(super.getGenericParameterTypes()); - } - @Override Type[] getGenericExceptionTypes() { - return resolveInPlace(super.getGenericExceptionTypes()); - } - @Override public TypeToken<T> getOwnerType() { - return TypeToken.this; - } - }; - } - - /** - * Returns the {@link Invokable} for {@code constructor}, which must be a member of {@code T}. - * - * @since 14.0 - */ - public final Invokable<T, T> constructor(Constructor<?> constructor) { - checkArgument(constructor.getDeclaringClass() == getRawType(), - "%s not declared by %s", constructor, getRawType()); - return new Invokable.ConstructorInvokable<T>(constructor) { - @Override Type getGenericReturnType() { - return resolveType(super.getGenericReturnType()).getType(); - } - @Override Type[] getGenericParameterTypes() { - return resolveInPlace(super.getGenericParameterTypes()); - } - @Override Type[] getGenericExceptionTypes() { - return resolveInPlace(super.getGenericExceptionTypes()); - } - @Override public TypeToken<T> getOwnerType() { - return TypeToken.this; - } - }; - } - - /** - * The set of interfaces and classes that {@code T} is or is a subtype of. {@link Object} is not - * included in the set if this type is an interface. - */ - public class TypeSet extends ForwardingSet<TypeToken<? super T>> implements Serializable { - - private transient ImmutableSet<TypeToken<? super T>> types; - - TypeSet() {} - - /** Returns the types that are interfaces implemented by this type. */ - public TypeSet interfaces() { - return new InterfaceSet(this); - } - - /** Returns the types that are classes. */ - public TypeSet classes() { - return new ClassSet(); - } - - @Override protected Set<TypeToken<? super T>> delegate() { - ImmutableSet<TypeToken<? super T>> filteredTypes = types; - if (filteredTypes == null) { - // Java has no way to express ? super T when we parameterize TypeToken vs. Class. - @SuppressWarnings({"unchecked", "rawtypes"}) - ImmutableList<TypeToken<? super T>> collectedTypes = (ImmutableList) - TypeCollector.FOR_GENERIC_TYPE.collectTypes(TypeToken.this); - return (types = FluentIterable.from(collectedTypes) - .filter(TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD) - .toSet()); - } else { - return filteredTypes; - } - } - - /** Returns the raw types of the types in this set, in the same order. */ - public Set<Class<? super T>> rawTypes() { - // Java has no way to express ? super T when we parameterize TypeToken vs. Class. - @SuppressWarnings({"unchecked", "rawtypes"}) - ImmutableList<Class<? super T>> collectedTypes = (ImmutableList) - TypeCollector.FOR_RAW_TYPE.collectTypes(getImmediateRawTypes()); - return ImmutableSet.copyOf(collectedTypes); - } - - private static final long serialVersionUID = 0; - } - - private final class InterfaceSet extends TypeSet { - - private transient final TypeSet allTypes; - private transient ImmutableSet<TypeToken<? super T>> interfaces; - - InterfaceSet(TypeSet allTypes) { - this.allTypes = allTypes; - } - - @Override protected Set<TypeToken<? super T>> delegate() { - ImmutableSet<TypeToken<? super T>> result = interfaces; - if (result == null) { - return (interfaces = FluentIterable.from(allTypes) - .filter(TypeFilter.INTERFACE_ONLY) - .toSet()); - } else { - return result; - } - } - - @Override public TypeSet interfaces() { - return this; - } - - @Override public Set<Class<? super T>> rawTypes() { - // Java has no way to express ? super T when we parameterize TypeToken vs. Class. - @SuppressWarnings({"unchecked", "rawtypes"}) - ImmutableList<Class<? super T>> collectedTypes = (ImmutableList) - TypeCollector.FOR_RAW_TYPE.collectTypes(getImmediateRawTypes()); - return FluentIterable.from(collectedTypes) - .filter(new Predicate<Class<?>>() { - @Override public boolean apply(Class<?> type) { - return type.isInterface(); - } - }) - .toSet(); - } - - @Override public TypeSet classes() { - throw new UnsupportedOperationException("interfaces().classes() not supported."); - } - - private Object readResolve() { - return getTypes().interfaces(); - } - - private static final long serialVersionUID = 0; - } - - private final class ClassSet extends TypeSet { - - private transient ImmutableSet<TypeToken<? super T>> classes; - - @Override protected Set<TypeToken<? super T>> delegate() { - ImmutableSet<TypeToken<? super T>> result = classes; - if (result == null) { - @SuppressWarnings({"unchecked", "rawtypes"}) - ImmutableList<TypeToken<? super T>> collectedTypes = (ImmutableList) - TypeCollector.FOR_GENERIC_TYPE.classesOnly().collectTypes(TypeToken.this); - return (classes = FluentIterable.from(collectedTypes) - .filter(TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD) - .toSet()); - } else { - return result; - } - } - - @Override public TypeSet classes() { - return this; - } - - @Override public Set<Class<? super T>> rawTypes() { - // Java has no way to express ? super T when we parameterize TypeToken vs. Class. - @SuppressWarnings({"unchecked", "rawtypes"}) - ImmutableList<Class<? super T>> collectedTypes = (ImmutableList) - TypeCollector.FOR_RAW_TYPE.classesOnly().collectTypes(getImmediateRawTypes()); - return ImmutableSet.copyOf(collectedTypes); - } - - @Override public TypeSet interfaces() { - throw new UnsupportedOperationException("classes().interfaces() not supported."); - } - - private Object readResolve() { - return getTypes().classes(); - } - - private static final long serialVersionUID = 0; - } - - private enum TypeFilter implements Predicate<TypeToken<?>> { - - IGNORE_TYPE_VARIABLE_OR_WILDCARD { - @Override public boolean apply(TypeToken<?> type) { - return !(type.runtimeType instanceof TypeVariable - || type.runtimeType instanceof WildcardType); - } - }, - INTERFACE_ONLY { - @Override public boolean apply(TypeToken<?> type) { - return type.getRawType().isInterface(); - } - } - } - - /** - * Returns true if {@code o} is another {@code TypeToken} that represents the same {@link Type}. - */ - @Override public boolean equals(@Nullable Object o) { - if (o instanceof TypeToken) { - TypeToken<?> that = (TypeToken<?>) o; - return runtimeType.equals(that.runtimeType); - } - return false; - } - - @Override public int hashCode() { - return runtimeType.hashCode(); - } - - @Override public String toString() { - return Types.toString(runtimeType); - } - - /** Implemented to support serialization of subclasses. */ - protected Object writeReplace() { - // TypeResolver just transforms the type to our own impls that are Serializable - // except TypeVariable. - return of(new TypeResolver().resolveType(runtimeType)); - } - - /** - * Ensures that this type token doesn't contain type variables, which can cause unchecked type - * errors for callers like {@link TypeToInstanceMap}. - */ - final TypeToken<T> rejectTypeVariables() { - checkArgument(!Types.containsTypeVariable(runtimeType), - "%s contains a type variable and is not safe for the operation"); - return this; - } - - private static boolean isAssignable(Type from, Type to) { - if (to.equals(from)) { - return true; - } - if (to instanceof WildcardType) { - return isAssignableToWildcardType(from, (WildcardType) to); - } - // if "from" is type variable, it's assignable if any of its "extends" - // bounds is assignable to "to". - if (from instanceof TypeVariable) { - return isAssignableFromAny(((TypeVariable<?>) from).getBounds(), to); - } - // if "from" is wildcard, it'a assignable to "to" if any of its "extends" - // bounds is assignable to "to". - if (from instanceof WildcardType) { - return isAssignableFromAny(((WildcardType) from).getUpperBounds(), to); - } - if (from instanceof GenericArrayType) { - return isAssignableFromGenericArrayType((GenericArrayType) from, to); - } - // Proceed to regular Type assignability check - if (to instanceof Class) { - return isAssignableToClass(from, (Class<?>) to); - } else if (to instanceof ParameterizedType) { - return isAssignableToParameterizedType(from, (ParameterizedType) to); - } else if (to instanceof GenericArrayType) { - return isAssignableToGenericArrayType(from, (GenericArrayType) to); - } else { // to instanceof TypeVariable - return false; - } - } - - private static boolean isAssignableFromAny(Type[] fromTypes, Type to) { - for (Type from : fromTypes) { - if (isAssignable(from, to)) { - return true; - } - } - return false; - } - - private static boolean isAssignableToClass(Type from, Class<?> to) { - return to.isAssignableFrom(getRawType(from)); - } - - private static boolean isAssignableToWildcardType( - Type from, WildcardType to) { - // if "to" is <? extends Foo>, "from" can be: - // Foo, SubFoo, <? extends Foo>, <? extends SubFoo>, <T extends Foo> or - // <T extends SubFoo>. - // if "to" is <? super Foo>, "from" can be: - // Foo, SuperFoo, <? super Foo> or <? super SuperFoo>. - return isAssignable(from, supertypeBound(to)) && isAssignableBySubtypeBound(from, to); - } - - private static boolean isAssignableBySubtypeBound(Type from, WildcardType to) { - Type toSubtypeBound = subtypeBound(to); - if (toSubtypeBound == null) { - return true; - } - Type fromSubtypeBound = subtypeBound(from); - if (fromSubtypeBound == null) { - return false; - } - return isAssignable(toSubtypeBound, fromSubtypeBound); - } - - private static boolean isAssignableToParameterizedType(Type from, ParameterizedType to) { - Class<?> matchedClass = getRawType(to); - if (!matchedClass.isAssignableFrom(getRawType(from))) { - return false; - } - Type[] typeParams = matchedClass.getTypeParameters(); - Type[] toTypeArgs = to.getActualTypeArguments(); - TypeToken<?> fromTypeToken = of(from); - for (int i = 0; i < typeParams.length; i++) { - // If "to" is "List<? extends CharSequence>" - // and "from" is StringArrayList, - // First step is to figure out StringArrayList "is-a" List<E> and <E> is - // String. - // typeParams[0] is E and fromTypeToken.get(typeParams[0]) will resolve to - // String. - // String is then matched against <? extends CharSequence>. - Type fromTypeArg = fromTypeToken.resolveType(typeParams[i]).runtimeType; - if (!matchTypeArgument(fromTypeArg, toTypeArgs[i])) { - return false; - } - } - return true; - } - - private static boolean isAssignableToGenericArrayType(Type from, GenericArrayType to) { - if (from instanceof Class) { - Class<?> fromClass = (Class<?>) from; - if (!fromClass.isArray()) { - return false; - } - return isAssignable(fromClass.getComponentType(), to.getGenericComponentType()); - } else if (from instanceof GenericArrayType) { - GenericArrayType fromArrayType = (GenericArrayType) from; - return isAssignable(fromArrayType.getGenericComponentType(), to.getGenericComponentType()); - } else { - return false; - } - } - - private static boolean isAssignableFromGenericArrayType(GenericArrayType from, Type to) { - if (to instanceof Class) { - Class<?> toClass = (Class<?>) to; - if (!toClass.isArray()) { - return toClass == Object.class; // any T[] is assignable to Object - } - return isAssignable(from.getGenericComponentType(), toClass.getComponentType()); - } else if (to instanceof GenericArrayType) { - GenericArrayType toArrayType = (GenericArrayType) to; - return isAssignable(from.getGenericComponentType(), toArrayType.getGenericComponentType()); - } else { - return false; - } - } - - private static boolean matchTypeArgument(Type from, Type to) { - if (from.equals(to)) { - return true; - } - if (to instanceof WildcardType) { - return isAssignableToWildcardType(from, (WildcardType) to); - } - return false; - } - - private static Type supertypeBound(Type type) { - if (type instanceof WildcardType) { - return supertypeBound((WildcardType) type); - } - return type; - } - - private static Type supertypeBound(WildcardType type) { - Type[] upperBounds = type.getUpperBounds(); - if (upperBounds.length == 1) { - return supertypeBound(upperBounds[0]); - } else if (upperBounds.length == 0) { - return Object.class; - } else { - throw new AssertionError( - "There should be at most one upper bound for wildcard type: " + type); - } - } - - @Nullable private static Type subtypeBound(Type type) { - if (type instanceof WildcardType) { - return subtypeBound((WildcardType) type); - } else { - return type; - } - } - - @Nullable private static Type subtypeBound(WildcardType type) { - Type[] lowerBounds = type.getLowerBounds(); - if (lowerBounds.length == 1) { - return subtypeBound(lowerBounds[0]); - } else if (lowerBounds.length == 0) { - return null; - } else { - throw new AssertionError( - "Wildcard should have at most one lower bound: " + type); - } - } - - @VisibleForTesting static Class<?> getRawType(Type type) { - // For wildcard or type variable, the first bound determines the runtime type. - return getRawTypes(type).iterator().next(); - } - - @VisibleForTesting static ImmutableSet<Class<?>> getRawTypes(Type type) { - if (type instanceof Class) { - return ImmutableSet.<Class<?>>of((Class<?>) type); - } else if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - // JDK implementation declares getRawType() to return Class<?>: http://goo.gl/YzaEd - return ImmutableSet.<Class<?>>of((Class<?>) parameterizedType.getRawType()); - } else if (type instanceof GenericArrayType) { - GenericArrayType genericArrayType = (GenericArrayType) type; - return ImmutableSet.<Class<?>>of(Types.getArrayClass( - getRawType(genericArrayType.getGenericComponentType()))); - } else if (type instanceof TypeVariable) { - return getRawTypes(((TypeVariable<?>) type).getBounds()); - } else if (type instanceof WildcardType) { - return getRawTypes(((WildcardType) type).getUpperBounds()); - } else { - throw new AssertionError(type + " unsupported"); - } - } - - private static ImmutableSet<Class<?>> getRawTypes(Type[] types) { - ImmutableSet.Builder<Class<?>> builder = ImmutableSet.builder(); - for (Type type : types) { - builder.addAll(getRawTypes(type)); - } - return builder.build(); - } - - /** - * Returns the type token representing the generic type declaration of {@code cls}. For example: - * {@code TypeToken.getGenericType(Iterable.class)} returns {@code Iterable<T>}. - * - * <p>If {@code cls} isn't parameterized and isn't a generic array, the type token of the class is - * returned. - */ - @VisibleForTesting static <T> TypeToken<? extends T> toGenericType(Class<T> cls) { - if (cls.isArray()) { - Type arrayOfGenericType = Types.newArrayType( - // If we are passed with int[].class, don't turn it to GenericArrayType - toGenericType(cls.getComponentType()).runtimeType); - @SuppressWarnings("unchecked") // array is covariant - TypeToken<? extends T> result = (TypeToken<? extends T>) of(arrayOfGenericType); - return result; - } - TypeVariable<Class<T>>[] typeParams = cls.getTypeParameters(); - if (typeParams.length > 0) { - @SuppressWarnings("unchecked") // Like, it's Iterable<T> for Iterable.class - TypeToken<? extends T> type = (TypeToken<? extends T>) - of(Types.newParameterizedType(cls, typeParams)); - return type; - } else { - return of(cls); - } - } - - private TypeToken<? super T> getSupertypeFromUpperBounds( - Class<? super T> supertype, Type[] upperBounds) { - for (Type upperBound : upperBounds) { - @SuppressWarnings("unchecked") // T's upperbound is <? super T>. - TypeToken<? super T> bound = (TypeToken<? super T>) of(upperBound); - if (of(supertype).isAssignableFrom(bound)) { - @SuppressWarnings({"rawtypes", "unchecked"}) // guarded by the isAssignableFrom check. - TypeToken<? super T> result = bound.getSupertype((Class) supertype); - return result; - } - } - throw new IllegalArgumentException(supertype + " isn't a super type of " + this); - } - - private TypeToken<? extends T> getSubtypeFromLowerBounds(Class<?> subclass, Type[] lowerBounds) { - for (Type lowerBound : lowerBounds) { - @SuppressWarnings("unchecked") // T's lower bound is <? extends T> - TypeToken<? extends T> bound = (TypeToken<? extends T>) of(lowerBound); - // Java supports only one lowerbound anyway. - return bound.getSubtype(subclass); - } - throw new IllegalArgumentException(subclass + " isn't a subclass of " + this); - } - - private TypeToken<? super T> getArraySupertype(Class<? super T> supertype) { - // with component type, we have lost generic type information - // Use raw type so that compiler allows us to call getSupertype() - @SuppressWarnings("rawtypes") - TypeToken componentType = checkNotNull(getComponentType(), - "%s isn't a super type of %s", supertype, this); - // array is covariant. component type is super type, so is the array type. - @SuppressWarnings("unchecked") // going from raw type back to generics - TypeToken<?> componentSupertype = componentType.getSupertype(supertype.getComponentType()); - @SuppressWarnings("unchecked") // component type is super type, so is array type. - TypeToken<? super T> result = (TypeToken<? super T>) - // If we are passed with int[].class, don't turn it to GenericArrayType - of(newArrayClassOrGenericArrayType(componentSupertype.runtimeType)); - return result; - } - - private TypeToken<? extends T> getArraySubtype(Class<?> subclass) { - // array is covariant. component type is subtype, so is the array type. - TypeToken<?> componentSubtype = getComponentType() - .getSubtype(subclass.getComponentType()); - @SuppressWarnings("unchecked") // component type is subtype, so is array type. - TypeToken<? extends T> result = (TypeToken<? extends T>) - // If we are passed with int[].class, don't turn it to GenericArrayType - of(newArrayClassOrGenericArrayType(componentSubtype.runtimeType)); - return result; - } - - private Type resolveTypeArgsForSubclass(Class<?> subclass) { - if (runtimeType instanceof Class) { - // no resolution needed - return subclass; - } - // class Base<A, B> {} - // class Sub<X, Y> extends Base<X, Y> {} - // Base<String, Integer>.subtype(Sub.class): - - // Sub<X, Y>.getSupertype(Base.class) => Base<X, Y> - // => X=String, Y=Integer - // => Sub<X, Y>=Sub<String, Integer> - TypeToken<?> genericSubtype = toGenericType(subclass); - @SuppressWarnings({"rawtypes", "unchecked"}) // subclass isn't <? extends T> - Type supertypeWithArgsFromSubtype = genericSubtype - .getSupertype((Class) getRawType()) - .runtimeType; - return new TypeResolver().where(supertypeWithArgsFromSubtype, runtimeType) - .resolveType(genericSubtype.runtimeType); - } - - /** - * Creates an array class if {@code componentType} is a class, or else, a - * {@link GenericArrayType}. This is what Java7 does for generic array type - * parameters. - */ - private static Type newArrayClassOrGenericArrayType(Type componentType) { - return Types.JavaVersion.JAVA7.newArrayType(componentType); - } - - private static final class SimpleTypeToken<T> extends TypeToken<T> { - - SimpleTypeToken(Type type) { - super(type); - } - - private static final long serialVersionUID = 0; - } - - /** - * Collects parent types from a sub type. - * - * @param <K> The type "kind". Either a TypeToken, or Class. - */ - private abstract static class TypeCollector<K> { - - static final TypeCollector<TypeToken<?>> FOR_GENERIC_TYPE = - new TypeCollector<TypeToken<?>>() { - @Override Class<?> getRawType(TypeToken<?> type) { - return type.getRawType(); - } - - @Override Iterable<? extends TypeToken<?>> getInterfaces(TypeToken<?> type) { - return type.getGenericInterfaces(); - } - - @Nullable - @Override TypeToken<?> getSuperclass(TypeToken<?> type) { - return type.getGenericSuperclass(); - } - }; - - static final TypeCollector<Class<?>> FOR_RAW_TYPE = - new TypeCollector<Class<?>>() { - @Override Class<?> getRawType(Class<?> type) { - return type; - } - - @Override Iterable<? extends Class<?>> getInterfaces(Class<?> type) { - return Arrays.asList(type.getInterfaces()); - } - - @Nullable - @Override Class<?> getSuperclass(Class<?> type) { - return type.getSuperclass(); - } - }; - - /** For just classes, we don't have to traverse interfaces. */ - final TypeCollector<K> classesOnly() { - return new ForwardingTypeCollector<K>(this) { - @Override Iterable<? extends K> getInterfaces(K type) { - return ImmutableSet.of(); - } - @Override ImmutableList<K> collectTypes(Iterable<? extends K> types) { - ImmutableList.Builder<K> builder = ImmutableList.builder(); - for (K type : types) { - if (!getRawType(type).isInterface()) { - builder.add(type); - } - } - return super.collectTypes(builder.build()); - } - }; - } - - final ImmutableList<K> collectTypes(K type) { - return collectTypes(ImmutableList.of(type)); - } - - ImmutableList<K> collectTypes(Iterable<? extends K> types) { - // type -> order number. 1 for Object, 2 for anything directly below, so on so forth. - Map<K, Integer> map = Maps.newHashMap(); - for (K type : types) { - collectTypes(type, map); - } - return sortKeysByValue(map, Ordering.natural().reverse()); - } - - /** Collects all types to map, and returns the total depth from T up to Object. */ - private int collectTypes(K type, Map<? super K, Integer> map) { - Integer existing = map.get(this); - if (existing != null) { - // short circuit: if set contains type it already contains its supertypes - return existing; - } - int aboveMe = getRawType(type).isInterface() - ? 1 // interfaces should be listed before Object - : 0; - for (K interfaceType : getInterfaces(type)) { - aboveMe = Math.max(aboveMe, collectTypes(interfaceType, map)); - } - K superclass = getSuperclass(type); - if (superclass != null) { - aboveMe = Math.max(aboveMe, collectTypes(superclass, map)); - } - /* - * TODO(benyu): should we include Object for interface? - * Also, CharSequence[] and Object[] for String[]? - * - */ - map.put(type, aboveMe + 1); - return aboveMe + 1; - } - - private static <K, V> ImmutableList<K> sortKeysByValue( - final Map<K, V> map, final Comparator<? super V> valueComparator) { - Ordering<K> keyOrdering = new Ordering<K>() { - @Override public int compare(K left, K right) { - return valueComparator.compare(map.get(left), map.get(right)); - } - }; - return keyOrdering.immutableSortedCopy(map.keySet()); - } - - abstract Class<?> getRawType(K type); - abstract Iterable<? extends K> getInterfaces(K type); - @Nullable abstract K getSuperclass(K type); - - private static class ForwardingTypeCollector<K> extends TypeCollector<K> { - - private final TypeCollector<K> delegate; - - ForwardingTypeCollector(TypeCollector<K> delegate) { - this.delegate = delegate; - } - - @Override Class<?> getRawType(K type) { - return delegate.getRawType(type); - } - - @Override Iterable<? extends K> getInterfaces(K type) { - return delegate.getInterfaces(type); - } - - @Override K getSuperclass(K type) { - return delegate.getSuperclass(type); - } - } - } -} diff --git a/guava/src/com/google/common/reflect/Types.java b/guava/src/com/google/common/reflect/Types.java deleted file mode 100644 index c19f38e..0000000 --- a/guava/src/com/google/common/reflect/Types.java +++ /dev/null @@ -1,504 +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.reflect; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.Iterables.transform; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.base.Objects; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; - -import java.io.Serializable; -import java.lang.reflect.Array; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.GenericDeclaration; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; -import java.util.Arrays; -import java.util.Collection; - -import javax.annotation.Nullable; - -/** - * Utilities for working with {@link Type}. - * - * @author Ben Yu - */ -final class Types { - - /** Class#toString without the "class " and "interface " prefixes */ - private static final Function<Type, String> TYPE_TO_STRING = - new Function<Type, String>() { - @Override public String apply(Type from) { - return Types.toString(from); - } - }; - - private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null"); - - /** Returns the array type of {@code componentType}. */ - static Type newArrayType(Type componentType) { - if (componentType instanceof WildcardType) { - WildcardType wildcard = (WildcardType) componentType; - Type[] lowerBounds = wildcard.getLowerBounds(); - checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds."); - if (lowerBounds.length == 1) { - return supertypeOf(newArrayType(lowerBounds[0])); - } else { - Type[] upperBounds = wildcard.getUpperBounds(); - checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound."); - return subtypeOf(newArrayType(upperBounds[0])); - } - } - return JavaVersion.CURRENT.newArrayType(componentType); - } - - /** - * Returns a type where {@code rawType} is parameterized by - * {@code arguments} and is owned by {@code ownerType}. - */ - static ParameterizedType newParameterizedTypeWithOwner( - @Nullable Type ownerType, Class<?> rawType, Type... arguments) { - if (ownerType == null) { - return newParameterizedType(rawType, arguments); - } - // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE - checkNotNull(arguments); - checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType); - return new ParameterizedTypeImpl(ownerType, rawType, arguments); - } - - /** - * Returns a type where {@code rawType} is parameterized by - * {@code arguments}. - */ - static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) { - return new ParameterizedTypeImpl( - ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments); - } - - /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */ - private enum ClassOwnership { - - OWNED_BY_ENCLOSING_CLASS { - @Nullable - @Override - Class<?> getOwnerType(Class<?> rawType) { - return rawType.getEnclosingClass(); - } - }, - LOCAL_CLASS_HAS_NO_OWNER { - @Nullable - @Override - Class<?> getOwnerType(Class<?> rawType) { - if (rawType.isLocalClass()) { - return null; - } else { - return rawType.getEnclosingClass(); - } - } - }; - - @Nullable abstract Class<?> getOwnerType(Class<?> rawType); - - static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior(); - - private static ClassOwnership detectJvmBehavior() { - class LocalClass<T> {} - Class<?> subclass = new LocalClass<String>() {}.getClass(); - ParameterizedType parameterizedType = (ParameterizedType) - subclass.getGenericSuperclass(); - for (ClassOwnership behavior : ClassOwnership.values()) { - if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) { - return behavior; - } - } - throw new AssertionError(); - } - } - - /** - * Returns a new {@link TypeVariable} that belongs to {@code declaration} with - * {@code name} and {@code bounds}. - */ - static <D extends GenericDeclaration> TypeVariable<D> newTypeVariable( - D declaration, String name, Type... bounds) { - return new TypeVariableImpl<D>( - declaration, - name, - (bounds.length == 0) - ? new Type[] { Object.class } - : bounds); - } - - /** Returns a new {@link WildcardType} with {@code upperBound}. */ - @VisibleForTesting static WildcardType subtypeOf(Type upperBound) { - return new WildcardTypeImpl(new Type[0], new Type[] { upperBound }); - } - - /** Returns a new {@link WildcardType} with {@code lowerBound}. */ - @VisibleForTesting static WildcardType supertypeOf(Type lowerBound) { - return new WildcardTypeImpl(new Type[] { lowerBound }, new Type[] { Object.class }); - } - - /** - * Returns human readable string representation of {@code type}. - * <ul> - * <li> For array type {@code Foo[]}, {@code "com.mypackage.Foo[]"} are - * returned. - * <li> For any class, {@code theClass.getName()} are returned. - * <li> For all other types, {@code type.toString()} are returned. - * </ul> - */ - static String toString(Type type) { - return (type instanceof Class) - ? ((Class<?>) type).getName() - : type.toString(); - } - - @Nullable static Type getComponentType(Type type) { - checkNotNull(type); - if (type instanceof Class) { - return ((Class<?>) type).getComponentType(); - } else if (type instanceof GenericArrayType) { - return ((GenericArrayType) type).getGenericComponentType(); - } else if (type instanceof WildcardType) { - return subtypeOfComponentType(((WildcardType) type).getUpperBounds()); - } else if (type instanceof TypeVariable) { - return subtypeOfComponentType(((TypeVariable<?>) type).getBounds()); - } else { - return null; - } - } - - /** - * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null - * otherwise. - */ - @Nullable private static Type subtypeOfComponentType(Type[] bounds) { - for (Type bound : bounds) { - Type componentType = getComponentType(bound); - if (componentType != null) { - // Only the first bound can be a class or array. - // Bounds after the first can only be interfaces. - if (componentType instanceof Class) { - Class<?> componentClass = (Class<?>) componentType; - if (componentClass.isPrimitive()) { - return componentClass; - } - } - return subtypeOf(componentType); - } - } - return null; - } - - static boolean containsTypeVariable(@Nullable Type type) { - if (type instanceof TypeVariable) { - return true; - } - if (type instanceof GenericArrayType) { - return containsTypeVariable(((GenericArrayType) type).getGenericComponentType()); - } - if (type instanceof ParameterizedType) { - return containsTypeVariable(((ParameterizedType) type).getActualTypeArguments()); - } - if (type instanceof WildcardType) { - WildcardType wildcard = (WildcardType) type; - return containsTypeVariable(wildcard.getUpperBounds()) - || containsTypeVariable(wildcard.getLowerBounds()); - } - return false; - } - - private static boolean containsTypeVariable(Type[] types) { - for (Type paramType : types) { - if (containsTypeVariable(paramType)) { - return true; - } - } - return false; - } - - private static final class GenericArrayTypeImpl - implements GenericArrayType, Serializable { - - private final Type componentType; - - GenericArrayTypeImpl(Type componentType) { - this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType); - } - - @Override public Type getGenericComponentType() { - return componentType; - } - - @Override public String toString() { - return Types.toString(componentType) + "[]"; - } - - @Override public int hashCode() { - return componentType.hashCode(); - } - - @Override public boolean equals(Object obj) { - if (obj instanceof GenericArrayType) { - GenericArrayType that = (GenericArrayType) obj; - return Objects.equal( - getGenericComponentType(), that.getGenericComponentType()); - } - return false; - } - - private static final long serialVersionUID = 0; - } - - private static final class ParameterizedTypeImpl - implements ParameterizedType, Serializable { - - private final Type ownerType; - private final ImmutableList<Type> argumentsList; - private final Class<?> rawType; - - ParameterizedTypeImpl( - @Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) { - checkNotNull(rawType); - checkArgument(typeArguments.length == rawType.getTypeParameters().length); - disallowPrimitiveType(typeArguments, "type parameter"); - this.ownerType = ownerType; - this.rawType = rawType; - this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments); - } - - @Override public Type[] getActualTypeArguments() { - return toArray(argumentsList); - } - - @Override public Type getRawType() { - return rawType; - } - - @Override public Type getOwnerType() { - return ownerType; - } - - @Override public String toString() { - StringBuilder builder = new StringBuilder(); - if (ownerType != null) { - builder.append(Types.toString(ownerType)).append('.'); - } - builder.append(rawType.getName()) - .append('<') - .append(COMMA_JOINER.join(transform(argumentsList, TYPE_TO_STRING))) - .append('>'); - return builder.toString(); - } - - @Override public int hashCode() { - return (ownerType == null ? 0 : ownerType.hashCode()) - ^ argumentsList.hashCode() ^ rawType.hashCode(); - } - - @Override public boolean equals(Object other) { - if (!(other instanceof ParameterizedType)) { - return false; - } - ParameterizedType that = (ParameterizedType) other; - return getRawType().equals(that.getRawType()) - && Objects.equal(getOwnerType(), that.getOwnerType()) - && Arrays.equals( - getActualTypeArguments(), that.getActualTypeArguments()); - } - - private static final long serialVersionUID = 0; - } - - private static final class TypeVariableImpl<D extends GenericDeclaration> - implements TypeVariable<D> { - - private final D genericDeclaration; - private final String name; - private final ImmutableList<Type> bounds; - - TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) { - disallowPrimitiveType(bounds, "bound for type variable"); - this.genericDeclaration = checkNotNull(genericDeclaration); - this.name = checkNotNull(name); - this.bounds = ImmutableList.copyOf(bounds); - } - - @Override public Type[] getBounds() { - return toArray(bounds); - } - - @Override public D getGenericDeclaration() { - return genericDeclaration; - } - - @Override public String getName() { - return name; - } - - @Override public String toString() { - return name; - } - - @Override public int hashCode() { - return genericDeclaration.hashCode() ^ name.hashCode(); - } - - @Override public boolean equals(Object obj) { - if (obj instanceof TypeVariable) { - TypeVariable<?> that = (TypeVariable<?>) obj; - return name.equals(that.getName()) - && genericDeclaration.equals(that.getGenericDeclaration()); - } - return false; - } - } - - static final class WildcardTypeImpl implements WildcardType, Serializable { - - private final ImmutableList<Type> lowerBounds; - private final ImmutableList<Type> upperBounds; - - WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) { - disallowPrimitiveType(lowerBounds, "lower bound for wildcard"); - disallowPrimitiveType(upperBounds, "upper bound for wildcard"); - this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds); - this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds); - } - - @Override public Type[] getLowerBounds() { - return toArray(lowerBounds); - } - - @Override public Type[] getUpperBounds() { - return toArray(upperBounds); - } - - @Override public boolean equals(Object obj) { - if (obj instanceof WildcardType) { - WildcardType that = (WildcardType) obj; - return lowerBounds.equals(Arrays.asList(that.getLowerBounds())) - && upperBounds.equals(Arrays.asList(that.getUpperBounds())); - } - return false; - } - - @Override public int hashCode() { - return lowerBounds.hashCode() ^ upperBounds.hashCode(); - } - - @Override public String toString() { - StringBuilder builder = new StringBuilder("?"); - for (Type lowerBound : lowerBounds) { - builder.append(" super ").append(Types.toString(lowerBound)); - } - for (Type upperBound : filterUpperBounds(upperBounds)) { - builder.append(" extends ").append(Types.toString(upperBound)); - } - return builder.toString(); - } - - private static final long serialVersionUID = 0; - } - - private static Type[] toArray(Collection<Type> types) { - return types.toArray(new Type[types.size()]); - } - - private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) { - return Iterables.filter( - bounds, Predicates.not(Predicates.<Type>equalTo(Object.class))); - } - - private static void disallowPrimitiveType(Type[] types, String usedAs) { - for (Type type : types) { - if (type instanceof Class) { - Class<?> cls = (Class<?>) type; - checkArgument(!cls.isPrimitive(), - "Primitive type '%s' used as %s", cls, usedAs); - } - } - } - - /** Returns the {@code Class} object of arrays with {@code componentType}. */ - static Class<?> getArrayClass(Class<?> componentType) { - // TODO(user): This is not the most efficient way to handle generic - // arrays, but is there another way to extract the array class in a - // non-hacky way (i.e. using String value class names- "[L...")? - return Array.newInstance(componentType, 0).getClass(); - } - - // TODO(benyu): Once we are on Java 7, delete this abstraction - enum JavaVersion { - - JAVA6 { - @Override GenericArrayType newArrayType(Type componentType) { - return new GenericArrayTypeImpl(componentType); - } - @Override Type usedInGenericType(Type type) { - checkNotNull(type); - if (type instanceof Class) { - Class<?> cls = (Class<?>) type; - if (cls.isArray()) { - return new GenericArrayTypeImpl(cls.getComponentType()); - } - } - return type; - } - }, - JAVA7 { - @Override Type newArrayType(Type componentType) { - if (componentType instanceof Class) { - return getArrayClass((Class<?>) componentType); - } else { - return new GenericArrayTypeImpl(componentType); - } - } - @Override Type usedInGenericType(Type type) { - return checkNotNull(type); - } - } - ; - - static final JavaVersion CURRENT = - (new TypeCapture<int[]>() {}.capture() instanceof Class) - ? JAVA7 : JAVA6; - abstract Type newArrayType(Type componentType); - abstract Type usedInGenericType(Type type); - - final ImmutableList<Type> usedInGenericType(Type[] types) { - ImmutableList.Builder<Type> builder = ImmutableList.builder(); - for (Type type : types) { - builder.add(usedInGenericType(type)); - } - return builder.build(); - } - } - - private Types() {} -} diff --git a/guava/src/com/google/common/reflect/package-info.java b/guava/src/com/google/common/reflect/package-info.java deleted file mode 100644 index e8ac02a..0000000 --- a/guava/src/com/google/common/reflect/package-info.java +++ /dev/null @@ -1,23 +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. - */ - -/** - * This package contains utilities to work with Java reflection. - * It is a part of the open-source - * <a href="http://guava-libraries.googlecode.com">Guava libraries</a>. - */ -@javax.annotation.ParametersAreNonnullByDefault -package com.google.common.reflect; diff --git a/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java b/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java index a1cb641..8136d23 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java +++ b/guava/src/com/google/common/util/concurrent/AbstractExecutionThreadService.java @@ -55,8 +55,7 @@ public abstract class AbstractExecutionThreadService implements Service { shutDown(); } catch (Exception ignored) { logger.log(Level.WARNING, - "Error while attempting to shut down the service" - + " after failure.", ignored); + "Error while attempting to shut down the service after failure.", ignored); } throw t; } @@ -78,14 +77,7 @@ public abstract class AbstractExecutionThreadService implements Service { }; /** - * Constructor for use by subclasses. - */ - protected AbstractExecutionThreadService() {} - - /** * Start the service. This method is invoked on the execution thread. - * - * <p>By default this method does nothing. */ protected void startUp() throws Exception {} @@ -107,16 +99,12 @@ public abstract class AbstractExecutionThreadService implements Service { /** * Stop the service. This method is invoked on the execution thread. - * - * <p>By default this method does nothing. */ // TODO: consider supporting a TearDownTestCase-like API protected void shutDown() throws Exception {} /** * Invoked to request the service to stop. - * - * <p>By default this method does nothing. */ protected void triggerShutdown() {} @@ -129,19 +117,19 @@ public abstract class AbstractExecutionThreadService implements Service { * promptly. * * <p>The default implementation returns a new {@link Executor} that sets the - * name of its threads to the string returned by {@link #serviceName} + * name of its threads to the string returned by {@link #getServiceName} */ protected Executor executor() { return new Executor() { @Override public void execute(Runnable command) { - MoreExecutors.newThread(serviceName(), command).start(); + new Thread(command, getServiceName()).start(); } }; } @Override public String toString() { - return serviceName() + " [" + state() + "]"; + return getServiceName() + " [" + state() + "]"; } // We override instead of using ForwardingService so that these can be final. @@ -171,28 +159,14 @@ public abstract class AbstractExecutionThreadService implements Service { } /** - * @since 13.0 - */ - @Override public final void addListener(Listener listener, Executor executor) { - delegate.addListener(listener, executor); - } - - /** - * @since 14.0 - */ - @Override public final Throwable failureCause() { - return delegate.failureCause(); - } - - /** - * Returns the name of this service. {@link AbstractExecutionThreadService} - * may include the name in debugging output. + * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name + * in debugging output. * * <p>Subclasses may override this method. * - * @since 14.0 (present in 10.0 as getServiceName) + * @since 10.0 */ - protected String serviceName() { + protected String getServiceName() { return getClass().getSimpleName(); } } diff --git a/guava/src/com/google/common/util/concurrent/AbstractFuture.java b/guava/src/com/google/common/util/concurrent/AbstractFuture.java index e14a111..bef3d3d 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractFuture.java +++ b/guava/src/com/google/common/util/concurrent/AbstractFuture.java @@ -70,11 +70,6 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { // The execution list to hold our executors. private final ExecutionList executionList = new ExecutionList(); - /** - * Constructor for use by subclasses. - */ - protected AbstractFuture() {} - /* * Improve the documentation of when InterruptedException is thrown. Our * behavior matches the JDK's, but the JDK's documentation is misleading. @@ -128,7 +123,7 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { @Override public boolean cancel(boolean mayInterruptIfRunning) { - if (!sync.cancel(mayInterruptIfRunning)) { + if (!sync.cancel()) { return false; } executionList.execute(); @@ -151,16 +146,6 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { } /** - * Returns true if this future was cancelled with {@code - * mayInterruptIfRunning} set to {@code true}. - * - * @since 14.0 - */ - protected final boolean wasInterrupted() { - return sync.wasInterrupted(); - } - - /** * {@inheritDoc} * * @since 10.0 @@ -216,14 +201,13 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { * private subclass to hold the synchronizer. This synchronizer is used to * implement the blocking and waiting calls as well as to handle state changes * in a thread-safe manner. The current state of the future is held in the - * Sync state, and the lock is released whenever the state changes to - * {@link #COMPLETED}, {@link #CANCELLED}, or {@link #INTERRUPTED} + * Sync state, and the lock is released whenever the state changes to either + * {@link #COMPLETED} or {@link #CANCELLED}. * * <p>To avoid races between threads doing release and acquire, we transition * to the final state in two steps. One thread will successfully CAS from * RUNNING to COMPLETING, that thread will then set the result of the - * computation, and only then transition to COMPLETED, CANCELLED, or - * INTERRUPTED. + * computation, and only then transition to COMPLETED or CANCELLED. * * <p>We don't use the integer argument passed between acquire methods so we * pass around a -1 everywhere. @@ -237,7 +221,6 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { static final int COMPLETING = 1; static final int COMPLETED = 2; static final int CANCELLED = 4; - static final int INTERRUPTED = 8; private V value; private Throwable exception; @@ -309,9 +292,7 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { } case CANCELLED: - case INTERRUPTED: - throw cancellationExceptionWithCause( - "Task was cancelled.", exception); + throw new CancellationException("Task was cancelled."); default: throw new IllegalStateException( @@ -320,25 +301,17 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { } /** - * Checks if the state is {@link #COMPLETED}, {@link #CANCELLED}, or {@link - * INTERRUPTED}. + * Checks if the state is {@link #COMPLETED} or {@link #CANCELLED}. */ boolean isDone() { - return (getState() & (COMPLETED | CANCELLED | INTERRUPTED)) != 0; + return (getState() & (COMPLETED | CANCELLED)) != 0; } /** - * Checks if the state is {@link #CANCELLED} or {@link #INTERRUPTED}. + * Checks if the state is {@link #CANCELLED}. */ boolean isCancelled() { - return (getState() & (CANCELLED | INTERRUPTED)) != 0; - } - - /** - * Checks if the state is {@link #INTERRUPTED}. - */ - boolean wasInterrupted() { - return getState() == INTERRUPTED; + return getState() == CANCELLED; } /** @@ -356,10 +329,10 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { } /** - * Transition to the CANCELLED or INTERRUPTED state. + * Transition to the CANCELLED state. */ - boolean cancel(boolean interrupt) { - return complete(null, null, interrupt ? INTERRUPTED : CANCELLED); + boolean cancel() { + return complete(null, null, CANCELLED); } /** @@ -367,8 +340,7 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { * be set but not both. The {@code finalState} is the state to change to * from {@link #RUNNING}. If the state is not in the RUNNING state we * return {@code false} after waiting for the state to be set to a valid - * final state ({@link #COMPLETED}, {@link #CANCELLED}, or {@link - * #INTERRUPTED}). + * final state ({@link #COMPLETED} or {@link #CANCELLED}). * * @param v the value to set as the result of the computation. * @param t the exception to set as the result of the computation. @@ -381,9 +353,7 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { // If this thread successfully transitioned to COMPLETING, set the value // and exception and then release to the final state. this.value = v; - // Don't actually construct a CancellationException until necessary. - this.exception = ((finalState & (CANCELLED | INTERRUPTED)) != 0) - ? new CancellationException("Future.cancel() was called.") : t; + this.exception = t; releaseShared(finalState); } else if (getState() == COMPLETING) { // If some other thread is currently completing the future, block until @@ -393,11 +363,4 @@ public abstract class AbstractFuture<V> implements ListenableFuture<V> { return doCompletion; } } - - static final CancellationException cancellationExceptionWithCause( - @Nullable String message, @Nullable Throwable cause) { - CancellationException exception = new CancellationException(message); - exception.initCause(cause); - return exception; - } } diff --git a/guava/src/com/google/common/util/concurrent/AbstractIdleService.java b/guava/src/com/google/common/util/concurrent/AbstractIdleService.java index 96a6ff3..504a6bc 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractIdleService.java +++ b/guava/src/com/google/common/util/concurrent/AbstractIdleService.java @@ -37,7 +37,7 @@ public abstract class AbstractIdleService implements Service { /* use AbstractService for state management */ private final Service delegate = new AbstractService() { @Override protected final void doStart() { - executor().execute(new Runnable() { + executor(State.STARTING).execute(new Runnable() { @Override public void run() { try { startUp(); @@ -51,7 +51,7 @@ public abstract class AbstractIdleService implements Service { } @Override protected final void doStop() { - executor().execute(new Runnable() { + executor(State.STOPPING).execute(new Runnable() { @Override public void run() { try { shutDown(); @@ -65,9 +65,6 @@ public abstract class AbstractIdleService implements Service { } }; - /** Constructor for use by subclasses. */ - protected AbstractIdleService() {} - /** Start the service. */ protected abstract void startUp() throws Exception; @@ -81,19 +78,22 @@ public abstract class AbstractIdleService implements Service { * priority. The returned executor's {@link Executor#execute(Runnable) * execute()} method is called when this service is started and stopped, * and should return promptly. + * + * @param state {@link Service.State#STARTING} or + * {@link Service.State#STOPPING}, used by the default implementation for + * naming the thread */ - protected Executor executor() { - final State state = state(); + protected Executor executor(final State state) { return new Executor() { @Override public void execute(Runnable command) { - MoreExecutors.newThread(serviceName() + " " + state, command).start(); + new Thread(command, getServiceName() + " " + state).start(); } }; } @Override public String toString() { - return serviceName() + " [" + state() + "]"; + return getServiceName() + " [" + state() + "]"; } // We override instead of using ForwardingService so that these can be final. @@ -122,27 +122,7 @@ public abstract class AbstractIdleService implements Service { return delegate.stopAndWait(); } - /** - * @since 13.0 - */ - @Override public final void addListener(Listener listener, Executor executor) { - delegate.addListener(listener, executor); - } - - /** - * @since 14.0 - */ - @Override public final Throwable failureCause() { - return delegate.failureCause(); - } - - /** - * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging - * output. - * - * @since 14.0 - */ - protected String serviceName() { + private String getServiceName() { return getClass().getSimpleName(); } } diff --git a/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java b/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java index c3e33a5..24f596f 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java +++ b/guava/src/com/google/common/util/concurrent/AbstractListeningExecutorService.java @@ -14,9 +14,7 @@ package com.google.common.util.concurrent; -import static com.google.common.util.concurrent.MoreExecutors.invokeAnyImpl; - -import com.google.common.annotations.Beta; +import static com.google.common.base.Preconditions.checkArgument; import java.util.ArrayList; import java.util.Collection; @@ -25,12 +23,11 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import javax.annotation.Nullable; - /** * Implements {@link ListeningExecutorService} execution methods atop the abstract {@link #execute} * method. More concretely, the {@code submit}, {@code invokeAny} and {@code invokeAll} methods @@ -40,17 +37,15 @@ import javax.annotation.Nullable; * termination. * * @author Doug Lea - * @since 14.0 */ -@Beta -public abstract class AbstractListeningExecutorService implements ListeningExecutorService { +abstract class AbstractListeningExecutorService implements ListeningExecutorService { @Override public ListenableFuture<?> submit(Runnable task) { ListenableFutureTask<Void> ftask = ListenableFutureTask.create(task, null); execute(ftask); return ftask; } - @Override public <T> ListenableFuture<T> submit(Runnable task, @Nullable T result) { + @Override public <T> ListenableFuture<T> submit(Runnable task, T result) { ListenableFutureTask<T> ftask = ListenableFutureTask.create(task, result); execute(ftask); return ftask; @@ -62,10 +57,82 @@ public abstract class AbstractListeningExecutorService implements ListeningExecu return ftask; } + /** + * the main mechanics of invokeAny. + */ + private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) + throws InterruptedException, ExecutionException, TimeoutException { + int ntasks = tasks.size(); + checkArgument(ntasks > 0); + List<Future<T>> futures = new ArrayList<Future<T>>(ntasks); + ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this); + + // For efficiency, especially in executors with limited + // parallelism, check to see if previously submitted tasks are + // done before submitting more of them. This interleaving + // plus the exception mechanics account for messiness of main + // loop. + + try { + // Record exceptions so that if we fail to obtain any + // result, we can throw the last exception we got. + ExecutionException ee = null; + long lastTime = timed ? System.nanoTime() : 0; + Iterator<? extends Callable<T>> it = tasks.iterator(); + + // Start one task for sure; the rest incrementally + futures.add(ecs.submit(it.next())); + --ntasks; + int active = 1; + + for (;;) { + Future<T> f = ecs.poll(); + if (f == null) { + if (ntasks > 0) { + --ntasks; + futures.add(ecs.submit(it.next())); + ++active; + } else if (active == 0) { + break; + } else if (timed) { + f = ecs.poll(nanos, TimeUnit.NANOSECONDS); + if (f == null) { + throw new TimeoutException(); + } + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + } else { + f = ecs.take(); + } + } + if (f != null) { + --active; + try { + return f.get(); + } catch (ExecutionException eex) { + ee = eex; + } catch (RuntimeException rex) { + ee = new ExecutionException(rex); + } + } + } + + if (ee == null) { + ee = new ExecutionException(null); + } + throw ee; + } finally { + for (Future<T> f : futures) { + f.cancel(true); + } + } + } + @Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { try { - return invokeAnyImpl(this, tasks, false, 0); + return doInvokeAny(tasks, false, 0); } catch (TimeoutException cannotHappen) { throw new AssertionError(); } @@ -74,7 +141,7 @@ public abstract class AbstractListeningExecutorService implements ListeningExecu @Override public <T> T invokeAny( Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return invokeAnyImpl(this, tasks, true, unit.toNanos(timeout)); + return doInvokeAny(tasks, true, unit.toNanos(timeout)); } @Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) diff --git a/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java b/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java index 949f76a..f847205 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java +++ b/guava/src/com/google/common/util/concurrent/AbstractScheduledService.java @@ -21,11 +21,9 @@ import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import java.util.concurrent.Callable; -import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; @@ -98,8 +96,9 @@ public abstract class AbstractScheduledService implements Service { * * <p>Consider using the {@link #newFixedDelaySchedule} and {@link #newFixedRateSchedule} factory * methods, these provide {@link Scheduler} instances for the common use case of running the - * service with a fixed schedule. If more flexibility is needed then consider subclassing - * {@link CustomScheduler}. + * service with a fixed schedule. If more flexibility is needed then consider subclassing the + * {@link CustomScheduler} abstract class in preference to creating your own {@link Scheduler} + * implementation. * * @author Luke Sandberg * @since 11.0 @@ -230,9 +229,6 @@ public abstract class AbstractScheduledService implements Service { } }; - /** Constructor for use by subclasses. */ - protected AbstractScheduledService() {} - /** * Run one iteration of the scheduled task. If any invocation of this method throws an exception, * the service will transition to the {@link Service.State#FAILED} state and this method will no @@ -240,19 +236,11 @@ public abstract class AbstractScheduledService implements Service { */ protected abstract void runOneIteration() throws Exception; - /** - * Start the service. - * - * <p>By default this method does nothing. - */ - protected void startUp() throws Exception {} + /** Start the service. */ + protected abstract void startUp() throws Exception; - /** - * Stop the service. This is guaranteed not to run concurrently with {@link #runOneIteration}. - * - * <p>By default this method does nothing. - */ - protected void shutDown() throws Exception {} + /** Stop the service. This is guaranteed not to run concurrently with {@link #runOneIteration}. */ + protected abstract void shutDown() throws Exception; /** * Returns the {@link Scheduler} object used to configure this service. This method will only be @@ -262,56 +250,19 @@ public abstract class AbstractScheduledService implements Service { /** * Returns the {@link ScheduledExecutorService} that will be used to execute the {@link #startUp}, - * {@link #runOneIteration} and {@link #shutDown} methods. If this method is overridden the - * executor will not be {@linkplain ScheduledExecutorService#shutdown shutdown} when this - * service {@linkplain Service.State#TERMINATED terminates} or - * {@linkplain Service.State#TERMINATED fails}. Subclasses may override this method to supply a - * custom {@link ScheduledExecutorService} instance. This method is guaranteed to only be called - * once. + * {@link #runOneIteration} and {@link #shutDown} methods. The executor will not be + * {@link ScheduledExecutorService#shutdown} when this service stops. Subclasses may override this + * method to use a custom {@link ScheduledExecutorService} instance. * * <p>By default this returns a new {@link ScheduledExecutorService} with a single thread thread - * pool that sets the name of the thread to the {@linkplain #serviceName() service name}. - * Also, the pool will be {@linkplain ScheduledExecutorService#shutdown() shut down} when the - * service {@linkplain Service.State#TERMINATED terminates} or - * {@linkplain Service.State#TERMINATED fails}. + * pool. This method will only be called once. */ protected ScheduledExecutorService executor() { - final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor( - new ThreadFactory() { - @Override public Thread newThread(Runnable runnable) { - return MoreExecutors.newThread(serviceName(), runnable); - } - }); - // Add a listener to shutdown the executor after the service is stopped. This ensures that the - // JVM shutdown will not be prevented from exiting after this service has stopped or failed. - // Technically this listener is added after start() was called so it is a little gross, but it - // is called within doStart() so we know that the service cannot terminate or fail concurrently - // with adding this listener so it is impossible to miss an event that we are interested in. - addListener(new Listener() { - @Override public void starting() {} - @Override public void running() {} - @Override public void stopping(State from) {} - @Override public void terminated(State from) { - executor.shutdown(); - } - @Override public void failed(State from, Throwable failure) { - executor.shutdown(); - }}, MoreExecutors.sameThreadExecutor()); - return executor; + return Executors.newSingleThreadScheduledExecutor(); } - /** - * Returns the name of this service. {@link AbstractScheduledService} may include the name in - * debugging output. - * - * @since 14.0 - */ - protected String serviceName() { - return getClass().getSimpleName(); - } - @Override public String toString() { - return serviceName() + " [" + state() + "]"; + return getClass().getSimpleName() + " [" + state() + "]"; } // We override instead of using ForwardingService so that these can be final. @@ -341,20 +292,6 @@ public abstract class AbstractScheduledService implements Service { } /** - * @since 13.0 - */ - @Override public final void addListener(Listener listener, Executor executor) { - delegate.addListener(listener, executor); - } - - /** - * @since 14.0 - */ - @Override public final Throwable failureCause() { - return delegate.failureCause(); - } - - /** * A {@link Scheduler} that provides a convenient way for the {@link AbstractScheduledService} to * use a dynamically changing schedule. After every execution of the task, assuming it hasn't * been cancelled, the {@link #getNextSchedule} method will be called. diff --git a/guava/src/com/google/common/util/concurrent/AbstractService.java b/guava/src/com/google/common/util/concurrent/AbstractService.java index f028a59..f84b374 100644 --- a/guava/src/com/google/common/util/concurrent/AbstractService.java +++ b/guava/src/com/google/common/util/concurrent/AbstractService.java @@ -16,148 +16,68 @@ package com.google.common.util.concurrent; -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.collect.Lists; -import com.google.common.collect.Queues; import com.google.common.util.concurrent.Service.State; // javadoc needs this -import java.util.List; -import java.util.Queue; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.Immutable; /** - * Base class for implementing services that can handle {@link #doStart} and {@link #doStop} - * requests, responding to them with {@link #notifyStarted()} and {@link #notifyStopped()} - * callbacks. Its subclasses must manage threads manually; consider - * {@link AbstractExecutionThreadService} if you need only a single execution thread. + * Base class for implementing services that can handle {@link #doStart} and + * {@link #doStop} requests, responding to them with {@link #notifyStarted()} + * and {@link #notifyStopped()} callbacks. Its subclasses must manage threads + * manually; consider {@link AbstractExecutionThreadService} if you need only a + * single execution thread. * * @author Jesse Wilson - * @author Luke Sandberg * @since 1.0 */ @Beta public abstract class AbstractService implements Service { - private static final Logger logger = Logger.getLogger(AbstractService.class.getName()); + private final ReentrantLock lock = new ReentrantLock(); private final Transition startup = new Transition(); private final Transition shutdown = new Transition(); /** - * The listeners to notify during a state transition. - */ - @GuardedBy("lock") - private final List<ListenerExecutorPair> listeners = Lists.newArrayList(); - - /** - * The queue of listeners that are waiting to be executed. - * - * <p>Enqueue operations should be protected by {@link #lock} while dequeue operations should be - * protected by the implicit lock on this object. Dequeue operations should be executed atomically - * with the execution of the {@link Runnable} and additionally the {@link #lock} should not be - * held when the listeners are being executed. Use {@link #executeListeners} for this operation. - * This is necessary to ensure that elements on the queue are executed in the correct order. - * Enqueue operations should be protected so that listeners are added in the correct order. We use - * a concurrent queue implementation so that enqueues can be executed concurrently with dequeues. + * The internal state, which equals external state unless + * shutdownWhenStartupFinishes is true. Guarded by {@code lock}. */ - @GuardedBy("queuedListeners") - private final Queue<Runnable> queuedListeners = Queues.newConcurrentLinkedQueue(); - + private State state = State.NEW; + /** - * The current state of the service. This should be written with the lock held but can be read - * without it because it is an immutable object in a volatile field. This is desirable so that - * methods like {@link #state}, {@link #failureCause} and notably {@link #toString} can be run - * without grabbing the lock. - * - * <p>To update this field correctly the lock must be held to guarantee that the state is - * consistent. + * If true, the user requested a shutdown while the service was still starting + * up. Guarded by {@code lock}. */ - @GuardedBy("lock") - private volatile StateSnapshot snapshot = new StateSnapshot(State.NEW); + private boolean shutdownWhenStartupFinishes = false; - /** Constructor for use by subclasses. */ - protected AbstractService() { - // Add a listener to update the futures. This needs to be added first so that it is executed - // before the other listeners. This way the other listeners can access the completed futures. - addListener( - new Listener() { - @Override public void starting() {} - - @Override public void running() { - startup.set(State.RUNNING); - } - - @Override public void stopping(State from) { - if (from == State.STARTING) { - startup.set(State.STOPPING); - } - } - - @Override public void terminated(State from) { - if (from == State.NEW) { - startup.set(State.TERMINATED); - } - shutdown.set(State.TERMINATED); - } - - @Override public void failed(State from, Throwable failure) { - switch (from) { - case STARTING: - startup.setException(failure); - shutdown.setException(new Exception("Service failed to start.", failure)); - break; - case RUNNING: - shutdown.setException(new Exception("Service failed while running", failure)); - break; - case STOPPING: - shutdown.setException(failure); - break; - case TERMINATED: /* fall-through */ - case FAILED: /* fall-through */ - case NEW: /* fall-through */ - default: - throw new AssertionError("Unexpected from state: " + from); - } - } - }, - MoreExecutors.sameThreadExecutor()); - } - /** - * This method is called by {@link #start} to initiate service startup. The invocation of this - * method should cause a call to {@link #notifyStarted()}, either during this method's run, or - * after it has returned. If startup fails, the invocation should cause a call to - * {@link #notifyFailed(Throwable)} instead. + * This method is called by {@link #start} to initiate service startup. The + * invocation of this method should cause a call to {@link #notifyStarted()}, + * either during this method's run, or after it has returned. If startup + * fails, the invocation should cause a call to {@link + * #notifyFailed(Throwable)} instead. * - * <p>This method should return promptly; prefer to do work on a different thread where it is - * convenient. It is invoked exactly once on service startup, even when {@link #start} is called - * multiple times. + * <p>This method should return promptly; prefer to do work on a different + * thread where it is convenient. It is invoked exactly once on service + * startup, even when {@link #start} is called multiple times. */ protected abstract void doStart(); /** - * This method should be used to initiate service shutdown. The invocation of this method should - * cause a call to {@link #notifyStopped()}, either during this method's run, or after it has - * returned. If shutdown fails, the invocation should cause a call to - * {@link #notifyFailed(Throwable)} instead. + * This method should be used to initiate service shutdown. The invocation + * of this method should cause a call to {@link #notifyStopped()}, either + * during this method's run, or after it has returned. If shutdown fails, the + * invocation should cause a call to {@link #notifyFailed(Throwable)} instead. * - * <p> This method should return promptly; prefer to do work on a different thread where it is - * convenient. It is invoked exactly once on service shutdown, even when {@link #stop} is called - * multiple times. + * <p>This method should return promptly; prefer to do work on a different + * thread where it is convenient. It is invoked exactly once on service + * shutdown, even when {@link #stop} is called multiple times. */ protected abstract void doStop(); @@ -165,16 +85,15 @@ public abstract class AbstractService implements Service { public final ListenableFuture<State> start() { lock.lock(); try { - if (snapshot.state == State.NEW) { - snapshot = new StateSnapshot(State.STARTING); - starting(); + if (state == State.NEW) { + state = State.STARTING; doStart(); } } catch (Throwable startupFailure) { + // put the exception in the future, the user can get it via Future.get() notifyFailed(startupFailure); } finally { lock.unlock(); - executeListeners(); } return startup; @@ -184,33 +103,22 @@ public abstract class AbstractService implements Service { public final ListenableFuture<State> stop() { lock.lock(); try { - switch (snapshot.state) { - case NEW: - snapshot = new StateSnapshot(State.TERMINATED); - terminated(State.NEW); - break; - case STARTING: - snapshot = new StateSnapshot(State.STARTING, true, null); - stopping(State.STARTING); - break; - case RUNNING: - snapshot = new StateSnapshot(State.STOPPING); - stopping(State.RUNNING); - doStop(); - break; - case STOPPING: - case TERMINATED: - case FAILED: - // do nothing - break; - default: - throw new AssertionError("Unexpected state: " + snapshot.state); + if (state == State.NEW) { + state = State.TERMINATED; + startup.set(State.TERMINATED); + shutdown.set(State.TERMINATED); + } else if (state == State.STARTING) { + shutdownWhenStartupFinishes = true; + startup.set(State.STOPPING); + } else if (state == State.RUNNING) { + state = State.STOPPING; + doStop(); } } catch (Throwable shutdownFailure) { + // put the exception in the future, the user can get it via Future.get() notifyFailed(shutdownFailure); } finally { lock.unlock(); - executeListeners(); } return shutdown; @@ -227,91 +135,84 @@ public abstract class AbstractService implements Service { } /** - * Implementing classes should invoke this method once their service has started. It will cause - * the service to transition from {@link State#STARTING} to {@link State#RUNNING}. + * Implementing classes should invoke this method once their service has + * started. It will cause the service to transition from {@link + * State#STARTING} to {@link State#RUNNING}. * - * @throws IllegalStateException if the service is not {@link State#STARTING}. + * @throws IllegalStateException if the service is not + * {@link State#STARTING}. */ protected final void notifyStarted() { lock.lock(); try { - if (snapshot.state != State.STARTING) { + if (state != State.STARTING) { IllegalStateException failure = new IllegalStateException( - "Cannot notifyStarted() when the service is " + snapshot.state); + "Cannot notifyStarted() when the service is " + state); notifyFailed(failure); throw failure; } - if (snapshot.shutdownWhenStartupFinishes) { - snapshot = new StateSnapshot(State.STOPPING); - // We don't call listeners here because we already did that when we set the - // shutdownWhenStartupFinishes flag. - doStop(); + state = State.RUNNING; + if (shutdownWhenStartupFinishes) { + stop(); } else { - snapshot = new StateSnapshot(State.RUNNING); - running(); + startup.set(State.RUNNING); } } finally { lock.unlock(); - executeListeners(); } } /** - * Implementing classes should invoke this method once their service has stopped. It will cause - * the service to transition from {@link State#STOPPING} to {@link State#TERMINATED}. + * Implementing classes should invoke this method once their service has + * stopped. It will cause the service to transition from {@link + * State#STOPPING} to {@link State#TERMINATED}. * - * @throws IllegalStateException if the service is neither {@link State#STOPPING} nor - * {@link State#RUNNING}. + * @throws IllegalStateException if the service is neither {@link + * State#STOPPING} nor {@link State#RUNNING}. */ protected final void notifyStopped() { lock.lock(); try { - if (snapshot.state != State.STOPPING && snapshot.state != State.RUNNING) { + if (state != State.STOPPING && state != State.RUNNING) { IllegalStateException failure = new IllegalStateException( - "Cannot notifyStopped() when the service is " + snapshot.state); + "Cannot notifyStopped() when the service is " + state); notifyFailed(failure); throw failure; } - State previous = snapshot.state; - snapshot = new StateSnapshot(State.TERMINATED); - terminated(previous); + + state = State.TERMINATED; + shutdown.set(State.TERMINATED); } finally { lock.unlock(); - executeListeners(); } } /** - * Invoke this method to transition the service to the {@link State#FAILED}. The service will - * <b>not be stopped</b> if it is running. Invoke this method when a service has failed critically - * or otherwise cannot be started nor stopped. + * Invoke this method to transition the service to the + * {@link State#FAILED}. The service will <b>not be stopped</b> if it + * is running. Invoke this method when a service has failed critically or + * otherwise cannot be started nor stopped. */ protected final void notifyFailed(Throwable cause) { checkNotNull(cause); lock.lock(); try { - switch (snapshot.state) { - case NEW: - case TERMINATED: - throw new IllegalStateException("Failed while in state:" + snapshot.state, cause); - case RUNNING: - case STARTING: - case STOPPING: - State previous = snapshot.state; - snapshot = new StateSnapshot(State.FAILED, false, cause); - failed(previous, cause); - break; - case FAILED: - // Do nothing - break; - default: - throw new AssertionError("Unexpected state: " + snapshot.state); + if (state == State.STARTING) { + startup.setException(cause); + shutdown.setException(new Exception( + "Service failed to start.", cause)); + } else if (state == State.STOPPING) { + shutdown.setException(cause); + } else if (state == State.RUNNING) { + shutdown.setException(new Exception("Service failed while running", cause)); + } else if (state == State.NEW || state == State.TERMINATED) { + throw new IllegalStateException("Failed while in state:" + state, cause); } + state = State.FAILED; } finally { lock.unlock(); - executeListeners(); } } @@ -322,28 +223,12 @@ public abstract class AbstractService implements Service { @Override public final State state() { - return snapshot.externalState(); - } - - /** - * @since 14.0 - */ - @Override - public final Throwable failureCause() { - return snapshot.failureCause(); - } - - /** - * @since 13.0 - */ - @Override - public final void addListener(Listener listener, Executor executor) { - checkNotNull(listener, "listener"); - checkNotNull(executor, "executor"); lock.lock(); try { - if (snapshot.state != State.TERMINATED && snapshot.state != State.FAILED) { - listeners.add(new ListenerExecutorPair(listener, executor)); + if (shutdownWhenStartupFinishes && state == State.STARTING) { + return State.STOPPING; + } else { + return state; } } finally { lock.unlock(); @@ -368,182 +253,4 @@ public abstract class AbstractService implements Service { } } } - - /** - * Attempts to execute all the listeners in {@link #queuedListeners} while not holding the - * {@link #lock}. - */ - private void executeListeners() { - if (!lock.isHeldByCurrentThread()) { - synchronized (queuedListeners) { - Runnable listener; - while ((listener = queuedListeners.poll()) != null) { - listener.run(); - } - } - } - } - - @GuardedBy("lock") - private void starting() { - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.starting(); - } - }); - } - }); - } - } - - @GuardedBy("lock") - private void running() { - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.running(); - } - }); - } - }); - } - } - - @GuardedBy("lock") - private void stopping(final State from) { - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.stopping(from); - } - }); - } - }); - } - } - - @GuardedBy("lock") - private void terminated(final State from) { - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.terminated(from); - } - }); - } - }); - } - // There are no more state transitions so we can clear this out. - listeners.clear(); - } - - @GuardedBy("lock") - private void failed(final State from, final Throwable cause) { - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.failed(from, cause); - } - }); - } - }); - } - // There are no more state transitions so we can clear this out. - listeners.clear(); - } - - /** A simple holder for a listener and its executor. */ - private static class ListenerExecutorPair { - final Listener listener; - final Executor executor; - - ListenerExecutorPair(Listener listener, Executor executor) { - this.listener = listener; - this.executor = executor; - } - - /** - * Executes the given {@link Runnable} on {@link #executor} logging and swallowing all - * exceptions - */ - void execute(Runnable runnable) { - try { - executor.execute(runnable); - } catch (Exception e) { - logger.log(Level.SEVERE, "Exception while executing listener " + listener - + " with executor " + executor, e); - } - } - } - - /** - * An immutable snapshot of the current state of the service. This class represents a consistent - * snapshot of the state and therefore it can be used to answer simple queries without needing to - * grab a lock. - */ - @Immutable - private static final class StateSnapshot { - /** - * The internal state, which equals external state unless - * shutdownWhenStartupFinishes is true. - */ - final State state; - - /** - * If true, the user requested a shutdown while the service was still starting - * up. - */ - final boolean shutdownWhenStartupFinishes; - - /** - * The exception that caused this service to fail. This will be {@code null} - * unless the service has failed. - */ - @Nullable - final Throwable failure; - - StateSnapshot(State internalState) { - this(internalState, false, null); - } - - StateSnapshot( - State internalState, boolean shutdownWhenStartupFinishes, @Nullable Throwable failure) { - checkArgument(!shutdownWhenStartupFinishes || internalState == State.STARTING, - "shudownWhenStartupFinishes can only be set if state is STARTING. Got %s instead.", - internalState); - checkArgument(!(failure != null ^ internalState == State.FAILED), - "A failure cause should be set if and only if the state is failed. Got %s and %s " - + "instead.", internalState, failure); - this.state = internalState; - this.shutdownWhenStartupFinishes = shutdownWhenStartupFinishes; - this.failure = failure; - } - - /** @see Service#state() */ - State externalState() { - if (shutdownWhenStartupFinishes && state == State.STARTING) { - return State.STOPPING; - } else { - return state; - } - } - - /** @see Service#failureCause() */ - Throwable failureCause() { - checkState(state == State.FAILED, - "failureCause() is only valid if the service has failed, service is %s", state); - return failure; - } - } } diff --git a/guava/src/com/google/common/util/concurrent/AsyncFunction.java b/guava/src/com/google/common/util/concurrent/AsyncFunction.java index cdb1228..441c029 100644 --- a/guava/src/com/google/common/util/concurrent/AsyncFunction.java +++ b/guava/src/com/google/common/util/concurrent/AsyncFunction.java @@ -16,6 +16,8 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.Beta; + import java.util.concurrent.Future; /** @@ -25,6 +27,7 @@ import java.util.concurrent.Future; * @author Chris Povirk * @since 11.0 */ +@Beta public interface AsyncFunction<I, O> { /** * Returns an output {@code Future} to use in place of the given {@code diff --git a/guava/src/com/google/common/util/concurrent/AtomicDouble.java b/guava/src/com/google/common/util/concurrent/AtomicDouble.java index d007f45..0f38fb9 100644 --- a/guava/src/com/google/common/util/concurrent/AtomicDouble.java +++ b/guava/src/com/google/common/util/concurrent/AtomicDouble.java @@ -14,9 +14,10 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.Beta; + import static java.lang.Double.doubleToRawLongBits; import static java.lang.Double.longBitsToDouble; - import java.util.concurrent.atomic.AtomicLongFieldUpdater; /** @@ -42,16 +43,17 @@ import java.util.concurrent.atomic.AtomicLongFieldUpdater; * * <p>It is possible to write a more scalable updater, at the cost of * giving up strict atomicity. See for example - * <a href="http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/DoubleAdder.html"> - * DoubleAdder</a> + * <a href="http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/DoubleAdder.html" + * DoubleAdder> * and - * <a href="http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/DoubleMaxUpdater.html"> - * DoubleMaxUpdater</a>. + * <a href="http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/DoubleMaxUpdater.html" + * DoubleMaxUpdater>. * * @author Doug Lea * @author Martin Buchholz * @since 11.0 */ +@Beta public class AtomicDouble extends Number implements java.io.Serializable { private static final long serialVersionUID = 0L; diff --git a/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java b/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java index 407cd7c..37f4c5c 100644 --- a/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java +++ b/guava/src/com/google/common/util/concurrent/AtomicDoubleArray.java @@ -13,9 +13,10 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.Beta; + import static java.lang.Double.doubleToRawLongBits; import static java.lang.Double.longBitsToDouble; - import java.util.concurrent.atomic.AtomicLongArray; /** @@ -39,6 +40,7 @@ import java.util.concurrent.atomic.AtomicLongArray; * @author Martin Buchholz * @since 11.0 */ +@Beta public class AtomicDoubleArray implements java.io.Serializable { private static final long serialVersionUID = 0L; diff --git a/guava/src/com/google/common/util/concurrent/AtomicLongMap.java b/guava/src/com/google/common/util/concurrent/AtomicLongMap.java index d0af965..c49f84c 100644 --- a/guava/src/com/google/common/util/concurrent/AtomicLongMap.java +++ b/guava/src/com/google/common/util/concurrent/AtomicLongMap.java @@ -1,24 +1,10 @@ -/* - * 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. - */ +// Copyright 2011 Google Inc. All Rights Reserved. package com.google.common.util.concurrent; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.Beta; import com.google.common.base.Function; import com.google.common.collect.Maps; @@ -50,7 +36,7 @@ import java.util.concurrent.atomic.AtomicLong; * @author Charles Fry * @since 11.0 */ -@GwtCompatible +@Beta public final class AtomicLongMap<K> { private final ConcurrentHashMap<K, AtomicLong> map; diff --git a/guava/src/com/google/common/util/concurrent/Atomics.java b/guava/src/com/google/common/util/concurrent/Atomics.java index efb7946..fece83d 100644 --- a/guava/src/com/google/common/util/concurrent/Atomics.java +++ b/guava/src/com/google/common/util/concurrent/Atomics.java @@ -16,6 +16,8 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.Beta; + import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -28,6 +30,7 @@ import javax.annotation.Nullable; * @author Kurt Alfred Kluever * @since 10.0 */ +@Beta public final class Atomics { private Atomics() {} diff --git a/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java b/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java deleted file mode 100644 index 528fc8e..0000000 --- a/guava/src/com/google/common/util/concurrent/CycleDetectingLockFactory.java +++ /dev/null @@ -1,1038 +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.util.concurrent; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.collect.MapMaker; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.ThreadSafe; - -/** - * The {@code CycleDetectingLockFactory} creates {@link ReentrantLock} instances and - * {@link ReentrantReadWriteLock} instances that detect potential deadlock by checking - * for cycles in lock acquisition order. - * <p> - * Potential deadlocks detected when calling the {@code lock()}, - * {@code lockInterruptibly()}, or {@code tryLock()} methods will result in the - * execution of the {@link Policy} specified when creating the factory. The - * currently available policies are: - * <ul> - * <li>DISABLED - * <li>WARN - * <li>THROW - * </ul> - * The locks created by a factory instance will detect lock acquisition cycles - * with locks created by other {@code CycleDetectingLockFactory} instances - * (except those with {@code Policy.DISABLED}). A lock's behavior when a cycle - * is detected, however, is defined by the {@code Policy} of the factory that - * created it. This allows detection of cycles across components while - * delegating control over lock behavior to individual components. - * <p> - * Applications are encouraged to use a {@code CycleDetectingLockFactory} to - * create any locks for which external/unmanaged code is executed while the lock - * is held. (See caveats under <strong>Performance</strong>). - * <p> - * <strong>Cycle Detection</strong> - * <p> - * Deadlocks can arise when locks are acquired in an order that forms a cycle. - * In a simple example involving two locks and two threads, deadlock occurs - * when one thread acquires Lock A, and then Lock B, while another thread - * acquires Lock B, and then Lock A: - * <pre> - * Thread1: acquire(LockA) --X acquire(LockB) - * Thread2: acquire(LockB) --X acquire(LockA) - * </pre> - * Neither thread will progress because each is waiting for the other. In more - * complex applications, cycles can arise from interactions among more than 2 - * locks: - * <pre> - * Thread1: acquire(LockA) --X acquire(LockB) - * Thread2: acquire(LockB) --X acquire(LockC) - * ... - * ThreadN: acquire(LockN) --X acquire(LockA) - * </pre> - * The implementation detects cycles by constructing a directed graph in which - * each lock represents a node and each edge represents an acquisition ordering - * between two locks. - * <ul> - * <li>Each lock adds (and removes) itself to/from a ThreadLocal Set of acquired - * locks when the Thread acquires its first hold (and releases its last - * remaining hold). - * <li>Before the lock is acquired, the lock is checked against the current set - * of acquired locks---to each of the acquired locks, an edge from the - * soon-to-be-acquired lock is either verified or created. - * <li>If a new edge needs to be created, the outgoing edges of the acquired - * locks are traversed to check for a cycle that reaches the lock to be - * acquired. If no cycle is detected, a new "safe" edge is created. - * <li>If a cycle is detected, an "unsafe" (cyclic) edge is created to represent - * a potential deadlock situation, and the appropriate Policy is executed. - * </ul> - * Note that detection of potential deadlock does not necessarily indicate that - * deadlock will happen, as it is possible that higher level application logic - * prevents the cyclic lock acquisition from occurring. One example of a false - * positive is: - * <pre> - * LockA -> LockB -> LockC - * LockA -> LockC -> LockB - * </pre> - * - * <strong>ReadWriteLocks</strong> - * <p> - * While {@code ReadWriteLock} instances have different properties and can form cycles - * without potential deadlock, this class treats {@code ReadWriteLock} instances as - * equivalent to traditional exclusive locks. Although this increases the false - * positives that the locks detect (i.e. cycles that will not actually result in - * deadlock), it simplifies the algorithm and implementation considerably. The - * assumption is that a user of this factory wishes to eliminate any cyclic - * acquisition ordering. - * <p> - * <strong>Explicit Lock Acquisition Ordering</strong> - * <p> - * The {@link CycleDetectingLockFactory.WithExplicitOrdering} class can be used - * to enforce an application-specific ordering in addition to performing general - * cycle detection. - * <p> - * <strong>Garbage Collection</strong> - * <p> - * In order to allow proper garbage collection of unused locks, the edges of - * the lock graph are weak references. - * <p> - * <strong>Performance</strong> - * <p> - * The extra bookkeeping done by cycle detecting locks comes at some cost to - * performance. Benchmarks (as of December 2011) show that: - * - * <ul> - * <li>for an unnested {@code lock()} and {@code unlock()}, a cycle detecting - * lock takes 38ns as opposed to the 24ns taken by a plain lock. - * <li>for nested locking, the cost increases with the depth of the nesting: - * <ul> - * <li> 2 levels: average of 64ns per lock()/unlock() - * <li> 3 levels: average of 77ns per lock()/unlock() - * <li> 4 levels: average of 99ns per lock()/unlock() - * <li> 5 levels: average of 103ns per lock()/unlock() - * <li>10 levels: average of 184ns per lock()/unlock() - * <li>20 levels: average of 393ns per lock()/unlock() - * </ul> - * </ul> - * - * As such, the CycleDetectingLockFactory may not be suitable for - * performance-critical applications which involve tightly-looped or - * deeply-nested locking algorithms. - * - * @author Darick Tong - * @since 13.0 - */ -@Beta -@ThreadSafe -public class CycleDetectingLockFactory { - - /** - * Encapsulates the action to be taken when a potential deadlock is - * encountered. Clients can use one of the predefined {@link Policies} or - * specify a custom implementation. Implementations must be thread-safe. - * - * @since 13.0 - */ - @Beta - @ThreadSafe - public interface Policy { - - /** - * Called when a potential deadlock is encountered. Implementations can - * throw the given {@code exception} and/or execute other desired logic. - * <p> - * Note that the method will be called even upon an invocation of - * {@code tryLock()}. Although {@code tryLock()} technically recovers from - * deadlock by eventually timing out, this behavior is chosen based on the - * assumption that it is the application's wish to prohibit any cyclical - * lock acquisitions. - */ - void handlePotentialDeadlock(PotentialDeadlockException exception); - } - - /** - * Pre-defined {@link Policy} implementations. - * - * @since 13.0 - */ - @Beta - public enum Policies implements Policy { - /** - * When potential deadlock is detected, this policy results in the throwing - * of the {@code PotentialDeadlockException} indicating the potential - * deadlock, which includes stack traces illustrating the cycle in lock - * acquisition order. - */ - THROW { - @Override - public void handlePotentialDeadlock(PotentialDeadlockException e) { - throw e; - } - }, - - /** - * When potential deadlock is detected, this policy results in the logging - * of a {@link Level#SEVERE} message indicating the potential deadlock, - * which includes stack traces illustrating the cycle in lock acquisition - * order. - */ - WARN { - @Override - public void handlePotentialDeadlock(PotentialDeadlockException e) { - logger.log(Level.SEVERE, "Detected potential deadlock", e); - } - }, - - /** - * Disables cycle detection. This option causes the factory to return - * unmodified lock implementations provided by the JDK, and is provided to - * allow applications to easily parameterize when cycle detection is - * enabled. - * <p> - * Note that locks created by a factory with this policy will <em>not</em> - * participate the cycle detection performed by locks created by other - * factories. - */ - DISABLED { - @Override - public void handlePotentialDeadlock(PotentialDeadlockException e) { - } - }; - } - - /** - * Creates a new factory with the specified policy. - */ - public static CycleDetectingLockFactory newInstance(Policy policy) { - return new CycleDetectingLockFactory(policy); - } - - /** - * Equivalent to {@code newReentrantLock(lockName, false)}. - */ - public ReentrantLock newReentrantLock(String lockName) { - return newReentrantLock(lockName, false); - } - - /** - * Creates a {@link ReentrantLock} with the given fairness policy. The - * {@code lockName} is used in the warning or exception output to help - * identify the locks involved in the detected deadlock. - */ - public ReentrantLock newReentrantLock(String lockName, boolean fair) { - return policy == Policies.DISABLED ? new ReentrantLock(fair) - : new CycleDetectingReentrantLock( - new LockGraphNode(lockName), fair); - } - - /** - * Equivalent to {@code newReentrantReadWriteLock(lockName, false)}. - */ - public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName) { - return newReentrantReadWriteLock(lockName, false); - } - - /** - * Creates a {@link ReentrantReadWriteLock} with the given fairness policy. - * The {@code lockName} is used in the warning or exception output to help - * identify the locks involved in the detected deadlock. - */ - public ReentrantReadWriteLock newReentrantReadWriteLock( - String lockName, boolean fair) { - return policy == Policies.DISABLED ? new ReentrantReadWriteLock(fair) - : new CycleDetectingReentrantReadWriteLock( - new LockGraphNode(lockName), fair); - } - - // A static mapping from an Enum type to its set of LockGraphNodes. - private static final Map<Class<? extends Enum>, - Map<? extends Enum, LockGraphNode>> lockGraphNodesPerType = - new MapMaker().weakKeys().makeComputingMap( - new OrderedLockGraphNodesCreator()); - - /** - * Creates a {@code CycleDetectingLockFactory.WithExplicitOrdering<E>}. - */ - public static <E extends Enum<E>> WithExplicitOrdering<E> - newInstanceWithExplicitOrdering(Class<E> enumClass, Policy policy) { - // OrderedLockGraphNodesCreator maps each enumClass to a Map with the - // corresponding enum key type. - checkNotNull(enumClass); - checkNotNull(policy); - @SuppressWarnings("unchecked") - Map<E, LockGraphNode> lockGraphNodes = - (Map<E, LockGraphNode>) lockGraphNodesPerType.get(enumClass); - return new WithExplicitOrdering<E>(policy, lockGraphNodes); - } - - /** - * A {@code CycleDetectingLockFactory.WithExplicitOrdering} provides the - * additional enforcement of an application-specified ordering of lock - * acquisitions. The application defines the allowed ordering with an - * {@code Enum} whose values each correspond to a lock type. The order in - * which the values are declared dictates the allowed order of lock - * acquisition. In other words, locks corresponding to smaller values of - * {@link Enum#ordinal()} should only be acquired before locks with larger - * ordinals. Example: - * - * <pre> {@code - * enum MyLockOrder { - * FIRST, SECOND, THIRD; - * } - * - * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory = - * CycleDetectingLockFactory.newInstanceWithExplicitOrdering(Policies.THROW); - * - * Lock lock1 = factory.newReentrantLock(MyLockOrder.FIRST); - * Lock lock2 = factory.newReentrantLock(MyLockOrder.SECOND); - * Lock lock3 = factory.newReentrantLock(MyLockOrder.THIRD); - * - * lock1.lock(); - * lock3.lock(); - * lock2.lock(); // will throw an IllegalStateException - * }</pre> - * - * As with all locks created by instances of {@code CycleDetectingLockFactory} - * explicitly ordered locks participate in general cycle detection with all - * other cycle detecting locks, and a lock's behavior when detecting a cyclic - * lock acquisition is defined by the {@code Policy} of the factory that - * created it. - * <p> - * Note, however, that although multiple locks can be created for a given Enum - * value, whether it be through separate factory instances or through multiple - * calls to the same factory, attempting to acquire multiple locks with the - * same Enum value (within the same thread) will result in an - * IllegalStateException regardless of the factory's policy. For example: - * - * <pre> {@code - * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory1 = - * CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...); - * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory2 = - * CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...); - * - * Lock lockA = factory1.newReentrantLock(MyLockOrder.FIRST); - * Lock lockB = factory1.newReentrantLock(MyLockOrder.FIRST); - * Lock lockC = factory2.newReentrantLock(MyLockOrder.FIRST); - * - * lockA.lock(); - * - * lockB.lock(); // will throw an IllegalStateException - * lockC.lock(); // will throw an IllegalStateException - * - * lockA.lock(); // reentrant acquisition is okay - * }</pre> - * - * It is the responsibility of the application to ensure that multiple lock - * instances with the same rank are never acquired in the same thread. - * - * @param <E> The Enum type representing the explicit lock ordering. - * @since 13.0 - */ - @Beta - public static final class WithExplicitOrdering<E extends Enum<E>> - extends CycleDetectingLockFactory { - - private final Map<E, LockGraphNode> lockGraphNodes; - - @VisibleForTesting - WithExplicitOrdering( - Policy policy, Map<E, LockGraphNode> lockGraphNodes) { - super(policy); - this.lockGraphNodes = lockGraphNodes; - } - - /** - * Equivalent to {@code newReentrantLock(rank, false)}. - */ - public ReentrantLock newReentrantLock(E rank) { - return newReentrantLock(rank, false); - } - - /** - * Creates a {@link ReentrantLock} with the given fairness policy and rank. - * The values returned by {@link Enum#getDeclaringClass()} and - * {@link Enum#name()} are used to describe the lock in warning or - * exception output. - * - * @throws IllegalStateException If the factory has already created a - * {@code Lock} with the specified rank. - */ - public ReentrantLock newReentrantLock(E rank, boolean fair) { - return policy == Policies.DISABLED ? new ReentrantLock(fair) - : new CycleDetectingReentrantLock(lockGraphNodes.get(rank), fair); - } - - /** - * Equivalent to {@code newReentrantReadWriteLock(rank, false)}. - */ - public ReentrantReadWriteLock newReentrantReadWriteLock(E rank) { - return newReentrantReadWriteLock(rank, false); - } - - /** - * Creates a {@link ReentrantReadWriteLock} with the given fairness policy - * and rank. The values returned by {@link Enum#getDeclaringClass()} and - * {@link Enum#name()} are used to describe the lock in warning or exception - * output. - * - * @throws IllegalStateException If the factory has already created a - * {@code Lock} with the specified rank. - */ - public ReentrantReadWriteLock newReentrantReadWriteLock( - E rank, boolean fair) { - return policy == Policies.DISABLED ? new ReentrantReadWriteLock(fair) - : new CycleDetectingReentrantReadWriteLock( - lockGraphNodes.get(rank), fair); - } - } - - /** - * For a given Enum type, creates an immutable map from each of the Enum's - * values to a corresponding LockGraphNode, with the - * {@code allowedPriorLocks} and {@code disallowedPriorLocks} prepopulated - * with nodes according to the natural ordering of the associated Enum values. - */ - @VisibleForTesting - static class OrderedLockGraphNodesCreator - implements Function<Class<? extends Enum>, - Map<? extends Enum, LockGraphNode>> { - - @Override - @SuppressWarnings("unchecked") // There's no way to properly express with - // wildcards the recursive Enum type required by createNodesFor(), and the - // Map/Function types must use wildcards since they accept any Enum class. - public Map<? extends Enum, LockGraphNode> apply( - Class<? extends Enum> clazz) { - return createNodesFor(clazz); - } - - <E extends Enum<E>> Map<E, LockGraphNode> createNodesFor(Class<E> clazz) { - EnumMap<E, LockGraphNode> map = Maps.newEnumMap(clazz); - E[] keys = clazz.getEnumConstants(); - final int numKeys = keys.length; - ArrayList<LockGraphNode> nodes = - Lists.newArrayListWithCapacity(numKeys); - // Create a LockGraphNode for each enum value. - for (E key : keys) { - LockGraphNode node = new LockGraphNode(getLockName(key)); - nodes.add(node); - map.put(key, node); - } - // Pre-populate all allowedPriorLocks with nodes of smaller ordinal. - for (int i = 1; i < numKeys; i++) { - nodes.get(i).checkAcquiredLocks(Policies.THROW, nodes.subList(0, i)); - } - // Pre-populate all disallowedPriorLocks with nodes of larger ordinal. - for (int i = 0; i < numKeys - 1; i++) { - nodes.get(i).checkAcquiredLocks( - Policies.DISABLED, nodes.subList(i + 1, numKeys)); - } - return Collections.unmodifiableMap(map); - } - - /** - * For the given Enum value {@code rank}, returns the value's - * {@code "EnumClass.name"}, which is used in exception and warning - * output. - */ - private String getLockName(Enum<?> rank) { - return rank.getDeclaringClass().getSimpleName() + "." + rank.name(); - } - } - - //////// Implementation ///////// - - private static final Logger logger = Logger.getLogger( - CycleDetectingLockFactory.class.getName()); - - final Policy policy; - - private CycleDetectingLockFactory(Policy policy) { - this.policy = checkNotNull(policy); - } - - /** - * Tracks the currently acquired locks for each Thread, kept up to date by - * calls to {@link #aboutToAcquire(CycleDetectingLock)} and - * {@link #lockStateChanged(CycleDetectingLock)}. - */ - // This is logically a Set, but an ArrayList is used to minimize the amount - // of allocation done on lock()/unlock(). - private static final ThreadLocal<ArrayList<LockGraphNode>> - acquiredLocks = new ThreadLocal<ArrayList<LockGraphNode>>() { - @Override - protected ArrayList<LockGraphNode> initialValue() { - return Lists.<LockGraphNode>newArrayListWithCapacity(3); - } - }; - - /** - * A Throwable used to record a stack trace that illustrates an example of - * a specific lock acquisition ordering. The top of the stack trace is - * truncated such that it starts with the acquisition of the lock in - * question, e.g. - * - * <pre> - * com...ExampleStackTrace: LockB -> LockC - * at com...CycleDetectingReentrantLock.lock(CycleDetectingLockFactory.java:443) - * at ... - * at ... - * at com...MyClass.someMethodThatAcquiresLockB(MyClass.java:123) - * </pre> - */ - private static class ExampleStackTrace extends IllegalStateException { - - static final StackTraceElement[] EMPTY_STACK_TRACE = - new StackTraceElement[0]; - - static Set<String> EXCLUDED_CLASS_NAMES = ImmutableSet.of( - CycleDetectingLockFactory.class.getName(), - ExampleStackTrace.class.getName(), - LockGraphNode.class.getName()); - - ExampleStackTrace(LockGraphNode node1, LockGraphNode node2) { - super(node1.getLockName() + " -> " + node2.getLockName()); - StackTraceElement[] origStackTrace = getStackTrace(); - for (int i = 0, n = origStackTrace.length; i < n; i++) { - if (WithExplicitOrdering.class.getName().equals( - origStackTrace[i].getClassName())) { - // For pre-populated disallowedPriorLocks edges, omit the stack trace. - setStackTrace(EMPTY_STACK_TRACE); - break; - } - if (!EXCLUDED_CLASS_NAMES.contains(origStackTrace[i].getClassName())) { - setStackTrace(Arrays.copyOfRange(origStackTrace, i, n)); - break; - } - } - } - } - - /** - * Represents a detected cycle in lock acquisition ordering. The exception - * includes a causal chain of {@code ExampleStackTrace} instances to illustrate the - * cycle, e.g. - * - * <pre> - * com....PotentialDeadlockException: Potential Deadlock from LockC -> ReadWriteA - * at ... - * at ... - * Caused by: com...ExampleStackTrace: LockB -> LockC - * at ... - * at ... - * Caused by: com...ExampleStackTrace: ReadWriteA -> LockB - * at ... - * at ... - * </pre> - * - * Instances are logged for the {@code Policies.WARN}, and thrown for - * {@code Policies.THROW}. - * - * @since 13.0 - */ - @Beta - public static final class PotentialDeadlockException - extends ExampleStackTrace { - - private final ExampleStackTrace conflictingStackTrace; - - private PotentialDeadlockException( - LockGraphNode node1, - LockGraphNode node2, - ExampleStackTrace conflictingStackTrace) { - super(node1, node2); - this.conflictingStackTrace = conflictingStackTrace; - initCause(conflictingStackTrace); - } - - public ExampleStackTrace getConflictingStackTrace() { - return conflictingStackTrace; - } - - /** - * Appends the chain of messages from the {@code conflictingStackTrace} to - * the original {@code message}. - */ - @Override - public String getMessage() { - StringBuilder message = new StringBuilder(super.getMessage()); - for (Throwable t = conflictingStackTrace; t != null; t = t.getCause()) { - message.append(", ").append(t.getMessage()); - } - return message.toString(); - } - } - - /** - * Internal Lock implementations implement the {@code CycleDetectingLock} - * interface, allowing the detection logic to treat all locks in the same - * manner. - */ - private interface CycleDetectingLock { - - /** @return the {@link LockGraphNode} associated with this lock. */ - LockGraphNode getLockGraphNode(); - - /** @return {@code true} if the current thread has acquired this lock. */ - boolean isAcquiredByCurrentThread(); - } - - /** - * A {@code LockGraphNode} associated with each lock instance keeps track of - * the directed edges in the lock acquisition graph. - */ - private static class LockGraphNode { - - /** - * The map tracking the locks that are known to be acquired before this - * lock, each associated with an example stack trace. Locks are weakly keyed - * to allow proper garbage collection when they are no longer referenced. - */ - final Map<LockGraphNode, ExampleStackTrace> allowedPriorLocks = - new MapMaker().weakKeys().makeMap(); - - /** - * The map tracking lock nodes that can cause a lock acquisition cycle if - * acquired before this node. - */ - final Map<LockGraphNode, PotentialDeadlockException> - disallowedPriorLocks = new MapMaker().weakKeys().makeMap(); - - final String lockName; - - LockGraphNode(String lockName) { - this.lockName = Preconditions.checkNotNull(lockName); - } - - String getLockName() { - return lockName; - } - - void checkAcquiredLocks( - Policy policy, List<LockGraphNode> acquiredLocks) { - for (int i = 0, size = acquiredLocks.size(); i < size; i++) { - checkAcquiredLock(policy, acquiredLocks.get(i)); - } - } - - /** - * Checks the acquisition-ordering between {@code this}, which is about to - * be acquired, and the specified {@code acquiredLock}. - * <p> - * When this method returns, the {@code acquiredLock} should be in either - * the {@code preAcquireLocks} map, for the case in which it is safe to - * acquire {@code this} after the {@code acquiredLock}, or in the - * {@code disallowedPriorLocks} map, in which case it is not safe. - */ - void checkAcquiredLock(Policy policy, LockGraphNode acquiredLock) { - // checkAcquiredLock() should never be invoked by a lock that has already - // been acquired. For unordered locks, aboutToAcquire() ensures this by - // checking isAcquiredByCurrentThread(). For ordered locks, however, this - // can happen because multiple locks may share the same LockGraphNode. In - // this situation, throw an IllegalStateException as defined by contract - // described in the documentation of WithExplicitOrdering. - Preconditions.checkState( - this != acquiredLock, - "Attempted to acquire multiple locks with the same rank " + - acquiredLock.getLockName()); - - if (allowedPriorLocks.containsKey(acquiredLock)) { - // The acquisition ordering from "acquiredLock" to "this" has already - // been verified as safe. In a properly written application, this is - // the common case. - return; - } - PotentialDeadlockException previousDeadlockException = - disallowedPriorLocks.get(acquiredLock); - if (previousDeadlockException != null) { - // Previously determined to be an unsafe lock acquisition. - // Create a new PotentialDeadlockException with the same causal chain - // (the example cycle) as that of the cached exception. - PotentialDeadlockException exception = new PotentialDeadlockException( - acquiredLock, this, - previousDeadlockException.getConflictingStackTrace()); - policy.handlePotentialDeadlock(exception); - return; - } - // Otherwise, it's the first time seeing this lock relationship. Look for - // a path from the acquiredLock to this. - Set<LockGraphNode> seen = Sets.newIdentityHashSet(); - ExampleStackTrace path = acquiredLock.findPathTo(this, seen); - - if (path == null) { - // this can be safely acquired after the acquiredLock. - // - // Note that there is a race condition here which can result in missing - // a cyclic edge: it's possible for two threads to simultaneous find - // "safe" edges which together form a cycle. Preventing this race - // condition efficiently without _introducing_ deadlock is probably - // tricky. For now, just accept the race condition---missing a warning - // now and then is still better than having no deadlock detection. - allowedPriorLocks.put( - acquiredLock, new ExampleStackTrace(acquiredLock, this)); - } else { - // Unsafe acquisition order detected. Create and cache a - // PotentialDeadlockException. - PotentialDeadlockException exception = - new PotentialDeadlockException(acquiredLock, this, path); - disallowedPriorLocks.put(acquiredLock, exception); - policy.handlePotentialDeadlock(exception); - } - } - - /** - * Performs a depth-first traversal of the graph edges defined by each - * node's {@code allowedPriorLocks} to find a path between {@code this} and - * the specified {@code lock}. - * - * @return If a path was found, a chained {@link ExampleStackTrace} - * illustrating the path to the {@code lock}, or {@code null} if no path - * was found. - */ - @Nullable - private ExampleStackTrace findPathTo( - LockGraphNode node, Set<LockGraphNode> seen) { - if (!seen.add(this)) { - return null; // Already traversed this node. - } - ExampleStackTrace found = allowedPriorLocks.get(node); - if (found != null) { - return found; // Found a path ending at the node! - } - // Recurse the edges. - for (Map.Entry<LockGraphNode, ExampleStackTrace> entry : - allowedPriorLocks.entrySet()) { - LockGraphNode preAcquiredLock = entry.getKey(); - found = preAcquiredLock.findPathTo(node, seen); - if (found != null) { - // One of this node's allowedPriorLocks found a path. Prepend an - // ExampleStackTrace(preAcquiredLock, this) to the returned chain of - // ExampleStackTraces. - ExampleStackTrace path = - new ExampleStackTrace(preAcquiredLock, this); - path.setStackTrace(entry.getValue().getStackTrace()); - path.initCause(found); - return path; - } - } - return null; - } - } - - /** - * CycleDetectingLock implementations must call this method before attempting - * to acquire the lock. - */ - private void aboutToAcquire(CycleDetectingLock lock) { - if (!lock.isAcquiredByCurrentThread()) { - ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get(); - LockGraphNode node = lock.getLockGraphNode(); - node.checkAcquiredLocks(policy, acquiredLockList); - acquiredLockList.add(node); - } - } - - /** - * CycleDetectingLock implementations must call this method in a - * {@code finally} clause after any attempt to change the lock state, - * including both lock and unlock attempts. Failure to do so can result in - * corrupting the acquireLocks set. - */ - private void lockStateChanged(CycleDetectingLock lock) { - if (!lock.isAcquiredByCurrentThread()) { - ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get(); - LockGraphNode node = lock.getLockGraphNode(); - // Iterate in reverse because locks are usually locked/unlocked in a - // LIFO order. - for (int i = acquiredLockList.size() - 1; i >=0; i--) { - if (acquiredLockList.get(i) == node) { - acquiredLockList.remove(i); - break; - } - } - } - } - - final class CycleDetectingReentrantLock - extends ReentrantLock implements CycleDetectingLock { - - private final LockGraphNode lockGraphNode; - - private CycleDetectingReentrantLock( - LockGraphNode lockGraphNode, boolean fair) { - super(fair); - this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode); - } - - ///// CycleDetectingLock methods. ///// - - @Override - public LockGraphNode getLockGraphNode() { - return lockGraphNode; - } - - @Override - public boolean isAcquiredByCurrentThread() { - return isHeldByCurrentThread(); - } - - ///// Overridden ReentrantLock methods. ///// - - @Override - public void lock() { - aboutToAcquire(this); - try { - super.lock(); - } finally { - lockStateChanged(this); - } - } - - @Override - public void lockInterruptibly() throws InterruptedException { - aboutToAcquire(this); - try { - super.lockInterruptibly(); - } finally { - lockStateChanged(this); - } - } - - @Override - public boolean tryLock() { - aboutToAcquire(this); - try { - return super.tryLock(); - } finally { - lockStateChanged(this); - } - } - - @Override - public boolean tryLock(long timeout, TimeUnit unit) - throws InterruptedException { - aboutToAcquire(this); - try { - return super.tryLock(timeout, unit); - } finally { - lockStateChanged(this); - } - } - - @Override - public void unlock() { - try { - super.unlock(); - } finally { - lockStateChanged(this); - } - } - } - - final class CycleDetectingReentrantReadWriteLock - extends ReentrantReadWriteLock implements CycleDetectingLock { - - // These ReadLock/WriteLock implementations shadow those in the - // ReentrantReadWriteLock superclass. They are simply wrappers around the - // internal Sync object, so this is safe since the shadowed locks are never - // exposed or used. - private final CycleDetectingReentrantReadLock readLock; - private final CycleDetectingReentrantWriteLock writeLock; - - private final LockGraphNode lockGraphNode; - - private CycleDetectingReentrantReadWriteLock( - LockGraphNode lockGraphNode, boolean fair) { - super(fair); - this.readLock = new CycleDetectingReentrantReadLock(this); - this.writeLock = new CycleDetectingReentrantWriteLock(this); - this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode); - } - - ///// Overridden ReentrantReadWriteLock methods. ///// - - @Override - public ReadLock readLock() { - return readLock; - } - - @Override - public WriteLock writeLock() { - return writeLock; - } - - ///// CycleDetectingLock methods. ///// - - @Override - public LockGraphNode getLockGraphNode() { - return lockGraphNode; - } - - @Override - public boolean isAcquiredByCurrentThread() { - return isWriteLockedByCurrentThread() || getReadHoldCount() > 0; - } - } - - private class CycleDetectingReentrantReadLock - extends ReentrantReadWriteLock.ReadLock { - - final CycleDetectingReentrantReadWriteLock readWriteLock; - - CycleDetectingReentrantReadLock( - CycleDetectingReentrantReadWriteLock readWriteLock) { - super(readWriteLock); - this.readWriteLock = readWriteLock; - } - - @Override - public void lock() { - aboutToAcquire(readWriteLock); - try { - super.lock(); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public void lockInterruptibly() throws InterruptedException { - aboutToAcquire(readWriteLock); - try { - super.lockInterruptibly(); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public boolean tryLock() { - aboutToAcquire(readWriteLock); - try { - return super.tryLock(); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public boolean tryLock(long timeout, TimeUnit unit) - throws InterruptedException { - aboutToAcquire(readWriteLock); - try { - return super.tryLock(timeout, unit); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public void unlock() { - try { - super.unlock(); - } finally { - lockStateChanged(readWriteLock); - } - } - } - - private class CycleDetectingReentrantWriteLock - extends ReentrantReadWriteLock.WriteLock { - - final CycleDetectingReentrantReadWriteLock readWriteLock; - - CycleDetectingReentrantWriteLock( - CycleDetectingReentrantReadWriteLock readWriteLock) { - super(readWriteLock); - this.readWriteLock = readWriteLock; - } - - @Override - public void lock() { - aboutToAcquire(readWriteLock); - try { - super.lock(); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public void lockInterruptibly() throws InterruptedException { - aboutToAcquire(readWriteLock); - try { - super.lockInterruptibly(); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public boolean tryLock() { - aboutToAcquire(readWriteLock); - try { - return super.tryLock(); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public boolean tryLock(long timeout, TimeUnit unit) - throws InterruptedException { - aboutToAcquire(readWriteLock); - try { - return super.tryLock(timeout, unit); - } finally { - lockStateChanged(readWriteLock); - } - } - - @Override - public void unlock() { - try { - super.unlock(); - } finally { - lockStateChanged(readWriteLock); - } - } - } -} diff --git a/guava/src/com/google/common/util/concurrent/ExecutionError.java b/guava/src/com/google/common/util/concurrent/ExecutionError.java index e462969..ce588eb 100644 --- a/guava/src/com/google/common/util/concurrent/ExecutionError.java +++ b/guava/src/com/google/common/util/concurrent/ExecutionError.java @@ -17,8 +17,7 @@ package com.google.common.util.concurrent; import com.google.common.annotations.GwtCompatible; - -import javax.annotation.Nullable; +import com.google.common.annotations.Beta; /** * {@link Error} variant of {@link java.util.concurrent.ExecutionException}. As @@ -32,6 +31,7 @@ import javax.annotation.Nullable; * @author Chris Povirk * @since 10.0 */ +@Beta @GwtCompatible public class ExecutionError extends Error { /** @@ -42,21 +42,21 @@ public class ExecutionError extends Error { /** * Creates a new instance with the given detail message. */ - protected ExecutionError(@Nullable String message) { + protected ExecutionError(String message) { super(message); } /** * Creates a new instance with the given detail message and cause. */ - public ExecutionError(@Nullable String message, @Nullable Error cause) { + public ExecutionError(String message, Error cause) { super(message, cause); } /** * Creates a new instance with the given cause. */ - public ExecutionError(@Nullable Error cause) { + public ExecutionError(Error cause) { super(cause); } diff --git a/guava/src/com/google/common/util/concurrent/ExecutionList.java b/guava/src/com/google/common/util/concurrent/ExecutionList.java index e1a40d0..d1b78f5 100644 --- a/guava/src/com/google/common/util/concurrent/ExecutionList.java +++ b/guava/src/com/google/common/util/concurrent/ExecutionList.java @@ -16,7 +16,6 @@ package com.google.common.util.concurrent; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; @@ -46,7 +45,7 @@ import java.util.logging.Logger; public final class ExecutionList { // Logger to log exceptions caught when running runnables. - @VisibleForTesting static final Logger log = + private static final Logger log = Logger.getLogger(ExecutionList.class.getName()); // The runnable,executor pairs to execute. diff --git a/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java b/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java index 75a1e94..890479d 100644 --- a/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java +++ b/guava/src/com/google/common/util/concurrent/FakeTimeLimiter.java @@ -16,8 +16,6 @@ package com.google.common.util.concurrent; -import static com.google.common.base.Preconditions.checkNotNull; - import com.google.common.annotations.Beta; import java.util.concurrent.Callable; @@ -38,16 +36,12 @@ public final class FakeTimeLimiter implements TimeLimiter { @Override public <T> T newProxy(T target, Class<T> interfaceType, long timeoutDuration, TimeUnit timeoutUnit) { - checkNotNull(target); - checkNotNull(interfaceType); - checkNotNull(timeoutUnit); return target; // ha ha } @Override public <T> T callWithTimeout(Callable<T> callable, long timeoutDuration, TimeUnit timeoutUnit, boolean amInterruptible) throws Exception { - checkNotNull(timeoutUnit); return callable.call(); // fooled you } } diff --git a/guava/src/com/google/common/util/concurrent/ForwardingService.java b/guava/src/com/google/common/util/concurrent/ForwardingService.java index 8774232..e39e4db 100644 --- a/guava/src/com/google/common/util/concurrent/ForwardingService.java +++ b/guava/src/com/google/common/util/concurrent/ForwardingService.java @@ -19,22 +19,13 @@ package com.google.common.util.concurrent; import com.google.common.annotations.Beta; import com.google.common.collect.ForwardingObject; -import java.util.concurrent.Executor; - /** * A {@link Service} that forwards all method calls to another service. * - * @deprecated Instead of using a {@link ForwardingService}, consider using the - * {@link Service.Listener} functionality to hook into the {@link Service} - * lifecycle, or if you really do need to provide access to some Service - * methods, consider just providing the few that you actually need (e.g. just - * {@link #startAndWait()}) and not implementing Service. - * * @author Chris Nokleberg * @since 1.0 */ @Beta -@Deprecated public abstract class ForwardingService extends ForwardingObject implements Service { @@ -66,20 +57,6 @@ public abstract class ForwardingService extends ForwardingObject @Override public boolean isRunning() { return delegate().isRunning(); } - - /** - * @since 13.0 - */ - @Override public void addListener(Listener listener, Executor executor) { - delegate().addListener(listener, executor); - } - - /** - * @since 14.0 - */ - @Override public Throwable failureCause() { - return delegate().failureCause(); - } /** * A sensible default implementation of {@link #startAndWait()}, in terms of diff --git a/guava/src/com/google/common/util/concurrent/FutureCallback.java b/guava/src/com/google/common/util/concurrent/FutureCallback.java index 735d6ab..7b39d4a 100644 --- a/guava/src/com/google/common/util/concurrent/FutureCallback.java +++ b/guava/src/com/google/common/util/concurrent/FutureCallback.java @@ -16,6 +16,8 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.Beta; + import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -28,6 +30,7 @@ import java.util.concurrent.Future; * @author Anthony Zana * @since 10.0 */ +@Beta public interface FutureCallback<V> { /** * Invoked with the result of the {@code Future} computation when it is diff --git a/guava/src/com/google/common/util/concurrent/FutureFallback.java b/guava/src/com/google/common/util/concurrent/FutureFallback.java deleted file mode 100644 index 7d03c67..0000000 --- a/guava/src/com/google/common/util/concurrent/FutureFallback.java +++ /dev/null @@ -1,48 +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.util.concurrent; - -import com.google.common.annotations.Beta; - -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -/** - * Provides a backup {@code Future} to replace an earlier failed {@code Future}. - * An implementation of this interface can be applied to an input {@code Future} - * with {@link Futures#withFallback}. - * - * @param <V> the result type of the provided backup {@code Future} - * - * @author Bruno Diniz - * @since 14.0 - */ -@Beta -public interface FutureFallback<V> { - /** - * Returns a {@code Future} to be used in place of the {@code Future} that - * failed with the given exception. The exception is provided so that the - * {@code Fallback} implementation can conditionally determine whether to - * propagate the exception or to attempt to recover. - * - * @param t the exception that made the future fail. If the future's {@link - * Future#get() get} method throws an {@link ExecutionException}, then the - * cause is passed to this method. Any other thrown object is passed - * unaltered. - */ - ListenableFuture<V> create(Throwable t) throws Exception; -} diff --git a/guava/src/com/google/common/util/concurrent/Futures.java b/guava/src/com/google/common/util/concurrent/Futures.java index aad6b43..dc703c5 100644 --- a/guava/src/com/google/common/util/concurrent/Futures.java +++ b/guava/src/com/google/common/util/concurrent/Futures.java @@ -21,14 +21,15 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; import static com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly; +import static com.google.common.util.concurrent.Uninterruptibles.putUninterruptibly; +import static com.google.common.util.concurrent.Uninterruptibles.takeUninterruptibly; import static java.lang.Thread.currentThread; import static java.util.Arrays.asList; +import static java.util.concurrent.TimeUnit.NANOSECONDS; import com.google.common.annotations.Beta; import com.google.common.base.Function; -import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; @@ -38,27 +39,22 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.UndeclaredThrowableException; import java.util.Arrays; import java.util.List; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.annotation.Nullable; /** * Static utility methods pertaining to the {@link Future} interface. * - * <p>Many of these methods use the {@link ListenableFuture} API; consult the - * Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained"> - * {@code ListenableFuture}</a>. - * * @author Kevin Bourrillion * @author Nishant Thakkar * @author Sven Mawson @@ -75,7 +71,7 @@ public final class Futures { * * <p>The given mapping function will be applied to an * {@link InterruptedException}, a {@link CancellationException}, or an - * {@link ExecutionException}. + * {@link ExecutionException} with the actual cause of the exception. * See {@link Future#get()} for details on the exceptions thrown. * * @since 9.0 (source-compatible since 1.0) @@ -85,151 +81,6 @@ public final class Futures { return new MappingCheckedFuture<V, X>(checkNotNull(future), mapper); } - private abstract static class ImmediateFuture<V> - implements ListenableFuture<V> { - - private static final Logger log = - Logger.getLogger(ImmediateFuture.class.getName()); - - @Override - public void addListener(Runnable listener, Executor executor) { - checkNotNull(listener, "Runnable was null."); - checkNotNull(executor, "Executor was null."); - try { - executor.execute(listener); - } catch (RuntimeException e) { - // ListenableFuture's contract is that it will not throw unchecked - // exceptions, so log the bad runnable and/or executor and swallow it. - log.log(Level.SEVERE, "RuntimeException while executing runnable " - + listener + " with executor " + executor, e); - } - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return false; - } - - @Override - public abstract V get() throws ExecutionException; - - @Override - public V get(long timeout, TimeUnit unit) throws ExecutionException { - checkNotNull(unit); - return get(); - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return true; - } - } - - private static class ImmediateSuccessfulFuture<V> extends ImmediateFuture<V> { - - @Nullable private final V value; - - ImmediateSuccessfulFuture(@Nullable V value) { - this.value = value; - } - - @Override - public V get() { - return value; - } - } - - private static class ImmediateSuccessfulCheckedFuture<V, X extends Exception> - extends ImmediateFuture<V> implements CheckedFuture<V, X> { - - @Nullable private final V value; - - ImmediateSuccessfulCheckedFuture(@Nullable V value) { - this.value = value; - } - - @Override - public V get() { - return value; - } - - @Override - public V checkedGet() { - return value; - } - - @Override - public V checkedGet(long timeout, TimeUnit unit) { - checkNotNull(unit); - return value; - } - } - - private static class ImmediateFailedFuture<V> extends ImmediateFuture<V> { - - private final Throwable thrown; - - ImmediateFailedFuture(Throwable thrown) { - this.thrown = thrown; - } - - @Override - public V get() throws ExecutionException { - throw new ExecutionException(thrown); - } - } - - private static class ImmediateCancelledFuture<V> extends ImmediateFuture<V> { - - private final CancellationException thrown; - - ImmediateCancelledFuture() { - this.thrown = new CancellationException("Immediate cancelled future."); - } - - @Override - public boolean isCancelled() { - return true; - } - - @Override - public V get() { - throw AbstractFuture.cancellationExceptionWithCause( - "Task was cancelled.", thrown); - } - } - - private static class ImmediateFailedCheckedFuture<V, X extends Exception> - extends ImmediateFuture<V> implements CheckedFuture<V, X> { - - private final X thrown; - - ImmediateFailedCheckedFuture(X thrown) { - this.thrown = thrown; - } - - @Override - public V get() throws ExecutionException { - throw new ExecutionException(thrown); - } - - @Override - public V checkedGet() throws X { - throw thrown; - } - - @Override - public V checkedGet(long timeout, TimeUnit unit) throws X { - checkNotNull(unit); - throw thrown; - } - } - /** * Creates a {@code ListenableFuture} which has its value set immediately upon * construction. The getters just return the value. This {@code Future} can't @@ -237,7 +88,9 @@ public final class Futures { * {@code true}. */ public static <V> ListenableFuture<V> immediateFuture(@Nullable V value) { - return new ImmediateSuccessfulFuture<V>(value); + SettableFuture<V> future = SettableFuture.create(); + future.set(value); + return future; } /** @@ -250,7 +103,14 @@ public final class Futures { */ public static <V, X extends Exception> CheckedFuture<V, X> immediateCheckedFuture(@Nullable V value) { - return new ImmediateSuccessfulCheckedFuture<V, X>(value); + SettableFuture<V> future = SettableFuture.create(); + future.set(value); + return Futures.makeChecked(future, new Function<Exception, X>() { + @Override + public X apply(Exception e) { + throw new AssertionError("impossible"); + } + }); } /** @@ -261,21 +121,15 @@ public final class Futures { * method always returns {@code true}. Calling {@code get()} will immediately * throw the provided {@code Throwable} wrapped in an {@code * ExecutionException}. + * + * @throws Error if the throwable is an {@link Error}. */ public static <V> ListenableFuture<V> immediateFailedFuture( Throwable throwable) { checkNotNull(throwable); - return new ImmediateFailedFuture<V>(throwable); - } - - /** - * Creates a {@code ListenableFuture} which is cancelled immediately upon - * construction, so that {@code isCancelled()} always returns {@code true}. - * - * @since 14.0 - */ - public static <V> ListenableFuture<V> immediateCancelledFuture() { - return new ImmediateCancelledFuture<V>(); + SettableFuture<V> future = SettableFuture.create(); + future.setException(throwable); + return future; } /** @@ -284,224 +138,149 @@ public final class Futures { * * <p>The returned {@code Future} can't be cancelled, and its {@code isDone()} * method always returns {@code true}. Calling {@code get()} will immediately - * throw the provided {@code Exception} wrapped in an {@code + * throw the provided {@code Throwable} wrapped in an {@code * ExecutionException}, and calling {@code checkedGet()} will throw the * provided exception itself. + * + * @throws Error if the throwable is an {@link Error}. */ public static <V, X extends Exception> CheckedFuture<V, X> - immediateFailedCheckedFuture(X exception) { + immediateFailedCheckedFuture(final X exception) { checkNotNull(exception); - return new ImmediateFailedCheckedFuture<V, X>(exception); + return makeChecked(Futures.<V>immediateFailedFuture(exception), + new Function<Exception, X>() { + @Override + public X apply(Exception e) { + return exception; + } + }); } /** - * Returns a {@code Future} whose result is taken from the given primary - * {@code input} or, if the primary input fails, from the {@code Future} - * provided by the {@code fallback}. {@link FutureFallback#create} is not - * invoked until the primary input has failed, so if the primary input - * succeeds, it is never invoked. If, during the invocation of {@code - * fallback}, an exception is thrown, this exception is used as the result of - * the output {@code Future}. - * - * <p>Below is an example of a fallback that returns a default value if an - * exception occurs: - * - * <pre> {@code - * ListenableFuture<Integer> fetchCounterFuture = ...; - * - * // Falling back to a zero counter in case an exception happens when - * // processing the RPC to fetch counters. - * ListenableFuture<Integer> faultTolerantFuture = Futures.withFallback( - * fetchCounterFuture, new FutureFallback<Integer>() { - * public ListenableFuture<Integer> create(Throwable t) { - * // Returning "0" as the default for the counter when the - * // exception happens. - * return immediateFuture(0); - * } - * }); - * }</pre> - * - * The fallback can also choose to propagate the original exception when - * desired: + * <p>Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code Function} to the result of the original {@code + * Future}. Example: * * <pre> {@code - * ListenableFuture<Integer> fetchCounterFuture = ...; - * - * // Falling back to a zero counter only in case the exception was a - * // TimeoutException. - * ListenableFuture<Integer> faultTolerantFuture = Futures.withFallback( - * fetchCounterFuture, new FutureFallback<Integer>() { - * public ListenableFuture<Integer> create(Throwable t) { - * if (t instanceof TimeoutException) { - * return immediateFuture(0); - * } - * return immediateFailedFuture(t); + * ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query); + * Function<RowKey, ListenableFuture<QueryResult>> queryFunction = + * new Function<RowKey, ListenableFuture<QueryResult>>() { + * public ListenableFuture<QueryResult> apply(RowKey rowKey) { + * return dataService.read(rowKey); * } - * }); + * }; + * ListenableFuture<QueryResult> queryFuture = + * chain(rowKeyFuture, queryFunction); * }</pre> * - * Note: If the derived {@code Future} is slow or heavyweight to create - * (whether the {@code Future} itself is slow or heavyweight to complete is - * irrelevant), consider {@linkplain #withFallback(ListenableFuture, - * FutureFallback, Executor) supplying an executor}. If you do not supply an - * executor, {@code withFallback} will use {@link - * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some - * caveats for heavier operations. For example, the call to {@code - * fallback.create} may run on an unpredictable or undesirable thread: - * - * <ul> - * <li>If the input {@code Future} is done at the time {@code withFallback} - * is called, {@code withFallback} will call {@code fallback.create} inline. - * <li>If the input {@code Future} is not yet done, {@code withFallback} will - * schedule {@code fallback.create} to be run by the thread that completes - * the input {@code Future}, which may be an internal system thread such as - * an RPC network thread. - * </ul> + * <p>Note: This overload of {@code chain} is designed for cases in which the + * work of creating the derived future is fast and lightweight, as the method + * does not accept an {@code Executor} in which to perform the the work. For + * heavier derivations, this overload carries some caveats: First, the thread + * that the derivation runs in depends on whether the input {@code Future} is + * done at the time {@code chain} is called. In particular, if called late, + * {@code chain} will run the derivation in the thread that called {@code + * chain}. Second, derivations may run in an internal thread of the system + * responsible for the input {@code Future}, such as an RPC network thread. + * Finally, during the execution of a {@code sameThreadExecutor} {@code + * chain} function, all other registered but unexecuted listeners are + * prevented from running, even if those listeners are to run in other + * executors. * - * Also note that, regardless of which thread executes {@code - * fallback.create}, all other registered but unexecuted listeners are - * prevented from running during its execution, even if those listeners are - * to run in other executors. + * <p>The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * chain function. That is, if the returned {@code Future} is cancelled, it + * will attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. * - * @param input the primary input {@code Future} - * @param fallback the {@link FutureFallback} implementation to be called if - * {@code input} fails - * @since 14.0 + * @param input The future to chain + * @param function A function to chain the results of the provided future + * to the results of the returned future. This will be run in the thread + * that notifies input it is complete. + * @return A future that holds result of the chain. + * @deprecated Convert your {@code Function} to a {@code AsyncFunction}, and + * use {@link #transform(ListenableFuture, AsyncFunction)}. This method is + * scheduled to be removed from Guava in Guava release 12.0. */ - public static <V> ListenableFuture<V> withFallback( - ListenableFuture<? extends V> input, - FutureFallback<? extends V> fallback) { - return withFallback(input, fallback, sameThreadExecutor()); + @Deprecated + public static <I, O> ListenableFuture<O> chain( + ListenableFuture<I> input, + Function<? super I, ? extends ListenableFuture<? extends O>> function) { + return chain(input, function, MoreExecutors.sameThreadExecutor()); } /** - * Returns a {@code Future} whose result is taken from the given primary - * {@code input} or, if the primary input fails, from the {@code Future} - * provided by the {@code fallback}. {@link FutureFallback#create} is not - * invoked until the primary input has failed, so if the primary input - * succeeds, it is never invoked. If, during the invocation of {@code - * fallback}, an exception is thrown, this exception is used as the result of - * the output {@code Future}. - * - * <p>Below is an example of a fallback that returns a default value if an - * exception occurs: + * <p>Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code Function} to the result of the original {@code + * Future}. Example: * * <pre> {@code - * ListenableFuture<Integer> fetchCounterFuture = ...; - * - * // Falling back to a zero counter in case an exception happens when - * // processing the RPC to fetch counters. - * ListenableFuture<Integer> faultTolerantFuture = Futures.withFallback( - * fetchCounterFuture, new FutureFallback<Integer>() { - * public ListenableFuture<Integer> create(Throwable t) { - * // Returning "0" as the default for the counter when the - * // exception happens. - * return immediateFuture(0); + * ListenableFuture<RowKey> rowKeyFuture = indexService.lookUp(query); + * Function<RowKey, ListenableFuture<QueryResult>> queryFunction = + * new Function<RowKey, ListenableFuture<QueryResult>>() { + * public ListenableFuture<QueryResult> apply(RowKey rowKey) { + * return dataService.read(rowKey); * } - * }, sameThreadExecutor()); + * }; + * ListenableFuture<QueryResult> queryFuture = + * chain(rowKeyFuture, queryFunction, executor); * }</pre> * - * The fallback can also choose to propagate the original exception when - * desired: - * - * <pre> {@code - * ListenableFuture<Integer> fetchCounterFuture = ...; - * - * // Falling back to a zero counter only in case the exception was a - * // TimeoutException. - * ListenableFuture<Integer> faultTolerantFuture = Futures.withFallback( - * fetchCounterFuture, new FutureFallback<Integer>() { - * public ListenableFuture<Integer> create(Throwable t) { - * if (t instanceof TimeoutException) { - * return immediateFuture(0); - * } - * return immediateFailedFuture(t); - * } - * }, sameThreadExecutor()); - * }</pre> + * <p>The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * chain function. That is, if the returned {@code Future} is cancelled, it + * will attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. * - * When the execution of {@code fallback.create} is fast and lightweight - * (though the {@code Future} it returns need not meet these criteria), - * consider {@linkplain #withFallback(ListenableFuture, FutureFallback) - * omitting the executor} or explicitly specifying {@code - * sameThreadExecutor}. However, be aware of the caveats documented in the - * link above. - * - * @param input the primary input {@code Future} - * @param fallback the {@link FutureFallback} implementation to be called if - * {@code input} fails - * @param executor the executor that runs {@code fallback} if {@code input} - * fails - * @since 14.0 - */ - public static <V> ListenableFuture<V> withFallback( - ListenableFuture<? extends V> input, - FutureFallback<? extends V> fallback, Executor executor) { - checkNotNull(fallback); - return new FallbackFuture<V>(input, fallback, executor); - } - - /** - * A future that falls back on a second, generated future, in case its - * original future fails. + * <p>Note: For cases in which the work of creating the derived future is + * fast and lightweight, consider {@linkplain Futures#chain(ListenableFuture, + * Function) the other overload} or explicit use of {@code + * sameThreadExecutor}. For heavier derivations, this choice carries some + * caveats: First, the thread that the derivation runs in depends on whether + * the input {@code Future} is done at the time {@code chain} is called. In + * particular, if called late, {@code chain} will run the derivation in the + * thread that called {@code chain}. Second, derivations may run in an + * internal thread of the system responsible for the input {@code Future}, + * such as an RPC network thread. Finally, during the execution of a {@code + * sameThreadExecutor} {@code chain} function, all other registered but + * unexecuted listeners are prevented from running, even if those listeners + * are to run in other executors. + * + * @param input The future to chain + * @param function A function to chain the results of the provided future + * to the results of the returned future. + * @param executor Executor to run the function in. + * @return A future that holds result of the chain. + * @deprecated Convert your {@code Function} to a {@code AsyncFunction}, and + * use {@link #transform(ListenableFuture, AsyncFunction, Executor)}. This + * method is scheduled to be removed from Guava in Guava release 12.0. */ - private static class FallbackFuture<V> extends AbstractFuture<V> { - - private volatile ListenableFuture<? extends V> running; - - FallbackFuture(ListenableFuture<? extends V> input, - final FutureFallback<? extends V> fallback, - final Executor executor) { - running = input; - addCallback(running, new FutureCallback<V>() { - @Override - public void onSuccess(V value) { - set(value); - } - - @Override - public void onFailure(Throwable t) { - if (isCancelled()) { - return; - } - try { - running = fallback.create(t); - if (isCancelled()) { // in case cancel called in the meantime - running.cancel(wasInterrupted()); - return; - } - addCallback(running, new FutureCallback<V>() { - @Override - public void onSuccess(V value) { - set(value); - } - - @Override - public void onFailure(Throwable t) { - if (running.isCancelled()) { - cancel(false); - } else { - setException(t); - } - } - }, sameThreadExecutor()); - } catch (Exception e) { - setException(e); - } catch (Error e) { - setException(e); // note: rethrows + @Deprecated + public static <I, O> ListenableFuture<O> chain(ListenableFuture<I> input, + final Function<? super I, ? extends ListenableFuture<? extends O>> + function, + Executor executor) { + checkNotNull(function); + ChainingListenableFuture<I, O> chain = + new ChainingListenableFuture<I, O>(new AsyncFunction<I, O>() { + @Override + /* + * All methods of ListenableFuture are covariant, and we don't expose + * the object anywhere that would allow it to be downcast. + */ + @SuppressWarnings("unchecked") + public ListenableFuture<O> apply(I input) { + return (ListenableFuture) function.apply(input); } - } - }, executor); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - if (super.cancel(mayInterruptIfRunning)) { - running.cancel(mayInterruptIfRunning); - return true; - } - return false; - } + }, input); + input.addListener(chain, executor); + return chain; } /** @@ -523,28 +302,20 @@ public final class Futures { * transform(rowKeyFuture, queryFunction); * }</pre> * - * Note: If the derived {@code Future} is slow or heavyweight to create - * (whether the {@code Future} itself is slow or heavyweight to complete is - * irrelevant), consider {@linkplain #transform(ListenableFuture, - * AsyncFunction, Executor) supplying an executor}. If you do not supply an - * executor, {@code transform} will use {@link - * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some - * caveats for heavier operations. For example, the call to {@code - * function.apply} may run on an unpredictable or undesirable thread: - * - * <ul> - * <li>If the input {@code Future} is done at the time {@code transform} is - * called, {@code transform} will call {@code function.apply} inline. - * <li>If the input {@code Future} is not yet done, {@code transform} will - * schedule {@code function.apply} to be run by the thread that completes the - * input {@code Future}, which may be an internal system thread such as an - * RPC network thread. - * </ul> - * - * Also note that, regardless of which thread executes {@code - * function.apply}, all other registered but unexecuted listeners are - * prevented from running during its execution, even if those listeners are - * to run in other executors. + * <p>Note: This overload of {@code transform} is designed for cases in which + * the work of creating the derived {@code Future} is fast and lightweight, + * as the method does not accept an {@code Executor} in which to perform the + * the work. (The created {@code Future} itself need not complete quickly.) + * For heavier operations, this overload carries some caveats: First, the + * thread that {@code function.apply} runs in depends on whether the input + * {@code Future} is done at the time {@code transform} is called. In + * particular, if called late, {@code transform} will run the operation in + * the thread that called {@code transform}. Second, {@code function.apply} + * may run in an internal thread of the system responsible for the input + * {@code Future}, such as an RPC network thread. Finally, during the + * execution of a {@code sameThreadExecutor} {@code function.apply}, all + * other registered but unexecuted listeners are prevented from running, even + * if those listeners are to run in other executors. * * <p>The returned {@code Future} attempts to keep its cancellation state in * sync with that of the input future and that of the future returned by the @@ -591,11 +362,20 @@ public final class Futures { * cancelled, the returned {@code Future} will receive a callback in which it * will attempt to cancel itself. * - * <p>When the execution of {@code function.apply} is fast and lightweight - * (though the {@code Future} it returns need not meet these criteria), - * consider {@linkplain #transform(ListenableFuture, AsyncFunction) omitting - * the executor} or explicitly specifying {@code sameThreadExecutor}. - * However, be aware of the caveats documented in the link above. + * <p>Note: For cases in which the work of creating the derived future is + * fast and lightweight, consider {@linkplain + * Futures#transform(ListenableFuture, Function) the other overload} or + * explicit use of {@code sameThreadExecutor}. For heavier derivations, this + * choice carries some caveats: First, the thread that {@code function.apply} + * runs in depends on whether the input {@code Future} is done at the time + * {@code transform} is called. In particular, if called late, {@code + * transform} will run the operation in the thread that called {@code + * transform}. Second, {@code function.apply} may run in an internal thread + * of the system responsible for the input {@code Future}, such as an RPC + * network thread. Finally, during the execution of a {@code + * sameThreadExecutor} {@code function.apply}, all other registered but + * unexecuted listeners are prevented from running, even if those listeners + * are to run in other executors. * * @param input The future to transform * @param function A function to transform the result of the input future @@ -631,26 +411,19 @@ public final class Futures { * transform(queryFuture, rowsFunction); * }</pre> * - * Note: If the transformation is slow or heavyweight, consider {@linkplain - * #transform(ListenableFuture, Function, Executor) supplying an executor}. - * If you do not supply an executor, {@code transform} will use {@link - * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some - * caveats for heavier operations. For example, the call to {@code - * function.apply} may run on an unpredictable or undesirable thread: - * - * <ul> - * <li>If the input {@code Future} is done at the time {@code transform} is - * called, {@code transform} will call {@code function.apply} inline. - * <li>If the input {@code Future} is not yet done, {@code transform} will - * schedule {@code function.apply} to be run by the thread that completes the - * input {@code Future}, which may be an internal system thread such as an - * RPC network thread. - * </ul> - * - * Also note that, regardless of which thread executes {@code - * function.apply}, all other registered but unexecuted listeners are - * prevented from running during its execution, even if those listeners are - * to run in other executors. + * <p>Note: This overload of {@code transform} is designed for cases in which + * the transformation is fast and lightweight, as the method does not accept + * an {@code Executor} in which to perform the the work. For heavier + * transformations, this overload carries some caveats: First, the thread + * that the transformation runs in depends on whether the input {@code + * Future} is done at the time {@code transform} is called. In particular, if + * called late, {@code transform} will perform the transformation in the + * thread that called {@code transform}. Second, transformations may run in + * an internal thread of the system responsible for the input {@code Future}, + * such as an RPC network thread. Finally, during the execution of a {@code + * sameThreadExecutor} transformation, all other registered but unexecuted + * listeners are prevented from running, even if those listeners are to run + * in other executors. * * <p>The returned {@code Future} attempts to keep its cancellation state in * sync with that of the input future. That is, if the returned {@code Future} @@ -661,16 +434,16 @@ public final class Futures { * <p>An example use of this method is to convert a serializable object * returned from an RPC into a POJO. * - * @param input The future to transform + * @param future The future to transform * @param function A Function to transform the results of the provided future * to the results of the returned future. This will be run in the thread * that notifies input it is complete. * @return A future that holds result of the transformation. * @since 9.0 (in 1.0 as {@code compose}) */ - public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> input, + public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> future, final Function<? super I, ? extends O> function) { - return transform(input, function, MoreExecutors.sameThreadExecutor()); + return transform(future, function, MoreExecutors.sameThreadExecutor()); } /** @@ -699,29 +472,39 @@ public final class Futures { * <p>An example use of this method is to convert a serializable object * returned from an RPC into a POJO. * - * <p>When the transformation is fast and lightweight, consider {@linkplain - * #transform(ListenableFuture, Function) omitting the executor} or - * explicitly specifying {@code sameThreadExecutor}. However, be aware of the - * caveats documented in the link above. - * - * @param input The future to transform + * <p>Note: For cases in which the transformation is fast and lightweight, + * consider {@linkplain Futures#transform(ListenableFuture, Function) the + * other overload} or explicit use of {@link + * MoreExecutors#sameThreadExecutor}. For heavier transformations, this + * choice carries some caveats: First, the thread that the transformation + * runs in depends on whether the input {@code Future} is done at the time + * {@code transform} is called. In particular, if called late, {@code + * transform} will perform the transformation in the thread that called + * {@code transform}. Second, transformations may run in an internal thread + * of the system responsible for the input {@code Future}, such as an RPC + * network thread. Finally, during the execution of a {@code + * sameThreadExecutor} transformation, all other registered but unexecuted + * listeners are prevented from running, even if those listeners are to run + * in other executors. + * + * @param future The future to transform * @param function A Function to transform the results of the provided future * to the results of the returned future. * @param executor Executor to run the function in. * @return A future that holds result of the transformation. * @since 9.0 (in 2.0 as {@code compose}) */ - public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> input, + public static <I, O> ListenableFuture<O> transform(ListenableFuture<I> future, final Function<? super I, ? extends O> function, Executor executor) { checkNotNull(function); - AsyncFunction<I, O> wrapperFunction - = new AsyncFunction<I, O>() { + Function<I, ListenableFuture<O>> wrapperFunction + = new Function<I, ListenableFuture<O>>() { @Override public ListenableFuture<O> apply(I input) { O output = function.apply(input); return immediateFuture(output); } }; - return transform(input, wrapperFunction, executor); + return chain(future, wrapperFunction, executor); } /** @@ -741,43 +524,43 @@ public final class Futures { * who don't have a {@code ListenableFuture} available and * do not mind repeated, lazy function evaluation. * - * @param input The future to transform + * @param future The future to transform * @param function A Function to transform the results of the provided future * to the results of the returned future. * @return A future that returns the result of the transformation. * @since 10.0 */ @Beta - public static <I, O> Future<O> lazyTransform(final Future<I> input, + public static <I, O> Future<O> lazyTransform(final Future<I> future, final Function<? super I, ? extends O> function) { - checkNotNull(input); + checkNotNull(future); checkNotNull(function); return new Future<O>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { - return input.cancel(mayInterruptIfRunning); + return future.cancel(mayInterruptIfRunning); } @Override public boolean isCancelled() { - return input.isCancelled(); + return future.isCancelled(); } @Override public boolean isDone() { - return input.isDone(); + return future.isDone(); } @Override public O get() throws InterruptedException, ExecutionException { - return applyTransformation(input.get()); + return applyTransformation(future.get()); } @Override public O get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return applyTransformation(input.get(timeout, unit)); + return applyTransformation(future.get(timeout, unit)); } private O applyTransformation(I input) throws ExecutionException { @@ -806,6 +589,8 @@ public final class Futures { private AsyncFunction<? super I, ? extends O> function; private ListenableFuture<? extends I> inputFuture; private volatile ListenableFuture<? extends O> outputFuture; + private final BlockingQueue<Boolean> mayInterruptIfRunningChannel = + new LinkedBlockingQueue<Boolean>(1); private final CountDownLatch outputCreated = new CountDownLatch(1); private ChainingListenableFuture( @@ -815,6 +600,90 @@ public final class Futures { this.inputFuture = checkNotNull(inputFuture); } + /** + * Delegate the get() to the input and output futures, in case + * their implementations defer starting computation until their + * own get() is invoked. + */ + @Override + public O get() throws InterruptedException, ExecutionException { + if (!isDone()) { + // Invoking get on the inputFuture will ensure our own run() + // method below is invoked as a listener when inputFuture sets + // its value. Therefore when get() returns we should then see + // the outputFuture be created. + ListenableFuture<? extends I> inputFuture = this.inputFuture; + if (inputFuture != null) { + inputFuture.get(); + } + + // If our listener was scheduled to run on an executor we may + // need to wait for our listener to finish running before the + // outputFuture has been constructed by the function. + outputCreated.await(); + + // Like above with the inputFuture, we have a listener on + // the outputFuture that will set our own value when its + // value is set. Invoking get will ensure the output can + // complete and invoke our listener, so that we can later + // get the result. + ListenableFuture<? extends O> outputFuture = this.outputFuture; + if (outputFuture != null) { + outputFuture.get(); + } + } + return super.get(); + } + + /** + * Delegate the get() to the input and output futures, in case + * their implementations defer starting computation until their + * own get() is invoked. + */ + @Override + public O get(long timeout, TimeUnit unit) throws TimeoutException, + ExecutionException, InterruptedException { + if (!isDone()) { + // Use a single time unit so we can decrease remaining timeout + // as we wait for various phases to complete. + if (unit != NANOSECONDS) { + timeout = NANOSECONDS.convert(timeout, unit); + unit = NANOSECONDS; + } + + // Invoking get on the inputFuture will ensure our own run() + // method below is invoked as a listener when inputFuture sets + // its value. Therefore when get() returns we should then see + // the outputFuture be created. + ListenableFuture<? extends I> inputFuture = this.inputFuture; + if (inputFuture != null) { + long start = System.nanoTime(); + inputFuture.get(timeout, unit); + timeout -= Math.max(0, System.nanoTime() - start); + } + + // If our listener was scheduled to run on an executor we may + // need to wait for our listener to finish running before the + // outputFuture has been constructed by the function. + long start = System.nanoTime(); + if (!outputCreated.await(timeout, unit)) { + throw new TimeoutException(); + } + timeout -= Math.max(0, System.nanoTime() - start); + + // Like above with the inputFuture, we have a listener on + // the outputFuture that will set our own value when its + // value is set. Invoking get will ensure the output can + // complete and invoke our listener, so that we can later + // get the result. + ListenableFuture<? extends O> outputFuture = this.outputFuture; + if (outputFuture != null) { + outputFuture.get(timeout, unit); + } + } + return super.get(timeout, unit); + } + @Override public boolean cancel(boolean mayInterruptIfRunning) { /* @@ -824,6 +693,7 @@ public final class Futures { if (super.cancel(mayInterruptIfRunning)) { // This should never block since only one thread is allowed to cancel // this Future. + putUninterruptibly(mayInterruptIfRunningChannel, mayInterruptIfRunning); cancel(inputFuture, mayInterruptIfRunning); cancel(outputFuture, mayInterruptIfRunning); return true; @@ -859,7 +729,13 @@ public final class Futures { final ListenableFuture<? extends O> outputFuture = this.outputFuture = function.apply(sourceResult); if (isCancelled()) { - outputFuture.cancel(wasInterrupted()); + // Handles the case where cancel was called while the function was + // being applied. + // There is a gap in cancel(boolean) between calling sync.cancel() + // and storing the value of mayInterruptIfRunning, so this thread + // needs to block, waiting for that value. + outputFuture.cancel( + takeUninterruptibly(mayInterruptIfRunningChannel)); this.outputFuture = null; return; } @@ -907,52 +783,14 @@ public final class Futures { } /** - * Returns a new {@code ListenableFuture} whose result is the product of - * calling {@code get()} on the {@code Future} nested within the given {@code - * Future}, effectively chaining the futures one after the other. Example: - * - * <pre> {@code - * SettableFuture<ListenableFuture<String>> nested = SettableFuture.create(); - * ListenableFuture<String> dereferenced = dereference(nested); - * }</pre> - * - * <p>This call has the same cancellation and execution semantics as {@link - * #transform(ListenableFuture, AsyncFunction)}, in that the returned {@code - * Future} attempts to keep its cancellation state in sync with both the - * input {@code Future} and the nested {@code Future}. The transformation - * is very lightweight and therefore takes place in the thread that called - * {@code dereference}. - * - * @param nested The nested future to transform. - * @return A future that holds result of the inner future. - * @since 13.0 - */ - @Beta - @SuppressWarnings({"rawtypes", "unchecked"}) - public static <V> ListenableFuture<V> dereference( - ListenableFuture<? extends ListenableFuture<? extends V>> nested) { - return Futures.transform((ListenableFuture) nested, (AsyncFunction) DEREFERENCER); - } - - /** - * Helper {@code Function} for {@link #dereference}. - */ - private static final AsyncFunction<ListenableFuture<Object>, Object> DEREFERENCER = - new AsyncFunction<ListenableFuture<Object>, Object>() { - @Override public ListenableFuture<Object> apply(ListenableFuture<Object> input) { - return input; - } - }; - - /** * Creates a new {@code ListenableFuture} whose value is a list containing the * values of all its input futures, if all succeed. If any input fails, the * returned future fails. * * <p>The list of results is in the same order as the input list. * - * <p>Canceling this future will attempt to cancel all the component futures, - * and if any of the provided futures fails or is canceled, this one is, + * <p>Canceling this future does not cancel any of the component futures; + * however, if any of the provided futures fails or is canceled, this one is, * too. * * @param futures futures to combine @@ -963,7 +801,7 @@ public final class Futures { @Beta public static <V> ListenableFuture<List<V>> allAsList( ListenableFuture<? extends V>... futures) { - return listFuture(ImmutableList.copyOf(futures), true, + return new ListFuture<V>(ImmutableList.copyOf(futures), true, MoreExecutors.sameThreadExecutor()); } @@ -974,8 +812,8 @@ public final class Futures { * * <p>The list of results is in the same order as the input list. * - * <p>Canceling this future will attempt to cancel all the component futures, - * and if any of the provided futures fails or is canceled, this one is, + * <p>Canceling this future does not cancel any of the component futures; + * however, if any of the provided futures fails or is canceled, this one is, * too. * * @param futures futures to combine @@ -986,7 +824,7 @@ public final class Futures { @Beta public static <V> ListenableFuture<List<V>> allAsList( Iterable<? extends ListenableFuture<? extends V>> futures) { - return listFuture(ImmutableList.copyOf(futures), true, + return new ListFuture<V>(ImmutableList.copyOf(futures), true, MoreExecutors.sameThreadExecutor()); } @@ -998,8 +836,6 @@ public final class Futures { * indistinguishable from the future having a successful value of * {@code null}). * - * <p>Canceling this future will attempt to cancel all the component futures. - * * @param futures futures to combine * @return a future that provides a list of the results of the component * futures @@ -1008,7 +844,7 @@ public final class Futures { @Beta public static <V> ListenableFuture<List<V>> successfulAsList( ListenableFuture<? extends V>... futures) { - return listFuture(ImmutableList.copyOf(futures), false, + return new ListFuture<V>(ImmutableList.copyOf(futures), false, MoreExecutors.sameThreadExecutor()); } @@ -1020,8 +856,6 @@ public final class Futures { * indistinguishable from the future having a successful value of * {@code null}). * - * <p>Canceling this future will attempt to cancel all the component futures. - * * @param futures futures to combine * @return a future that provides a list of the results of the component * futures @@ -1030,7 +864,7 @@ public final class Futures { @Beta public static <V> ListenableFuture<List<V>> successfulAsList( Iterable<? extends ListenableFuture<? extends V>> futures) { - return listFuture(ImmutableList.copyOf(futures), false, + return new ListFuture<V>(ImmutableList.copyOf(futures), false, MoreExecutors.sameThreadExecutor()); } @@ -1055,26 +889,19 @@ public final class Futures { * } * });}</pre> * - * Note: If the callback is slow or heavyweight, consider {@linkplain - * #addCallback(ListenableFuture, FutureCallback, Executor) supplying an - * executor}. If you do not supply an executor, {@code addCallback} will use - * {@link MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries - * some caveats for heavier operations. For example, the callback may run on - * an unpredictable or undesirable thread: - * - * <ul> - * <li>If the input {@code Future} is done at the time {@code addCallback} is - * called, {@code addCallback} will execute the callback inline. - * <li>If the input {@code Future} is not yet done, {@code addCallback} will - * schedule the callback to be run by the thread that completes the input - * {@code Future}, which may be an internal system thread such as an RPC - * network thread. - * </ul> - * - * Also note that, regardless of which thread executes the callback, all - * other registered but unexecuted listeners are prevented from running - * during its execution, even if those listeners are to run in other - * executors. + * <p>Note: This overload of {@code addCallback} is designed for cases in + * which the callack is fast and lightweight, as the method does not accept + * an {@code Executor} in which to perform the the work. For heavier + * callbacks, this overload carries some caveats: First, the thread that the + * callback runs in depends on whether the input {@code Future} is done at the + * time {@code addCallback} is called and on whether the input {@code Future} + * is ever cancelled. In particular, {@code addCallback} may execute the + * callback in the thread that calls {@code addCallback} or {@code + * Future.cancel}. Second, callbacks may run in an internal thread of the + * system responsible for the input {@code Future}, such as an RPC network + * thread. Finally, during the execution of a {@code sameThreadExecutor} + * callback, all other registered but unexecuted listeners are prevented from + * running, even if those listeners are to run in other executors. * * <p>For a more general interface to attach a completion listener to a * {@code Future}, see {@link ListenableFuture#addListener addListener}. @@ -1111,10 +938,20 @@ public final class Futures { * } * });}</pre> * - * When the callback is fast and lightweight, consider {@linkplain - * #addCallback(ListenableFuture, FutureCallback) omitting the executor} or - * explicitly specifying {@code sameThreadExecutor}. However, be aware of the - * caveats documented in the link above. + * When the callback is fast and lightweight consider {@linkplain + * Futures#addCallback(ListenableFuture, FutureCallback) the other overload} + * or explicit use of {@link MoreExecutors#sameThreadExecutor + * sameThreadExecutor}. For heavier callbacks, this choice carries some + * caveats: First, the thread that the callback runs in depends on whether + * the input {@code Future} is done at the time {@code addCallback} is called + * and on whether the input {@code Future} is ever cancelled. In particular, + * {@code addCallback} may execute the callback in the thread that calls + * {@code addCallback} or {@code Future.cancel}. Second, callbacks may run in + * an internal thread of the system responsible for the input {@code Future}, + * such as an RPC network thread. Finally, during the execution of a {@code + * sameThreadExecutor} callback, all other registered but unexecuted + * listeners are prevented from running, even if those listeners are to run + * in other executors. * * <p>For a more general interface to attach a completion listener to a * {@code Future}, see {@link ListenableFuture#addListener addListener}. @@ -1131,22 +968,18 @@ public final class Futures { Runnable callbackListener = new Runnable() { @Override public void run() { - final V value; try { // TODO(user): (Before Guava release), validate that this // is the thing for IE. - value = getUninterruptibly(future); + V value = getUninterruptibly(future); + callback.onSuccess(value); } catch (ExecutionException e) { callback.onFailure(e.getCause()); - return; } catch (RuntimeException e) { callback.onFailure(e); - return; } catch (Error e) { callback.onFailure(e); - return; } - callback.onSuccess(value); } }; future.addListener(callbackListener, executor); @@ -1434,53 +1267,49 @@ public final class Futures { } } - private interface FutureCombiner<V, C> { - C combine(List<Optional<V>> values); - } - - private static class CombinedFuture<V, C> extends AbstractFuture<C> { - ImmutableCollection<? extends ListenableFuture<? extends V>> futures; + /** + * Class that implements {@link #allAsList} and {@link #successfulAsList}. + * The idea is to create a (null-filled) List and register a listener with + * each component future to fill out the value in the List when that future + * completes. + */ + private static class ListFuture<V> extends AbstractFuture<List<V>> { + ImmutableList<? extends ListenableFuture<? extends V>> futures; final boolean allMustSucceed; final AtomicInteger remaining; - FutureCombiner<V, C> combiner; - List<Optional<V>> values; + List<V> values; - CombinedFuture( - ImmutableCollection<? extends ListenableFuture<? extends V>> futures, - boolean allMustSucceed, Executor listenerExecutor, - FutureCombiner<V, C> combiner) { + /** + * Constructor. + * + * @param futures all the futures to build the list from + * @param allMustSucceed whether a single failure or cancellation should + * propagate to this future + * @param listenerExecutor used to run listeners on all the passed in + * futures. + */ + ListFuture( + final ImmutableList<? extends ListenableFuture<? extends V>> futures, + final boolean allMustSucceed, final Executor listenerExecutor) { this.futures = futures; + this.values = Lists.newArrayListWithCapacity(futures.size()); this.allMustSucceed = allMustSucceed; this.remaining = new AtomicInteger(futures.size()); - this.combiner = combiner; - this.values = Lists.newArrayListWithCapacity(futures.size()); + init(listenerExecutor); } - /** - * Must be called at the end of the constructor. - */ - protected void init(final Executor listenerExecutor) { + private void init(final Executor listenerExecutor) { // First, schedule cleanup to execute when the Future is done. addListener(new Runnable() { @Override public void run() { - // Cancel all the component futures. - if (CombinedFuture.this.isCancelled()) { - for (ListenableFuture<?> future : CombinedFuture.this.futures) { - future.cancel(CombinedFuture.this.wasInterrupted()); - } - } - // By now the values array has either been set as the Future's value, // or (in case of failure) is no longer useful. - CombinedFuture.this.futures = null; + ListFuture.this.values = null; // Let go of the memory held by other futures - CombinedFuture.this.values = null; - - // The combiner may also hold state, so free that as well - CombinedFuture.this.combiner = null; + ListFuture.this.futures = null; } }, MoreExecutors.sameThreadExecutor()); @@ -1488,7 +1317,7 @@ public final class Futures { // Corner case: List is empty. if (futures.isEmpty()) { - set(combiner.combine(ImmutableList.<Optional<V>>of())); + set(Lists.newArrayList(values)); return; } @@ -1503,11 +1332,11 @@ public final class Futures { // this loop, the last call to addListener() will callback to // setOneValue(), transitively call our cleanup listener, and set // this.futures to null. - // This is not actually a problem, since the foreach only needs - // this.futures to be non-null at the beginning of the loop. - int i = 0; - for (final ListenableFuture<? extends V> listenable : futures) { - final int index = i++; + // We store a reference to futures to avoid the NPE. + ImmutableList<? extends ListenableFuture<? extends V>> localFutures = futures; + for (int i = 0; i < localFutures.size(); i++) { + final ListenableFuture<? extends V> listenable = localFutures.get(i); + final int index = i; listenable.addListener(new Runnable() { @Override public void run() { @@ -1521,12 +1350,12 @@ public final class Futures { * Sets the value at the given index to that of the given future. */ private void setOneValue(int index, Future<? extends V> future) { - List<Optional<V>> localValues = values; + List<V> localValues = values; if (isDone() || localValues == null) { // Some other future failed or has been cancelled, causing this one to // also be cancelled or have an exception set. This should only happen - // if allMustSucceed is true or if the output itself has been cancelled. - checkState(allMustSucceed || isCancelled(), + // if allMustSucceed is true. + checkState(allMustSucceed, "Future was done before all dependencies completed"); return; } @@ -1534,8 +1363,7 @@ public final class Futures { try { checkState(future.isDone(), "Tried to set value from future which is not done"); - V returnValue = getUninterruptibly(future); - localValues.set(index, Optional.fromNullable(returnValue)); + localValues.set(index, getUninterruptibly(future)); } catch (CancellationException e) { if (allMustSucceed) { // Set ourselves as cancelled. Let the input futures keep running @@ -1561,9 +1389,9 @@ public final class Futures { int newRemaining = remaining.decrementAndGet(); checkState(newRemaining >= 0, "Less than 0 remaining futures"); if (newRemaining == 0) { - FutureCombiner<V, C> localCombiner = combiner; - if (localCombiner != null) { - set(localCombiner.combine(localValues)); + localValues = values; + if (localValues != null) { + set(Lists.newArrayList(localValues)); } else { checkState(isDone()); } @@ -1571,25 +1399,46 @@ public final class Futures { } } - } + @Override + public List<V> get() throws InterruptedException, ExecutionException { + callAllGets(); - /** Used for {@link #allAsList} and {@link #successfulAsList}. */ - private static <V> ListenableFuture<List<V>> listFuture( - ImmutableList<ListenableFuture<? extends V>> futures, - boolean allMustSucceed, Executor listenerExecutor) { - return new CombinedFuture<V, List<V>>( - futures, allMustSucceed, listenerExecutor, - new FutureCombiner<V, List<V>>() { - @Override - public List<V> combine(List<Optional<V>> values) { - List<V> result = Lists.newArrayList(); - for (Optional<V> element : values) { - result.add(element != null ? element.orNull() : null); + // This may still block in spite of the calls above, as the listeners may + // be scheduled for execution in other threads. + return super.get(); + } + + /** + * Calls the get method of all dependency futures to work around a bug in + * some ListenableFutures where the listeners aren't called until get() is + * called. + */ + private void callAllGets() throws InterruptedException { + List<? extends ListenableFuture<? extends V>> oldFutures = futures; + if (oldFutures != null && !isDone()) { + for (ListenableFuture<? extends V> future : oldFutures) { + // We wait for a little while for the future, but if it's not done, + // we check that no other futures caused a cancellation or failure. + // This can introduce a delay of up to 10ms in reporting an exception. + while (!future.isDone()) { + try { + future.get(); + } catch (Error e) { + throw e; + } catch (InterruptedException e) { + throw e; + } catch (Throwable e) { + // ExecutionException / CancellationException / RuntimeException + if (allMustSucceed) { + return; + } else { + continue; + } } - // TODO(user): This should ultimately return an unmodifiableList - return result; } - }); + } + } + } } /** diff --git a/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java b/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java index 645a648..6d74bda 100644 --- a/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java +++ b/guava/src/com/google/common/util/concurrent/JdkFutureAdapters.java @@ -19,6 +19,7 @@ package com.google.common.util.concurrent; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -41,7 +42,7 @@ public final class JdkFutureAdapters { * Assigns a thread to the given {@link Future} to provide {@link * ListenableFuture} functionality. * - * <p><b>Warning:</b> If the input future does not already implement {@code + * <p><b>Warning:</b> If the input future does not already implement {@link * ListenableFuture}, the returned future will emulate {@link * ListenableFuture#addListener} by taking a thread from an internal, * unbounded pool at the first call to {@code addListener} and holding it @@ -56,40 +57,17 @@ public final class JdkFutureAdapters { */ public static <V> ListenableFuture<V> listenInPoolThread( Future<V> future) { - if (future instanceof ListenableFuture) { + if (future instanceof ListenableFuture<?>) { return (ListenableFuture<V>) future; } return new ListenableFutureAdapter<V>(future); } - /** - * Submits a blocking task for the given {@link Future} to provide {@link - * ListenableFuture} functionality. - * - * <p><b>Warning:</b> If the input future does not already implement {@code - * ListenableFuture}, the returned future will emulate {@link - * ListenableFuture#addListener} by submitting a task to the given executor at - * at the first call to {@code addListener}. The task must be started by the - * executor promptly, or else the returned {@code ListenableFuture} may fail - * to work. The task's execution consists of blocking until the input future - * is {@linkplain Future#isDone() done}, so each call to this method may - * claim and hold a thread for an arbitrary length of time. Use of bounded - * executors or other executors that may fail to execute a task promptly may - * result in deadlocks. - * - * <p>Prefer to create {@code ListenableFuture} instances with {@link - * SettableFuture}, {@link MoreExecutors#listeningDecorator( - * java.util.concurrent.ExecutorService)}, {@link ListenableFutureTask}, - * {@link AbstractFuture}, and other utilities over creating plain {@code - * Future} instances to be upgraded to {@code ListenableFuture} after the - * fact. - * - * @since 12.0 - */ - public static <V> ListenableFuture<V> listenInPoolThread( + @VisibleForTesting + static <V> ListenableFuture<V> listenInPoolThread( Future<V> future, Executor executor) { checkNotNull(executor); - if (future instanceof ListenableFuture) { + if (future instanceof ListenableFuture<?>) { return (ListenableFuture<V>) future; } return new ListenableFutureAdapter<V>(future, executor); diff --git a/guava/src/com/google/common/util/concurrent/ListenableFuture.java b/guava/src/com/google/common/util/concurrent/ListenableFuture.java index eb05354..a0ab2db 100644 --- a/guava/src/com/google/common/util/concurrent/ListenableFuture.java +++ b/guava/src/com/google/common/util/concurrent/ListenableFuture.java @@ -28,10 +28,6 @@ import java.util.concurrent.RejectedExecutionException; * computation is {@linkplain Future#isDone() complete}. If the computation has * already completed when the listener is added, the listener will execute * immediately. - * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ListenableFutureExplained"> - * {@code ListenableFuture}</a>. * * <h3>Purpose</h3> * @@ -102,28 +98,20 @@ public interface ListenableFuture<V> extends Future<V> { * * <p>Note: For fast, lightweight listeners that would be safe to execute in * any thread, consider {@link MoreExecutors#sameThreadExecutor}. For heavier - * listeners, {@code sameThreadExecutor()} carries some caveats. For - * example, the listener may run on an unpredictable or undesirable thread: + * listeners, {@code sameThreadExecutor()} carries some caveats: First, the + * thread that the listener runs in depends on whether the {@code Future} is + * done at the time it is added and on whether it is ever canclled. In + * particular, listeners may run in the thread that calls {@code addListener} + * or the thread that calls {@code cancel}. Second, listeners may run in an + * internal thread of the system responsible for the input {@code Future}, + * such as an RPC network thread. Finally, during the execution of a {@code + * sameThreadExecutor()} listener, all other registered but unexecuted + * listeners are prevented from running, even if those listeners are to run + * in other executors. * - * <ul> - * <li>If the input {@code Future} is done at the time {@code addListener} is - * called, {@code addListener} will execute the listener inline. - * <li>If the input {@code Future} is not yet done, {@code addListener} will - * schedule the listener to be run by the thread that completes the input - * {@code Future}, which may be an internal system thread such as an RPC - * network thread. - * </ul> - * - * Also note that, regardless of which thread executes the listener, all - * other registered but unexecuted listeners are prevented from running - * during its execution, even if those listeners are to run in other - * executors. - * - * <p>This is the most general listener interface. For common operations - * performed using listeners, see {@link - * com.google.common.util.concurrent.Futures}. For a simplified but general - * listener interface, see {@link - * com.google.common.util.concurrent.Futures#addCallback addCallback()}. + * <p>This is the most general listener interface. + * For common operations performed using listeners, + * see {@link com.google.common.util.concurrent.Futures} * * @param listener the listener to run when the computation is complete * @param executor the executor to run the listener in diff --git a/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java b/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java index 35d6f13..474635c 100644 --- a/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java +++ b/guava/src/com/google/common/util/concurrent/ListenableFutureTask.java @@ -27,17 +27,12 @@ import javax.annotation.Nullable; * interface. Unlike {@code FutureTask}, {@code ListenableFutureTask} does not * provide an overrideable {@link FutureTask#done() done()} method. For similar * functionality, call {@link #addListener}. - * - * <p> * * @author Sven Mawson * @since 1.0 */ -public class ListenableFutureTask<V> extends FutureTask<V> +public final class ListenableFutureTask<V> extends FutureTask<V> implements ListenableFuture<V> { - // TODO(cpovirk): explore ways of making ListenableFutureTask final. There are - // some valid reasons such as BoundedQueueExecutorService to allow extends but it - // would be nice to make it final to avoid unintended usage. // The execution list to hold our listeners. private final ExecutionList executionList = new ExecutionList(); @@ -70,11 +65,11 @@ public class ListenableFutureTask<V> extends FutureTask<V> return new ListenableFutureTask<V>(runnable, result); } - ListenableFutureTask(Callable<V> callable) { + private ListenableFutureTask(Callable<V> callable) { super(callable); } - ListenableFutureTask(Runnable runnable, @Nullable V result) { + private ListenableFutureTask(Runnable runnable, @Nullable V result) { super(runnable, result); } diff --git a/guava/src/com/google/common/util/concurrent/MoreExecutors.java b/guava/src/com/google/common/util/concurrent/MoreExecutors.java index bd94db7..915b96d 100644 --- a/guava/src/com/google/common/util/concurrent/MoreExecutors.java +++ b/guava/src/com/google/common/util/concurrent/MoreExecutors.java @@ -16,26 +16,15 @@ package com.google.common.util.concurrent; -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.VisibleForTesting; -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import com.google.common.collect.Queues; -import java.lang.reflect.InvocationTargetException; -import java.util.Collection; import java.util.Collections; -import java.util.Iterator; import java.util.List; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -44,7 +33,6 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -79,8 +67,16 @@ public final class MoreExecutors { @Beta public static ExecutorService getExitingExecutorService( ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { - return new Application() - .getExitingExecutorService(executor, terminationTimeout, timeUnit); + executor.setThreadFactory(new ThreadFactoryBuilder() + .setDaemon(true) + .setThreadFactory(executor.getThreadFactory()) + .build()); + + ExecutorService service = Executors.unconfigurableExecutorService(executor); + + addDelayedShutdownHook(service, terminationTimeout, timeUnit); + + return service; } /** @@ -101,9 +97,19 @@ public final class MoreExecutors { */ @Beta public static ScheduledExecutorService getExitingScheduledExecutorService( - ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { - return new Application() - .getExitingScheduledExecutorService(executor, terminationTimeout, timeUnit); + ScheduledThreadPoolExecutor executor, long terminationTimeout, + TimeUnit timeUnit) { + executor.setThreadFactory(new ThreadFactoryBuilder() + .setDaemon(true) + .setThreadFactory(executor.getThreadFactory()) + .build()); + + ScheduledExecutorService service = + Executors.unconfigurableScheduledExecutorService(executor); + + addDelayedShutdownHook(service, terminationTimeout, timeUnit); + + return service; } /** @@ -119,9 +125,24 @@ public final class MoreExecutors { */ @Beta public static void addDelayedShutdownHook( - ExecutorService service, long terminationTimeout, TimeUnit timeUnit) { - new Application() - .addDelayedShutdownHook(service, terminationTimeout, timeUnit); + final ExecutorService service, final long terminationTimeout, + final TimeUnit timeUnit) { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + try { + // We'd like to log progress and failures that may arise in the + // following code, but unfortunately the behavior of logging + // is undefined in shutdown hooks. + // This is because the logging code installs a shutdown hook of its + // own. See Cleaner class inside {@link LogManager}. + service.shutdown(); + service.awaitTermination(terminationTimeout, timeUnit); + } catch (InterruptedException ignored) { + // We're shutting down anyway, so just ignore. + } + } + })); } /** @@ -140,8 +161,9 @@ public final class MoreExecutors { * @return an unmodifiable version of the input which will not hang the JVM */ @Beta - public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { - return new Application().getExitingExecutorService(executor); + public static ExecutorService getExitingExecutorService( + ThreadPoolExecutor executor) { + return getExitingExecutorService(executor, 120, TimeUnit.SECONDS); } /** @@ -162,69 +184,7 @@ public final class MoreExecutors { @Beta public static ScheduledExecutorService getExitingScheduledExecutorService( ScheduledThreadPoolExecutor executor) { - return new Application().getExitingScheduledExecutorService(executor); - } - - /** Represents the current application to register shutdown hooks. */ - @VisibleForTesting static class Application { - - final ExecutorService getExitingExecutorService( - ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { - useDaemonThreadFactory(executor); - ExecutorService service = Executors.unconfigurableExecutorService(executor); - addDelayedShutdownHook(service, terminationTimeout, timeUnit); - return service; - } - - final ScheduledExecutorService getExitingScheduledExecutorService( - ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { - useDaemonThreadFactory(executor); - ScheduledExecutorService service = Executors.unconfigurableScheduledExecutorService(executor); - addDelayedShutdownHook(service, terminationTimeout, timeUnit); - return service; - } - - final void addDelayedShutdownHook( - final ExecutorService service, final long terminationTimeout, final TimeUnit timeUnit) { - checkNotNull(service); - checkNotNull(timeUnit); - addShutdownHook(MoreExecutors.newThread("DelayedShutdownHook-for-" + service, new Runnable() { - @Override - public void run() { - try { - // We'd like to log progress and failures that may arise in the - // following code, but unfortunately the behavior of logging - // is undefined in shutdown hooks. - // This is because the logging code installs a shutdown hook of its - // own. See Cleaner class inside {@link LogManager}. - service.shutdown(); - service.awaitTermination(terminationTimeout, timeUnit); - } catch (InterruptedException ignored) { - // We're shutting down anyway, so just ignore. - } - } - })); - } - - final ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { - return getExitingExecutorService(executor, 120, TimeUnit.SECONDS); - } - - final ScheduledExecutorService getExitingScheduledExecutorService( - ScheduledThreadPoolExecutor executor) { - return getExitingScheduledExecutorService(executor, 120, TimeUnit.SECONDS); - } - - @VisibleForTesting void addShutdownHook(Thread hook) { - Runtime.getRuntime().addShutdownHook(hook); - } - } - - private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { - executor.setThreadFactory(new ThreadFactoryBuilder() - .setDaemon(true) - .setThreadFactory(executor.getThreadFactory()) - .build()); + return getExitingScheduledExecutorService(executor, 120, TimeUnit.SECONDS); } /** @@ -483,7 +443,6 @@ public final class MoreExecutors { private static class ScheduledListeningDecorator extends ListeningDecorator implements ListeningScheduledExecutorService { - @SuppressWarnings("hiding") final ScheduledExecutorService delegate; ScheduledListeningDecorator(ScheduledExecutorService delegate) { @@ -516,172 +475,4 @@ public final class MoreExecutors { command, initialDelay, delay, unit); } } - - /* - * This following method is a modified version of one found in - * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck/AbstractExecutorServiceTest.java?revision=1.30 - * 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/ - * Other contributors include Andrew Wright, Jeffrey Hayes, - * Pat Fisher, Mike Judd. - */ - - /** - * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} - * implementations. - */ static <T> T invokeAnyImpl(ListeningExecutorService executorService, - Collection<? extends Callable<T>> tasks, boolean timed, long nanos) - throws InterruptedException, ExecutionException, TimeoutException { - checkNotNull(executorService); - int ntasks = tasks.size(); - checkArgument(ntasks > 0); - List<Future<T>> futures = Lists.newArrayListWithCapacity(ntasks); - BlockingQueue<Future<T>> futureQueue = Queues.newLinkedBlockingQueue(); - - // For efficiency, especially in executors with limited - // parallelism, check to see if previously submitted tasks are - // done before submitting more of them. This interleaving - // plus the exception mechanics account for messiness of main - // loop. - - try { - // Record exceptions so that if we fail to obtain any - // result, we can throw the last exception we got. - ExecutionException ee = null; - long lastTime = timed ? System.nanoTime() : 0; - Iterator<? extends Callable<T>> it = tasks.iterator(); - - futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue)); - --ntasks; - int active = 1; - - for (;;) { - Future<T> f = futureQueue.poll(); - if (f == null) { - if (ntasks > 0) { - --ntasks; - futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue)); - ++active; - } else if (active == 0) { - break; - } else if (timed) { - f = futureQueue.poll(nanos, TimeUnit.NANOSECONDS); - if (f == null) { - throw new TimeoutException(); - } - long now = System.nanoTime(); - nanos -= now - lastTime; - lastTime = now; - } else { - f = futureQueue.take(); - } - } - if (f != null) { - --active; - try { - return f.get(); - } catch (ExecutionException eex) { - ee = eex; - } catch (RuntimeException rex) { - ee = new ExecutionException(rex); - } - } - } - - if (ee == null) { - ee = new ExecutionException(null); - } - throw ee; - } finally { - for (Future<T> f : futures) { - f.cancel(true); - } - } - } - - /** - * Submits the task and adds a listener that adds the future to {@code queue} when it completes. - */ - private static <T> ListenableFuture<T> submitAndAddQueueListener( - ListeningExecutorService executorService, Callable<T> task, - final BlockingQueue<Future<T>> queue) { - final ListenableFuture<T> future = executorService.submit(task); - future.addListener(new Runnable() { - @Override public void run() { - queue.add(future); - } - }, MoreExecutors.sameThreadExecutor()); - return future; - } - - /** - * Returns a default thread factory used to create new threads. - * - * <p>On AppEngine, returns {@code ThreadManager.currentRequestThreadFactory()}. - * Otherwise, returns {@link Executors#defaultThreadFactory()}. - * - * @since 14.0 - */ - @Beta - public static ThreadFactory platformThreadFactory() { - if (!isAppEngine()) { - return Executors.defaultThreadFactory(); - } - try { - return (ThreadFactory) Class.forName("com.google.appengine.api.ThreadManager") - .getMethod("currentRequestThreadFactory") - .invoke(null); - } catch (IllegalAccessException e) { - throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); - } catch (ClassNotFoundException e) { - throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); - } catch (NoSuchMethodException e) { - throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); - } catch (InvocationTargetException e) { - throw Throwables.propagate(e.getCause()); - } - } - - private static boolean isAppEngine() { - if (System.getProperty("com.google.appengine.runtime.environment") == null) { - return false; - } - try { - // If the current environment is null, we're not inside AppEngine. - return Class.forName("com.google.apphosting.api.ApiProxy") - .getMethod("getCurrentEnvironment") - .invoke(null) != null; - } catch (ClassNotFoundException e) { - // If ApiProxy doesn't exist, we're not on AppEngine at all. - return false; - } catch (InvocationTargetException e) { - // If ApiProxy throws an exception, we're not in a proper AppEngine environment. - return false; - } catch (IllegalAccessException e) { - // If the method isn't accessible, we're not on a supported version of AppEngine; - return false; - } catch (NoSuchMethodException e) { - // If the method doesn't exist, we're not on a supported version of AppEngine; - return false; - } - } - - /** - * Creates a thread using {@link #platformThreadFactory}, and sets its name to {@code name} - * unless changing the name is forbidden by the security manager. - */ - static Thread newThread(String name, Runnable runnable) { - checkNotNull(name); - checkNotNull(runnable); - Thread result = platformThreadFactory().newThread(runnable); - try { - result.setName(name); - } catch (SecurityException e) { - // OK if we can't set the name in this environment. - } - return result; - } } diff --git a/guava/src/com/google/common/util/concurrent/RateLimiter.java b/guava/src/com/google/common/util/concurrent/RateLimiter.java deleted file mode 100644 index 4085654..0000000 --- a/guava/src/com/google/common/util/concurrent/RateLimiter.java +++ /dev/null @@ -1,690 +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.util.concurrent; - -import com.google.common.annotations.Beta; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.base.Ticker; - -import java.util.concurrent.TimeUnit; - -import javax.annotation.concurrent.ThreadSafe; - -/** - * A rate limiter. Conceptually, a rate limiter distributes permits at a - * configurable rate. Each {@link #acquire()} blocks if necessary until a permit is - * available, and then takes it. Once acquired, permits need not be released. - * - * <p>Rate limiters are often used to restrict the rate at which some - * physical or logical resource is accessed. This is in contrast to {@link - * java.util.concurrent.Semaphore} which restricts the number of concurrent - * accesses instead of the rate (note though that concurrency and rate are closely related, - * e.g. see <a href="http://en.wikipedia.org/wiki/Little's_law">Little's Law</a>). - * - * <p>A {@code RateLimiter} is defined primarily by the rate at which permits - * are issued. Absent additional configuration, permits will be distributed at a - * fixed rate, defined in terms of permits per second. Permits will be distributed - * smoothly, with the delay between individual permits being adjusted to ensure - * that the configured rate is maintained. - * - * <p>It is possible to configure a {@code RateLimiter} to have a warmup - * period during which time the permits issued each second steadily increases until - * it hits the stable rate. - * - * <p>As an example, imagine that we have a list of tasks to execute, but we don't want to - * submit more than 2 per second: - *<pre> {@code - * final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second" - * void submitTasks(List<Runnable> tasks, Executor executor) { - * for (Runnable task : tasks) { - * rateLimiter.acquire(); // may wait - * executor.execute(task); - * } - * } - *}</pre> - * - * <p>As another example, imagine that we produce a stream of data, and we want to cap it - * at 5kb per second. This could be accomplished by requiring a permit per byte, and specifying - * a rate of 5000 permits per second: - *<pre> {@code - * final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second - * void submitPacket(byte[] packet) { - * rateLimiter.acquire(packet.length); - * networkService.send(packet); - * } - *}</pre> - * - * <p>It is important to note that the number of permits requested <i>never</i> - * affect the throttling of the request itself (an invocation to {@code acquire(1)} - * and an invocation to {@code acquire(1000)} will result in exactly the same throttling, if any), - * but it affects the throttling of the <i>next</i> request. I.e., if an expensive task - * arrives at an idle RateLimiter, it will be granted immediately, but it is the <i>next</i> - * request that will experience extra throttling, thus paying for the cost of the expensive - * task. - * - * <p>Note: {@code RateLimiter} does not provide fairness guarantees. - * - * @author Dimitris Andreou - * @since 13.0 - */ -// TODO(user): switch to nano precision. A natural unit of cost is "bytes", and a micro precision -// would mean a maximum rate of "1MB/s", which might be small in some cases. -@ThreadSafe -@Beta -public abstract class RateLimiter { - /* - * How is the RateLimiter designed, and why? - * - * The primary feature of a RateLimiter is its "stable rate", the maximum rate that - * is should allow at normal conditions. This is enforced by "throttling" incoming - * requests as needed, i.e. compute, for an incoming request, the appropriate throttle time, - * and make the calling thread wait as much. - * - * The simplest way to maintain a rate of QPS is to keep the timestamp of the last - * granted request, and ensure that (1/QPS) seconds have elapsed since then. For example, - * for a rate of QPS=5 (5 tokens per second), if we ensure that a request isn't granted - * earlier than 200ms after the the last one, then we achieve the intended rate. - * If a request comes and the last request was granted only 100ms ago, then we wait for - * another 100ms. At this rate, serving 15 fresh permits (i.e. for an acquire(15) request) - * naturally takes 3 seconds. - * - * It is important to realize that such a RateLimiter has a very superficial memory - * of the past: it only remembers the last request. What if the RateLimiter was unused for - * a long period of time, then a request arrived and was immediately granted? - * This RateLimiter would immediately forget about that past underutilization. This may - * result in either underutilization or overflow, depending on the real world consequences - * of not using the expected rate. - * - * Past underutilization could mean that excess resources are available. Then, the RateLimiter - * should speed up for a while, to take advantage of these resources. This is important - * when the rate is applied to networking (limiting bandwidth), where past underutilization - * typically translates to "almost empty buffers", which can be filled immediately. - * - * On the other hand, past underutilization could mean that "the server responsible for - * handling the request has become less ready for future requests", i.e. its caches become - * stale, and requests become more likely to trigger expensive operations (a more extreme - * case of this example is when a server has just booted, and it is mostly busy with getting - * itself up to speed). - * - * To deal with such scenarios, we add an extra dimension, that of "past underutilization", - * modeled by "storedPermits" variable. This variable is zero when there is no - * underutilization, and it can grow up to maxStoredPermits, for sufficiently large - * underutilization. So, the requested permits, by an invocation acquire(permits), - * are served from: - * - stored permits (if available) - * - fresh permits (for any remaining permits) - * - * How this works is best explained with an example: - * - * For a RateLimiter that produces 1 token per second, every second - * that goes by with the RateLimiter being unused, we increase storedPermits by 1. - * Say we leave the RateLimiter unused for 10 seconds (i.e., we expected a request at time - * X, but we are at time X + 10 seconds before a request actually arrives; this is - * also related to the point made in the last paragraph), thus storedPermits - * becomes 10.0 (assuming maxStoredPermits >= 10.0). At that point, a request of acquire(3) - * arrives. We serve this request out of storedPermits, and reduce that to 7.0 (how this is - * translated to throttling time is discussed later). Immediately after, assume that an - * acquire(10) request arriving. We serve the request partly from storedPermits, - * using all the remaining 7.0 permits, and the remaining 3.0, we serve them by fresh permits - * produced by the rate limiter. - * - * We already know how much time it takes to serve 3 fresh permits: if the rate is - * "1 token per second", then this will take 3 seconds. But what does it mean to serve 7 - * stored permits? As explained above, there is no unique answer. If we are primarily - * interested to deal with underutilization, then we want stored permits to be given out - * /faster/ than fresh ones, because underutilization = free resources for the taking. - * If we are primarily interested to deal with overflow, then stored permits could - * be given out /slower/ than fresh ones. Thus, we require a (different in each case) - * function that translates storedPermits to throtting time. - * - * This role is played by storedPermitsToWaitTime(double storedPermits, double permitsToTake). - * The underlying model is a continuous function mapping storedPermits - * (from 0.0 to maxStoredPermits) onto the 1/rate (i.e. intervals) that is effective at the given - * storedPermits. "storedPermits" essentially measure unused time; we spend unused time - * buying/storing permits. Rate is "permits / time", thus "1 / rate = time / permits". - * Thus, "1/rate" (time / permits) times "permits" gives time, i.e., integrals on this - * function (which is what storedPermitsToWaitTime() computes) correspond to minimum intervals - * between subsequent requests, for the specified number of requested permits. - * - * Here is an example of storedPermitsToWaitTime: - * If storedPermits == 10.0, and we want 3 permits, we take them from storedPermits, - * reducing them to 7.0, and compute the throttling for these as a call to - * storedPermitsToWaitTime(storedPermits = 10.0, permitsToTake = 3.0), which will - * evaluate the integral of the function from 7.0 to 10.0. - * - * Using integrals guarantees that the effect of a single acquire(3) is equivalent - * to { acquire(1); acquire(1); acquire(1); }, or { acquire(2); acquire(1); }, etc, - * since the integral of the function in [7.0, 10.0] is equivalent to the sum of the - * integrals of [7.0, 8.0], [8.0, 9.0], [9.0, 10.0] (and so on), no matter - * what the function is. This guarantees that we handle correctly requests of varying weight - * (permits), /no matter/ what the actual function is - so we can tweak the latter freely. - * (The only requirement, obviously, is that we can compute its integrals). - * - * Note well that if, for this function, we chose a horizontal line, at height of exactly - * (1/QPS), then the effect of the function is non-existent: we serve storedPermits at - * exactly the same cost as fresh ones (1/QPS is the cost for each). We use this trick later. - * - * If we pick a function that goes /below/ that horizontal line, it means that we reduce - * the area of the function, thus time. Thus, the RateLimiter becomes /faster/ after a - * period of underutilization. If, on the other hand, we pick a function that - * goes /above/ that horizontal line, then it means that the area (time) is increased, - * thus storedPermits are more costly than fresh permits, thus the RateLimiter becomes - * /slower/ after a period of underutilization. - * - * Last, but not least: consider a RateLimiter with rate of 1 permit per second, currently - * completely unused, and an expensive acquire(100) request comes. It would be nonsensical - * to just wait for 100 seconds, and /then/ start the actual task. Why wait without doing - * anything? A much better approach is to /allow/ the request right away (as if it was an - * acquire(1) request instead), and postpone /subsequent/ requests as needed. In this version, - * we allow starting the task immediately, and postpone by 100 seconds future requests, - * thus we allow for work to get done in the meantime instead of waiting idly. - * - * This has important consequences: it means that the RateLimiter doesn't remember the time - * of the _last_ request, but it remembers the (expected) time of the _next_ request. This - * also enables us to tell immediately (see tryAcquire(timeout)) whether a particular - * timeout is enough to get us to the point of the next scheduling time, since we always - * maintain that. And what we mean by "an unused RateLimiter" is also defined by that - * notion: when we observe that the "expected arrival time of the next request" is actually - * in the past, then the difference (now - past) is the amount of time that the RateLimiter - * was formally unused, and it is that amount of time which we translate to storedPermits. - * (We increase storedPermits with the amount of permits that would have been produced - * in that idle time). So, if rate == 1 permit per second, and arrivals come exactly - * one second after the previous, then storedPermits is _never_ increased -- we would only - * increase it for arrivals _later_ than the expected one second. - */ - - /** - * Creates a {@code RateLimiter} with the specified stable throughput, given as - * "permits per second" (commonly referred to as <i>QPS</i>, queries per second). - * - * <p>The returned {@code RateLimiter} ensures that on average no more than {@code - * permitsPerSecond} are issued during any given second, with sustained requests - * being smoothly spread over each second. When the incoming request rate exceeds - * {@code permitsPerSecond} the rate limiter will release one permit every {@code - * (1.0 / permitsPerSecond)} seconds. When the rate limiter is unused, - * bursts of up to {@code permitsPerSecond} permits will be allowed, with subsequent - * requests being smoothly limited at the stable rate of {@code permitsPerSecond}. - * - * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in - * how many permits become available per second. - */ - public static RateLimiter create(double permitsPerSecond) { - return create(SleepingTicker.SYSTEM_TICKER, permitsPerSecond); - } - - @VisibleForTesting - static RateLimiter create(SleepingTicker ticker, double permitsPerSecond) { - RateLimiter rateLimiter = new Bursty(ticker); - rateLimiter.setRate(permitsPerSecond); - return rateLimiter; - } - - /** - * Creates a {@code RateLimiter} with the specified stable throughput, given as - * "permits per second" (commonly referred to as <i>QPS</i>, queries per second), and a - * <i>warmup period</i>, during which the {@code RateLimiter} smoothly ramps up its rate, - * until it reaches its maximum rate at the end of the period (as long as there are enough - * requests to saturate it). Similarly, if the {@code RateLimiter} is left <i>unused</i> for - * a duration of {@code warmupPeriod}, it will gradually return to its "cold" state, - * i.e. it will go through the same warming up process as when it was first created. - * - * <p>The returned {@code RateLimiter} is intended for cases where the resource that actually - * fulfils the requests (e.g., a remote server) needs "warmup" time, rather than - * being immediately accessed at the stable (maximum) rate. - * - * <p>The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period - * will follow), and if it is left unused for long enough, it will return to that state. - * - * @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in - * how many permits become available per second - * @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its - * rate, before reaching its stable (maximum) rate - * @param unit the time unit of the warmupPeriod argument - */ - // TODO(user): add a burst size of 1-second-worth of permits, as in the metronome? - public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) { - return create(SleepingTicker.SYSTEM_TICKER, permitsPerSecond, warmupPeriod, unit); - } - - @VisibleForTesting - static RateLimiter create( - SleepingTicker ticker, double permitsPerSecond, long warmupPeriod, TimeUnit timeUnit) { - RateLimiter rateLimiter = new WarmingUp(ticker, warmupPeriod, timeUnit); - rateLimiter.setRate(permitsPerSecond); - return rateLimiter; - } - - @VisibleForTesting - static RateLimiter createBursty( - SleepingTicker ticker, double permitsPerSecond, int maxBurstSize) { - Bursty rateLimiter = new Bursty(ticker); - rateLimiter.setRate(permitsPerSecond); - rateLimiter.maxPermits = maxBurstSize; - return rateLimiter; - } - - /** - * The underlying timer; used both to measure elapsed time and sleep as necessary. A separate - * object to facilitate testing. - */ - private final SleepingTicker ticker; - - /** - * The timestamp when the RateLimiter was created; used to avoid possible overflow/time-wrapping - * errors. - */ - private final long offsetNanos; - - /** - * The currently stored permits. - */ - double storedPermits; - - /** - * The maximum number of stored permits. - */ - double maxPermits; - - /** - * The interval between two unit requests, at our stable rate. E.g., a stable rate of 5 permits - * per second has a stable interval of 200ms. - */ - volatile double stableIntervalMicros; - - private final Object mutex = new Object(); - - /** - * The time when the next request (no matter its size) will be granted. After granting a request, - * this is pushed further in the future. Large requests push this further than small requests. - */ - private long nextFreeTicketMicros = 0L; // could be either in the past or future - - private RateLimiter(SleepingTicker ticker) { - this.ticker = ticker; - this.offsetNanos = ticker.read(); - } - - /** - * Updates the stable rate of this {@code RateLimiter}, that is, the - * {@code permitsPerSecond} argument provided in the factory method that - * constructed the {@code RateLimiter}. Currently throttled threads will <b>not</b> - * be awakened as a result of this invocation, thus they do not observe the new rate; - * only subsequent requests will. - * - * <p>Note though that, since each request repays (by waiting, if necessary) the cost - * of the <i>previous</i> request, this means that the very next request - * after an invocation to {@code setRate} will not be affected by the new rate; - * it will pay the cost of the previous request, which is in terms of the previous rate. - * - * <p>The behavior of the {@code RateLimiter} is not modified in any other way, - * e.g. if the {@code RateLimiter} was configured with a warmup period of 20 seconds, - * it still has a warmup period of 20 seconds after this method invocation. - * - * @param permitsPerSecond the new stable rate of this {@code RateLimiter}. - */ - public final void setRate(double permitsPerSecond) { - Preconditions.checkArgument(permitsPerSecond > 0.0 - && !Double.isNaN(permitsPerSecond), "rate must be positive"); - synchronized (mutex) { - resync(readSafeMicros()); - double stableIntervalMicros = TimeUnit.SECONDS.toMicros(1L) / permitsPerSecond; - this.stableIntervalMicros = stableIntervalMicros; - doSetRate(permitsPerSecond, stableIntervalMicros); - } - } - - abstract void doSetRate(double permitsPerSecond, double stableIntervalMicros); - - /** - * Returns the stable rate (as {@code permits per seconds}) with which this - * {@code RateLimiter} is configured with. The initial value of this is the same as - * the {@code permitsPerSecond} argument passed in the factory method that produced - * this {@code RateLimiter}, and it is only updated after invocations - * to {@linkplain #setRate}. - */ - public final double getRate() { - return TimeUnit.SECONDS.toMicros(1L) / stableIntervalMicros; - } - - /** - * Acquires a permit from this {@code RateLimiter}, blocking until the request can be granted. - * - * <p>This method is equivalent to {@code acquire(1)}. - */ - public void acquire() { - acquire(1); - } - - /** - * Acquires the given number of permits from this {@code RateLimiter}, blocking until the - * request be granted. - * - * @param permits the number of permits to acquire - */ - public void acquire(int permits) { - checkPermits(permits); - long microsToWait; - synchronized (mutex) { - microsToWait = reserveNextTicket(permits, readSafeMicros()); - } - ticker.sleepMicrosUninterruptibly(microsToWait); - } - - /** - * Acquires a permit from this {@code RateLimiter} if it can be obtained - * without exceeding the specified {@code timeout}, or returns {@code false} - * immediately (without waiting) if the permit would not have been granted - * before the timeout expired. - * - * <p>This method is equivalent to {@code tryAcquire(1, timeout, unit)}. - * - * @param timeout the maximum time to wait for the permit - * @param unit the time unit of the timeout argument - * @return {@code true} if the permit was acquired, {@code false} otherwise - */ - public boolean tryAcquire(long timeout, TimeUnit unit) { - return tryAcquire(1, timeout, unit); - } - - /** - * Acquires permits from this {@link RateLimiter} if it can be acquired immediately without delay. - * - * <p> - * This method is equivalent to {@code tryAcquire(permits, 0, anyUnit)}. - * - * @param permits the number of permits to acquire - * @return {@code true} if the permits were acquired, {@code false} otherwise - * @since 14.0 - */ - public boolean tryAcquire(int permits) { - return tryAcquire(permits, 0, TimeUnit.MICROSECONDS); - } - - /** - * Acquires a permit from this {@link RateLimiter} if it can be acquired immediately without - * delay. - * - * <p> - * This method is equivalent to {@code tryAcquire(1)}. - * - * @return {@code true} if the permit was acquired, {@code false} otherwise - * @since 14.0 - */ - public boolean tryAcquire() { - return tryAcquire(1, 0, TimeUnit.MICROSECONDS); - } - - /** - * Acquires the given number of permits from this {@code RateLimiter} if it can be obtained - * without exceeding the specified {@code timeout}, or returns {@code false} - * immediately (without waiting) if the permits would not have been granted - * before the timeout expired. - * - * @param permits the number of permits to acquire - * @param timeout the maximum time to wait for the permits - * @param unit the time unit of the timeout argument - * @return {@code true} if the permits were acquired, {@code false} otherwise - */ - public boolean tryAcquire(int permits, long timeout, TimeUnit unit) { - long timeoutMicros = unit.toMicros(timeout); - checkPermits(permits); - long microsToWait; - synchronized (mutex) { - long nowMicros = readSafeMicros(); - if (nextFreeTicketMicros > nowMicros + timeoutMicros) { - return false; - } else { - microsToWait = reserveNextTicket(permits, nowMicros); - } - } - ticker.sleepMicrosUninterruptibly(microsToWait); - return true; - } - - private static void checkPermits(int permits) { - Preconditions.checkArgument(permits > 0, "Requested permits must be positive"); - } - - /** - * Reserves next ticket and returns the wait time that the caller must wait for. - */ - private long reserveNextTicket(double requiredPermits, long nowMicros) { - resync(nowMicros); - long microsToNextFreeTicket = nextFreeTicketMicros - nowMicros; - double storedPermitsToSpend = Math.min(requiredPermits, this.storedPermits); - double freshPermits = requiredPermits - storedPermitsToSpend; - - long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend) - + (long) (freshPermits * stableIntervalMicros); - - this.nextFreeTicketMicros = nextFreeTicketMicros + waitMicros; - this.storedPermits -= storedPermitsToSpend; - return microsToNextFreeTicket; - } - - /** - * Translates a specified portion of our currently stored permits which we want to - * spend/acquire, into a throttling time. Conceptually, this evaluates the integral - * of the underlying function we use, for the range of - * [(storedPermits - permitsToTake), storedPermits]. - * - * This always holds: {@code 0 <= permitsToTake <= storedPermits} - */ - abstract long storedPermitsToWaitTime(double storedPermits, double permitsToTake); - - private void resync(long nowMicros) { - // if nextFreeTicket is in the past, resync to now - if (nowMicros > nextFreeTicketMicros) { - storedPermits = Math.min(maxPermits, - storedPermits + (nowMicros - nextFreeTicketMicros) / stableIntervalMicros); - nextFreeTicketMicros = nowMicros; - } - } - - private long readSafeMicros() { - return TimeUnit.NANOSECONDS.toMicros(ticker.read() - offsetNanos); - } - - @Override - public String toString() { - return String.format("RateLimiter[stableRate=%3.1fqps]", 1000000.0 / stableIntervalMicros); - } - - /** - * This implements the following function: - * - * ^ throttling - * | - * 3*stable + / - * interval | /. - * (cold) | / . - * | / . <-- "warmup period" is the area of the trapezoid between - * 2*stable + / . halfPermits and maxPermits - * interval | / . - * | / . - * | / . - * stable +----------/ WARM . } - * interval | . UP . } <-- this rectangle (from 0 to maxPermits, and - * | . PERIOD. } height == stableInterval) defines the cooldown period, - * | . . } and we want cooldownPeriod == warmupPeriod - * |---------------------------------> storedPermits - * (halfPermits) (maxPermits) - * - * Before going into the details of this particular function, let's keep in mind the basics: - * 1) The state of the RateLimiter (storedPermits) is a vertical line in this figure. - * 2) When the RateLimiter is not used, this goes right (up to maxPermits) - * 3) When the RateLimiter is used, this goes left (down to zero), since if we have storedPermits, - * we serve from those first - * 4) When _unused_, we go right at the same speed (rate)! I.e., if our rate is - * 2 permits per second, and 3 unused seconds pass, we will always save 6 permits - * (no matter what our initial position was), up to maxPermits. - * If we invert the rate, we get the "stableInterval" (interval between two requests - * in a perfectly spaced out sequence of requests of the given rate). Thus, if you - * want to see "how much time it will take to go from X storedPermits to X+K storedPermits?", - * the answer is always stableInterval * K. In the same example, for 2 permits per second, - * stableInterval is 500ms. Thus to go from X storedPermits to X+6 storedPermits, we - * require 6 * 500ms = 3 seconds. - * - * In short, the time it takes to move to the right (save K permits) is equal to the - * rectangle of width == K and height == stableInterval. - * 4) When _used_, the time it takes, as explained in the introductory class note, is - * equal to the integral of our function, between X permits and X-K permits, assuming - * we want to spend K saved permits. - * - * In summary, the time it takes to move to the left (spend K permits), is equal to the - * area of the function of width == K. - * - * Let's dive into this function now: - * - * When we have storedPermits <= halfPermits (the left portion of the function), then - * we spend them at the exact same rate that - * fresh permits would be generated anyway (that rate is 1/stableInterval). We size - * this area to be equal to _half_ the specified warmup period. Why we need this? - * And why half? We'll explain shortly below (after explaining the second part). - * - * Stored permits that are beyond halfPermits, are mapped to an ascending line, that goes - * from stableInterval to 3 * stableInterval. The average height for that part is - * 2 * stableInterval, and is sized appropriately to have an area _equal_ to the - * specified warmup period. Thus, by point (4) above, it takes "warmupPeriod" amount of time - * to go from maxPermits to halfPermits. - * - * BUT, by point (3) above, it only takes "warmupPeriod / 2" amount of time to return back - * to maxPermits, from halfPermits! (Because the trapezoid has double the area of the rectangle - * of height stableInterval and equivalent width). We decided that the "cooldown period" - * time should be equivalent to "warmup period", thus a fully saturated RateLimiter - * (with zero stored permits, serving only fresh ones) can go to a fully unsaturated - * (with storedPermits == maxPermits) in the same amount of time it takes for a fully - * unsaturated RateLimiter to return to the stableInterval -- which happens in halfPermits, - * since beyond that point, we use a horizontal line of "stableInterval" height, simulating - * the regular rate. - * - * Thus, we have figured all dimensions of this shape, to give all the desired - * properties: - * - the width is warmupPeriod / stableInterval, to make cooldownPeriod == warmupPeriod - * - the slope starts at the middle, and goes from stableInterval to 3*stableInterval so - * to have halfPermits being spend in double the usual time (half the rate), while their - * respective rate is steadily ramping up - */ - private static class WarmingUp extends RateLimiter { - - final long warmupPeriodMicros; - /** - * The slope of the line from the stable interval (when permits == 0), to the cold interval - * (when permits == maxPermits) - */ - private double slope; - private double halfPermits; - - WarmingUp(SleepingTicker ticker, long warmupPeriod, TimeUnit timeUnit) { - super(ticker); - this.warmupPeriodMicros = timeUnit.toMicros(warmupPeriod); - } - - @Override - void doSetRate(double permitsPerSecond, double stableIntervalMicros) { - double oldMaxPermits = maxPermits; - maxPermits = warmupPeriodMicros / stableIntervalMicros; - halfPermits = maxPermits / 2.0; - // Stable interval is x, cold is 3x, so on average it's 2x. Double the time -> halve the rate - double coldIntervalMicros = stableIntervalMicros * 3.0; - slope = (coldIntervalMicros - stableIntervalMicros) / halfPermits; - if (oldMaxPermits == Double.POSITIVE_INFINITY) { - // if we don't special-case this, we would get storedPermits == NaN, below - storedPermits = 0.0; - } else { - storedPermits = (oldMaxPermits == 0.0) - ? maxPermits // initial state is cold - : storedPermits * maxPermits / oldMaxPermits; - } - } - - @Override - long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { - double availablePermitsAboveHalf = storedPermits - halfPermits; - long micros = 0; - // measuring the integral on the right part of the function (the climbing line) - if (availablePermitsAboveHalf > 0.0) { - double permitsAboveHalfToTake = Math.min(availablePermitsAboveHalf, permitsToTake); - micros = (long) (permitsAboveHalfToTake * (permitsToTime(availablePermitsAboveHalf) - + permitsToTime(availablePermitsAboveHalf - permitsAboveHalfToTake)) / 2.0); - permitsToTake -= permitsAboveHalfToTake; - } - // measuring the integral on the left part of the function (the horizontal line) - micros += (stableIntervalMicros * permitsToTake); - return micros; - } - - private double permitsToTime(double permits) { - return stableIntervalMicros + permits * slope; - } - } - - /** - * This implements a trivial function, where storedPermits are translated to - * zero throttling - thus, a client gets an infinite speedup for permits acquired out - * of the storedPermits pool. This is also used for the special case of the "metronome", - * where the width of the function is also zero; maxStoredPermits is zero, thus - * storedPermits and permitsToTake are always zero as well. Such a RateLimiter can - * not save permits when unused, thus all permits it serves are fresh, using the - * designated rate. - */ - private static class Bursty extends RateLimiter { - Bursty(SleepingTicker ticker) { - super(ticker); - } - - @Override - void doSetRate(double permitsPerSecond, double stableIntervalMicros) { - double oldMaxPermits = this.maxPermits; - /* - * We allow the equivalent work of up to one second to be granted with zero waiting, if the - * rate limiter has been unused for as much. This is to avoid potentially producing tiny - * wait interval between subsequent requests for sufficiently large rates, which would - * unnecessarily overconstrain the thread scheduler. - */ - maxPermits = permitsPerSecond; // one second worth of permits - storedPermits = (oldMaxPermits == 0.0) - ? 0.0 // initial state - : storedPermits * maxPermits / oldMaxPermits; - } - - @Override - long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { - return 0L; - } - } - - @VisibleForTesting - static abstract class SleepingTicker extends Ticker { - abstract void sleepMicrosUninterruptibly(long micros); - - static final SleepingTicker SYSTEM_TICKER = new SleepingTicker() { - @Override - public long read() { - return systemTicker().read(); - } - - @Override - public void sleepMicrosUninterruptibly(long micros) { - if (micros > 0) { - Uninterruptibles.sleepUninterruptibly(micros, TimeUnit.MICROSECONDS); - } - } - }; - } -} diff --git a/guava/src/com/google/common/util/concurrent/Service.java b/guava/src/com/google/common/util/concurrent/Service.java index 861164e..9ad1f3d 100644 --- a/guava/src/com/google/common/util/concurrent/Service.java +++ b/guava/src/com/google/common/util/concurrent/Service.java @@ -19,58 +19,57 @@ package com.google.common.util.concurrent; import com.google.common.annotations.Beta; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; /** - * An object with an operational state, plus asynchronous {@link #start()} and {@link #stop()} - * lifecycle methods to transition between states. Example services include webservers, RPC servers - * and timers. - * - * <p>The normal lifecycle of a service is: + * An object with an operational state, plus asynchronous {@link #start()} and + * {@link #stop()} lifecycle methods to transfer into and out of this state. + * Example services include webservers, RPC servers and timers. The normal + * lifecycle of a service is: * <ul> - * <li>{@linkplain State#NEW NEW} -> - * <li>{@linkplain State#STARTING STARTING} -> - * <li>{@linkplain State#RUNNING RUNNING} -> - * <li>{@linkplain State#STOPPING STOPPING} -> - * <li>{@linkplain State#TERMINATED TERMINATED} + * <li>{@link State#NEW} -></li> + * <li>{@link State#STARTING} -></li> + * <li>{@link State#RUNNING} -></li> + * <li>{@link State#STOPPING} -></li> + * <li>{@link State#TERMINATED}</li> * </ul> * - * <p>There are deviations from this if there are failures or if {@link Service#stop} is called - * before the {@link Service} reaches the {@linkplain State#RUNNING RUNNING} state. The set of legal - * transitions form a <a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG</a>, - * therefore every method of the listener will be called at most once. N.B. The {@link State#FAILED} - * and {@link State#TERMINATED} states are terminal states, once a service enters either of these - * states it cannot ever leave them. + * If the service fails while starting, running or stopping, its state will be + * {@link State#FAILED}, and its behavior is undefined. Such a service cannot be + * started nor stopped. * - * <p>Implementors of this interface are strongly encouraged to extend one of the abstract classes - * in this package which implement this interface and make the threading and state management - * easier. + * <p>Implementors of this interface are strongly encouraged to extend one of + * the abstract classes in this package which implement this interface and + * make the threading and state management easier. * * @author Jesse Wilson - * @author Luke Sandberg - * @since 9.0 (in 1.0 as {@code com.google.common.base.Service}) + * @since 9.0 (in 1.0 as + * {@code com.google.common.base.Service}) */ -@Beta +@Beta // TODO(kevinb): make abstract class? public interface Service { /** - * If the service state is {@link State#NEW}, this initiates service startup and returns - * immediately. If the service has already been started, this method returns immediately without - * taking action. A stopped service may not be restarted. + * If the service state is {@link State#NEW}, this initiates service startup + * and returns immediately. If the service has already been started, this + * method returns immediately without taking action. A stopped service may not + * be restarted. * - * @return a future for the startup result, regardless of whether this call initiated startup. - * Calling {@link ListenableFuture#get} will block until the service has finished - * starting, and returns one of {@link State#RUNNING}, {@link State#STOPPING} or - * {@link State#TERMINATED}. If the service fails to start, {@link ListenableFuture#get} - * will throw an {@link ExecutionException}, and the service's state will be - * {@link State#FAILED}. If it has already finished starting, {@link ListenableFuture#get} - * returns immediately. Cancelling this future has no effect on the service. + * @return a future for the startup result, regardless of whether this call + * initiated startup. Calling {@link ListenableFuture#get} will block + * until the service has finished starting, and returns one of {@link + * State#RUNNING}, {@link State#STOPPING} or {@link State#TERMINATED}. If + * the service fails to start, {@link ListenableFuture#get} will throw an + * {@link ExecutionException}, and the service's state will be {@link + * State#FAILED}. If it has already finished starting, {@link + * ListenableFuture#get} returns immediately. Cancelling this future has + * no effect on the service. */ ListenableFuture<State> start(); /** - * Initiates service startup (if necessary), returning once the service has finished starting. - * Unlike calling {@code start().get()}, this method throws no checked exceptions, and it cannot - * be {@linkplain Thread#interrupt interrupted}. + * Initiates service startup (if necessary), returning once the service has + * finished starting. Unlike calling {@code start().get()}, this method throws + * no checked exceptions, and it cannot be {@linkplain Thread#interrupt + * interrupted}. * * @throws UncheckedExecutionException if startup failed * @return the state of the service when startup finished. @@ -88,67 +87,39 @@ public interface Service { State state(); /** - * If the service is {@linkplain State#STARTING starting} or {@linkplain State#RUNNING running}, - * this initiates service shutdown and returns immediately. If the service is - * {@linkplain State#NEW new}, it is {@linkplain State#TERMINATED terminated} without having been - * started nor stopped. If the service has already been stopped, this method returns immediately - * without taking action. + * If the service is {@linkplain State#STARTING starting} or {@linkplain + * State#RUNNING running}, this initiates service shutdown and returns + * immediately. If the service is {@linkplain State#NEW new}, it is + * {@linkplain State#TERMINATED terminated} without having been started nor + * stopped. If the service has already been stopped, this method returns + * immediately without taking action. * - * @return a future for the shutdown result, regardless of whether this call initiated shutdown. - * Calling {@link ListenableFuture#get} will block until the service has finished shutting - * down, and either returns {@link State#TERMINATED} or throws an - * {@link ExecutionException}. If it has already finished stopping, - * {@link ListenableFuture#get} returns immediately. Cancelling this future has no effect - * on the service. + * @return a future for the shutdown result, regardless of whether this call + * initiated shutdown. Calling {@link ListenableFuture#get} will block + * until the service has finished shutting down, and either returns + * {@link State#TERMINATED} or throws an {@link ExecutionException}. If + * it has already finished stopping, {@link ListenableFuture#get} returns + * immediately. Cancelling this future has no effect on the service. */ ListenableFuture<State> stop(); /** - * Initiates service shutdown (if necessary), returning once the service has finished stopping. If - * this is {@link State#STARTING}, startup will be cancelled. If this is {@link State#NEW}, it is - * {@link State#TERMINATED terminated} without having been started nor stopped. Unlike calling - * {@code stop().get()}, this method throws no checked exceptions. + * Initiates service shutdown (if necessary), returning once the service has + * finished stopping. If this is {@link State#STARTING}, startup will be + * cancelled. If this is {@link State#NEW}, it is {@link State#TERMINATED + * terminated} without having been started nor stopped. Unlike calling {@code + * stop().get()}, this method throws no checked exceptions. * - * @throws UncheckedExecutionException if the service has failed or fails during shutdown + * @throws UncheckedExecutionException if shutdown failed * @return the state of the service when shutdown finished. */ State stopAndWait(); /** - * Returns the {@link Throwable} that caused this service to fail. - * - * @throws IllegalStateException if this service's state isn't {@linkplain State#FAILED FAILED}. - * - * @since 14.0 - */ - Throwable failureCause(); - - /** - * Registers a {@link Listener} to be {@linkplain Executor#execute executed} on the given - * executor. The listener will have the corresponding transition method called whenever the - * service changes state. The listener will not have previous state changes replayed, so it is - * suggested that listeners are added before the service starts. - * - * <p>There is no guaranteed ordering of execution of listeners, but any listener added through - * this method is guaranteed to be called whenever there is a state change. - * - * <p>Exceptions thrown by a listener will be propagated up to the executor. Any exception thrown - * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException} or an exception - * thrown by {@linkplain MoreExecutors#sameThreadExecutor inline execution}) will be caught and - * logged. - * - * @param listener the listener to run when the service changes state is complete - * @param executor the executor in which the the listeners callback methods will be run. For fast, - * lightweight listeners that would be safe to execute in any thread, consider - * {@link MoreExecutors#sameThreadExecutor}. - * @since 13.0 - */ - void addListener(Listener listener, Executor executor); - - /** * The lifecycle states of a service. * - * @since 9.0 (in 1.0 as {@code com.google.common.base.Service.State}) + * @since 9.0 (in 1.0 as + * {@code com.google.common.base.Service.State}) */ @Beta // should come out of Beta when Service does enum State { @@ -174,70 +145,15 @@ public interface Service { STOPPING, /** - * A service in this state has completed execution normally. It does minimal work and consumes - * minimal resources. + * A service in this state has completed execution normally. It does minimal + * work and consumes minimal resources. */ TERMINATED, /** - * A service in this state has encountered a problem and may not be operational. It cannot be - * started nor stopped. + * A service in this state has encountered a problem and may not be + * operational. It cannot be started nor stopped. */ FAILED } - - /** - * A listener for the various state changes that a {@link Service} goes through in its lifecycle. - * - * @author Luke Sandberg - * @since 13.0 - */ - @Beta // should come out of Beta when Service does - interface Listener { - /** - * Called when the service transitions from {@linkplain State#NEW NEW} to - * {@linkplain State#STARTING STARTING}. This occurs when {@link Service#start} or - * {@link Service#startAndWait} is called the first time. - */ - void starting(); - - /** - * Called when the service transitions from {@linkplain State#STARTING STARTING} to - * {@linkplain State#RUNNING RUNNING}. This occurs when a service has successfully started. - */ - void running(); - - /** - * Called when the service transitions to the {@linkplain State#STOPPING STOPPING} state. The - * only valid values for {@code from} are {@linkplain State#STARTING STARTING} or - * {@linkplain State#RUNNING RUNNING}. This occurs when {@link Service#stop} is called. - * - * @param from The previous state that is being transitioned from. - */ - void stopping(State from); - - /** - * Called when the service transitions to the {@linkplain State#TERMINATED TERMINATED} state. - * The {@linkplain State#TERMINATED TERMINATED} state is a terminal state in the transition - * diagram. Therefore, if this method is called, no other methods will be called on the - * {@link Listener}. - * - * @param from The previous state that is being transitioned from. The only valid values for - * this are {@linkplain State#NEW NEW}, {@linkplain State#RUNNING RUNNING} or - * {@linkplain State#STOPPING STOPPING}. - */ - void terminated(State from); - - /** - * Called when the service transitions to the {@linkplain State#FAILED FAILED} state. The - * {@linkplain State#FAILED FAILED} state is a terminal state in the transition diagram. - * Therefore, if this method is called, no other methods will be called on the {@link Listener}. - * - * @param from The previous state that is being transitioned from. Failure can occur in any - * state with the exception of {@linkplain State#NEW NEW} or - * {@linkplain State#TERMINATED TERMINATED}. - * @param failure The exception that caused the failure. - */ - void failed(State from, Throwable failure); - } } diff --git a/guava/src/com/google/common/util/concurrent/ServiceManager.java b/guava/src/com/google/common/util/concurrent/ServiceManager.java deleted file mode 100644 index c779b23..0000000 --- a/guava/src/com/google/common/util/concurrent/ServiceManager.java +++ /dev/null @@ -1,724 +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.util.concurrent; - -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 java.util.concurrent.TimeUnit.MILLISECONDS; - -import com.google.common.annotations.Beta; -import com.google.common.base.Function; -import com.google.common.base.Objects; -import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Ordering; -import com.google.common.collect.Queues; -import com.google.common.util.concurrent.Service.State; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.annotation.concurrent.GuardedBy; -import javax.annotation.concurrent.Immutable; -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * A manager for monitoring and controlling a set of {@link Service services}. This class provides - * methods for {@linkplain #startAsync() starting}, {@linkplain #stopAsync() stopping} and - * {@linkplain #servicesByState inspecting} a collection of {@linkplain Service services}. - * Additionally, users can monitor state transitions with the {@link Listener listener} mechanism. - * - * <p>While it is recommended that service lifecycles be managed via this class, state transitions - * initiated via other mechanisms do not impact the correctness of its methods. For example, if the - * services are started by some mechanism besides {@link #startAsync}, the listeners will be invoked - * when appropriate and {@link #awaitHealthy} will still work as expected. - * - * <p>Here is a simple example of how to use a {@link ServiceManager} to start a server. - * <pre> {@code - * class Server { - * public static void main(String[] args) { - * Set<Service> services = ...; - * ServiceManager manager = new ServiceManager(services); - * manager.addListener(new Listener() { - * public void stopped() {} - * public void healthy() { - * // Services have been initialized and are healthy, start accepting requests... - * } - * public void failure(Service service) { - * // Something failed, at this point we could log it, notify a load balancer, or take - * // some other action. For now we will just exit. - * System.exit(1); - * } - * }, - * MoreExecutors.sameThreadExecutor()); - * - * Runtime.getRuntime().addShutdownHook(new Thread() { - * public void run() { - * // Give the services 5 seconds to stop to ensure that we are responsive to shutdown - * // requests. - * try { - * manager.stopAsync().awaitStopped(5, TimeUnit.SECONDS); - * } catch (TimeoutException timeout) { - * // stopping timed out - * } - * } - * }); - * manager.startAsync(); // start all the services asynchronously - * } - * }}</pre> - * - * This class uses the ServiceManager's methods to start all of its services, to respond to service - * failure and to ensure that when the JVM is shutting down all the services are stopped. - * - * @author Luke Sandberg - * @since 14.0 - */ -@Beta -@Singleton -public final class ServiceManager { - private static final Logger logger = Logger.getLogger(ServiceManager.class.getName()); - - /** - * A listener for the aggregate state changes of the services that are under management. Users - * that need to listen to more fine-grained events (such as when each particular - * {@link Service service} starts, or terminates), should attach {@link Service.Listener service - * listeners} to each individual service. - * - * @author Luke Sandberg - * @since 14.0 - */ - @Beta // Should come out of Beta when ServiceManager does - public static interface Listener { - /** - * Called when the service initially becomes healthy. - * - * <p>This will be called at most once after all the services have entered the - * {@linkplain State#RUNNING running} state. If any services fail during start up or - * {@linkplain State#FAILED fail}/{@linkplain State#TERMINATED terminate} before all other - * services have started {@linkplain State#RUNNING running} then this method will not be called. - */ - void healthy(); - - /** - * Called when the all of the component services have reached a terminal state, either - * {@linkplain State#TERMINATED terminated} or {@linkplain State#FAILED failed}. - */ - void stopped(); - - /** - * Called when a component service has {@linkplain State#FAILED failed}. - * - * @param service The service that failed. - */ - void failure(Service service); - } - - /** - * An encapsulation of all of the state that is accessed by the {@linkplain ServiceListener - * service listeners}. This is extracted into its own object so that {@link ServiceListener} - * could be made {@code static} and its instances can be safely constructed and added in the - * {@link ServiceManager} constructor without having to close over the partially constructed - * {@link ServiceManager} instance (i.e. avoid leaking a pointer to {@code this}). - */ - private final ServiceManagerState state; - private final ImmutableMap<Service, ServiceListener> services; - - /** - * Constructs a new instance for managing the given services. - * - * @param services The services to manage - * - * @throws IllegalArgumentException if not all services are {@link State#NEW new} or if there are - * any duplicate services. - */ - public ServiceManager(Iterable<? extends Service> services) { - ImmutableList<Service> copy = ImmutableList.copyOf(services); - this.state = new ServiceManagerState(copy.size()); - ImmutableMap.Builder<Service, ServiceListener> builder = ImmutableMap.builder(); - Executor executor = MoreExecutors.sameThreadExecutor(); - for (Service service : copy) { - ServiceListener listener = new ServiceListener(service, state); - service.addListener(listener, executor); - // We check the state after adding the listener as a way to ensure that our listener was added - // to a NEW service. - checkArgument(service.state() == State.NEW, "Can only manage NEW services, %s", service); - builder.put(service, listener); - } - this.services = builder.build(); - } - - /** - * Constructs a new instance for managing the given services. This constructor is provided so that - * dependency injection frameworks can inject instances of {@link ServiceManager}. - * - * @param services The services to manage - * - * @throws IllegalStateException if not all services are {@link State#NEW new}. - */ - @Inject ServiceManager(Set<Service> services) { - this((Iterable<Service>) services); - } - - /** - * Registers a {@link Listener} to be {@linkplain Executor#execute executed} on the given - * executor. The listener will not have previous state changes replayed, so it is - * suggested that listeners are added before any of the managed services are - * {@linkplain Service#start started}. - * - * <p>There is no guaranteed ordering of execution of listeners, but any listener added through - * this method is guaranteed to be called whenever there is a state change. - * - * <p>Exceptions thrown by a listener will be propagated up to the executor. Any exception thrown - * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException} or an exception - * thrown by {@linkplain MoreExecutors#sameThreadExecutor inline execution}) will be caught and - * logged. - * - * @param listener the listener to run when the manager changes state - * @param executor the executor in which the the listeners callback methods will be run. For fast, - * lightweight listeners that would be safe to execute in any thread, consider - * {@link MoreExecutors#sameThreadExecutor}. - */ - public void addListener(Listener listener, Executor executor) { - state.addListener(listener, executor); - } - - /** - * Initiates service {@linkplain Service#start startup} on all the services being managed. It is - * only valid to call this method if all of the services are {@linkplain State#NEW new}. - * - * @return this - * @throws IllegalStateException if any of the Services are not {@link State#NEW new} when the - * method is called, {@link State#TERMINATED terminated} or {@link State#FAILED failed}. - */ - public ServiceManager startAsync() { - for (Map.Entry<Service, ServiceListener> entry : services.entrySet()) { - Service service = entry.getKey(); - State state = service.state(); - checkState(state == State.NEW, "Service %s is %s, cannot start it.", service, - state); - } - for (ServiceListener service : services.values()) { - service.start(); - } - return this; - } - - /** - * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy}. The manager - * will become healthy after all the component services have reached the {@linkplain State#RUNNING - * running} state. - * - * @throws IllegalStateException if the service manager reaches a state from which it cannot - * become {@linkplain #isHealthy() healthy}. - */ - public void awaitHealthy() { - state.awaitHealthy(); - checkState(isHealthy(), "Expected to be healthy after starting"); - } - - /** - * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy} for no more - * than the given time. The manager will become healthy after all the component services have - * reached the {@linkplain State#RUNNING running} state. - * - * @param timeout the maximum time to wait - * @param unit the time unit of the timeout argument - * @throws TimeoutException if not all of the services have finished starting within the deadline - * @throws IllegalStateException if the service manager reaches a state from which it cannot - * become {@linkplain #isHealthy() healthy}. - */ - public void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException { - if (!state.awaitHealthy(timeout, unit)) { - // It would be nice to tell the caller who we are still waiting on, and this information is - // likely to be in servicesByState(), however due to race conditions we can't actually tell - // which services are holding up healthiness. The current set of NEW or STARTING services is - // likely to point out the culprit, but may not. If we really wanted to solve this we could - // change state to track exactly which services have started and then we could accurately - // report on this. But it is only for logging so we likely don't care. - throw new TimeoutException("Timeout waiting for the services to become healthy."); - } - checkState(isHealthy(), "Expected to be healthy after starting"); - } - - /** - * Initiates service {@linkplain Service#stop shutdown} if necessary on all the services being - * managed. - * - * @return this - */ - public ServiceManager stopAsync() { - for (Service service : services.keySet()) { - service.stop(); - } - return this; - } - - /** - * Waits for the all the services to reach a terminal state. After this method returns all - * services will either be {@link Service.State#TERMINATED terminated} or - * {@link Service.State#FAILED failed} - */ - public void awaitStopped() { - state.awaitStopped(); - } - - /** - * Waits for the all the services to reach a terminal state for no more than the given time. After - * this method returns all services will either be {@link Service.State#TERMINATED terminated} or - * {@link Service.State#FAILED failed} - * - * @param timeout the maximum time to wait - * @param unit the time unit of the timeout argument - * @throws TimeoutException if not all of the services have stopped within the deadline - */ - public void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException { - if (!state.awaitStopped(timeout, unit)) { - throw new TimeoutException("Timeout waiting for the services to stop."); - } - } - - /** - * Returns true if all services are currently in the {@linkplain State#RUNNING running} state. - * - * <p>Users who want more detailed information should use the {@link #servicesByState} method to - * get detailed information about which services are not running. - */ - public boolean isHealthy() { - for (Service service : services.keySet()) { - if (!service.isRunning()) { - return false; - } - } - return true; - } - - /** - * Provides a snapshot of the current state of all the services under management. - * - * <p>N.B. This snapshot it not guaranteed to be consistent, i.e. the set of states returned may - * not correspond to any particular point in time view of the services. - */ - public ImmutableMultimap<State, Service> servicesByState() { - ImmutableMultimap.Builder<State, Service> builder = ImmutableMultimap.builder(); - for (Service service : services.keySet()) { - builder.put(service.state(), service); - } - return builder.build(); - } - - /** - * Returns the service load times. This value will only return startup times for services that - * have finished starting. - * - * @return Map of services and their corresponding startup time in millis, the map entries will be - * ordered by startup time. - */ - public ImmutableMap<Service, Long> startupTimes() { - Map<Service, Long> loadTimeMap = Maps.newHashMapWithExpectedSize(services.size()); - for (Map.Entry<Service, ServiceListener> entry : services.entrySet()) { - State state = entry.getKey().state(); - if (state != State.NEW && state != State.STARTING) { - loadTimeMap.put(entry.getKey(), entry.getValue().startupTimeMillis()); - } - } - List<Entry<Service, Long>> servicesByStartTime = Ordering.<Long>natural() - .onResultOf(new Function<Map.Entry<Service, Long>, Long>() { - @Override public Long apply(Map.Entry<Service, Long> input) { - return input.getValue(); - } - }) - .sortedCopy(loadTimeMap.entrySet()); - ImmutableMap.Builder<Service, Long> builder = ImmutableMap.builder(); - for (Map.Entry<Service, Long> entry : servicesByStartTime) { - builder.put(entry); - } - return builder.build(); - } - - @Override public String toString() { - return Objects.toStringHelper(ServiceManager.class) - .add("services", services.keySet()) - .toString(); - } - - /** - * An encapsulation of all the mutable state of the {@link ServiceManager} that needs to be - * accessed by instances of {@link ServiceListener}. - */ - private static final class ServiceManagerState { - final Monitor monitor = new Monitor(); - final int numberOfServices; - /** The number of services that have not finished starting up. */ - @GuardedBy("monitor") - int unstartedServices; - /** The number of services that have not reached a terminal state. */ - @GuardedBy("monitor") - int unstoppedServices; - /** - * Controls how long to wait for all the service manager to either become healthy or reach a - * state where it is guaranteed that it can never become healthy. - */ - final Monitor.Guard awaitHealthGuard = new Monitor.Guard(monitor) { - @Override public boolean isSatisfied() { - // All services have started or some service has terminated/failed. - return unstartedServices == 0 || unstoppedServices != numberOfServices; - } - }; - /** - * Controls how long to wait for all services to reach a terminal state. - */ - final Monitor.Guard stoppedGuard = new Monitor.Guard(monitor) { - @Override public boolean isSatisfied() { - return unstoppedServices == 0; - } - }; - /** The listeners to notify during a state transition. */ - @GuardedBy("monitor") - final List<ListenerExecutorPair> listeners = Lists.newArrayList(); - /** - * The queue of listeners that are waiting to be executed. - * - * <p>Enqueue operations should be protected by {@link #monitor} while dequeue operations should - * be protected by the implicit lock on this object. This is to ensure that listeners are - * executed in the correct order and also so that a listener can not hold the {@link #monitor} - * for an arbitrary amount of time (listeners can only block other listeners, not internal state - * transitions). We use a concurrent queue implementation so that enqueues can be executed - * concurrently with dequeues. - */ - @GuardedBy("queuedListeners") - final Queue<Runnable> queuedListeners = Queues.newConcurrentLinkedQueue(); - - ServiceManagerState(int numberOfServices) { - this.numberOfServices = numberOfServices; - this.unstoppedServices = numberOfServices; - this.unstartedServices = numberOfServices; - } - - void addListener(Listener listener, Executor executor) { - checkNotNull(listener, "listener"); - checkNotNull(executor, "executor"); - monitor.enter(); - try { - // no point in adding a listener that will never be called - if (unstartedServices > 0 || unstoppedServices > 0) { - listeners.add(new ListenerExecutorPair(listener, executor)); - } - } finally { - monitor.leave(); - } - } - - void awaitHealthy() { - monitor.enter(); - try { - monitor.waitForUninterruptibly(awaitHealthGuard); - } finally { - monitor.leave(); - } - } - - boolean awaitHealthy(long timeout, TimeUnit unit) { - monitor.enter(); - try { - if (monitor.waitForUninterruptibly(awaitHealthGuard, timeout, unit)) { - return true; - } - return false; - } finally { - monitor.leave(); - } - } - - void awaitStopped() { - monitor.enter(); - try { - monitor.waitForUninterruptibly(stoppedGuard); - } finally { - monitor.leave(); - } - } - - boolean awaitStopped(long timeout, TimeUnit unit) { - monitor.enter(); - try { - return monitor.waitForUninterruptibly(stoppedGuard, timeout, unit); - } finally { - monitor.leave(); - } - } - - /** - * This should be called when a service finishes starting up. - * - * @param currentlyHealthy whether or not the service that finished starting was healthy at the - * time that it finished starting. - */ - @GuardedBy("monitor") - private void serviceFinishedStarting(Service service, boolean currentlyHealthy) { - checkState(unstartedServices > 0, - "All services should have already finished starting but %s just finished.", service); - unstartedServices--; - if (currentlyHealthy && unstartedServices == 0 && unstoppedServices == numberOfServices) { - // This means that the manager is currently healthy, or at least it should have been - // healthy at some point from some perspective. Calling isHealthy is not currently - // guaranteed to return true because any service could fail right now. However, the - // happens-before relationship enforced by the monitor ensures that this method was called - // before either serviceTerminated or serviceFailed, so we know that the manager was at - // least healthy for some period of time. Furthermore we are guaranteed that this call to - // healthy() will be before any call to terminated() or failure(Service) on the listener. - // So it is correct to execute the listener's health() callback. - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.healthy(); - } - }); - } - }); - } - } - } - - /** - * This should be called when a service is {@linkplain State#TERMINATED terminated}. - */ - @GuardedBy("monitor") - private void serviceTerminated(Service service) { - serviceStopped(service); - } - - /** - * This should be called when a service is {@linkplain State#FAILED failed}. - */ - @GuardedBy("monitor") - private void serviceFailed(final Service service) { - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.failure(service); - } - }); - } - }); - } - serviceStopped(service); - } - - /** - * Should be called whenever a service reaches a terminal state ( - * {@linkplain State#TERMINATED terminated} or - * {@linkplain State#FAILED failed}). - */ - @GuardedBy("monitor") - private void serviceStopped(Service service) { - checkState(unstoppedServices > 0, - "All services should have already stopped but %s just stopped.", service); - unstoppedServices--; - if (unstoppedServices == 0) { - checkState(unstartedServices == 0, - "All services are stopped but %d services haven't finished starting", - unstartedServices); - for (final ListenerExecutorPair pair : listeners) { - queuedListeners.add(new Runnable() { - @Override public void run() { - pair.execute(new Runnable() { - @Override public void run() { - pair.listener.stopped(); - } - }); - } - }); - } - // no more listeners could possibly be called, so clear them out - listeners.clear(); - } - } - - /** - * Attempts to execute all the listeners in {@link #queuedListeners}. - */ - private void executeListeners() { - checkState(!monitor.isOccupiedByCurrentThread(), - "It is incorrect to execute listeners with the monitor held."); - synchronized (queuedListeners) { - Runnable listener; - while ((listener = queuedListeners.poll()) != null) { - listener.run(); - } - } - } - } - - /** - * A {@link Service} that wraps another service and times how long it takes for it to start and - * also calls the {@link ServiceManagerState#serviceFinishedStarting}, - * {@link ServiceManagerState#serviceTerminated} and {@link ServiceManagerState#serviceFailed} - * according to its current state. - */ - private static final class ServiceListener implements Service.Listener { - @GuardedBy("watch") // AFAICT Stopwatch is not thread safe so we need to protect accesses - final Stopwatch watch = new Stopwatch(); - final Service service; - final ServiceManagerState state; - - /** - * @param service the service that - */ - ServiceListener(Service service, ServiceManagerState state) { - this.service = service; - this.state = state; - } - - @Override public void starting() { - // This can happen if someone besides the ServiceManager starts the service, in this case - // our timings may be inaccurate. - startTimer(); - } - - @Override public void running() { - state.monitor.enter(); - try { - finishedStarting(true); - } finally { - state.monitor.leave(); - state.executeListeners(); - } - } - - @Override public void stopping(State from) { - if (from == State.STARTING) { - state.monitor.enter(); - try { - finishedStarting(false); - } finally { - state.monitor.leave(); - state.executeListeners(); - } - } - } - - @Override public void terminated(State from) { - logger.info("Service " + service + " has terminated. Previous state was " + from + " state."); - state.monitor.enter(); - try { - if (from == State.NEW) { - // startTimer is idempotent, so this is safe to call and it may be necessary if no one has - // started the timer yet. - startTimer(); - finishedStarting(false); - } - state.serviceTerminated(service); - } finally { - state.monitor.leave(); - state.executeListeners(); - } - } - - @Override public void failed(State from, Throwable failure) { - logger.log(Level.SEVERE, "Service " + service + " has failed in the " + from + " state.", - failure); - state.monitor.enter(); - try { - if (from == State.STARTING) { - finishedStarting(false); - } - state.serviceFailed(service); - } finally { - state.monitor.leave(); - state.executeListeners(); - } - } - - /** - * Stop the stopwatch, log the startup time and decrement the startup latch - * - * @param currentlyHealthy whether or not the service that finished starting is currently - * healthy - */ - @GuardedBy("monitor") - void finishedStarting(boolean currentlyHealthy) { - synchronized (watch) { - watch.stop(); - logger.log(Level.INFO, "Started " + service + " in " + startupTimeMillis() + " ms."); - } - state.serviceFinishedStarting(service, currentlyHealthy); - } - - void start() { - startTimer(); - service.start(); - } - - /** Start the timer if it hasn't been started. */ - void startTimer() { - synchronized (watch) { - if (!watch.isRunning()) { // only start the watch once. - watch.start(); - logger.log(Level.INFO, "Starting {0}", service); - } - } - } - - /** Returns the amount of time it took for the service to finish starting in milliseconds. */ - synchronized long startupTimeMillis() { - synchronized (watch) { - return watch.elapsed(MILLISECONDS); - } - } - } - - /** Simple value object binding a listener to its executor. */ - @Immutable private static final class ListenerExecutorPair { - final Listener listener; - final Executor executor; - - ListenerExecutorPair(Listener listener, Executor executor) { - this.listener = listener; - this.executor = executor; - } - - /** - * Executes the given {@link Runnable} on {@link #executor} logging and swallowing all - * exceptions - */ - void execute(Runnable runnable) { - try { - executor.execute(runnable); - } catch (Exception e) { - logger.log(Level.SEVERE, "Exception while executing listener " + listener - + " with executor " + executor, e); - } - } - } -} diff --git a/guava/src/com/google/common/util/concurrent/Striped.java b/guava/src/com/google/common/util/concurrent/Striped.java deleted file mode 100644 index 3c426f0..0000000 --- a/guava/src/com/google/common/util/concurrent/Striped.java +++ /dev/null @@ -1,376 +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.util.concurrent; - -import com.google.common.annotations.Beta; -import com.google.common.base.Functions; -import com.google.common.base.Preconditions; -import com.google.common.base.Supplier; -import com.google.common.collect.Iterables; -import com.google.common.collect.MapMaker; -import com.google.common.math.IntMath; -import com.google.common.primitives.Ints; - -import java.math.RoundingMode; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Semaphore; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -/** - * A striped {@code Lock/Semaphore/ReadWriteLock}. This offers the underlying lock striping - * similar to that of {@code ConcurrentHashMap} in a reusable form, and extends it for - * semaphores and read-write locks. Conceptually, lock striping is the technique of dividing a lock - * into many <i>stripes</i>, increasing the granularity of a single lock and allowing independent - * operations to lock different stripes and proceed concurrently, instead of creating contention - * for a single lock. - * - * <p>The guarantee provided by this class is that equal keys lead to the same lock (or semaphore), - * i.e. {@code if (key1.equals(key2))} then {@code striped.get(key1) == striped.get(key2)} - * (assuming {@link Object#hashCode()} is correctly implemented for the keys). Note - * that if {@code key1} is <strong>not</strong> equal to {@code key2}, it is <strong>not</strong> - * guaranteed that {@code striped.get(key1) != striped.get(key2)}; the elements might nevertheless - * be mapped to the same lock. The lower the number of stripes, the higher the probability of this - * happening. - * - * <p>There are three flavors of this class: {@code Striped<Lock>}, {@code Striped<Semaphore>}, - * and {@code Striped<ReadWriteLock>}. For each type, two implementations are offered: - * {@linkplain #lock(int) strong} and {@linkplain #lazyWeakLock(int) weak} - * {@code Striped<Lock>}, {@linkplain #semaphore(int, int) strong} and {@linkplain - * #lazyWeakSemaphore(int, int) weak} {@code Striped<Semaphore>}, and {@linkplain - * #readWriteLock(int) strong} and {@linkplain #lazyWeakReadWriteLock(int) weak} - * {@code Striped<ReadWriteLock>}. <i>Strong</i> means that all stripes (locks/semaphores) are - * initialized eagerly, and are not reclaimed unless {@code Striped} itself is reclaimable. - * <i>Weak</i> means that locks/semaphores are created lazily, and they are allowed to be reclaimed - * if nobody is holding on to them. This is useful, for example, if one wants to create a {@code - * Striped<Lock>} of many locks, but worries that in most cases only a small portion of these - * would be in use. - * - * <p>Prior to this class, one might be tempted to use {@code Map<K, Lock>}, where {@code K} - * represents the task. This maximizes concurrency by having each unique key mapped to a unique - * lock, but also maximizes memory footprint. On the other extreme, one could use a single lock - * for all tasks, which minimizes memory footprint but also minimizes concurrency. Instead of - * choosing either of these extremes, {@code Striped} allows the user to trade between required - * concurrency and memory footprint. For example, if a set of tasks are CPU-bound, one could easily - * create a very compact {@code Striped<Lock>} of {@code availableProcessors() * 4} stripes, - * instead of possibly thousands of locks which could be created in a {@code Map<K, Lock>} - * structure. - * - * @author Dimitris Andreou - * @since 13.0 - */ -@Beta -public abstract class Striped<L> { - private Striped() {} - - /** - * Returns the stripe that corresponds to the passed key. It is always guaranteed that if - * {@code key1.equals(key2)}, then {@code get(key1) == get(key2)}. - * - * @param key an arbitrary, non-null key - * @return the stripe that the passed key corresponds to - */ - public abstract L get(Object key); - - /** - * Returns the stripe at the specified index. Valid indexes are 0, inclusively, to - * {@code size()}, exclusively. - * - * @param index the index of the stripe to return; must be in {@code [0...size())} - * @return the stripe at the specified index - */ - public abstract L getAt(int index); - - /** - * Returns the index to which the given key is mapped, so that getAt(indexFor(key)) == get(key). - */ - abstract int indexFor(Object key); - - /** - * Returns the total number of stripes in this instance. - */ - public abstract int size(); - - /** - * Returns the stripes that correspond to the passed objects, in ascending (as per - * {@link #getAt(int)}) order. Thus, threads that use the stripes in the order returned - * by this method are guaranteed to not deadlock each other. - * - * <p>It should be noted that using a {@code Striped<L>} with relatively few stripes, and - * {@code bulkGet(keys)} with a relative large number of keys can cause an excessive number - * of shared stripes (much like the birthday paradox, where much fewer than anticipated birthdays - * are needed for a pair of them to match). Please consider carefully the implications of the - * number of stripes, the intended concurrency level, and the typical number of keys used in a - * {@code bulkGet(keys)} operation. See <a href="http://www.mathpages.com/home/kmath199.htm">Balls - * in Bins model</a> for mathematical formulas that can be used to estimate the probability of - * collisions. - * - * @param keys arbitrary non-null keys - * @return the stripes corresponding to the objects (one per each object, derived by delegating - * to {@link #get(Object)}; may contain duplicates), in an increasing index order. - */ - public Iterable<L> bulkGet(Iterable<?> keys) { - // Initially using the array to store the keys, then reusing it to store the respective L's - final Object[] array = Iterables.toArray(keys, Object.class); - int[] stripes = new int[array.length]; - for (int i = 0; i < array.length; i++) { - stripes[i] = indexFor(array[i]); - } - Arrays.sort(stripes); - for (int i = 0; i < array.length; i++) { - array[i] = getAt(stripes[i]); - } - /* - * Note that the returned Iterable holds references to the returned stripes, to avoid - * error-prone code like: - * - * Striped<Lock> stripedLock = Striped.lazyWeakXXX(...)' - * Iterable<Lock> locks = stripedLock.bulkGet(keys); - * for (Lock lock : locks) { - * lock.lock(); - * } - * operation(); - * for (Lock lock : locks) { - * lock.unlock(); - * } - * - * If we only held the int[] stripes, translating it on the fly to L's, the original locks - * might be garbage collected after locking them, ending up in a huge mess. - */ - @SuppressWarnings("unchecked") // we carefully replaced all keys with their respective L's - List<L> asList = (List<L>) Arrays.asList(array); - return Collections.unmodifiableList(asList); - } - - // Static factories - - /** - * Creates a {@code Striped<Lock>} with eagerly initialized, strongly referenced locks, with the - * specified fairness. Every lock is reentrant. - * - * @param stripes the minimum number of stripes (locks) required - * @return a new {@code Striped<Lock>} - */ - public static Striped<Lock> lock(int stripes) { - return new CompactStriped<Lock>(stripes, new Supplier<Lock>() { - public Lock get() { - return new PaddedLock(); - } - }); - } - - /** - * Creates a {@code Striped<Lock>} with lazily initialized, weakly referenced locks, with the - * specified fairness. Every lock is reentrant. - * - * @param stripes the minimum number of stripes (locks) required - * @return a new {@code Striped<Lock>} - */ - public static Striped<Lock> lazyWeakLock(int stripes) { - return new LazyStriped<Lock>(stripes, new Supplier<Lock>() { - public Lock get() { - return new ReentrantLock(false); - } - }); - } - - /** - * Creates a {@code Striped<Semaphore>} with eagerly initialized, strongly referenced semaphores, - * with the specified number of permits and fairness. - * - * @param stripes the minimum number of stripes (semaphores) required - * @param permits the number of permits in each semaphore - * @return a new {@code Striped<Semaphore>} - */ - public static Striped<Semaphore> semaphore(int stripes, final int permits) { - return new CompactStriped<Semaphore>(stripes, new Supplier<Semaphore>() { - public Semaphore get() { - return new PaddedSemaphore(permits); - } - }); - } - - /** - * Creates a {@code Striped<Semaphore>} with lazily initialized, weakly referenced semaphores, - * with the specified number of permits and fairness. - * - * @param stripes the minimum number of stripes (semaphores) required - * @param permits the number of permits in each semaphore - * @return a new {@code Striped<Semaphore>} - */ - public static Striped<Semaphore> lazyWeakSemaphore(int stripes, final int permits) { - return new LazyStriped<Semaphore>(stripes, new Supplier<Semaphore>() { - public Semaphore get() { - return new Semaphore(permits, false); - } - }); - } - - /** - * Creates a {@code Striped<ReadWriteLock>} with eagerly initialized, strongly referenced - * read-write locks, with the specified fairness. Every lock is reentrant. - * - * @param stripes the minimum number of stripes (locks) required - * @return a new {@code Striped<ReadWriteLock>} - */ - public static Striped<ReadWriteLock> readWriteLock(int stripes) { - return new CompactStriped<ReadWriteLock>(stripes, READ_WRITE_LOCK_SUPPLIER); - } - - /** - * Creates a {@code Striped<ReadWriteLock>} with lazily initialized, weakly referenced - * read-write locks, with the specified fairness. Every lock is reentrant. - * - * @param stripes the minimum number of stripes (locks) required - * @return a new {@code Striped<ReadWriteLock>} - */ - public static Striped<ReadWriteLock> lazyWeakReadWriteLock(int stripes) { - return new LazyStriped<ReadWriteLock>(stripes, READ_WRITE_LOCK_SUPPLIER); - } - - // ReentrantReadWriteLock is large enough to make padding probably unnecessary - private static final Supplier<ReadWriteLock> READ_WRITE_LOCK_SUPPLIER = - new Supplier<ReadWriteLock>() { - public ReadWriteLock get() { - return new ReentrantReadWriteLock(); - } - }; - - private abstract static class PowerOfTwoStriped<L> extends Striped<L> { - /** Capacity (power of two) minus one, for fast mod evaluation */ - final int mask; - - PowerOfTwoStriped(int stripes) { - Preconditions.checkArgument(stripes > 0, "Stripes must be positive"); - this.mask = stripes > Ints.MAX_POWER_OF_TWO ? ALL_SET : ceilToPowerOfTwo(stripes) - 1; - } - - @Override final int indexFor(Object key) { - int hash = smear(key.hashCode()); - return hash & mask; - } - - @Override public final L get(Object key) { - return getAt(indexFor(key)); - } - } - - /** - * Implementation of Striped where 2^k stripes are represented as an array of the same length, - * eagerly initialized. - */ - private static class CompactStriped<L> extends PowerOfTwoStriped<L> { - /** Size is a power of two. */ - private final Object[] array; - - private CompactStriped(int stripes, Supplier<L> supplier) { - super(stripes); - Preconditions.checkArgument(stripes <= Ints.MAX_POWER_OF_TWO, "Stripes must be <= 2^30)"); - - this.array = new Object[mask + 1]; - for (int i = 0; i < array.length; i++) { - array[i] = supplier.get(); - } - } - - @SuppressWarnings("unchecked") // we only put L's in the array - @Override public L getAt(int index) { - return (L) array[index]; - } - - @Override public int size() { - return array.length; - } - } - - /** - * Implementation of Striped where up to 2^k stripes can be represented, using a Cache - * where the key domain is [0..2^k). To map a user key into a stripe, we take a k-bit slice of the - * user key's (smeared) hashCode(). The stripes are lazily initialized and are weakly referenced. - */ - private static class LazyStriped<L> extends PowerOfTwoStriped<L> { - final ConcurrentMap<Integer, L> cache; - final int size; - - LazyStriped(int stripes, Supplier<L> supplier) { - super(stripes); - this.size = (mask == ALL_SET) ? Integer.MAX_VALUE : mask + 1; - this.cache = new MapMaker().weakValues().makeComputingMap(Functions.forSupplier(supplier)); - } - - @Override public L getAt(int index) { - Preconditions.checkElementIndex(index, size()); - return cache.get(index); - } - - @Override public int size() { - return size; - } - } - - /** - * A bit mask were all bits are set. - */ - private static final int ALL_SET = ~0; - - private static int ceilToPowerOfTwo(int x) { - return 1 << IntMath.log2(x, RoundingMode.CEILING); - } - - /* - * This method was 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/licenses/publicdomain - * - * As of 2010/06/11, this method is identical to the (package private) hash - * method in OpenJDK 7's java.util.HashMap class. - */ - // Copied from java/com/google/common/collect/Hashing.java - private static int smear(int hashCode) { - hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12); - return hashCode ^ (hashCode >>> 7) ^ (hashCode >>> 4); - } - - private static class PaddedLock extends ReentrantLock { - /* - * Padding from 40 into 64 bytes, same size as cache line. Might be beneficial to add - * a fourth long here, to minimize chance of interference between consecutive locks, - * but I couldn't observe any benefit from that. - */ - @SuppressWarnings("unused") - long q1, q2, q3; - - PaddedLock() { - super(false); - } - } - - private static class PaddedSemaphore extends Semaphore { - // See PaddedReentrantLock comment - @SuppressWarnings("unused") - long q1, q2, q3; - - PaddedSemaphore(int permits) { - super(permits, false); - } - } -} diff --git a/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java b/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java index a05247e..167ad11 100644 --- a/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java +++ b/guava/src/com/google/common/util/concurrent/ThreadFactoryBuilder.java @@ -61,12 +61,9 @@ public final class ThreadFactoryBuilder { * @param nameFormat a {@link String#format(String, Object...)}-compatible * format String, to which a unique integer (0, 1, etc.) will be supplied * as the single parameter. This integer will be unique to the built - * instance of the ThreadFactory and will be assigned sequentially. For - * example, {@code "rpc-pool-%d"} will generate thread names like - * {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. + * instance of the ThreadFactory and will be assigned sequentially. * @return this for the builder pattern */ - @SuppressWarnings("ReturnValueIgnored") public ThreadFactoryBuilder setNameFormat(String nameFormat) { String.format(nameFormat, 0); // fail fast if the format is bad or null this.nameFormat = nameFormat; diff --git a/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java b/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java index 77afdc3..c0c99e1 100644 --- a/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java +++ b/guava/src/com/google/common/util/concurrent/UncheckedExecutionException.java @@ -16,10 +16,9 @@ package com.google.common.util.concurrent; +import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; -import javax.annotation.Nullable; - /** * Unchecked variant of {@link java.util.concurrent.ExecutionException}. As with * {@code ExecutionException}, the exception's {@linkplain #getCause() cause} @@ -27,16 +26,17 @@ import javax.annotation.Nullable; * * <p>{@code UncheckedExecutionException} is intended as an alternative to * {@code ExecutionException} when the exception thrown by a task is an - * unchecked exception. However, it may also wrap a checked exception in some - * cases. + * unchecked exception. This allows the client code to continue to distinguish + * between checked and unchecked exceptions, even when they come from other + * threads. * * <p>When wrapping an {@code Error} from another thread, prefer {@link - * ExecutionError}. When wrapping a checked exception, prefer {@code - * ExecutionException}. + * ExecutionError}. * * @author Charles Fry * @since 10.0 */ +@Beta @GwtCompatible public class UncheckedExecutionException extends RuntimeException { /** @@ -47,21 +47,21 @@ public class UncheckedExecutionException extends RuntimeException { /** * Creates a new instance with the given detail message. */ - protected UncheckedExecutionException(@Nullable String message) { + protected UncheckedExecutionException(String message) { super(message); } /** * Creates a new instance with the given detail message and cause. */ - public UncheckedExecutionException(@Nullable String message, @Nullable Throwable cause) { + public UncheckedExecutionException(String message, Throwable cause) { super(message, cause); } /** * Creates a new instance with the given cause. */ - public UncheckedExecutionException(@Nullable Throwable cause) { + public UncheckedExecutionException(Throwable cause) { super(cause); } diff --git a/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java b/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java index cefe379..d821c84 100644 --- a/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java +++ b/guava/src/com/google/common/util/concurrent/UncheckedTimeoutException.java @@ -16,8 +16,6 @@ package com.google.common.util.concurrent; -import javax.annotation.Nullable; - /** * Unchecked version of {@link java.util.concurrent.TimeoutException}. * @@ -27,15 +25,15 @@ import javax.annotation.Nullable; public class UncheckedTimeoutException extends RuntimeException { public UncheckedTimeoutException() {} - public UncheckedTimeoutException(@Nullable String message) { + public UncheckedTimeoutException(String message) { super(message); } - public UncheckedTimeoutException(@Nullable Throwable cause) { + public UncheckedTimeoutException(Throwable cause) { super(cause); } - public UncheckedTimeoutException(@Nullable String message, @Nullable Throwable cause) { + public UncheckedTimeoutException(String message, Throwable cause) { super(message, cause); } diff --git a/guava/src/com/google/common/util/concurrent/Uninterruptibles.java b/guava/src/com/google/common/util/concurrent/Uninterruptibles.java index 79d7598..89f30b8 100644 --- a/guava/src/com/google/common/util/concurrent/Uninterruptibles.java +++ b/guava/src/com/google/common/util/concurrent/Uninterruptibles.java @@ -122,9 +122,6 @@ public final class Uninterruptibles { * <p>If instead, you wish to treat {@link InterruptedException} uniformly * with other exceptions, see {@link Futures#get(Future, Class) Futures.get} * or {@link Futures#makeChecked}. - * - * @throws ExecutionException if the computation threw an exception - * @throws CancellationException if the computation was cancelled */ public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException { @@ -152,10 +149,6 @@ public final class Uninterruptibles { * <p>If instead, you wish to treat {@link InterruptedException} uniformly * with other exceptions, see {@link Futures#get(Future, Class) Futures.get} * or {@link Futures#makeChecked}. - * - * @throws ExecutionException if the computation threw an exception - * @throws CancellationException if the computation was cancelled - * @throws TimeoutException if the wait timed out */ public static <V> V getUninterruptibly( Future<V> future, long timeout, TimeUnit unit) @@ -233,11 +226,6 @@ public final class Uninterruptibles { /** * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} * uninterruptibly. - * - * @throws ClassCastException if the class of the specified element prevents - * it from being added to the given queue - * @throws IllegalArgumentException if some property of the specified element - * prevents it from being added to the given queue */ public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) { boolean interrupted = false; diff --git a/guava/src/com/google/common/util/concurrent/package-info.java b/guava/src/com/google/common/util/concurrent/package-info.java index 6ea5069..6281552 100644 --- a/guava/src/com/google/common/util/concurrent/package-info.java +++ b/guava/src/com/google/common/util/concurrent/package-info.java @@ -33,4 +33,3 @@ package com.google.common.util.concurrent; import javax.annotation.ParametersAreNonnullByDefault; - |