commit bc46ad7f01eb1aacdfb00bc2cc3924e3944390a2 Author: Merith-TK Date: Tue Aug 9 17:30:12 2022 -0700 fork skaia from source diff --git a/etc/decompiled.zip b/etc/decompiled.zip new file mode 100644 index 0000000..4361631 Binary files /dev/null and b/etc/decompiled.zip differ diff --git a/etc/source.jar b/etc/source.jar new file mode 100644 index 0000000..74d2af1 Binary files /dev/null and b/etc/source.jar differ diff --git a/src/main/Log4j-config.xsd b/src/main/Log4j-config.xsd new file mode 100644 index 0000000..a19e125 --- /dev/null +++ b/src/main/Log4j-config.xsd @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/Log4j-events.dtd b/src/main/Log4j-events.dtd new file mode 100644 index 0000000..d6b34c8 --- /dev/null +++ b/src/main/Log4j-events.dtd @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/Log4j-events.xsd b/src/main/Log4j-events.xsd new file mode 100644 index 0000000..6e18154 --- /dev/null +++ b/src/main/Log4j-events.xsd @@ -0,0 +1,78 @@ + + + + + + Log4J 2.0 XML Schema for XML log event files. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/Log4j-levels.xsd b/src/main/Log4j-levels.xsd new file mode 100644 index 0000000..3ca10bb --- /dev/null +++ b/src/main/Log4j-levels.xsd @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + diff --git a/src/main/META-INF/MANIFEST.MF b/src/main/META-INF/MANIFEST.MF new file mode 100644 index 0000000..8ebbf15 --- /dev/null +++ b/src/main/META-INF/MANIFEST.MF @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 +Implementation-Version: v1.0 +Archiver-Version: Plexus Archiver +Built-By: Andrew +Created-By: Apache Maven 3.0.5 +Build-Jdk: 1.8.0_171 +Main-Class: net.mc.main.Main + diff --git a/src/main/Minecraft_Logo.png b/src/main/Minecraft_Logo.png new file mode 100644 index 0000000..5049c74 Binary files /dev/null and b/src/main/Minecraft_Logo.png differ diff --git a/src/main/cakehoohoohoo.png b/src/main/cakehoohoohoo.png new file mode 100644 index 0000000..2947892 Binary files /dev/null and b/src/main/cakehoohoohoo.png differ diff --git a/src/main/com/evilco/mc/nbt/error/TagNotFoundException.java b/src/main/com/evilco/mc/nbt/error/TagNotFoundException.java new file mode 100644 index 0000000..3193060 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/error/TagNotFoundException.java @@ -0,0 +1,23 @@ +package com.evilco.mc.nbt.error; + +import java.io.IOException; + +public class TagNotFoundException extends IOException { + private static final long serialVersionUID = -4631008535746749103L; + + public TagNotFoundException() { + super("The tag does not exist"); + } + + public TagNotFoundException(String message) { + super(message); + } + + public TagNotFoundException(Throwable cause) { + super(cause); + } + + public TagNotFoundException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/com/evilco/mc/nbt/error/UnexpectedTagTypeException.java b/src/main/com/evilco/mc/nbt/error/UnexpectedTagTypeException.java new file mode 100644 index 0000000..0367ba6 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/error/UnexpectedTagTypeException.java @@ -0,0 +1,23 @@ +package com.evilco.mc.nbt.error; + +import java.io.IOException; + +public class UnexpectedTagTypeException extends IOException { + private static final long serialVersionUID = -6604963428978583800L; + + public UnexpectedTagTypeException() { + super("The tag is not of the expected type"); + } + + public UnexpectedTagTypeException(String message) { + super(message); + } + + public UnexpectedTagTypeException(Throwable cause) { + super(cause); + } + + public UnexpectedTagTypeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/com/evilco/mc/nbt/stream/NbtInputStream.java b/src/main/com/evilco/mc/nbt/stream/NbtInputStream.java new file mode 100644 index 0000000..a5e3fab --- /dev/null +++ b/src/main/com/evilco/mc/nbt/stream/NbtInputStream.java @@ -0,0 +1,40 @@ +package com.evilco.mc.nbt.stream; + +import com.evilco.mc.nbt.tag.ITag; +import com.evilco.mc.nbt.tag.TagType; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; + +public class NbtInputStream extends DataInputStream { + public NbtInputStream(InputStream in) { + super(in); + } + + public ITag readTag() throws IOException { + byte type = this.readByte(); + TagType tagType = TagType.valueOf(type); + if (tagType == null) { + throw new IOException("Invalid NBT tag: Found unknown tag type " + type + "."); + } else { + return tagType == TagType.END ? null : this.readTag(tagType, false); + } + } + + public ITag readTag(TagType type, boolean anonymous) throws IOException { + Constructor constructor = null; + + try { + constructor = type.tagType.getConstructor(NbtInputStream.class, Boolean.TYPE); + } catch (NoSuchMethodException var6) { + throw new IOException("Invalid NBT implementation state: Type " + type.tagType.getName() + " has no de-serialization constructor."); + } + + try { + return (ITag)constructor.newInstance(this, anonymous); + } catch (Exception var5) { + throw new IOException("Invalid NBT implementation state: Type " + type.tagType.getName() + " in (" + this.getClass().getName() + ") has no valid constructor: " + var5.getMessage(), var5); + } + } +} diff --git a/src/main/com/evilco/mc/nbt/stream/NbtOutputStream.java b/src/main/com/evilco/mc/nbt/stream/NbtOutputStream.java new file mode 100644 index 0000000..08f589b --- /dev/null +++ b/src/main/com/evilco/mc/nbt/stream/NbtOutputStream.java @@ -0,0 +1,17 @@ +package com.evilco.mc.nbt.stream; + +import com.evilco.mc.nbt.tag.ITag; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class NbtOutputStream extends DataOutputStream { + public NbtOutputStream(OutputStream out) { + super(out); + } + + public void write(ITag tag) throws IOException { + this.writeByte(tag.getTagID()); + tag.write(this, false); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/AbstractTag.java b/src/main/com/evilco/mc/nbt/tag/AbstractTag.java new file mode 100644 index 0000000..bffa620 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/AbstractTag.java @@ -0,0 +1,76 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import com.google.common.base.Preconditions; +import java.io.IOException; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class AbstractTag implements ITag { + protected String name; + protected ITagContainer parent = null; + + public AbstractTag(@Nonnull String name) { + this.setName(name); + } + + public AbstractTag(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + Preconditions.checkNotNull(inputStream, "inputStream"); + if (!anonymous) { + int nameSize = inputStream.readShort(); + byte[] nameBytes = new byte[nameSize]; + inputStream.readFully(nameBytes); + this.setName(new String(nameBytes, STRING_CHARSET)); + } + + } + + public String getName() { + return this.name; + } + + public byte[] getNameBytes() { + return this.name.getBytes(STRING_CHARSET); + } + + public ITagContainer getParent() { + return this.parent; + } + + public abstract byte getTagID(); + + public void setName(@Nonnull String name) { + Preconditions.checkNotNull(name, "name"); + if (this.getParent() != null) { + this.getParent().removeTag(this); + } + + this.name = name; + if (this.getParent() != null) { + if (this.getParent() instanceof IAnonymousTagContainer) { + ((IAnonymousTagContainer)this.getParent()).addTag(this); + } else { + ((INamedTagContainer)this.getParent()).setTag(this); + } + } + + } + + public void setParent(@Nullable ITagContainer parent) { + if (this.getParent() != null) { + this.getParent().removeTag(this); + } + + this.parent = parent; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + if (!anonymous) { + byte[] name = this.getNameBytes(); + outputStream.writeShort(name.length); + outputStream.write(name); + } + + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/IAnonymousTagContainer.java b/src/main/com/evilco/mc/nbt/tag/IAnonymousTagContainer.java new file mode 100644 index 0000000..a900c49 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/IAnonymousTagContainer.java @@ -0,0 +1,15 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.error.UnexpectedTagTypeException; +import java.util.List; +import javax.annotation.Nonnull; + +public interface IAnonymousTagContainer extends ITagContainer { + void addTag(@Nonnull ITag var1); + + List getTags(); + + List getTags(Class var1) throws UnexpectedTagTypeException; + + void setTag(int var1, @Nonnull ITag var2); +} diff --git a/src/main/com/evilco/mc/nbt/tag/INamedTagContainer.java b/src/main/com/evilco/mc/nbt/tag/INamedTagContainer.java new file mode 100644 index 0000000..7a3c71c --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/INamedTagContainer.java @@ -0,0 +1,18 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.error.TagNotFoundException; +import com.evilco.mc.nbt.error.UnexpectedTagTypeException; +import java.util.Map; +import javax.annotation.Nonnull; + +public interface INamedTagContainer extends ITagContainer { + ITag getTag(@Nonnull String var1); + + T getTag(String var1, Class var2) throws UnexpectedTagTypeException, TagNotFoundException; + + Map getTags(); + + void removeTag(@Nonnull String var1); + + void setTag(@Nonnull ITag var1); +} diff --git a/src/main/com/evilco/mc/nbt/tag/ITag.java b/src/main/com/evilco/mc/nbt/tag/ITag.java new file mode 100644 index 0000000..f8e62b1 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/ITag.java @@ -0,0 +1,25 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface ITag { + Charset STRING_CHARSET = Charset.forName("UTF-8"); + + String getName(); + + byte[] getNameBytes(); + + ITagContainer getParent(); + + byte getTagID(); + + void setName(@Nonnull String var1); + + void setParent(@Nullable ITagContainer var1); + + void write(NbtOutputStream var1, boolean var2) throws IOException; +} diff --git a/src/main/com/evilco/mc/nbt/tag/ITagContainer.java b/src/main/com/evilco/mc/nbt/tag/ITagContainer.java new file mode 100644 index 0000000..9f008fe --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/ITagContainer.java @@ -0,0 +1,7 @@ +package com.evilco.mc.nbt.tag; + +import javax.annotation.Nonnull; + +public interface ITagContainer extends ITag { + void removeTag(@Nonnull ITag var1); +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagByte.java b/src/main/com/evilco/mc/nbt/tag/TagByte.java new file mode 100644 index 0000000..989df1d --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagByte.java @@ -0,0 +1,37 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagByte extends AbstractTag { + protected byte value; + + public TagByte(@Nonnull String name, byte value) { + super(name); + this.setValue(value); + } + + public TagByte(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + this.setValue(inputStream.readByte()); + } + + public byte getTagID() { + return TagType.BYTE.typeID; + } + + public byte getValue() { + return this.value; + } + + public void setValue(byte b) { + this.value = b; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.write(this.getValue()); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagByteArray.java b/src/main/com/evilco/mc/nbt/tag/TagByteArray.java new file mode 100644 index 0000000..1f2a34c --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagByteArray.java @@ -0,0 +1,43 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import com.google.common.base.Preconditions; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagByteArray extends AbstractTag { + protected byte[] value; + + public TagByteArray(@Nonnull String name, @Nonnull byte[] value) { + super(name); + this.setValue(value); + } + + public TagByteArray(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + int size = inputStream.readInt(); + byte[] data = new byte[size]; + inputStream.readFully(data); + this.setValue(data); + } + + public byte getTagID() { + return TagType.BYTE_ARRAY.typeID; + } + + public byte[] getValue() { + return this.value; + } + + public void setValue(@Nonnull byte[] b) { + Preconditions.checkNotNull(b, "b"); + this.value = b; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeInt(this.value.length); + outputStream.write(this.value); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagCompound.java b/src/main/com/evilco/mc/nbt/tag/TagCompound.java new file mode 100644 index 0000000..d3e3ac1 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagCompound.java @@ -0,0 +1,175 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.error.TagNotFoundException; +import com.evilco.mc.nbt.error.UnexpectedTagTypeException; +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nonnull; + +public class TagCompound extends AbstractTag implements INamedTagContainer { + protected Map tags = new HashMap(); + + public TagCompound(String name) { + super(name); + } + + public TagCompound(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + + while(true) { + byte type = inputStream.readByte(); + TagType tagType = TagType.valueOf(type); + if (tagType == null) { + throw new IOException("Could not find a tag for type ID " + type + "."); + } + + if (tagType == TagType.END) { + return; + } + + this.setTag(inputStream.readTag(tagType, false)); + } + } + + public ITag getTag(@Nonnull String name) { + Preconditions.checkNotNull(name, "name"); + return (ITag)this.tags.get(name); + } + + public T getTag(String name, Class tagClass) throws UnexpectedTagTypeException, TagNotFoundException { + ITag tag = this.getTag(name); + if (tag == null) { + throw new TagNotFoundException("The compound tag is missing a " + name + " entry"); + } else if (!tagClass.isInstance(tag)) { + throw new UnexpectedTagTypeException("The compound entry " + name + " should be of type " + tagClass.getSimpleName() + ", but is of type " + tag.getClass().getSimpleName()); + } else { + return tag; + } + } + + public TagCompound getCompound(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return (TagCompound)this.getTag(name, TagCompound.class); + } + + public int getInteger(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagInteger)this.getTag(name, TagInteger.class)).getValue(); + } + + public short getShort(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagShort)this.getTag(name, TagShort.class)).getValue(); + } + + public byte getByte(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagByte)this.getTag(name, TagByte.class)).getValue(); + } + + public long getLong(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagLong)this.getTag(name, TagLong.class)).getValue(); + } + + public double getDouble(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagDouble)this.getTag(name, TagDouble.class)).getValue(); + } + + public float getFloat(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagFloat)this.getTag(name, TagFloat.class)).getValue(); + } + + public String getString(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagString)this.getTag(name, TagString.class)).getValue(); + } + + public List getList(String name, Class itemClass) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagList)this.getTag(name, TagList.class)).getTags(itemClass); + } + + public int[] getIntegerArray(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagIntegerArray)this.getTag(name, TagIntegerArray.class)).getValues(); + } + + public byte[] getByteArray(String name) throws UnexpectedTagTypeException, TagNotFoundException { + return ((TagByteArray)this.getTag(name, TagByteArray.class)).getValue(); + } + + public String[] getStringArray(String name) throws UnexpectedTagTypeException, TagNotFoundException { + List tags = this.getList(name, TagString.class); + String[] array = new String[tags.size()]; + + for(int i = 0; i < tags.size(); ++i) { + array[i] = ((TagString)tags.get(i)).getValue(); + } + + return array; + } + + public double[] getDoubleArray(String name) throws UnexpectedTagTypeException, TagNotFoundException { + List tags = this.getList(name, TagDouble.class); + double[] array = new double[tags.size()]; + + for(int i = 0; i < tags.size(); ++i) { + array[i] = ((TagDouble)tags.get(i)).getValue(); + } + + return array; + } + + public float[] getFloatArray(String name) throws UnexpectedTagTypeException, TagNotFoundException { + List tags = this.getList(name, TagFloat.class); + float[] array = new float[tags.size()]; + + for(int i = 0; i < tags.size(); ++i) { + array[i] = ((TagFloat)tags.get(i)).getValue(); + } + + return array; + } + + public Map getTags() { + return (new ImmutableMap.Builder()).putAll(this.tags).build(); + } + + public void removeTag(@Nonnull ITag tag) { + Preconditions.checkNotNull(tag, "tag"); + this.tags.remove(tag.getName()); + } + + public void removeTag(@Nonnull String tag) { + Preconditions.checkNotNull(tag, "tag"); + this.tags.remove(tag); + } + + public void setTag(@Nonnull ITag tag) { + Preconditions.checkNotNull(tag, "tag"); + if (this.tags.containsKey(tag)) { + ((ITag)this.tags.get(tag.getName())).setParent((ITagContainer)null); + } + + this.tags.put(tag.getName(), tag); + tag.setParent(this); + } + + public byte getTagID() { + return TagType.COMPOUND.typeID; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + Iterator var3 = this.tags.entrySet().iterator(); + + while(var3.hasNext()) { + Entry tagEntry = (Entry)var3.next(); + outputStream.writeByte(((ITag)tagEntry.getValue()).getTagID()); + ((ITag)tagEntry.getValue()).write(outputStream, false); + } + + outputStream.writeByte(TagType.END.typeID); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagDouble.java b/src/main/com/evilco/mc/nbt/tag/TagDouble.java new file mode 100644 index 0000000..01442c0 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagDouble.java @@ -0,0 +1,37 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagDouble extends AbstractTag { + protected double value; + + public TagDouble(@Nonnull String name, double value) { + super(name); + this.setValue(value); + } + + public TagDouble(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + this.setValue(inputStream.readDouble()); + } + + public byte getTagID() { + return TagType.DOUBLE.typeID; + } + + public double getValue() { + return this.value; + } + + public void setValue(double d) { + this.value = d; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeDouble(this.value); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagFloat.java b/src/main/com/evilco/mc/nbt/tag/TagFloat.java new file mode 100644 index 0000000..3a5a174 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagFloat.java @@ -0,0 +1,37 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagFloat extends AbstractTag { + protected float value; + + public TagFloat(@Nonnull String name, float value) { + super(name); + this.setValue(value); + } + + public TagFloat(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + this.setValue(inputStream.readFloat()); + } + + public byte getTagID() { + return TagType.FLOAT.typeID; + } + + public float getValue() { + return this.value; + } + + public void setValue(float f) { + this.value = f; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeFloat(this.value); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagInteger.java b/src/main/com/evilco/mc/nbt/tag/TagInteger.java new file mode 100644 index 0000000..90144ac --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagInteger.java @@ -0,0 +1,37 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagInteger extends AbstractTag { + protected int value; + + public TagInteger(@Nonnull String name, int value) { + super(name); + this.setValue(value); + } + + public TagInteger(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + this.setValue(inputStream.readInt()); + } + + public byte getTagID() { + return TagType.INTEGER.typeID; + } + + public int getValue() { + return this.value; + } + + public void setValue(int i) { + this.value = i; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeInt(this.value); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagIntegerArray.java b/src/main/com/evilco/mc/nbt/tag/TagIntegerArray.java new file mode 100644 index 0000000..7f7eabe --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagIntegerArray.java @@ -0,0 +1,54 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import com.google.common.base.Preconditions; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagIntegerArray extends AbstractTag { + protected int[] values; + + public TagIntegerArray(@Nonnull String name, @Nonnull int[] values) { + super(name); + this.setValues(values); + } + + public TagIntegerArray(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + int size = inputStream.readInt(); + int[] data = new int[size]; + + for(int i = 0; i < size; ++i) { + data[i] = inputStream.readInt(); + } + + this.values = data; + } + + public byte getTagID() { + return TagType.INTEGER_ARRAY.typeID; + } + + public int[] getValues() { + return this.values; + } + + public void setValues(@Nonnull int[] i) { + Preconditions.checkNotNull(i, "i"); + this.values = i; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeInt(this.values.length); + int[] var3 = this.values; + int var4 = var3.length; + + for(int var5 = 0; var5 < var4; ++var5) { + int i = var3[var5]; + outputStream.writeInt(i); + } + + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagList.java b/src/main/com/evilco/mc/nbt/tag/TagList.java new file mode 100644 index 0000000..c831259 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagList.java @@ -0,0 +1,90 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.error.UnexpectedTagTypeException; +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nonnull; + +public class TagList extends AbstractTag implements IAnonymousTagContainer { + protected List tagList; + + public TagList(@Nonnull String name) { + super(name); + this.tagList = new ArrayList(); + } + + public TagList(@Nonnull String name, @Nonnull List tagList) { + super(name); + Preconditions.checkNotNull(tagList, "tagList"); + this.tagList = tagList; + } + + public TagList(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + this.tagList = new ArrayList(); + byte type = inputStream.readByte(); + TagType tagType = TagType.valueOf(type); + int size = inputStream.readInt(); + if (tagType != TagType.END) { + for(int i = 0; i < size; ++i) { + this.addTag(inputStream.readTag(tagType, true)); + } + + } + } + + public void addTag(@Nonnull ITag tag) { + this.tagList.add(tag); + } + + public List getTags() { + return (new ImmutableList.Builder()).addAll((Iterable)this.tagList).build(); + } + + public List getTags(Class tagClass) throws UnexpectedTagTypeException { + ImmutableList.Builder builder = new ImmutableList.Builder(); + Iterator var3 = this.tagList.iterator(); + + while(var3.hasNext()) { + ITag tag = (ITag)var3.next(); + if (!tagClass.isInstance(tag)) { + throw new UnexpectedTagTypeException("The list entry should be of type " + tagClass.getSimpleName() + ", but is of type " + tag.getClass().getSimpleName()); + } + + builder.add((Object)tag); + } + + return builder.build(); + } + + public byte getTagID() { + return TagType.LIST.typeID; + } + + public void removeTag(@Nonnull ITag tag) { + this.tagList.remove(tag); + } + + public void setTag(int i, @Nonnull ITag tag) { + this.tagList.set(i, tag); + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeByte(this.tagList.size() > 0 ? ((ITag)this.tagList.get(0)).getTagID() : TagType.END.typeID); + outputStream.writeInt(this.tagList.size()); + Iterator var3 = this.tagList.iterator(); + + while(var3.hasNext()) { + ITag tag = (ITag)var3.next(); + tag.write(outputStream, true); + } + + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagLong.java b/src/main/com/evilco/mc/nbt/tag/TagLong.java new file mode 100644 index 0000000..ffa72d9 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagLong.java @@ -0,0 +1,37 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagLong extends AbstractTag { + protected long value; + + public TagLong(@Nonnull String name, long value) { + super(name); + this.setValue(value); + } + + public TagLong(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + this.setValue(inputStream.readLong()); + } + + public byte getTagID() { + return TagType.LONG.typeID; + } + + public long getValue() { + return this.value; + } + + public void setValue(long l) { + this.value = l; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeLong(this.value); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagShort.java b/src/main/com/evilco/mc/nbt/tag/TagShort.java new file mode 100644 index 0000000..b83bcc3 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagShort.java @@ -0,0 +1,37 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagShort extends AbstractTag { + protected short value; + + public TagShort(@Nonnull String name, short value) { + super(name); + this.setValue(value); + } + + public TagShort(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + this.setValue(inputStream.readShort()); + } + + public byte getTagID() { + return TagType.SHORT.typeID; + } + + public short getValue() { + return this.value; + } + + public void setValue(short s) { + this.value = s; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + outputStream.writeShort(this.value); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagString.java b/src/main/com/evilco/mc/nbt/tag/TagString.java new file mode 100644 index 0000000..41f4714 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagString.java @@ -0,0 +1,42 @@ +package com.evilco.mc.nbt.tag; + +import com.evilco.mc.nbt.stream.NbtInputStream; +import com.evilco.mc.nbt.stream.NbtOutputStream; +import java.io.IOException; +import javax.annotation.Nonnull; + +public class TagString extends AbstractTag { + protected String value; + + public TagString(@Nonnull String name, @Nonnull String value) { + super(name); + this.setValue(value); + } + + public TagString(@Nonnull NbtInputStream inputStream, boolean anonymous) throws IOException { + super(inputStream, anonymous); + int size = inputStream.readShort(); + byte[] data = new byte[size]; + inputStream.readFully(data); + this.setValue(new String(data, ITag.STRING_CHARSET)); + } + + public byte getTagID() { + return TagType.STRING.typeID; + } + + public String getValue() { + return this.value; + } + + public void setValue(@Nonnull String s) { + this.value = s; + } + + public void write(NbtOutputStream outputStream, boolean anonymous) throws IOException { + super.write(outputStream, anonymous); + byte[] outputBytes = this.value.getBytes(ITag.STRING_CHARSET); + outputStream.writeShort(outputBytes.length); + outputStream.write(outputBytes); + } +} diff --git a/src/main/com/evilco/mc/nbt/tag/TagType.java b/src/main/com/evilco/mc/nbt/tag/TagType.java new file mode 100644 index 0000000..5468266 --- /dev/null +++ b/src/main/com/evilco/mc/nbt/tag/TagType.java @@ -0,0 +1,45 @@ +package com.evilco.mc.nbt.tag; + +import com.google.common.collect.ImmutableMap; +import java.util.Map; + +public enum TagType { + BYTE(1, TagByte.class), + BYTE_ARRAY(7, TagByteArray.class), + COMPOUND(10, TagCompound.class), + DOUBLE(6, TagDouble.class), + END(0, (Class)null), + FLOAT(5, TagFloat.class), + INTEGER(3, TagInteger.class), + INTEGER_ARRAY(11, TagIntegerArray.class), + LIST(9, TagList.class), + LONG(4, TagLong.class), + SHORT(2, TagShort.class), + STRING(8, TagString.class); + + protected static final Map typeMap; + public final Class tagType; + public final byte typeID; + + private TagType(int typeID, Class type) { + this.typeID = (byte)typeID; + this.tagType = type; + } + + public static TagType valueOf(byte typeID) { + return (TagType)typeMap.get(typeID); + } + + static { + ImmutableMap.Builder mapBuilder = new ImmutableMap.Builder(); + TagType[] var1 = values(); + int var2 = var1.length; + + for(int var3 = 0; var3 < var2; ++var3) { + TagType type = var1[var3]; + mapBuilder.put(type.typeID, type); + } + + typeMap = mapBuilder.build(); + } +} diff --git a/src/main/com/google/common/annotations/Beta.java b/src/main/com/google/common/annotations/Beta.java new file mode 100644 index 0000000..0dfe364 --- /dev/null +++ b/src/main/com/google/common/annotations/Beta.java @@ -0,0 +1,14 @@ +package com.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +@Documented +@GwtCompatible +public @interface Beta { +} diff --git a/src/main/com/google/common/annotations/GwtCompatible.java b/src/main/com/google/common/annotations/GwtCompatible.java new file mode 100644 index 0000000..c4cd899 --- /dev/null +++ b/src/main/com/google/common/annotations/GwtCompatible.java @@ -0,0 +1,17 @@ +package com.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.METHOD}) +@Documented +@GwtCompatible +public @interface GwtCompatible { + boolean serializable() default false; + + boolean emulated() default false; +} diff --git a/src/main/com/google/common/annotations/GwtIncompatible.java b/src/main/com/google/common/annotations/GwtIncompatible.java new file mode 100644 index 0000000..276f59b --- /dev/null +++ b/src/main/com/google/common/annotations/GwtIncompatible.java @@ -0,0 +1,15 @@ +package com.google.common.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) +@Documented +@GwtCompatible +public @interface GwtIncompatible { + String value(); +} diff --git a/src/main/com/google/common/annotations/VisibleForTesting.java b/src/main/com/google/common/annotations/VisibleForTesting.java new file mode 100644 index 0000000..02469f7 --- /dev/null +++ b/src/main/com/google/common/annotations/VisibleForTesting.java @@ -0,0 +1,5 @@ +package com.google.common.annotations; + +@GwtCompatible +public @interface VisibleForTesting { +} diff --git a/src/main/com/google/common/base/Absent.java b/src/main/com/google/common/base/Absent.java new file mode 100644 index 0000000..ef0cddc --- /dev/null +++ b/src/main/com/google/common/base/Absent.java @@ -0,0 +1,69 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +final class Absent extends Optional { + static final Absent INSTANCE = new Absent(); + private static final long serialVersionUID = 0L; + + static Optional withType() { + return INSTANCE; + } + + private Absent() { + } + + public boolean isPresent() { + return false; + } + + public T get() { + throw new IllegalStateException("Optional.get() cannot be called on an absent value"); + } + + public T or(T defaultValue) { + return Preconditions.checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); + } + + public Optional or(Optional secondChoice) { + return (Optional)Preconditions.checkNotNull(secondChoice); + } + + public T or(Supplier supplier) { + return Preconditions.checkNotNull(supplier.get(), "use Optional.orNull() instead of a Supplier that returns null"); + } + + @Nullable + public T orNull() { + return null; + } + + public Set asSet() { + return Collections.emptySet(); + } + + public Optional transform(Function function) { + Preconditions.checkNotNull(function); + return Optional.absent(); + } + + public boolean equals(@Nullable Object object) { + return object == this; + } + + public int hashCode() { + return 1502476572; + } + + public String toString() { + return "Optional.absent()"; + } + + private Object readResolve() { + return INSTANCE; + } +} diff --git a/src/main/com/google/common/base/AbstractIterator.java b/src/main/com/google/common/base/AbstractIterator.java new file mode 100644 index 0000000..4908c3d --- /dev/null +++ b/src/main/com/google/common/base/AbstractIterator.java @@ -0,0 +1,67 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; +import java.util.NoSuchElementException; + +@GwtCompatible +abstract class AbstractIterator implements Iterator { + private AbstractIterator.State state; + private T next; + + protected AbstractIterator() { + this.state = AbstractIterator.State.NOT_READY; + } + + protected abstract T computeNext(); + + protected final T endOfData() { + this.state = AbstractIterator.State.DONE; + return null; + } + + public final boolean hasNext() { + Preconditions.checkState(this.state != AbstractIterator.State.FAILED); + switch(this.state) { + case DONE: + return false; + case READY: + return true; + default: + return this.tryToComputeNext(); + } + } + + private boolean tryToComputeNext() { + this.state = AbstractIterator.State.FAILED; + this.next = this.computeNext(); + if (this.state != AbstractIterator.State.DONE) { + this.state = AbstractIterator.State.READY; + return true; + } else { + return false; + } + } + + public final T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + this.state = AbstractIterator.State.NOT_READY; + T result = this.next; + this.next = null; + return result; + } + } + + public final void remove() { + throw new UnsupportedOperationException(); + } + + private static enum State { + READY, + NOT_READY, + DONE, + FAILED; + } +} diff --git a/src/main/com/google/common/base/Ascii.java b/src/main/com/google/common/base/Ascii.java new file mode 100644 index 0000000..96a86f2 --- /dev/null +++ b/src/main/com/google/common/base/Ascii.java @@ -0,0 +1,183 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import javax.annotation.CheckReturnValue; + +@GwtCompatible +public final class Ascii { + public static final byte NUL = 0; + public static final byte SOH = 1; + public static final byte STX = 2; + public static final byte ETX = 3; + public static final byte EOT = 4; + public static final byte ENQ = 5; + public static final byte ACK = 6; + public static final byte BEL = 7; + public static final byte BS = 8; + public static final byte HT = 9; + public static final byte LF = 10; + public static final byte NL = 10; + public static final byte VT = 11; + public static final byte FF = 12; + public static final byte CR = 13; + public static final byte SO = 14; + public static final byte SI = 15; + public static final byte DLE = 16; + public static final byte DC1 = 17; + public static final byte XON = 17; + public static final byte DC2 = 18; + public static final byte DC3 = 19; + public static final byte XOFF = 19; + public static final byte DC4 = 20; + public static final byte NAK = 21; + public static final byte SYN = 22; + public static final byte ETB = 23; + public static final byte CAN = 24; + public static final byte EM = 25; + public static final byte SUB = 26; + public static final byte ESC = 27; + public static final byte FS = 28; + public static final byte GS = 29; + public static final byte RS = 30; + public static final byte US = 31; + public static final byte SP = 32; + public static final byte SPACE = 32; + public static final byte DEL = 127; + public static final char MIN = '\u0000'; + public static final char MAX = '\u007f'; + + private Ascii() { + } + + public static String toLowerCase(String string) { + int length = string.length(); + + for(int i = 0; i < length; ++i) { + if (isUpperCase(string.charAt(i))) { + char[] chars; + for(chars = string.toCharArray(); i < length; ++i) { + char c = chars[i]; + if (isUpperCase(c)) { + chars[i] = (char)(c ^ 32); + } + } + + return String.valueOf(chars); + } + } + + return string; + } + + public static String toLowerCase(CharSequence chars) { + if (chars instanceof String) { + return toLowerCase((String)chars); + } else { + int length = chars.length(); + StringBuilder builder = new StringBuilder(length); + + for(int i = 0; i < length; ++i) { + builder.append(toLowerCase(chars.charAt(i))); + } + + return builder.toString(); + } + } + + public static char toLowerCase(char c) { + return isUpperCase(c) ? (char)(c ^ 32) : c; + } + + public static String toUpperCase(String string) { + int length = string.length(); + + for(int i = 0; i < length; ++i) { + if (isLowerCase(string.charAt(i))) { + char[] chars; + for(chars = string.toCharArray(); i < length; ++i) { + char c = chars[i]; + if (isLowerCase(c)) { + chars[i] = (char)(c & 95); + } + } + + return String.valueOf(chars); + } + } + + return string; + } + + public static String toUpperCase(CharSequence chars) { + if (chars instanceof String) { + return toUpperCase((String)chars); + } else { + int length = chars.length(); + StringBuilder builder = new StringBuilder(length); + + for(int i = 0; i < length; ++i) { + builder.append(toUpperCase(chars.charAt(i))); + } + + return builder.toString(); + } + } + + public static char toUpperCase(char c) { + return isLowerCase(c) ? (char)(c & 95) : c; + } + + public static boolean isLowerCase(char c) { + return c >= 'a' && c <= 'z'; + } + + public static boolean isUpperCase(char c) { + return c >= 'A' && c <= 'Z'; + } + + @CheckReturnValue + @Beta + public static String truncate(CharSequence seq, int maxLength, String truncationIndicator) { + Preconditions.checkNotNull(seq); + int truncationLength = maxLength - truncationIndicator.length(); + Preconditions.checkArgument(truncationLength >= 0, "maxLength (%s) must be >= length of the truncation indicator (%s)", maxLength, truncationIndicator.length()); + if (((CharSequence)seq).length() <= maxLength) { + String string = ((CharSequence)seq).toString(); + if (string.length() <= maxLength) { + return string; + } + + seq = string; + } + + return (new StringBuilder(maxLength)).append((CharSequence)seq, 0, truncationLength).append(truncationIndicator).toString(); + } + + @Beta + public static boolean equalsIgnoreCase(CharSequence s1, CharSequence s2) { + int length = s1.length(); + if (s1 == s2) { + return true; + } else if (length != s2.length()) { + return false; + } else { + for(int i = 0; i < length; ++i) { + char c1 = s1.charAt(i); + char c2 = s2.charAt(i); + if (c1 != c2) { + int alphaIndex = getAlphaIndex(c1); + if (alphaIndex >= 26 || alphaIndex != getAlphaIndex(c2)) { + return false; + } + } + } + + return true; + } + } + + private static int getAlphaIndex(char c) { + return (char)((c | 32) - 97); + } +} diff --git a/src/main/com/google/common/base/CaseFormat.java b/src/main/com/google/common/base/CaseFormat.java new file mode 100644 index 0000000..4b000c8 --- /dev/null +++ b/src/main/com/google/common/base/CaseFormat.java @@ -0,0 +1,154 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import javax.annotation.Nullable; + +@GwtCompatible +public enum CaseFormat { + LOWER_HYPHEN(CharMatcher.is('-'), "-") { + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + String convert(CaseFormat format, String s) { + if (format == LOWER_UNDERSCORE) { + return s.replace('-', '_'); + } else { + return format == UPPER_UNDERSCORE ? Ascii.toUpperCase(s.replace('-', '_')) : super.convert(format, s); + } + } + }, + LOWER_UNDERSCORE(CharMatcher.is('_'), "_") { + String normalizeWord(String word) { + return Ascii.toLowerCase(word); + } + + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return s.replace('_', '-'); + } else { + return format == UPPER_UNDERSCORE ? Ascii.toUpperCase(s) : super.convert(format, s); + } + } + }, + LOWER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + String normalizeWord(String word) { + return CaseFormat.firstCharOnlyToUpper(word); + } + }, + UPPER_CAMEL(CharMatcher.inRange('A', 'Z'), "") { + String normalizeWord(String word) { + return CaseFormat.firstCharOnlyToUpper(word); + } + }, + UPPER_UNDERSCORE(CharMatcher.is('_'), "_") { + String normalizeWord(String word) { + return Ascii.toUpperCase(word); + } + + String convert(CaseFormat format, String s) { + if (format == LOWER_HYPHEN) { + return Ascii.toLowerCase(s.replace('_', '-')); + } else { + return format == LOWER_UNDERSCORE ? Ascii.toLowerCase(s) : super.convert(format, s); + } + } + }; + + private final CharMatcher wordBoundary; + private final String wordSeparator; + + private CaseFormat(CharMatcher wordBoundary, String wordSeparator) { + this.wordBoundary = wordBoundary; + this.wordSeparator = wordSeparator; + } + + public final String to(CaseFormat format, String str) { + Preconditions.checkNotNull(format); + Preconditions.checkNotNull(str); + return format == this ? str : this.convert(format, str); + } + + String convert(CaseFormat format, String s) { + StringBuilder out = null; + int i = 0; + int j = -1; + + while(true) { + ++j; + if ((j = this.wordBoundary.indexIn(s, j)) == -1) { + return i == 0 ? format.normalizeFirstWord(s) : out.append(format.normalizeWord(s.substring(i))).toString(); + } + + if (i == 0) { + out = new StringBuilder(s.length() + 4 * this.wordSeparator.length()); + out.append(format.normalizeFirstWord(s.substring(i, j))); + } else { + out.append(format.normalizeWord(s.substring(i, j))); + } + + out.append(format.wordSeparator); + i = j + this.wordSeparator.length(); + } + } + + @Beta + public Converter converterTo(CaseFormat targetFormat) { + return new CaseFormat.StringConverter(this, targetFormat); + } + + abstract String normalizeWord(String var1); + + private String normalizeFirstWord(String word) { + return this == LOWER_CAMEL ? Ascii.toLowerCase(word) : this.normalizeWord(word); + } + + 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(); + } + + // $FF: synthetic method + CaseFormat(CharMatcher x2, String x3, Object x4) { + this(x2, x3); + } + + private static final class StringConverter extends Converter implements Serializable { + private final CaseFormat sourceFormat; + private final CaseFormat targetFormat; + private static final long serialVersionUID = 0L; + + StringConverter(CaseFormat sourceFormat, CaseFormat targetFormat) { + this.sourceFormat = (CaseFormat)Preconditions.checkNotNull(sourceFormat); + this.targetFormat = (CaseFormat)Preconditions.checkNotNull(targetFormat); + } + + protected String doForward(String s) { + return s == null ? null : this.sourceFormat.to(this.targetFormat, s); + } + + protected String doBackward(String s) { + return s == null ? null : this.targetFormat.to(this.sourceFormat, s); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof CaseFormat.StringConverter)) { + return false; + } else { + CaseFormat.StringConverter that = (CaseFormat.StringConverter)object; + return this.sourceFormat.equals(that.sourceFormat) && this.targetFormat.equals(that.targetFormat); + } + } + + public int hashCode() { + return this.sourceFormat.hashCode() ^ this.targetFormat.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.sourceFormat)); + String var2 = String.valueOf(String.valueOf(this.targetFormat)); + return (new StringBuilder(14 + var1.length() + var2.length())).append(var1).append(".converterTo(").append(var2).append(")").toString(); + } + } +} diff --git a/src/main/com/google/common/base/CharMatcher.java b/src/main/com/google/common/base/CharMatcher.java new file mode 100644 index 0000000..0c3ef37 --- /dev/null +++ b/src/main/com/google/common/base/CharMatcher.java @@ -0,0 +1,988 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.util.Arrays; +import java.util.BitSet; +import javax.annotation.CheckReturnValue; + +@Beta +@GwtCompatible( + emulated = true +) +public abstract class CharMatcher implements Predicate { + public static final CharMatcher BREAKING_WHITESPACE = new CharMatcher() { + public boolean matches(char c) { + switch(c) { + case '\t': + case '\n': + case '\u000b': + case '\f': + case '\r': + case ' ': + case '\u0085': + case ' ': + case '\u2028': + case '\u2029': + case ' ': + case ' ': + return true; + case ' ': + return false; + default: + return c >= 8192 && c <= 8202; + } + } + + public String toString() { + return "CharMatcher.BREAKING_WHITESPACE"; + } + }; + public static final CharMatcher ASCII = inRange('\u0000', '\u007f', "CharMatcher.ASCII"); + private static final String ZEROES = "0٠۰߀०০੦૦୦௦౦೦൦๐໐༠၀႐០᠐᥆᧐᭐᮰᱀᱐꘠꣐꤀꩐0"; + private static final String NINES; + public static final CharMatcher DIGIT; + public static final CharMatcher JAVA_DIGIT; + public static final CharMatcher JAVA_LETTER; + public static final CharMatcher JAVA_LETTER_OR_DIGIT; + public static final CharMatcher JAVA_UPPER_CASE; + public static final CharMatcher JAVA_LOWER_CASE; + public static final CharMatcher JAVA_ISO_CONTROL; + public static final CharMatcher INVISIBLE; + public static final CharMatcher SINGLE_WIDTH; + public static final CharMatcher ANY; + public static final CharMatcher NONE; + final String description; + private static final int DISTINCT_CHARS = 65536; + static final String WHITESPACE_TABLE = "  \r\u0085    \u2029\u000b      \t     \f     \u2028\n  "; + static final int WHITESPACE_MULTIPLIER = 1682554634; + static final int WHITESPACE_SHIFT; + public static final CharMatcher WHITESPACE; + + private static String showCharacter(char c) { + String hex = "0123456789ABCDEF"; + char[] tmp = new char[]{'\\', 'u', '\u0000', '\u0000', '\u0000', '\u0000'}; + + for(int i = 0; i < 4; ++i) { + tmp[5 - i] = hex.charAt(c & 15); + c = (char)(c >> 4); + } + + return String.copyValueOf(tmp); + } + + public static CharMatcher is(final char match) { + String var2 = String.valueOf(String.valueOf(showCharacter(match))); + String description = (new StringBuilder(18 + var2.length())).append("CharMatcher.is('").append(var2).append("')").toString(); + return new CharMatcher.FastMatcher(description) { + public boolean matches(char c) { + return c == match; + } + + public String replaceFrom(CharSequence sequence, char replacement) { + return sequence.toString().replace(match, replacement); + } + + public CharMatcher and(CharMatcher other) { + return (CharMatcher)(other.matches(match) ? this : NONE); + } + + public CharMatcher or(CharMatcher other) { + return other.matches(match) ? other : super.or(other); + } + + public CharMatcher negate() { + return isNot(match); + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + table.set(match); + } + }; + } + + public static CharMatcher isNot(final char match) { + String var2 = String.valueOf(String.valueOf(showCharacter(match))); + String description = (new StringBuilder(21 + var2.length())).append("CharMatcher.isNot('").append(var2).append("')").toString(); + return new CharMatcher.FastMatcher(description) { + public boolean matches(char c) { + return c != match; + } + + public CharMatcher and(CharMatcher other) { + return other.matches(match) ? super.and(other) : other; + } + + public CharMatcher or(CharMatcher other) { + return (CharMatcher)(other.matches(match) ? ANY : this); + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + table.set(0, match); + table.set(match + 1, 65536); + } + + public CharMatcher negate() { + return is(match); + } + }; + } + + public static CharMatcher anyOf(CharSequence sequence) { + switch(sequence.length()) { + case 0: + return NONE; + case 1: + return is(sequence.charAt(0)); + case 2: + return isEither(sequence.charAt(0), sequence.charAt(1)); + default: + final char[] chars = sequence.toString().toCharArray(); + Arrays.sort(chars); + StringBuilder description = new StringBuilder("CharMatcher.anyOf(\""); + char[] arr$ = chars; + int len$ = chars.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char c = arr$[i$]; + description.append(showCharacter(c)); + } + + description.append("\")"); + return new CharMatcher(description.toString()) { + public boolean matches(char c) { + return Arrays.binarySearch(chars, c) >= 0; + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + char[] arr$ = chars; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char c = arr$[i$]; + table.set(c); + } + + } + }; + } + } + + private static CharMatcher isEither(final char match1, final char match2) { + String var3 = String.valueOf(String.valueOf(showCharacter(match1))); + String var4 = String.valueOf(String.valueOf(showCharacter(match2))); + String description = (new StringBuilder(21 + var3.length() + var4.length())).append("CharMatcher.anyOf(\"").append(var3).append(var4).append("\")").toString(); + return new CharMatcher.FastMatcher(description) { + public boolean matches(char c) { + return c == match1 || c == match2; + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + table.set(match1); + table.set(match2); + } + }; + } + + public static CharMatcher noneOf(CharSequence sequence) { + return anyOf(sequence).negate(); + } + + public static CharMatcher inRange(char startInclusive, char endInclusive) { + Preconditions.checkArgument(endInclusive >= startInclusive); + String var3 = String.valueOf(String.valueOf(showCharacter(startInclusive))); + String var4 = String.valueOf(String.valueOf(showCharacter(endInclusive))); + String description = (new StringBuilder(27 + var3.length() + var4.length())).append("CharMatcher.inRange('").append(var3).append("', '").append(var4).append("')").toString(); + return inRange(startInclusive, endInclusive, description); + } + + static CharMatcher inRange(final char startInclusive, final char endInclusive, String description) { + return new CharMatcher.FastMatcher(description) { + public boolean matches(char c) { + return startInclusive <= c && c <= endInclusive; + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + table.set(startInclusive, endInclusive + 1); + } + }; + } + + public static CharMatcher forPredicate(final Predicate predicate) { + Preconditions.checkNotNull(predicate); + if (predicate instanceof CharMatcher) { + return (CharMatcher)predicate; + } else { + String var2 = String.valueOf(String.valueOf(predicate)); + String description = (new StringBuilder(26 + var2.length())).append("CharMatcher.forPredicate(").append(var2).append(")").toString(); + return new CharMatcher(description) { + public boolean matches(char c) { + return predicate.apply(c); + } + + public boolean apply(Character character) { + return predicate.apply(Preconditions.checkNotNull(character)); + } + }; + } + } + + CharMatcher(String description) { + this.description = description; + } + + protected CharMatcher() { + this.description = super.toString(); + } + + public abstract boolean matches(char var1); + + public CharMatcher negate() { + return new CharMatcher.NegatedMatcher(this); + } + + public CharMatcher and(CharMatcher other) { + return new CharMatcher.And(this, (CharMatcher)Preconditions.checkNotNull(other)); + } + + public CharMatcher or(CharMatcher other) { + return new CharMatcher.Or(this, (CharMatcher)Preconditions.checkNotNull(other)); + } + + public CharMatcher precomputed() { + return Platform.precomputeCharMatcher(this); + } + + CharMatcher withToString(String description) { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible("java.util.BitSet") + CharMatcher precomputedInternal() { + BitSet table = new BitSet(); + this.setBits(table); + int totalCharacters = table.cardinality(); + if (totalCharacters * 2 <= 65536) { + return precomputedPositive(totalCharacters, table, this.description); + } else { + table.flip(0, 65536); + int negatedCharacters = 65536 - totalCharacters; + String suffix = ".negate()"; + String var10000; + if (this.description.endsWith(suffix)) { + var10000 = this.description.substring(0, this.description.length() - suffix.length()); + } else { + var10000 = String.valueOf(this.description); + String var10001 = String.valueOf(suffix); + if (var10001.length() != 0) { + var10000 = var10000.concat(var10001); + } else { + String var10002 = new String; + var10001 = var10000; + var10000 = var10002; + var10002.(var10001); + } + } + + String negatedDescription = var10000; + return new CharMatcher.NegatedFastMatcher(this.toString(), precomputedPositive(negatedCharacters, table, negatedDescription)); + } + } + + @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 (CharMatcher)(isSmall(totalCharacters, table.length()) ? SmallCharMatcher.from(table, description) : new CharMatcher.BitSetMatcher(table, description)); + } + } + + @GwtIncompatible("SmallCharMatcher") + private static boolean isSmall(int totalCharacters, int tableLength) { + return totalCharacters <= 1023 && tableLength > totalCharacters * 4 * 16; + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + for(int c = 65535; c >= 0; --c) { + if (this.matches((char)c)) { + table.set(c); + } + } + + } + + public boolean matchesAnyOf(CharSequence sequence) { + return !this.matchesNoneOf(sequence); + } + + public boolean matchesAllOf(CharSequence sequence) { + for(int i = sequence.length() - 1; i >= 0; --i) { + if (!this.matches(sequence.charAt(i))) { + return false; + } + } + + return true; + } + + public boolean matchesNoneOf(CharSequence sequence) { + return this.indexIn(sequence) == -1; + } + + public int indexIn(CharSequence sequence) { + int length = sequence.length(); + + for(int i = 0; i < length; ++i) { + if (this.matches(sequence.charAt(i))) { + return i; + } + } + + return -1; + } + + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + Preconditions.checkPositionIndex(start, length); + + for(int i = start; i < length; ++i) { + if (this.matches(sequence.charAt(i))) { + return i; + } + } + + return -1; + } + + public int lastIndexIn(CharSequence sequence) { + for(int i = sequence.length() - 1; i >= 0; --i) { + if (this.matches(sequence.charAt(i))) { + return i; + } + } + + return -1; + } + + public int countIn(CharSequence sequence) { + int count = 0; + + for(int i = 0; i < sequence.length(); ++i) { + if (this.matches(sequence.charAt(i))) { + ++count; + } + } + + return count; + } + + @CheckReturnValue + public String removeFrom(CharSequence sequence) { + String string = sequence.toString(); + int pos = this.indexIn(string); + if (pos == -1) { + return string; + } else { + char[] chars = string.toCharArray(); + int spread = 1; + + label25: + while(true) { + ++pos; + + while(pos != chars.length) { + if (this.matches(chars[pos])) { + ++spread; + continue label25; + } + + chars[pos - spread] = chars[pos]; + ++pos; + } + + return new String(chars, 0, pos - spread); + } + } + } + + @CheckReturnValue + public String retainFrom(CharSequence sequence) { + return this.negate().removeFrom(sequence); + } + + @CheckReturnValue + public String replaceFrom(CharSequence sequence, char replacement) { + String string = sequence.toString(); + int pos = this.indexIn(string); + if (pos == -1) { + return string; + } else { + char[] chars = string.toCharArray(); + chars[pos] = replacement; + + for(int i = pos + 1; i < chars.length; ++i) { + if (this.matches(chars[i])) { + chars[i] = replacement; + } + } + + return new String(chars); + } + } + + @CheckReturnValue + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + int replacementLen = replacement.length(); + if (replacementLen == 0) { + return this.removeFrom(sequence); + } else if (replacementLen == 1) { + return this.replaceFrom(sequence, replacement.charAt(0)); + } else { + String string = sequence.toString(); + int pos = this.indexIn(string); + if (pos == -1) { + return string; + } else { + int len = string.length(); + StringBuilder buf = new StringBuilder(len * 3 / 2 + 16); + int oldpos = 0; + + do { + buf.append(string, oldpos, pos); + buf.append(replacement); + oldpos = pos + 1; + pos = this.indexIn(string, oldpos); + } while(pos != -1); + + buf.append(string, oldpos, len); + return buf.toString(); + } + } + } + + @CheckReturnValue + public String trimFrom(CharSequence sequence) { + int len = sequence.length(); + + int first; + for(first = 0; first < len && this.matches(sequence.charAt(first)); ++first) { + } + + int last; + for(last = len - 1; last > first && this.matches(sequence.charAt(last)); --last) { + } + + return sequence.subSequence(first, last + 1).toString(); + } + + @CheckReturnValue + public String trimLeadingFrom(CharSequence sequence) { + int len = sequence.length(); + + for(int first = 0; first < len; ++first) { + if (!this.matches(sequence.charAt(first))) { + return sequence.subSequence(first, len).toString(); + } + } + + return ""; + } + + @CheckReturnValue + public String trimTrailingFrom(CharSequence sequence) { + int len = sequence.length(); + + for(int last = len - 1; last >= 0; --last) { + if (!this.matches(sequence.charAt(last))) { + return sequence.subSequence(0, last + 1).toString(); + } + } + + return ""; + } + + @CheckReturnValue + public String collapseFrom(CharSequence sequence, char replacement) { + int len = sequence.length(); + + for(int i = 0; i < len; ++i) { + char c = sequence.charAt(i); + if (this.matches(c)) { + if (c != replacement || i != len - 1 && this.matches(sequence.charAt(i + 1))) { + StringBuilder builder = (new StringBuilder(len)).append(sequence.subSequence(0, i)).append(replacement); + return this.finishCollapseFrom(sequence, i + 1, len, replacement, builder, true); + } + + ++i; + } + } + + return sequence.toString(); + } + + @CheckReturnValue + public String trimAndCollapseFrom(CharSequence sequence, char replacement) { + int len = sequence.length(); + + int first; + for(first = 0; first < len && this.matches(sequence.charAt(first)); ++first) { + } + + int last; + for(last = len - 1; last > first && this.matches(sequence.charAt(last)); --last) { + } + + return first == 0 && last == len - 1 ? this.collapseFrom(sequence, replacement) : this.finishCollapseFrom(sequence, first, last + 1, replacement, new StringBuilder(last + 1 - first), false); + } + + private String finishCollapseFrom(CharSequence sequence, int start, int end, char replacement, StringBuilder builder, boolean inMatchingGroup) { + for(int i = start; i < end; ++i) { + char c = sequence.charAt(i); + if (this.matches(c)) { + if (!inMatchingGroup) { + builder.append(replacement); + inMatchingGroup = true; + } + } else { + builder.append(c); + inMatchingGroup = false; + } + } + + return builder.toString(); + } + + /** @deprecated */ + @Deprecated + public boolean apply(Character character) { + return this.matches(character); + } + + public String toString() { + return this.description; + } + + static { + StringBuilder builder = new StringBuilder("0٠۰߀०০੦૦୦௦౦೦൦๐໐༠၀႐០᠐᥆᧐᭐᮰᱀᱐꘠꣐꤀꩐0".length()); + + for(int i = 0; i < "0٠۰߀०০੦૦୦௦౦೦൦๐໐༠၀႐០᠐᥆᧐᭐᮰᱀᱐꘠꣐꤀꩐0".length(); ++i) { + builder.append((char)("0٠۰߀०০੦૦୦௦౦೦൦๐໐༠၀႐០᠐᥆᧐᭐᮰᱀᱐꘠꣐꤀꩐0".charAt(i) + 9)); + } + + NINES = builder.toString(); + DIGIT = new CharMatcher.RangesMatcher("CharMatcher.DIGIT", "0٠۰߀०০੦૦୦௦౦೦൦๐໐༠၀႐០᠐᥆᧐᭐᮰᱀᱐꘠꣐꤀꩐0".toCharArray(), NINES.toCharArray()); + JAVA_DIGIT = new CharMatcher("CharMatcher.JAVA_DIGIT") { + public boolean matches(char c) { + return Character.isDigit(c); + } + }; + JAVA_LETTER = new CharMatcher("CharMatcher.JAVA_LETTER") { + public boolean matches(char c) { + return Character.isLetter(c); + } + }; + JAVA_LETTER_OR_DIGIT = new CharMatcher("CharMatcher.JAVA_LETTER_OR_DIGIT") { + public boolean matches(char c) { + return Character.isLetterOrDigit(c); + } + }; + JAVA_UPPER_CASE = new CharMatcher("CharMatcher.JAVA_UPPER_CASE") { + public boolean matches(char c) { + return Character.isUpperCase(c); + } + }; + JAVA_LOWER_CASE = new CharMatcher("CharMatcher.JAVA_LOWER_CASE") { + public boolean matches(char c) { + return Character.isLowerCase(c); + } + }; + JAVA_ISO_CONTROL = inRange('\u0000', '\u001f').or(inRange('\u007f', '\u009f')).withToString("CharMatcher.JAVA_ISO_CONTROL"); + INVISIBLE = new CharMatcher.RangesMatcher("CharMatcher.INVISIBLE", "\u0000\u007f\u00ad\u0600\u061c\u06dd\u070f ᠎ \u2028 \u2066\u2067\u2068\u2069\u206a \ud800\ufeff\ufff9\ufffa".toCharArray(), "  \u00ad\u0604\u061c\u06dd\u070f ᠎\u200f \u2064\u2066\u2067\u2068\u2069\u206f \uf8ff\ufeff\ufff9\ufffb".toCharArray()); + SINGLE_WIDTH = new CharMatcher.RangesMatcher("CharMatcher.SINGLE_WIDTH", "\u0000־א׳\u0600ݐ\u0e00Ḁ℀ﭐﹰ。".toCharArray(), "ӹ־ת״ۿݿ\u0e7f₯℺\ufdff\ufeffᅵ".toCharArray()); + ANY = new CharMatcher.FastMatcher("CharMatcher.ANY") { + public boolean matches(char c) { + return true; + } + + public int indexIn(CharSequence sequence) { + return sequence.length() == 0 ? -1 : 0; + } + + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + Preconditions.checkPositionIndex(start, length); + return start == length ? -1 : start; + } + + public int lastIndexIn(CharSequence sequence) { + return sequence.length() - 1; + } + + public boolean matchesAllOf(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return true; + } + + public boolean matchesNoneOf(CharSequence sequence) { + return sequence.length() == 0; + } + + public String removeFrom(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return ""; + } + + public String replaceFrom(CharSequence sequence, char replacement) { + char[] array = new char[sequence.length()]; + Arrays.fill(array, replacement); + return new String(array); + } + + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + StringBuilder retval = new StringBuilder(sequence.length() * replacement.length()); + + for(int i = 0; i < sequence.length(); ++i) { + retval.append(replacement); + } + + return retval.toString(); + } + + public String collapseFrom(CharSequence sequence, char replacement) { + return sequence.length() == 0 ? "" : String.valueOf(replacement); + } + + public String trimFrom(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return ""; + } + + public int countIn(CharSequence sequence) { + return sequence.length(); + } + + public CharMatcher and(CharMatcher other) { + return (CharMatcher)Preconditions.checkNotNull(other); + } + + public CharMatcher or(CharMatcher other) { + Preconditions.checkNotNull(other); + return this; + } + + public CharMatcher negate() { + return NONE; + } + }; + NONE = new CharMatcher.FastMatcher("CharMatcher.NONE") { + public boolean matches(char c) { + return false; + } + + public int indexIn(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return -1; + } + + public int indexIn(CharSequence sequence, int start) { + int length = sequence.length(); + Preconditions.checkPositionIndex(start, length); + return -1; + } + + public int lastIndexIn(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return -1; + } + + public boolean matchesAllOf(CharSequence sequence) { + return sequence.length() == 0; + } + + public boolean matchesNoneOf(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return true; + } + + public String removeFrom(CharSequence sequence) { + return sequence.toString(); + } + + public String replaceFrom(CharSequence sequence, char replacement) { + return sequence.toString(); + } + + public String replaceFrom(CharSequence sequence, CharSequence replacement) { + Preconditions.checkNotNull(replacement); + return sequence.toString(); + } + + public String collapseFrom(CharSequence sequence, char replacement) { + return sequence.toString(); + } + + public String trimFrom(CharSequence sequence) { + return sequence.toString(); + } + + public String trimLeadingFrom(CharSequence sequence) { + return sequence.toString(); + } + + public String trimTrailingFrom(CharSequence sequence) { + return sequence.toString(); + } + + public int countIn(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return 0; + } + + public CharMatcher and(CharMatcher other) { + Preconditions.checkNotNull(other); + return this; + } + + public CharMatcher or(CharMatcher other) { + return (CharMatcher)Preconditions.checkNotNull(other); + } + + public CharMatcher negate() { + return ANY; + } + }; + WHITESPACE_SHIFT = Integer.numberOfLeadingZeros("  \r\u0085    \u2029\u000b      \t     \f     \u2028\n  ".length() - 1); + WHITESPACE = new CharMatcher.FastMatcher("WHITESPACE") { + public boolean matches(char c) { + return "  \r\u0085    \u2029\u000b      \t     \f     \u2028\n  ".charAt(1682554634 * c >>> WHITESPACE_SHIFT) == c; + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + for(int i = 0; i < "  \r\u0085    \u2029\u000b      \t     \f     \u2028\n  ".length(); ++i) { + table.set("  \r\u0085    \u2029\u000b      \t     \f     \u2028\n  ".charAt(i)); + } + + } + }; + } + + @GwtIncompatible("java.util.BitSet") + private static class BitSetMatcher extends CharMatcher.FastMatcher { + private final BitSet table; + + private BitSetMatcher(BitSet table, String description) { + super(description); + if (table.length() + 64 < table.size()) { + table = (BitSet)table.clone(); + } + + this.table = table; + } + + public boolean matches(char c) { + return this.table.get(c); + } + + void setBits(BitSet bitSet) { + bitSet.or(this.table); + } + + // $FF: synthetic method + BitSetMatcher(BitSet x0, String x1, Object x2) { + this(x0, x1); + } + } + + static final class NegatedFastMatcher extends CharMatcher.NegatedMatcher { + NegatedFastMatcher(CharMatcher original) { + super(original); + } + + NegatedFastMatcher(String toString, CharMatcher original) { + super(toString, original); + } + + public final CharMatcher precomputed() { + return this; + } + + CharMatcher withToString(String description) { + return new CharMatcher.NegatedFastMatcher(description, this.original); + } + } + + abstract static class FastMatcher extends CharMatcher { + FastMatcher() { + } + + FastMatcher(String description) { + super(description); + } + + public final CharMatcher precomputed() { + return this; + } + + public CharMatcher negate() { + return new CharMatcher.NegatedFastMatcher(this); + } + } + + private static class Or extends CharMatcher { + final CharMatcher first; + final CharMatcher second; + + Or(CharMatcher a, CharMatcher b, String description) { + super(description); + this.first = (CharMatcher)Preconditions.checkNotNull(a); + this.second = (CharMatcher)Preconditions.checkNotNull(b); + } + + Or(CharMatcher a, CharMatcher b) { + String var3 = String.valueOf(String.valueOf(a)); + String var4 = String.valueOf(String.valueOf(b)); + this(a, b, (new StringBuilder(18 + var3.length() + var4.length())).append("CharMatcher.or(").append(var3).append(", ").append(var4).append(")").toString()); + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + this.first.setBits(table); + this.second.setBits(table); + } + + public boolean matches(char c) { + return this.first.matches(c) || this.second.matches(c); + } + + CharMatcher withToString(String description) { + return new CharMatcher.Or(this.first, this.second, description); + } + } + + private static class And extends CharMatcher { + final CharMatcher first; + final CharMatcher second; + + And(CharMatcher a, CharMatcher b) { + String var3 = String.valueOf(String.valueOf(a)); + String var4 = String.valueOf(String.valueOf(b)); + this(a, b, (new StringBuilder(19 + var3.length() + var4.length())).append("CharMatcher.and(").append(var3).append(", ").append(var4).append(")").toString()); + } + + And(CharMatcher a, CharMatcher b, String description) { + super(description); + this.first = (CharMatcher)Preconditions.checkNotNull(a); + this.second = (CharMatcher)Preconditions.checkNotNull(b); + } + + public boolean matches(char c) { + return this.first.matches(c) && this.second.matches(c); + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + BitSet tmp1 = new BitSet(); + this.first.setBits(tmp1); + BitSet tmp2 = new BitSet(); + this.second.setBits(tmp2); + tmp1.and(tmp2); + table.or(tmp1); + } + + CharMatcher withToString(String description) { + return new CharMatcher.And(this.first, this.second, description); + } + } + + private static class NegatedMatcher extends CharMatcher { + final CharMatcher original; + + NegatedMatcher(String toString, CharMatcher original) { + super(toString); + this.original = original; + } + + NegatedMatcher(CharMatcher original) { + String var2 = String.valueOf(String.valueOf(original)); + this((new StringBuilder(9 + var2.length())).append(var2).append(".negate()").toString(), original); + } + + public boolean matches(char c) { + return !this.original.matches(c); + } + + public boolean matchesAllOf(CharSequence sequence) { + return this.original.matchesNoneOf(sequence); + } + + public boolean matchesNoneOf(CharSequence sequence) { + return this.original.matchesAllOf(sequence); + } + + public int countIn(CharSequence sequence) { + return sequence.length() - this.original.countIn(sequence); + } + + @GwtIncompatible("java.util.BitSet") + void setBits(BitSet table) { + BitSet tmp = new BitSet(); + this.original.setBits(tmp); + tmp.flip(0, 65536); + table.or(tmp); + } + + public CharMatcher negate() { + return this.original; + } + + CharMatcher withToString(String description) { + return new CharMatcher.NegatedMatcher(description, this.original); + } + } + + 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; + Preconditions.checkArgument(rangeStarts.length == rangeEnds.length); + + for(int i = 0; i < rangeStarts.length; ++i) { + Preconditions.checkArgument(rangeStarts[i] <= rangeEnds[i]); + if (i + 1 < rangeStarts.length) { + Preconditions.checkArgument(rangeEnds[i] < rangeStarts[i + 1]); + } + } + + } + + public boolean matches(char c) { + int index = Arrays.binarySearch(this.rangeStarts, c); + if (index >= 0) { + return true; + } else { + index = ~index - 1; + return index >= 0 && c <= this.rangeEnds[index]; + } + } + } +} diff --git a/src/main/com/google/common/base/Charsets.java b/src/main/com/google/common/base/Charsets.java new file mode 100644 index 0000000..2cbbd92 --- /dev/null +++ b/src/main/com/google/common/base/Charsets.java @@ -0,0 +1,25 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.nio.charset.Charset; + +@GwtCompatible( + emulated = true +) +public final class Charsets { + @GwtIncompatible("Non-UTF-8 Charset") + public static final Charset US_ASCII = Charset.forName("US-ASCII"); + @GwtIncompatible("Non-UTF-8 Charset") + public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + public static final Charset UTF_8 = Charset.forName("UTF-8"); + @GwtIncompatible("Non-UTF-8 Charset") + public static final Charset UTF_16BE = Charset.forName("UTF-16BE"); + @GwtIncompatible("Non-UTF-8 Charset") + public static final Charset UTF_16LE = Charset.forName("UTF-16LE"); + @GwtIncompatible("Non-UTF-8 Charset") + public static final Charset UTF_16 = Charset.forName("UTF-16"); + + private Charsets() { + } +} diff --git a/src/main/com/google/common/base/Converter.java b/src/main/com/google/common/base/Converter.java new file mode 100644 index 0000000..2b3bcfd --- /dev/null +++ b/src/main/com/google/common/base/Converter.java @@ -0,0 +1,272 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Iterator; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public abstract class Converter implements Function { + private final boolean handleNullAutomatically; + private transient Converter reverse; + + protected Converter() { + this(true); + } + + Converter(boolean handleNullAutomatically) { + this.handleNullAutomatically = handleNullAutomatically; + } + + protected abstract B doForward(A var1); + + protected abstract A doBackward(B var1); + + @Nullable + public final B convert(@Nullable A a) { + return this.correctedDoForward(a); + } + + @Nullable + B correctedDoForward(@Nullable A a) { + if (this.handleNullAutomatically) { + return a == null ? null : Preconditions.checkNotNull(this.doForward(a)); + } else { + return this.doForward(a); + } + } + + @Nullable + A correctedDoBackward(@Nullable B b) { + if (this.handleNullAutomatically) { + return b == null ? null : Preconditions.checkNotNull(this.doBackward(b)); + } else { + return this.doBackward(b); + } + } + + public Iterable convertAll(final Iterable fromIterable) { + Preconditions.checkNotNull(fromIterable, "fromIterable"); + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + private final Iterator fromIterator = fromIterable.iterator(); + + public boolean hasNext() { + return this.fromIterator.hasNext(); + } + + public B next() { + return Converter.this.convert(this.fromIterator.next()); + } + + public void remove() { + this.fromIterator.remove(); + } + }; + } + }; + } + + public Converter reverse() { + Converter result = this.reverse; + return result == null ? (this.reverse = new Converter.ReverseConverter(this)) : result; + } + + public final Converter andThen(Converter secondConverter) { + return this.doAndThen(secondConverter); + } + + Converter doAndThen(Converter secondConverter) { + return new Converter.ConverterComposition(this, (Converter)Preconditions.checkNotNull(secondConverter)); + } + + /** @deprecated */ + @Deprecated + @Nullable + public final B apply(@Nullable A a) { + return this.convert(a); + } + + public boolean equals(@Nullable Object object) { + return super.equals(object); + } + + public static Converter from(Function forwardFunction, Function backwardFunction) { + return new Converter.FunctionBasedConverter(forwardFunction, backwardFunction); + } + + public static Converter identity() { + return Converter.IdentityConverter.INSTANCE; + } + + private static final class IdentityConverter extends Converter implements Serializable { + static final Converter.IdentityConverter INSTANCE = new Converter.IdentityConverter(); + private static final long serialVersionUID = 0L; + + protected T doForward(T t) { + return t; + } + + protected T doBackward(T t) { + return t; + } + + public Converter.IdentityConverter reverse() { + return this; + } + + Converter doAndThen(Converter otherConverter) { + return (Converter)Preconditions.checkNotNull(otherConverter, "otherConverter"); + } + + public String toString() { + return "Converter.identity()"; + } + + private Object readResolve() { + return INSTANCE; + } + } + + private static final class FunctionBasedConverter extends Converter implements Serializable { + private final Function forwardFunction; + private final Function backwardFunction; + + private FunctionBasedConverter(Function forwardFunction, Function backwardFunction) { + this.forwardFunction = (Function)Preconditions.checkNotNull(forwardFunction); + this.backwardFunction = (Function)Preconditions.checkNotNull(backwardFunction); + } + + protected B doForward(A a) { + return this.forwardFunction.apply(a); + } + + protected A doBackward(B b) { + return this.backwardFunction.apply(b); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof Converter.FunctionBasedConverter)) { + return false; + } else { + Converter.FunctionBasedConverter that = (Converter.FunctionBasedConverter)object; + return this.forwardFunction.equals(that.forwardFunction) && this.backwardFunction.equals(that.backwardFunction); + } + } + + public int hashCode() { + return this.forwardFunction.hashCode() * 31 + this.backwardFunction.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.forwardFunction)); + String var2 = String.valueOf(String.valueOf(this.backwardFunction)); + return (new StringBuilder(18 + var1.length() + var2.length())).append("Converter.from(").append(var1).append(", ").append(var2).append(")").toString(); + } + + // $FF: synthetic method + FunctionBasedConverter(Function x0, Function x1, Object x2) { + this(x0, x1); + } + } + + private static final class ConverterComposition extends Converter implements Serializable { + final Converter first; + final Converter second; + private static final long serialVersionUID = 0L; + + ConverterComposition(Converter first, Converter second) { + this.first = first; + this.second = second; + } + + protected C doForward(A a) { + throw new AssertionError(); + } + + protected A doBackward(C c) { + throw new AssertionError(); + } + + @Nullable + C correctedDoForward(@Nullable A a) { + return this.second.correctedDoForward(this.first.correctedDoForward(a)); + } + + @Nullable + A correctedDoBackward(@Nullable C c) { + return this.first.correctedDoBackward(this.second.correctedDoBackward(c)); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof Converter.ConverterComposition)) { + return false; + } else { + Converter.ConverterComposition that = (Converter.ConverterComposition)object; + return this.first.equals(that.first) && this.second.equals(that.second); + } + } + + public int hashCode() { + return 31 * this.first.hashCode() + this.second.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.first)); + String var2 = String.valueOf(String.valueOf(this.second)); + return (new StringBuilder(10 + var1.length() + var2.length())).append(var1).append(".andThen(").append(var2).append(")").toString(); + } + } + + private static final class ReverseConverter extends Converter implements Serializable { + final Converter original; + private static final long serialVersionUID = 0L; + + ReverseConverter(Converter original) { + this.original = original; + } + + protected A doForward(B b) { + throw new AssertionError(); + } + + protected B doBackward(A a) { + throw new AssertionError(); + } + + @Nullable + A correctedDoForward(@Nullable B b) { + return this.original.correctedDoBackward(b); + } + + @Nullable + B correctedDoBackward(@Nullable A a) { + return this.original.correctedDoForward(a); + } + + public Converter reverse() { + return this.original; + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Converter.ReverseConverter) { + Converter.ReverseConverter that = (Converter.ReverseConverter)object; + return this.original.equals(that.original); + } else { + return false; + } + } + + public int hashCode() { + return ~this.original.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.original)); + return (new StringBuilder(10 + var1.length())).append(var1).append(".reverse()").toString(); + } + } +} diff --git a/src/main/com/google/common/base/Defaults.java b/src/main/com/google/common/base/Defaults.java new file mode 100644 index 0000000..f860a43 --- /dev/null +++ b/src/main/com/google/common/base/Defaults.java @@ -0,0 +1,36 @@ +package com.google.common.base; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +public final class Defaults { + private static final Map, Object> DEFAULTS; + + private Defaults() { + } + + private static void put(Map, Object> map, Class type, T value) { + map.put(type, value); + } + + @Nullable + public static T defaultValue(Class type) { + T t = DEFAULTS.get(Preconditions.checkNotNull(type)); + return t; + } + + static { + Map, Object> map = new HashMap(); + put(map, Boolean.TYPE, false); + put(map, Character.TYPE, '\u0000'); + put(map, Byte.TYPE, (byte)0); + put(map, Short.TYPE, Short.valueOf((short)0)); + put(map, Integer.TYPE, 0); + put(map, Long.TYPE, 0L); + put(map, Float.TYPE, 0.0F); + put(map, Double.TYPE, 0.0D); + DEFAULTS = Collections.unmodifiableMap(map); + } +} diff --git a/src/main/com/google/common/base/Enums.java b/src/main/com/google/common/base/Enums.java new file mode 100644 index 0000000..95771c0 --- /dev/null +++ b/src/main/com/google/common/base/Enums.java @@ -0,0 +1,108 @@ +package com.google.common.base; + +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.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.WeakHashMap; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +@Beta +public final class Enums { + @GwtIncompatible("java.lang.ref.WeakReference") + private static final Map>, Map>>> enumConstantCache = new WeakHashMap(); + + private Enums() { + } + + @GwtIncompatible("reflection") + public static Field getField(Enum enumValue) { + Class clazz = enumValue.getDeclaringClass(); + + try { + return clazz.getDeclaredField(enumValue.name()); + } catch (NoSuchFieldException var3) { + throw new AssertionError(var3); + } + } + + public static > Optional getIfPresent(Class enumClass, String value) { + Preconditions.checkNotNull(enumClass); + Preconditions.checkNotNull(value); + return Platform.getEnumIfPresent(enumClass, value); + } + + @GwtIncompatible("java.lang.ref.WeakReference") + private static > Map>> populateCache(Class enumClass) { + Map>> result = new HashMap(); + Iterator i$ = EnumSet.allOf(enumClass).iterator(); + + while(i$.hasNext()) { + T enumInstance = (Enum)i$.next(); + result.put(enumInstance.name(), new WeakReference(enumInstance)); + } + + enumConstantCache.put(enumClass, result); + return result; + } + + @GwtIncompatible("java.lang.ref.WeakReference") + static > Map>> getEnumConstants(Class enumClass) { + synchronized(enumConstantCache) { + Map>> constants = (Map)enumConstantCache.get(enumClass); + if (constants == null) { + constants = populateCache(enumClass); + } + + return constants; + } + } + + public static > Converter stringConverter(Class enumClass) { + return new Enums.StringConverter(enumClass); + } + + private static final class StringConverter> extends Converter implements Serializable { + private final Class enumClass; + private static final long serialVersionUID = 0L; + + StringConverter(Class enumClass) { + this.enumClass = (Class)Preconditions.checkNotNull(enumClass); + } + + protected T doForward(String value) { + return Enum.valueOf(this.enumClass, value); + } + + protected String doBackward(T enumValue) { + return enumValue.name(); + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Enums.StringConverter) { + Enums.StringConverter that = (Enums.StringConverter)object; + return this.enumClass.equals(that.enumClass); + } else { + return false; + } + } + + public int hashCode() { + return this.enumClass.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.enumClass.getName())); + return (new StringBuilder(29 + var1.length())).append("Enums.stringConverter(").append(var1).append(".class)").toString(); + } + } +} diff --git a/src/main/com/google/common/base/Equivalence.java b/src/main/com/google/common/base/Equivalence.java new file mode 100644 index 0000000..6ac66c5 --- /dev/null +++ b/src/main/com/google/common/base/Equivalence.java @@ -0,0 +1,175 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class Equivalence { + protected Equivalence() { + } + + public final boolean equivalent(@Nullable T a, @Nullable T b) { + if (a == b) { + return true; + } else { + return a != null && b != null ? this.doEquivalent(a, b) : false; + } + } + + protected abstract boolean doEquivalent(T var1, T var2); + + public final int hash(@Nullable T t) { + return t == null ? 0 : this.doHash(t); + } + + protected abstract int doHash(T var1); + + public final Equivalence onResultOf(Function function) { + return new FunctionalEquivalence(function, this); + } + + public final Equivalence.Wrapper wrap(@Nullable S reference) { + return new Equivalence.Wrapper(this, reference); + } + + @GwtCompatible( + serializable = true + ) + public final Equivalence> pairwise() { + return new PairwiseEquivalence(this); + } + + @Beta + public final Predicate equivalentTo(@Nullable T target) { + return new Equivalence.EquivalentToPredicate(this, target); + } + + public static Equivalence equals() { + return Equivalence.Equals.INSTANCE; + } + + public static Equivalence identity() { + return Equivalence.Identity.INSTANCE; + } + + static final class Identity extends Equivalence implements Serializable { + static final Equivalence.Identity INSTANCE = new Equivalence.Identity(); + private static final long serialVersionUID = 1L; + + protected boolean doEquivalent(Object a, Object b) { + return false; + } + + protected int doHash(Object o) { + return System.identityHashCode(o); + } + + private Object readResolve() { + return INSTANCE; + } + } + + static final class Equals extends Equivalence implements Serializable { + static final Equivalence.Equals INSTANCE = new Equivalence.Equals(); + private static final long serialVersionUID = 1L; + + protected boolean doEquivalent(Object a, Object b) { + return a.equals(b); + } + + protected int doHash(Object o) { + return o.hashCode(); + } + + private Object readResolve() { + return INSTANCE; + } + } + + private static final class EquivalentToPredicate implements Predicate, Serializable { + private final Equivalence equivalence; + @Nullable + private final T target; + private static final long serialVersionUID = 0L; + + EquivalentToPredicate(Equivalence equivalence, @Nullable T target) { + this.equivalence = (Equivalence)Preconditions.checkNotNull(equivalence); + this.target = target; + } + + public boolean apply(@Nullable T input) { + return this.equivalence.equivalent(input, this.target); + } + + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } else if (!(obj instanceof Equivalence.EquivalentToPredicate)) { + return false; + } else { + Equivalence.EquivalentToPredicate that = (Equivalence.EquivalentToPredicate)obj; + return this.equivalence.equals(that.equivalence) && Objects.equal(this.target, that.target); + } + } + + public int hashCode() { + return Objects.hashCode(this.equivalence, this.target); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.equivalence)); + String var2 = String.valueOf(String.valueOf(this.target)); + return (new StringBuilder(15 + var1.length() + var2.length())).append(var1).append(".equivalentTo(").append(var2).append(")").toString(); + } + } + + public static final class Wrapper implements Serializable { + private final Equivalence equivalence; + @Nullable + private final T reference; + private static final long serialVersionUID = 0L; + + private Wrapper(Equivalence equivalence, @Nullable T reference) { + this.equivalence = (Equivalence)Preconditions.checkNotNull(equivalence); + this.reference = reference; + } + + @Nullable + public T get() { + return this.reference; + } + + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } else { + if (obj instanceof Equivalence.Wrapper) { + Equivalence.Wrapper that = (Equivalence.Wrapper)obj; + if (this.equivalence.equals(that.equivalence)) { + Equivalence equivalence = this.equivalence; + return equivalence.equivalent(this.reference, that.reference); + } + } + + return false; + } + } + + public int hashCode() { + return this.equivalence.hash(this.reference); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.equivalence)); + String var2 = String.valueOf(String.valueOf(this.reference)); + return (new StringBuilder(7 + var1.length() + var2.length())).append(var1).append(".wrap(").append(var2).append(")").toString(); + } + + // $FF: synthetic method + Wrapper(Equivalence x0, Object x1, Object x2) { + this(x0, x1); + } + } +} diff --git a/src/main/com/google/common/base/FinalizablePhantomReference.java b/src/main/com/google/common/base/FinalizablePhantomReference.java new file mode 100644 index 0000000..8f2075b --- /dev/null +++ b/src/main/com/google/common/base/FinalizablePhantomReference.java @@ -0,0 +1,10 @@ +package com.google.common.base; + +import java.lang.ref.PhantomReference; + +public abstract class FinalizablePhantomReference extends PhantomReference implements FinalizableReference { + protected FinalizablePhantomReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/src/main/com/google/common/base/FinalizableReference.java b/src/main/com/google/common/base/FinalizableReference.java new file mode 100644 index 0000000..42899b8 --- /dev/null +++ b/src/main/com/google/common/base/FinalizableReference.java @@ -0,0 +1,5 @@ +package com.google.common.base; + +public interface FinalizableReference { + void finalizeReferent(); +} diff --git a/src/main/com/google/common/base/FinalizableReferenceQueue.java b/src/main/com/google/common/base/FinalizableReferenceQueue.java new file mode 100644 index 0000000..4831b18 --- /dev/null +++ b/src/main/com/google/common/base/FinalizableReferenceQueue.java @@ -0,0 +1,179 @@ +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; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +public class FinalizableReferenceQueue implements Closeable { + private static final Logger logger = Logger.getLogger(FinalizableReferenceQueue.class.getName()); + private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer"; + private static final Method startFinalizer; + final ReferenceQueue queue = new ReferenceQueue(); + final PhantomReference frqRef; + final boolean threadStarted; + + public FinalizableReferenceQueue() { + this.frqRef = new PhantomReference(this, this.queue); + boolean threadStarted = false; + + try { + startFinalizer.invoke((Object)null, FinalizableReference.class, this.queue, this.frqRef); + threadStarted = true; + } catch (IllegalAccessException var3) { + throw new AssertionError(var3); + } catch (Throwable var4) { + logger.log(Level.INFO, "Failed to start reference finalizer thread. Reference cleanup will only occur when new references are created.", var4); + } + + this.threadStarted = threadStarted; + } + + public void close() { + this.frqRef.enqueue(); + this.cleanUp(); + } + + void cleanUp() { + if (!this.threadStarted) { + Reference reference; + while((reference = this.queue.poll()) != null) { + reference.clear(); + + try { + ((FinalizableReference)reference).finalizeReferent(); + } catch (Throwable var3) { + logger.log(Level.SEVERE, "Error cleaning up after reference.", var3); + } + } + + } + } + + private static Class loadFinalizer(FinalizableReferenceQueue.FinalizerLoader... loaders) { + FinalizableReferenceQueue.FinalizerLoader[] arr$ = loaders; + int len$ = loaders.length; + + for(int i$ = 0; i$ < len$; ++i$) { + FinalizableReferenceQueue.FinalizerLoader loader = arr$[i$]; + Class finalizer = loader.loadFinalizer(); + if (finalizer != null) { + return finalizer; + } + } + + throw new AssertionError(); + } + + static Method getStartFinalizer(Class finalizer) { + try { + return finalizer.getMethod("startFinalizer", Class.class, ReferenceQueue.class, PhantomReference.class); + } catch (NoSuchMethodException var2) { + throw new AssertionError(var2); + } + } + + static { + Class finalizer = loadFinalizer(new FinalizableReferenceQueue.SystemLoader(), new FinalizableReferenceQueue.DecoupledLoader(), new FinalizableReferenceQueue.DirectLoader()); + startFinalizer = getStartFinalizer(finalizer); + } + + static class DirectLoader implements FinalizableReferenceQueue.FinalizerLoader { + public Class loadFinalizer() { + try { + return Class.forName("com.google.common.base.internal.Finalizer"); + } catch (ClassNotFoundException var2) { + throw new AssertionError(var2); + } + } + } + + static class DecoupledLoader implements FinalizableReferenceQueue.FinalizerLoader { + private static final String LOADING_ERROR = "Could not load Finalizer in its own class loader.Loading Finalizer in the current class loader instead. As a result, you will not be ableto garbage collect this class loader. To support reclaiming this class loader, eitherresolve the underlying issue, or move Google Collections to your system class path."; + + public Class loadFinalizer() { + try { + ClassLoader finalizerLoader = this.newLoader(this.getBaseUrl()); + return finalizerLoader.loadClass("com.google.common.base.internal.Finalizer"); + } catch (Exception var2) { + FinalizableReferenceQueue.logger.log(Level.WARNING, "Could not load Finalizer in its own class loader.Loading Finalizer in the current class loader instead. As a result, you will not be ableto garbage collect this class loader. To support reclaiming this class loader, eitherresolve the underlying issue, or move Google Collections to your system class path.", var2); + return null; + } + } + + URL getBaseUrl() throws IOException { + String finalizerPath = String.valueOf("com.google.common.base.internal.Finalizer".replace('.', '/')).concat(".class"); + URL finalizerUrl = this.getClass().getClassLoader().getResource(finalizerPath); + if (finalizerUrl == null) { + throw new FileNotFoundException(finalizerPath); + } else { + String urlString = finalizerUrl.toString(); + if (!urlString.endsWith(finalizerPath)) { + IOException var10000 = new IOException; + String var10003 = String.valueOf(urlString); + String var10002; + if (var10003.length() != 0) { + var10002 = "Unsupported path style: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Unsupported path style: "); + } + + var10000.(var10002); + throw var10000; + } else { + urlString = urlString.substring(0, urlString.length() - finalizerPath.length()); + return new URL(finalizerUrl, urlString); + } + } + } + + URLClassLoader newLoader(URL base) { + return new URLClassLoader(new URL[]{base}, (ClassLoader)null); + } + } + + static class SystemLoader implements FinalizableReferenceQueue.FinalizerLoader { + @VisibleForTesting + static boolean disabled; + + public Class loadFinalizer() { + if (disabled) { + return null; + } else { + ClassLoader systemLoader; + try { + systemLoader = ClassLoader.getSystemClassLoader(); + } catch (SecurityException var4) { + FinalizableReferenceQueue.logger.info("Not allowed to access system class loader."); + return null; + } + + if (systemLoader != null) { + try { + return systemLoader.loadClass("com.google.common.base.internal.Finalizer"); + } catch (ClassNotFoundException var3) { + return null; + } + } else { + return null; + } + } + } + } + + interface FinalizerLoader { + @Nullable + Class loadFinalizer(); + } +} diff --git a/src/main/com/google/common/base/FinalizableSoftReference.java b/src/main/com/google/common/base/FinalizableSoftReference.java new file mode 100644 index 0000000..dfd0ce3 --- /dev/null +++ b/src/main/com/google/common/base/FinalizableSoftReference.java @@ -0,0 +1,10 @@ +package com.google.common.base; + +import java.lang.ref.SoftReference; + +public abstract class FinalizableSoftReference extends SoftReference implements FinalizableReference { + protected FinalizableSoftReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/src/main/com/google/common/base/FinalizableWeakReference.java b/src/main/com/google/common/base/FinalizableWeakReference.java new file mode 100644 index 0000000..50a40a7 --- /dev/null +++ b/src/main/com/google/common/base/FinalizableWeakReference.java @@ -0,0 +1,10 @@ +package com.google.common.base; + +import java.lang.ref.WeakReference; + +public abstract class FinalizableWeakReference extends WeakReference implements FinalizableReference { + protected FinalizableWeakReference(T referent, FinalizableReferenceQueue queue) { + super(referent, queue.queue); + queue.cleanUp(); + } +} diff --git a/src/main/com/google/common/base/Function.java b/src/main/com/google/common/base/Function.java new file mode 100644 index 0000000..d23b352 --- /dev/null +++ b/src/main/com/google/common/base/Function.java @@ -0,0 +1,12 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +public interface Function { + @Nullable + T apply(@Nullable F var1); + + boolean equals(@Nullable Object var1); +} diff --git a/src/main/com/google/common/base/FunctionalEquivalence.java b/src/main/com/google/common/base/FunctionalEquivalence.java new file mode 100644 index 0000000..ece9fda --- /dev/null +++ b/src/main/com/google/common/base/FunctionalEquivalence.java @@ -0,0 +1,48 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +final class FunctionalEquivalence extends Equivalence implements Serializable { + private static final long serialVersionUID = 0L; + private final Function function; + private final Equivalence resultEquivalence; + + FunctionalEquivalence(Function function, Equivalence resultEquivalence) { + this.function = (Function)Preconditions.checkNotNull(function); + this.resultEquivalence = (Equivalence)Preconditions.checkNotNull(resultEquivalence); + } + + protected boolean doEquivalent(F a, F b) { + return this.resultEquivalence.equivalent(this.function.apply(a), this.function.apply(b)); + } + + protected int doHash(F a) { + return this.resultEquivalence.hash(this.function.apply(a)); + } + + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof FunctionalEquivalence)) { + return false; + } else { + FunctionalEquivalence that = (FunctionalEquivalence)obj; + return this.function.equals(that.function) && this.resultEquivalence.equals(that.resultEquivalence); + } + } + + public int hashCode() { + return Objects.hashCode(this.function, this.resultEquivalence); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.resultEquivalence)); + String var2 = String.valueOf(String.valueOf(this.function)); + return (new StringBuilder(13 + var1.length() + var2.length())).append(var1).append(".onResultOf(").append(var2).append(")").toString(); + } +} diff --git a/src/main/com/google/common/base/Functions.java b/src/main/com/google/common/base/Functions.java new file mode 100644 index 0000000..b132ede --- /dev/null +++ b/src/main/com/google/common/base/Functions.java @@ -0,0 +1,277 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Functions { + private Functions() { + } + + public static Function toStringFunction() { + return Functions.ToStringFunction.INSTANCE; + } + + public static Function identity() { + return Functions.IdentityFunction.INSTANCE; + } + + public static Function forMap(Map map) { + return new Functions.FunctionForMapNoDefault(map); + } + + public static Function forMap(Map map, @Nullable V defaultValue) { + return new Functions.ForMapWithDefault(map, defaultValue); + } + + public static Function compose(Function g, Function f) { + return new Functions.FunctionComposition(g, f); + } + + public static Function forPredicate(Predicate predicate) { + return new Functions.PredicateFunction(predicate); + } + + public static Function constant(@Nullable E value) { + return new Functions.ConstantFunction(value); + } + + @Beta + public static Function forSupplier(Supplier supplier) { + return new Functions.SupplierFunction(supplier); + } + + private static class SupplierFunction implements Function, Serializable { + private final Supplier supplier; + private static final long serialVersionUID = 0L; + + private SupplierFunction(Supplier supplier) { + this.supplier = (Supplier)Preconditions.checkNotNull(supplier); + } + + public T apply(@Nullable Object input) { + return this.supplier.get(); + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Functions.SupplierFunction) { + Functions.SupplierFunction that = (Functions.SupplierFunction)obj; + return this.supplier.equals(that.supplier); + } else { + return false; + } + } + + public int hashCode() { + return this.supplier.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.supplier)); + return (new StringBuilder(13 + var1.length())).append("forSupplier(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + SupplierFunction(Supplier x0, Object x1) { + this(x0); + } + } + + private static class ConstantFunction implements Function, Serializable { + private final E value; + private static final long serialVersionUID = 0L; + + public ConstantFunction(@Nullable E value) { + this.value = value; + } + + public E apply(@Nullable Object from) { + return this.value; + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Functions.ConstantFunction) { + Functions.ConstantFunction that = (Functions.ConstantFunction)obj; + return Objects.equal(this.value, that.value); + } else { + return false; + } + } + + public int hashCode() { + return this.value == null ? 0 : this.value.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.value)); + return (new StringBuilder(10 + var1.length())).append("constant(").append(var1).append(")").toString(); + } + } + + private static class PredicateFunction implements Function, Serializable { + private final Predicate predicate; + private static final long serialVersionUID = 0L; + + private PredicateFunction(Predicate predicate) { + this.predicate = (Predicate)Preconditions.checkNotNull(predicate); + } + + public Boolean apply(@Nullable T t) { + return this.predicate.apply(t); + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Functions.PredicateFunction) { + Functions.PredicateFunction that = (Functions.PredicateFunction)obj; + return this.predicate.equals(that.predicate); + } else { + return false; + } + } + + public int hashCode() { + return this.predicate.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.predicate)); + return (new StringBuilder(14 + var1.length())).append("forPredicate(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + PredicateFunction(Predicate x0, Object x1) { + this(x0); + } + } + + private static class FunctionComposition implements Function, Serializable { + private final Function g; + private final Function f; + private static final long serialVersionUID = 0L; + + public FunctionComposition(Function g, Function f) { + this.g = (Function)Preconditions.checkNotNull(g); + this.f = (Function)Preconditions.checkNotNull(f); + } + + public C apply(@Nullable A a) { + return this.g.apply(this.f.apply(a)); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof Functions.FunctionComposition)) { + return false; + } else { + Functions.FunctionComposition that = (Functions.FunctionComposition)obj; + return this.f.equals(that.f) && this.g.equals(that.g); + } + } + + public int hashCode() { + return this.f.hashCode() ^ this.g.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.g)); + String var2 = String.valueOf(String.valueOf(this.f)); + return (new StringBuilder(2 + var1.length() + var2.length())).append(var1).append("(").append(var2).append(")").toString(); + } + } + + private static class ForMapWithDefault implements Function, Serializable { + final Map map; + final V defaultValue; + private static final long serialVersionUID = 0L; + + ForMapWithDefault(Map map, @Nullable V defaultValue) { + this.map = (Map)Preconditions.checkNotNull(map); + this.defaultValue = defaultValue; + } + + public V apply(@Nullable K key) { + V result = this.map.get(key); + return result == null && !this.map.containsKey(key) ? this.defaultValue : result; + } + + public boolean equals(@Nullable Object o) { + if (!(o instanceof Functions.ForMapWithDefault)) { + return false; + } else { + Functions.ForMapWithDefault that = (Functions.ForMapWithDefault)o; + return this.map.equals(that.map) && Objects.equal(this.defaultValue, that.defaultValue); + } + } + + public int hashCode() { + return Objects.hashCode(this.map, this.defaultValue); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.map)); + String var2 = String.valueOf(String.valueOf(this.defaultValue)); + return (new StringBuilder(23 + var1.length() + var2.length())).append("forMap(").append(var1).append(", defaultValue=").append(var2).append(")").toString(); + } + } + + private static class FunctionForMapNoDefault implements Function, Serializable { + final Map map; + private static final long serialVersionUID = 0L; + + FunctionForMapNoDefault(Map map) { + this.map = (Map)Preconditions.checkNotNull(map); + } + + public V apply(@Nullable K key) { + V result = this.map.get(key); + Preconditions.checkArgument(result != null || this.map.containsKey(key), "Key '%s' not present in map", key); + return result; + } + + public boolean equals(@Nullable Object o) { + if (o instanceof Functions.FunctionForMapNoDefault) { + Functions.FunctionForMapNoDefault that = (Functions.FunctionForMapNoDefault)o; + return this.map.equals(that.map); + } else { + return false; + } + } + + public int hashCode() { + return this.map.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.map)); + return (new StringBuilder(8 + var1.length())).append("forMap(").append(var1).append(")").toString(); + } + } + + private static enum IdentityFunction implements Function { + INSTANCE; + + @Nullable + public Object apply(@Nullable Object o) { + return o; + } + + public String toString() { + return "identity"; + } + } + + private static enum ToStringFunction implements Function { + INSTANCE; + + public String apply(Object o) { + Preconditions.checkNotNull(o); + return o.toString(); + } + + public String toString() { + return "toString"; + } + } +} diff --git a/src/main/com/google/common/base/Joiner.java b/src/main/com/google/common/base/Joiner.java new file mode 100644 index 0000000..3ff8521 --- /dev/null +++ b/src/main/com/google/common/base/Joiner.java @@ -0,0 +1,269 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.io.IOException; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +@GwtCompatible +public class Joiner { + private final String separator; + + public static Joiner on(String separator) { + return new Joiner(separator); + } + + public static Joiner on(char separator) { + return new Joiner(String.valueOf(separator)); + } + + private Joiner(String separator) { + this.separator = (String)Preconditions.checkNotNull(separator); + } + + private Joiner(Joiner prototype) { + this.separator = prototype.separator; + } + + public A appendTo(A appendable, Iterable parts) throws IOException { + return this.appendTo(appendable, parts.iterator()); + } + + public A appendTo(A appendable, Iterator parts) throws IOException { + Preconditions.checkNotNull(appendable); + if (parts.hasNext()) { + appendable.append(this.toString(parts.next())); + + while(parts.hasNext()) { + appendable.append(this.separator); + appendable.append(this.toString(parts.next())); + } + } + + return appendable; + } + + public final A appendTo(A appendable, Object[] parts) throws IOException { + return this.appendTo((Appendable)appendable, (Iterable)Arrays.asList(parts)); + } + + public final A appendTo(A appendable, @Nullable Object first, @Nullable Object second, Object... rest) throws IOException { + return this.appendTo(appendable, iterable(first, second, rest)); + } + + public final StringBuilder appendTo(StringBuilder builder, Iterable parts) { + return this.appendTo(builder, parts.iterator()); + } + + public final StringBuilder appendTo(StringBuilder builder, Iterator parts) { + try { + this.appendTo((Appendable)builder, (Iterator)parts); + return builder; + } catch (IOException var4) { + throw new AssertionError(var4); + } + } + + public final StringBuilder appendTo(StringBuilder builder, Object[] parts) { + return this.appendTo((StringBuilder)builder, (Iterable)Arrays.asList(parts)); + } + + public final StringBuilder appendTo(StringBuilder builder, @Nullable Object first, @Nullable Object second, Object... rest) { + return this.appendTo(builder, iterable(first, second, rest)); + } + + public final String join(Iterable parts) { + return this.join(parts.iterator()); + } + + public final String join(Iterator parts) { + return this.appendTo(new StringBuilder(), parts).toString(); + } + + public final String join(Object[] parts) { + return this.join((Iterable)Arrays.asList(parts)); + } + + public final String join(@Nullable Object first, @Nullable Object second, Object... rest) { + return this.join(iterable(first, second, rest)); + } + + @CheckReturnValue + public Joiner useForNull(final String nullText) { + Preconditions.checkNotNull(nullText); + return new Joiner(this) { + CharSequence toString(@Nullable Object part) { + return (CharSequence)(part == null ? nullText : Joiner.this.toString(part)); + } + + public Joiner useForNull(String nullTextx) { + throw new UnsupportedOperationException("already specified useForNull"); + } + + public Joiner skipNulls() { + throw new UnsupportedOperationException("already specified useForNull"); + } + }; + } + + @CheckReturnValue + public Joiner skipNulls() { + return new Joiner(this) { + public A appendTo(A appendable, Iterator parts) throws IOException { + Preconditions.checkNotNull(appendable, "appendable"); + Preconditions.checkNotNull(parts, "parts"); + + Object part; + while(parts.hasNext()) { + part = parts.next(); + if (part != null) { + appendable.append(Joiner.this.toString(part)); + break; + } + } + + while(parts.hasNext()) { + part = parts.next(); + if (part != null) { + appendable.append(Joiner.this.separator); + appendable.append(Joiner.this.toString(part)); + } + } + + return appendable; + } + + public Joiner useForNull(String nullText) { + throw new UnsupportedOperationException("already specified skipNulls"); + } + + public Joiner.MapJoiner withKeyValueSeparator(String kvs) { + throw new UnsupportedOperationException("can't use .skipNulls() with maps"); + } + }; + } + + @CheckReturnValue + public Joiner.MapJoiner withKeyValueSeparator(String keyValueSeparator) { + return new Joiner.MapJoiner(this, keyValueSeparator); + } + + CharSequence toString(Object part) { + Preconditions.checkNotNull(part); + return (CharSequence)(part instanceof CharSequence ? (CharSequence)part : part.toString()); + } + + private static Iterable iterable(final Object first, final Object second, final Object[] rest) { + Preconditions.checkNotNull(rest); + return new AbstractList() { + public int size() { + return rest.length + 2; + } + + public Object get(int index) { + switch(index) { + case 0: + return first; + case 1: + return second; + default: + return rest[index - 2]; + } + } + }; + } + + // $FF: synthetic method + Joiner(Joiner x0, Object x1) { + this(x0); + } + + public static final class MapJoiner { + private final Joiner joiner; + private final String keyValueSeparator; + + private MapJoiner(Joiner joiner, String keyValueSeparator) { + this.joiner = joiner; + this.keyValueSeparator = (String)Preconditions.checkNotNull(keyValueSeparator); + } + + public A appendTo(A appendable, Map map) throws IOException { + return this.appendTo((Appendable)appendable, (Iterable)map.entrySet()); + } + + public StringBuilder appendTo(StringBuilder builder, Map map) { + return this.appendTo((StringBuilder)builder, (Iterable)map.entrySet()); + } + + public String join(Map map) { + return this.join((Iterable)map.entrySet()); + } + + @Beta + public A appendTo(A appendable, Iterable> entries) throws IOException { + return this.appendTo(appendable, entries.iterator()); + } + + @Beta + public A appendTo(A appendable, Iterator> parts) throws IOException { + Preconditions.checkNotNull(appendable); + if (parts.hasNext()) { + Entry entry = (Entry)parts.next(); + appendable.append(this.joiner.toString(entry.getKey())); + appendable.append(this.keyValueSeparator); + appendable.append(this.joiner.toString(entry.getValue())); + + while(parts.hasNext()) { + appendable.append(this.joiner.separator); + Entry e = (Entry)parts.next(); + appendable.append(this.joiner.toString(e.getKey())); + appendable.append(this.keyValueSeparator); + appendable.append(this.joiner.toString(e.getValue())); + } + } + + return appendable; + } + + @Beta + public StringBuilder appendTo(StringBuilder builder, Iterable> entries) { + return this.appendTo(builder, entries.iterator()); + } + + @Beta + public StringBuilder appendTo(StringBuilder builder, Iterator> entries) { + try { + this.appendTo((Appendable)builder, (Iterator)entries); + return builder; + } catch (IOException var4) { + throw new AssertionError(var4); + } + } + + @Beta + public String join(Iterable> entries) { + return this.join(entries.iterator()); + } + + @Beta + public String join(Iterator> entries) { + return this.appendTo(new StringBuilder(), entries).toString(); + } + + @CheckReturnValue + public Joiner.MapJoiner useForNull(String nullText) { + return new Joiner.MapJoiner(this.joiner.useForNull(nullText), this.keyValueSeparator); + } + + // $FF: synthetic method + MapJoiner(Joiner x0, String x1, Object x2) { + this(x0, x1); + } + } +} diff --git a/src/main/com/google/common/base/MoreObjects.java b/src/main/com/google/common/base/MoreObjects.java new file mode 100644 index 0000000..78a4d44 --- /dev/null +++ b/src/main/com/google/common/base/MoreObjects.java @@ -0,0 +1,170 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +public final class MoreObjects { + public static T firstNonNull(@Nullable T first, @Nullable T second) { + return first != null ? first : Preconditions.checkNotNull(second); + } + + public static MoreObjects.ToStringHelper toStringHelper(Object self) { + return new MoreObjects.ToStringHelper(simpleName(self.getClass())); + } + + public static MoreObjects.ToStringHelper toStringHelper(Class clazz) { + return new MoreObjects.ToStringHelper(simpleName(clazz)); + } + + public static MoreObjects.ToStringHelper toStringHelper(String className) { + return new MoreObjects.ToStringHelper(className); + } + + static String simpleName(Class clazz) { + String name = clazz.getName(); + name = name.replaceAll("\\$[0-9]+", "\\$"); + int start = name.lastIndexOf(36); + if (start == -1) { + start = name.lastIndexOf(46); + } + + return name.substring(start + 1); + } + + private MoreObjects() { + } + + public static final class ToStringHelper { + private final String className; + private MoreObjects.ToStringHelper.ValueHolder holderHead; + private MoreObjects.ToStringHelper.ValueHolder holderTail; + private boolean omitNullValues; + + private ToStringHelper(String className) { + this.holderHead = new MoreObjects.ToStringHelper.ValueHolder(); + this.holderTail = this.holderHead; + this.omitNullValues = false; + this.className = (String)Preconditions.checkNotNull(className); + } + + public MoreObjects.ToStringHelper omitNullValues() { + this.omitNullValues = true; + return this; + } + + public MoreObjects.ToStringHelper add(String name, @Nullable Object value) { + return this.addHolder(name, value); + } + + public MoreObjects.ToStringHelper add(String name, boolean value) { + return this.addHolder(name, String.valueOf(value)); + } + + public MoreObjects.ToStringHelper add(String name, char value) { + return this.addHolder(name, String.valueOf(value)); + } + + public MoreObjects.ToStringHelper add(String name, double value) { + return this.addHolder(name, String.valueOf(value)); + } + + public MoreObjects.ToStringHelper add(String name, float value) { + return this.addHolder(name, String.valueOf(value)); + } + + public MoreObjects.ToStringHelper add(String name, int value) { + return this.addHolder(name, String.valueOf(value)); + } + + public MoreObjects.ToStringHelper add(String name, long value) { + return this.addHolder(name, String.valueOf(value)); + } + + public MoreObjects.ToStringHelper addValue(@Nullable Object value) { + return this.addHolder(value); + } + + public MoreObjects.ToStringHelper addValue(boolean value) { + return this.addHolder(String.valueOf(value)); + } + + public MoreObjects.ToStringHelper addValue(char value) { + return this.addHolder(String.valueOf(value)); + } + + public MoreObjects.ToStringHelper addValue(double value) { + return this.addHolder(String.valueOf(value)); + } + + public MoreObjects.ToStringHelper addValue(float value) { + return this.addHolder(String.valueOf(value)); + } + + public MoreObjects.ToStringHelper addValue(int value) { + return this.addHolder(String.valueOf(value)); + } + + public MoreObjects.ToStringHelper addValue(long value) { + return this.addHolder(String.valueOf(value)); + } + + public String toString() { + boolean omitNullValuesSnapshot = this.omitNullValues; + String nextSeparator = ""; + StringBuilder builder = (new StringBuilder(32)).append(this.className).append('{'); + + for(MoreObjects.ToStringHelper.ValueHolder valueHolder = this.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); + } + } + + return builder.append('}').toString(); + } + + private MoreObjects.ToStringHelper.ValueHolder addHolder() { + MoreObjects.ToStringHelper.ValueHolder valueHolder = new MoreObjects.ToStringHelper.ValueHolder(); + this.holderTail = this.holderTail.next = valueHolder; + return valueHolder; + } + + private MoreObjects.ToStringHelper addHolder(@Nullable Object value) { + MoreObjects.ToStringHelper.ValueHolder valueHolder = this.addHolder(); + valueHolder.value = value; + return this; + } + + private MoreObjects.ToStringHelper addHolder(String name, @Nullable Object value) { + MoreObjects.ToStringHelper.ValueHolder valueHolder = this.addHolder(); + valueHolder.value = value; + valueHolder.name = (String)Preconditions.checkNotNull(name); + return this; + } + + // $FF: synthetic method + ToStringHelper(String x0, Object x1) { + this(x0); + } + + private static final class ValueHolder { + String name; + Object value; + MoreObjects.ToStringHelper.ValueHolder next; + + private ValueHolder() { + } + + // $FF: synthetic method + ValueHolder(Object x0) { + this(); + } + } + } +} diff --git a/src/main/com/google/common/base/Objects.java b/src/main/com/google/common/base/Objects.java new file mode 100644 index 0000000..d8713b0 --- /dev/null +++ b/src/main/com/google/common/base/Objects.java @@ -0,0 +1,180 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import java.util.Arrays; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Objects { + private Objects() { + } + + @CheckReturnValue + public static boolean equal(@Nullable Object a, @Nullable Object b) { + return a == b || a != null && a.equals(b); + } + + public static int hashCode(@Nullable Object... objects) { + return Arrays.hashCode(objects); + } + + /** @deprecated */ + @Deprecated + public static Objects.ToStringHelper toStringHelper(Object self) { + return new Objects.ToStringHelper(MoreObjects.simpleName(self.getClass())); + } + + /** @deprecated */ + @Deprecated + public static Objects.ToStringHelper toStringHelper(Class clazz) { + return new Objects.ToStringHelper(MoreObjects.simpleName(clazz)); + } + + /** @deprecated */ + @Deprecated + public static Objects.ToStringHelper toStringHelper(String className) { + return new Objects.ToStringHelper(className); + } + + /** @deprecated */ + @Deprecated + public static T firstNonNull(@Nullable T first, @Nullable T second) { + return MoreObjects.firstNonNull(first, second); + } + + /** @deprecated */ + @Deprecated + public static final class ToStringHelper { + private final String className; + private Objects.ToStringHelper.ValueHolder holderHead; + private Objects.ToStringHelper.ValueHolder holderTail; + private boolean omitNullValues; + + private ToStringHelper(String className) { + this.holderHead = new Objects.ToStringHelper.ValueHolder(); + this.holderTail = this.holderHead; + this.omitNullValues = false; + this.className = (String)Preconditions.checkNotNull(className); + } + + public Objects.ToStringHelper omitNullValues() { + this.omitNullValues = true; + return this; + } + + public Objects.ToStringHelper add(String name, @Nullable Object value) { + return this.addHolder(name, value); + } + + public Objects.ToStringHelper add(String name, boolean value) { + return this.addHolder(name, String.valueOf(value)); + } + + public Objects.ToStringHelper add(String name, char value) { + return this.addHolder(name, String.valueOf(value)); + } + + public Objects.ToStringHelper add(String name, double value) { + return this.addHolder(name, String.valueOf(value)); + } + + public Objects.ToStringHelper add(String name, float value) { + return this.addHolder(name, String.valueOf(value)); + } + + public Objects.ToStringHelper add(String name, int value) { + return this.addHolder(name, String.valueOf(value)); + } + + public Objects.ToStringHelper add(String name, long value) { + return this.addHolder(name, String.valueOf(value)); + } + + public Objects.ToStringHelper addValue(@Nullable Object value) { + return this.addHolder(value); + } + + public Objects.ToStringHelper addValue(boolean value) { + return this.addHolder(String.valueOf(value)); + } + + public Objects.ToStringHelper addValue(char value) { + return this.addHolder(String.valueOf(value)); + } + + public Objects.ToStringHelper addValue(double value) { + return this.addHolder(String.valueOf(value)); + } + + public Objects.ToStringHelper addValue(float value) { + return this.addHolder(String.valueOf(value)); + } + + public Objects.ToStringHelper addValue(int value) { + return this.addHolder(String.valueOf(value)); + } + + public Objects.ToStringHelper addValue(long value) { + return this.addHolder(String.valueOf(value)); + } + + public String toString() { + boolean omitNullValuesSnapshot = this.omitNullValues; + String nextSeparator = ""; + StringBuilder builder = (new StringBuilder(32)).append(this.className).append('{'); + + for(Objects.ToStringHelper.ValueHolder valueHolder = this.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); + } + } + + return builder.append('}').toString(); + } + + private Objects.ToStringHelper.ValueHolder addHolder() { + Objects.ToStringHelper.ValueHolder valueHolder = new Objects.ToStringHelper.ValueHolder(); + this.holderTail = this.holderTail.next = valueHolder; + return valueHolder; + } + + private Objects.ToStringHelper addHolder(@Nullable Object value) { + Objects.ToStringHelper.ValueHolder valueHolder = this.addHolder(); + valueHolder.value = value; + return this; + } + + private Objects.ToStringHelper addHolder(String name, @Nullable Object value) { + Objects.ToStringHelper.ValueHolder valueHolder = this.addHolder(); + valueHolder.value = value; + valueHolder.name = (String)Preconditions.checkNotNull(name); + return this; + } + + // $FF: synthetic method + ToStringHelper(String x0, Object x1) { + this(x0); + } + + private static final class ValueHolder { + String name; + Object value; + Objects.ToStringHelper.ValueHolder next; + + private ValueHolder() { + } + + // $FF: synthetic method + ValueHolder(Object x0) { + this(); + } + } + } +} diff --git a/src/main/com/google/common/base/Optional.java b/src/main/com/google/common/base/Optional.java new file mode 100644 index 0000000..56b9935 --- /dev/null +++ b/src/main/com/google/common/base/Optional.java @@ -0,0 +1,81 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +public abstract class Optional implements Serializable { + private static final long serialVersionUID = 0L; + + public static Optional absent() { + return Absent.withType(); + } + + public static Optional of(T reference) { + return new Present(Preconditions.checkNotNull(reference)); + } + + public static Optional fromNullable(@Nullable T nullableReference) { + return (Optional)(nullableReference == null ? absent() : new Present(nullableReference)); + } + + Optional() { + } + + public abstract boolean isPresent(); + + public abstract T get(); + + public abstract T or(T var1); + + public abstract Optional or(Optional var1); + + @Beta + public abstract T or(Supplier var1); + + @Nullable + public abstract T orNull(); + + public abstract Set asSet(); + + public abstract Optional transform(Function var1); + + public abstract boolean equals(@Nullable Object var1); + + public abstract int hashCode(); + + public abstract String toString(); + + @Beta + public static Iterable presentInstances(final Iterable> optionals) { + Preconditions.checkNotNull(optionals); + return new Iterable() { + public Iterator iterator() { + return new AbstractIterator() { + private final Iterator> iterator = (Iterator)Preconditions.checkNotNull(optionals.iterator()); + + protected T computeNext() { + while(true) { + if (this.iterator.hasNext()) { + Optional optional = (Optional)this.iterator.next(); + if (!optional.isPresent()) { + continue; + } + + return optional.get(); + } + + return this.endOfData(); + } + } + }; + } + }; + } +} diff --git a/src/main/com/google/common/base/PairwiseEquivalence.java b/src/main/com/google/common/base/PairwiseEquivalence.java new file mode 100644 index 0000000..f360692 --- /dev/null +++ b/src/main/com/google/common/base/PairwiseEquivalence.java @@ -0,0 +1,60 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Iterator; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class PairwiseEquivalence extends Equivalence> implements Serializable { + final Equivalence elementEquivalence; + private static final long serialVersionUID = 1L; + + PairwiseEquivalence(Equivalence elementEquivalence) { + this.elementEquivalence = (Equivalence)Preconditions.checkNotNull(elementEquivalence); + } + + protected boolean doEquivalent(Iterable iterableA, Iterable iterableB) { + Iterator iteratorA = iterableA.iterator(); + Iterator iteratorB = iterableB.iterator(); + + while(iteratorA.hasNext() && iteratorB.hasNext()) { + if (!this.elementEquivalence.equivalent(iteratorA.next(), iteratorB.next())) { + return false; + } + } + + return !iteratorA.hasNext() && !iteratorB.hasNext(); + } + + protected int doHash(Iterable iterable) { + int hash = 78721; + + Object element; + for(Iterator i$ = iterable.iterator(); i$.hasNext(); hash = hash * 24943 + this.elementEquivalence.hash(element)) { + element = i$.next(); + } + + return hash; + } + + public boolean equals(@Nullable Object object) { + if (object instanceof PairwiseEquivalence) { + PairwiseEquivalence that = (PairwiseEquivalence)object; + return this.elementEquivalence.equals(that.elementEquivalence); + } else { + return false; + } + } + + public int hashCode() { + return this.elementEquivalence.hashCode() ^ 1185147655; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.elementEquivalence)); + return (new StringBuilder(11 + var1.length())).append(var1).append(".pairwise()").toString(); + } +} diff --git a/src/main/com/google/common/base/Platform.java b/src/main/com/google/common/base/Platform.java new file mode 100644 index 0000000..00fc3ca --- /dev/null +++ b/src/main/com/google/common/base/Platform.java @@ -0,0 +1,25 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import java.lang.ref.WeakReference; + +@GwtCompatible( + emulated = true +) +final class Platform { + private Platform() { + } + + static long systemNanoTime() { + return System.nanoTime(); + } + + static CharMatcher precomputeCharMatcher(CharMatcher matcher) { + return matcher.precomputedInternal(); + } + + static > Optional getEnumIfPresent(Class enumClass, String value) { + WeakReference> ref = (WeakReference)Enums.getEnumConstants(enumClass).get(value); + return ref == null ? Optional.absent() : Optional.of(enumClass.cast(ref.get())); + } +} diff --git a/src/main/com/google/common/base/Preconditions.java b/src/main/com/google/common/base/Preconditions.java new file mode 100644 index 0000000..339fec8 --- /dev/null +++ b/src/main/com/google/common/base/Preconditions.java @@ -0,0 +1,161 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Preconditions { + private Preconditions() { + } + + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + public static void checkArgument(boolean expression, @Nullable Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + public static void checkArgument(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + public static void checkState(boolean expression, @Nullable Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + public static void checkState(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } else { + return reference; + } + } + + public static T checkNotNull(T reference, @Nullable Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } else { + return reference; + } + } + + public static T checkNotNull(T reference, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { + if (reference == null) { + throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); + } else { + return reference; + } + } + + public static int checkElementIndex(int index, int size) { + return checkElementIndex(index, size, "index"); + } + + public static int checkElementIndex(int index, int size, @Nullable String desc) { + if (index >= 0 && index < size) { + return index; + } else { + throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); + } + } + + private static String badElementIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString()); + } else { + return format("%s (%s) must be less than size (%s)", desc, index, size); + } + } + + public static int checkPositionIndex(int index, int size) { + return checkPositionIndex(index, size, "index"); + } + + public static int checkPositionIndex(int index, int size, @Nullable String desc) { + if (index >= 0 && index <= size) { + return index; + } else { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); + } + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return format("%s (%s) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException((new StringBuilder(26)).append("negative size: ").append(size).toString()); + } else { + return format("%s (%s) must not be greater than size (%s)", desc, index, size); + } + } + + public static void checkPositionIndexes(int start, int end, int size) { + if (start < 0 || end < start || end > size) { + throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); + } + } + + private static String badPositionIndexes(int start, int end, int size) { + if (start >= 0 && start <= size) { + return end >= 0 && end <= size ? format("end index (%s) must not be less than start index (%s)", end, start) : badPositionIndex(end, size, "end index"); + } else { + return badPositionIndex(start, size, "start index"); + } + } + + static String format(String template, @Nullable Object... args) { + template = String.valueOf(template); + StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); + int templateStart = 0; + + int i; + int placeholderStart; + for(i = 0; i < args.length; templateStart = placeholderStart + 2) { + placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + + builder.append(template.substring(templateStart, placeholderStart)); + builder.append(args[i++]); + } + + builder.append(template.substring(templateStart)); + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + + while(i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + + builder.append(']'); + } + + return builder.toString(); + } +} diff --git a/src/main/com/google/common/base/Predicate.java b/src/main/com/google/common/base/Predicate.java new file mode 100644 index 0000000..d7b030e --- /dev/null +++ b/src/main/com/google/common/base/Predicate.java @@ -0,0 +1,11 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +public interface Predicate { + boolean apply(@Nullable T var1); + + boolean equals(@Nullable Object var1); +} diff --git a/src/main/com/google/common/base/Predicates.java b/src/main/com/google/common/base/Predicates.java new file mode 100644 index 0000000..45c2fb8 --- /dev/null +++ b/src/main/com/google/common/base/Predicates.java @@ -0,0 +1,536 @@ +package com.google.common.base; + +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.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Predicates { + private static final Joiner COMMA_JOINER = Joiner.on(','); + + private Predicates() { + } + + @GwtCompatible( + serializable = true + ) + public static Predicate alwaysTrue() { + return Predicates.ObjectPredicate.ALWAYS_TRUE.withNarrowedType(); + } + + @GwtCompatible( + serializable = true + ) + public static Predicate alwaysFalse() { + return Predicates.ObjectPredicate.ALWAYS_FALSE.withNarrowedType(); + } + + @GwtCompatible( + serializable = true + ) + public static Predicate isNull() { + return Predicates.ObjectPredicate.IS_NULL.withNarrowedType(); + } + + @GwtCompatible( + serializable = true + ) + public static Predicate notNull() { + return Predicates.ObjectPredicate.NOT_NULL.withNarrowedType(); + } + + public static Predicate not(Predicate predicate) { + return new Predicates.NotPredicate(predicate); + } + + public static Predicate and(Iterable> components) { + return new Predicates.AndPredicate(defensiveCopy(components)); + } + + public static Predicate and(Predicate... components) { + return new Predicates.AndPredicate(defensiveCopy((Object[])components)); + } + + public static Predicate and(Predicate first, Predicate second) { + return new Predicates.AndPredicate(asList((Predicate)Preconditions.checkNotNull(first), (Predicate)Preconditions.checkNotNull(second))); + } + + public static Predicate or(Iterable> components) { + return new Predicates.OrPredicate(defensiveCopy(components)); + } + + public static Predicate or(Predicate... components) { + return new Predicates.OrPredicate(defensiveCopy((Object[])components)); + } + + public static Predicate or(Predicate first, Predicate second) { + return new Predicates.OrPredicate(asList((Predicate)Preconditions.checkNotNull(first), (Predicate)Preconditions.checkNotNull(second))); + } + + public static Predicate equalTo(@Nullable T target) { + return (Predicate)(target == null ? isNull() : new Predicates.IsEqualToPredicate(target)); + } + + @GwtIncompatible("Class.isInstance") + public static Predicate instanceOf(Class clazz) { + return new Predicates.InstanceOfPredicate(clazz); + } + + @GwtIncompatible("Class.isAssignableFrom") + @Beta + public static Predicate> assignableFrom(Class clazz) { + return new Predicates.AssignableFromPredicate(clazz); + } + + public static Predicate in(Collection target) { + return new Predicates.InPredicate(target); + } + + public static Predicate compose(Predicate predicate, Function function) { + return new Predicates.CompositionPredicate(predicate, function); + } + + @GwtIncompatible("java.util.regex.Pattern") + public static Predicate containsPattern(String pattern) { + return new Predicates.ContainsPatternFromStringPredicate(pattern); + } + + @GwtIncompatible("java.util.regex.Pattern") + public static Predicate contains(Pattern pattern) { + return new Predicates.ContainsPatternPredicate(pattern); + } + + private static List> asList(Predicate first, Predicate second) { + return Arrays.asList(first, second); + } + + private static List defensiveCopy(T... array) { + return defensiveCopy((Iterable)Arrays.asList(array)); + } + + static List defensiveCopy(Iterable iterable) { + ArrayList list = new ArrayList(); + Iterator i$ = iterable.iterator(); + + while(i$.hasNext()) { + T element = i$.next(); + list.add(Preconditions.checkNotNull(element)); + } + + return list; + } + + @GwtIncompatible("Only used by other GWT-incompatible code.") + private static class ContainsPatternFromStringPredicate extends Predicates.ContainsPatternPredicate { + private static final long serialVersionUID = 0L; + + ContainsPatternFromStringPredicate(String string) { + super(Pattern.compile(string)); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.pattern.pattern())); + return (new StringBuilder(28 + var1.length())).append("Predicates.containsPattern(").append(var1).append(")").toString(); + } + } + + @GwtIncompatible("Only used by other GWT-incompatible code.") + private static class ContainsPatternPredicate implements Predicate, Serializable { + final Pattern pattern; + private static final long serialVersionUID = 0L; + + ContainsPatternPredicate(Pattern pattern) { + this.pattern = (Pattern)Preconditions.checkNotNull(pattern); + } + + public boolean apply(CharSequence t) { + return this.pattern.matcher(t).find(); + } + + public int hashCode() { + return Objects.hashCode(this.pattern.pattern(), this.pattern.flags()); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof Predicates.ContainsPatternPredicate)) { + return false; + } else { + Predicates.ContainsPatternPredicate that = (Predicates.ContainsPatternPredicate)obj; + return Objects.equal(this.pattern.pattern(), that.pattern.pattern()) && Objects.equal(this.pattern.flags(), that.pattern.flags()); + } + } + + public String toString() { + String patternString = Objects.toStringHelper((Object)this.pattern).add("pattern", this.pattern.pattern()).add("pattern.flags", this.pattern.flags()).toString(); + String var2 = String.valueOf(String.valueOf(patternString)); + return (new StringBuilder(21 + var2.length())).append("Predicates.contains(").append(var2).append(")").toString(); + } + } + + private static class CompositionPredicate implements Predicate, Serializable { + final Predicate p; + final Function f; + private static final long serialVersionUID = 0L; + + private CompositionPredicate(Predicate p, Function f) { + this.p = (Predicate)Preconditions.checkNotNull(p); + this.f = (Function)Preconditions.checkNotNull(f); + } + + public boolean apply(@Nullable A a) { + return this.p.apply(this.f.apply(a)); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof Predicates.CompositionPredicate)) { + return false; + } else { + Predicates.CompositionPredicate that = (Predicates.CompositionPredicate)obj; + return this.f.equals(that.f) && this.p.equals(that.p); + } + } + + public int hashCode() { + return this.f.hashCode() ^ this.p.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.p.toString())); + String var2 = String.valueOf(String.valueOf(this.f.toString())); + return (new StringBuilder(2 + var1.length() + var2.length())).append(var1).append("(").append(var2).append(")").toString(); + } + + // $FF: synthetic method + CompositionPredicate(Predicate x0, Function x1, Object x2) { + this(x0, x1); + } + } + + private static class InPredicate implements Predicate, Serializable { + private final Collection target; + private static final long serialVersionUID = 0L; + + private InPredicate(Collection target) { + this.target = (Collection)Preconditions.checkNotNull(target); + } + + public boolean apply(@Nullable T t) { + try { + return this.target.contains(t); + } catch (NullPointerException var3) { + return false; + } catch (ClassCastException var4) { + return false; + } + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Predicates.InPredicate) { + Predicates.InPredicate that = (Predicates.InPredicate)obj; + return this.target.equals(that.target); + } else { + return false; + } + } + + public int hashCode() { + return this.target.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.target)); + return (new StringBuilder(15 + var1.length())).append("Predicates.in(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + InPredicate(Collection x0, Object x1) { + this(x0); + } + } + + @GwtIncompatible("Class.isAssignableFrom") + private static class AssignableFromPredicate implements Predicate>, Serializable { + private final Class clazz; + private static final long serialVersionUID = 0L; + + private AssignableFromPredicate(Class clazz) { + this.clazz = (Class)Preconditions.checkNotNull(clazz); + } + + public boolean apply(Class input) { + return this.clazz.isAssignableFrom(input); + } + + public int hashCode() { + return this.clazz.hashCode(); + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Predicates.AssignableFromPredicate) { + Predicates.AssignableFromPredicate that = (Predicates.AssignableFromPredicate)obj; + return this.clazz == that.clazz; + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.clazz.getName())); + return (new StringBuilder(27 + var1.length())).append("Predicates.assignableFrom(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + AssignableFromPredicate(Class x0, Object x1) { + this(x0); + } + } + + @GwtIncompatible("Class.isInstance") + private static class InstanceOfPredicate implements Predicate, Serializable { + private final Class clazz; + private static final long serialVersionUID = 0L; + + private InstanceOfPredicate(Class clazz) { + this.clazz = (Class)Preconditions.checkNotNull(clazz); + } + + public boolean apply(@Nullable Object o) { + return this.clazz.isInstance(o); + } + + public int hashCode() { + return this.clazz.hashCode(); + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Predicates.InstanceOfPredicate) { + Predicates.InstanceOfPredicate that = (Predicates.InstanceOfPredicate)obj; + return this.clazz == that.clazz; + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.clazz.getName())); + return (new StringBuilder(23 + var1.length())).append("Predicates.instanceOf(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + InstanceOfPredicate(Class x0, Object x1) { + this(x0); + } + } + + private static class IsEqualToPredicate implements Predicate, Serializable { + private final T target; + private static final long serialVersionUID = 0L; + + private IsEqualToPredicate(T target) { + this.target = target; + } + + public boolean apply(T t) { + return this.target.equals(t); + } + + public int hashCode() { + return this.target.hashCode(); + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Predicates.IsEqualToPredicate) { + Predicates.IsEqualToPredicate that = (Predicates.IsEqualToPredicate)obj; + return this.target.equals(that.target); + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.target)); + return (new StringBuilder(20 + var1.length())).append("Predicates.equalTo(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + IsEqualToPredicate(Object x0, Object x1) { + this(x0); + } + } + + private static class OrPredicate implements Predicate, Serializable { + private final List> components; + private static final long serialVersionUID = 0L; + + private OrPredicate(List> components) { + this.components = components; + } + + public boolean apply(@Nullable T t) { + for(int i = 0; i < this.components.size(); ++i) { + if (((Predicate)this.components.get(i)).apply(t)) { + return true; + } + } + + return false; + } + + public int hashCode() { + return this.components.hashCode() + 87855567; + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Predicates.OrPredicate) { + Predicates.OrPredicate that = (Predicates.OrPredicate)obj; + return this.components.equals(that.components); + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(Predicates.COMMA_JOINER.join((Iterable)this.components))); + return (new StringBuilder(15 + var1.length())).append("Predicates.or(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + OrPredicate(List x0, Object x1) { + this(x0); + } + } + + private static class AndPredicate implements Predicate, Serializable { + private final List> components; + private static final long serialVersionUID = 0L; + + private AndPredicate(List> components) { + this.components = components; + } + + public boolean apply(@Nullable T t) { + for(int i = 0; i < this.components.size(); ++i) { + if (!((Predicate)this.components.get(i)).apply(t)) { + return false; + } + } + + return true; + } + + public int hashCode() { + return this.components.hashCode() + 306654252; + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Predicates.AndPredicate) { + Predicates.AndPredicate that = (Predicates.AndPredicate)obj; + return this.components.equals(that.components); + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(Predicates.COMMA_JOINER.join((Iterable)this.components))); + return (new StringBuilder(16 + var1.length())).append("Predicates.and(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + AndPredicate(List x0, Object x1) { + this(x0); + } + } + + private static class NotPredicate implements Predicate, Serializable { + final Predicate predicate; + private static final long serialVersionUID = 0L; + + NotPredicate(Predicate predicate) { + this.predicate = (Predicate)Preconditions.checkNotNull(predicate); + } + + public boolean apply(@Nullable T t) { + return !this.predicate.apply(t); + } + + public int hashCode() { + return ~this.predicate.hashCode(); + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Predicates.NotPredicate) { + Predicates.NotPredicate that = (Predicates.NotPredicate)obj; + return this.predicate.equals(that.predicate); + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.predicate.toString())); + return (new StringBuilder(16 + var1.length())).append("Predicates.not(").append(var1).append(")").toString(); + } + } + + static enum ObjectPredicate implements Predicate { + ALWAYS_TRUE { + public boolean apply(@Nullable Object o) { + return true; + } + + public String toString() { + return "Predicates.alwaysTrue()"; + } + }, + ALWAYS_FALSE { + public boolean apply(@Nullable Object o) { + return false; + } + + public String toString() { + return "Predicates.alwaysFalse()"; + } + }, + IS_NULL { + public boolean apply(@Nullable Object o) { + return o == null; + } + + public String toString() { + return "Predicates.isNull()"; + } + }, + NOT_NULL { + public boolean apply(@Nullable Object o) { + return o != null; + } + + public String toString() { + return "Predicates.notNull()"; + } + }; + + private ObjectPredicate() { + } + + Predicate withNarrowedType() { + return this; + } + + // $FF: synthetic method + ObjectPredicate(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/base/Present.java b/src/main/com/google/common/base/Present.java new file mode 100644 index 0000000..2a339ca --- /dev/null +++ b/src/main/com/google/common/base/Present.java @@ -0,0 +1,69 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collections; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +final class Present extends Optional { + private final T reference; + private static final long serialVersionUID = 0L; + + Present(T reference) { + this.reference = reference; + } + + public boolean isPresent() { + return true; + } + + public T get() { + return this.reference; + } + + public T or(T defaultValue) { + Preconditions.checkNotNull(defaultValue, "use Optional.orNull() instead of Optional.or(null)"); + return this.reference; + } + + public Optional or(Optional secondChoice) { + Preconditions.checkNotNull(secondChoice); + return this; + } + + public T or(Supplier supplier) { + Preconditions.checkNotNull(supplier); + return this.reference; + } + + public T orNull() { + return this.reference; + } + + public Set asSet() { + return Collections.singleton(this.reference); + } + + public Optional transform(Function function) { + return new Present(Preconditions.checkNotNull(function.apply(this.reference), "the Function passed to Optional.transform() must not return null.")); + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Present) { + Present other = (Present)object; + return this.reference.equals(other.reference); + } else { + return false; + } + } + + public int hashCode() { + return 1502476572 + this.reference.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.reference)); + return (new StringBuilder(13 + var1.length())).append("Optional.of(").append(var1).append(")").toString(); + } +} diff --git a/src/main/com/google/common/base/SmallCharMatcher.java b/src/main/com/google/common/base/SmallCharMatcher.java new file mode 100644 index 0000000..93975cf --- /dev/null +++ b/src/main/com/google/common/base/SmallCharMatcher.java @@ -0,0 +1,106 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import java.util.BitSet; + +@GwtIncompatible("no precomputation is done in GWT") +final class SmallCharMatcher extends CharMatcher.FastMatcher { + static final int MAX_SIZE = 1023; + private final char[] table; + private final boolean containsZero; + private final long filter; + private static final int C1 = -862048943; + private static final int C2 = 461845907; + private static final double DESIRED_LOAD_FACTOR = 0.5D; + + private SmallCharMatcher(char[] table, long filter, boolean containsZero, String description) { + super(description); + this.table = table; + this.filter = filter; + this.containsZero = containsZero; + } + + static int smear(int hashCode) { + return 461845907 * Integer.rotateLeft(hashCode * -862048943, 15); + } + + private boolean checkFilter(int c) { + return 1L == (1L & this.filter >> c); + } + + @VisibleForTesting + static int chooseTableSize(int setSize) { + if (setSize == 1) { + return 2; + } else { + int tableSize; + for(tableSize = Integer.highestOneBit(setSize - 1) << 1; (double)tableSize * 0.5D < (double)setSize; tableSize <<= 1) { + } + + return tableSize; + } + } + + static CharMatcher from(BitSet chars, String description) { + long filter = 0L; + int size = chars.cardinality(); + boolean containsZero = chars.get(0); + char[] table = new char[chooseTableSize(size)]; + int mask = table.length - 1; + + for(int c = chars.nextSetBit(0); c != -1; c = chars.nextSetBit(c + 1)) { + filter |= 1L << c; + + int index; + for(index = smear(c) & mask; table[index] != 0; index = index + 1 & mask) { + } + + table[index] = (char)c; + } + + return new SmallCharMatcher(table, filter, containsZero, description); + } + + public boolean matches(char c) { + if (c == 0) { + return this.containsZero; + } else if (!this.checkFilter(c)) { + return false; + } else { + int mask = this.table.length - 1; + int startingIndex = smear(c) & mask; + int index = startingIndex; + + while(this.table[index] != 0) { + if (this.table[index] == c) { + return true; + } + + index = index + 1 & mask; + if (index == startingIndex) { + return false; + } + } + + return false; + } + } + + void setBits(BitSet table) { + if (this.containsZero) { + table.set(0); + } + + char[] arr$ = this.table; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char c = arr$[i$]; + if (c != 0) { + table.set(c); + } + } + + } +} diff --git a/src/main/com/google/common/base/Splitter.java b/src/main/com/google/common/base/Splitter.java new file mode 100644 index 0000000..94d464f --- /dev/null +++ b/src/main/com/google/common/base/Splitter.java @@ -0,0 +1,310 @@ +package com.google.common.base; + +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.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.CheckReturnValue; + +@GwtCompatible( + emulated = true +) +public final class Splitter { + private final CharMatcher trimmer; + private final boolean omitEmptyStrings; + private final Splitter.Strategy strategy; + private final int limit; + + private Splitter(Splitter.Strategy strategy) { + this(strategy, false, CharMatcher.NONE, Integer.MAX_VALUE); + } + + private Splitter(Splitter.Strategy strategy, boolean omitEmptyStrings, CharMatcher trimmer, int limit) { + this.strategy = strategy; + this.omitEmptyStrings = omitEmptyStrings; + this.trimmer = trimmer; + this.limit = limit; + } + + public static Splitter on(char separator) { + return on(CharMatcher.is(separator)); + } + + public static Splitter on(final CharMatcher separatorMatcher) { + Preconditions.checkNotNull(separatorMatcher); + return new Splitter(new Splitter.Strategy() { + public Splitter.SplittingIterator iterator(Splitter splitter, CharSequence toSplit) { + return new Splitter.SplittingIterator(splitter, toSplit) { + int separatorStart(int start) { + return separatorMatcher.indexIn(this.toSplit, start); + } + + int separatorEnd(int separatorPosition) { + return separatorPosition + 1; + } + }; + } + }); + } + + public static Splitter on(final String separator) { + Preconditions.checkArgument(separator.length() != 0, "The separator may not be the empty string."); + return new Splitter(new Splitter.Strategy() { + public Splitter.SplittingIterator iterator(Splitter splitter, CharSequence toSplit) { + return new Splitter.SplittingIterator(splitter, toSplit) { + public int separatorStart(int start) { + int separatorLength = separator.length(); + int p = start; + + label24: + for(int last = this.toSplit.length() - separatorLength; p <= last; ++p) { + for(int i = 0; i < separatorLength; ++i) { + if (this.toSplit.charAt(i + p) != separator.charAt(i)) { + continue label24; + } + } + + return p; + } + + return -1; + } + + public int separatorEnd(int separatorPosition) { + return separatorPosition + separator.length(); + } + }; + } + }); + } + + @GwtIncompatible("java.util.regex") + public static Splitter on(final Pattern separatorPattern) { + Preconditions.checkNotNull(separatorPattern); + Preconditions.checkArgument(!separatorPattern.matcher("").matches(), "The pattern may not match the empty string: %s", separatorPattern); + return new Splitter(new Splitter.Strategy() { + public Splitter.SplittingIterator iterator(Splitter splitter, CharSequence toSplit) { + final Matcher matcher = separatorPattern.matcher(toSplit); + return new Splitter.SplittingIterator(splitter, toSplit) { + public int separatorStart(int start) { + return matcher.find(start) ? matcher.start() : -1; + } + + public int separatorEnd(int separatorPosition) { + return matcher.end(); + } + }; + } + }); + } + + @GwtIncompatible("java.util.regex") + public static Splitter onPattern(String separatorPattern) { + return on(Pattern.compile(separatorPattern)); + } + + public static Splitter fixedLength(final int length) { + Preconditions.checkArgument(length > 0, "The length may not be less than 1"); + return new Splitter(new Splitter.Strategy() { + public Splitter.SplittingIterator iterator(Splitter splitter, CharSequence toSplit) { + return new Splitter.SplittingIterator(splitter, toSplit) { + public int separatorStart(int start) { + int nextChunkStart = start + length; + return nextChunkStart < this.toSplit.length() ? nextChunkStart : -1; + } + + public int separatorEnd(int separatorPosition) { + return separatorPosition; + } + }; + } + }); + } + + @CheckReturnValue + public Splitter omitEmptyStrings() { + return new Splitter(this.strategy, true, this.trimmer, this.limit); + } + + @CheckReturnValue + public Splitter limit(int limit) { + Preconditions.checkArgument(limit > 0, "must be greater than zero: %s", limit); + return new Splitter(this.strategy, this.omitEmptyStrings, this.trimmer, limit); + } + + @CheckReturnValue + public Splitter trimResults() { + return this.trimResults(CharMatcher.WHITESPACE); + } + + @CheckReturnValue + public Splitter trimResults(CharMatcher trimmer) { + Preconditions.checkNotNull(trimmer); + return new Splitter(this.strategy, this.omitEmptyStrings, trimmer, this.limit); + } + + public Iterable split(final CharSequence sequence) { + Preconditions.checkNotNull(sequence); + return new Iterable() { + public Iterator iterator() { + return Splitter.this.splittingIterator(sequence); + } + + public String toString() { + return Joiner.on(", ").appendTo((StringBuilder)(new StringBuilder()).append('['), (Iterable)this).append(']').toString(); + } + }; + } + + private Iterator splittingIterator(CharSequence sequence) { + return this.strategy.iterator(this, sequence); + } + + @Beta + public List splitToList(CharSequence sequence) { + Preconditions.checkNotNull(sequence); + Iterator iterator = this.splittingIterator(sequence); + ArrayList result = new ArrayList(); + + while(iterator.hasNext()) { + result.add(iterator.next()); + } + + return Collections.unmodifiableList(result); + } + + @CheckReturnValue + @Beta + public Splitter.MapSplitter withKeyValueSeparator(String separator) { + return this.withKeyValueSeparator(on(separator)); + } + + @CheckReturnValue + @Beta + public Splitter.MapSplitter withKeyValueSeparator(char separator) { + return this.withKeyValueSeparator(on(separator)); + } + + @CheckReturnValue + @Beta + public Splitter.MapSplitter withKeyValueSeparator(Splitter keyValueSplitter) { + return new Splitter.MapSplitter(this, keyValueSplitter); + } + + private abstract static class SplittingIterator extends AbstractIterator { + final CharSequence toSplit; + final CharMatcher trimmer; + final boolean omitEmptyStrings; + int offset = 0; + int limit; + + abstract int separatorStart(int var1); + + abstract int separatorEnd(int var1); + + protected SplittingIterator(Splitter splitter, CharSequence toSplit) { + this.trimmer = splitter.trimmer; + this.omitEmptyStrings = splitter.omitEmptyStrings; + this.limit = splitter.limit; + this.toSplit = toSplit; + } + + protected String computeNext() { + int nextStart = this.offset; + + while(true) { + while(this.offset != -1) { + int start = nextStart; + int separatorPosition = this.separatorStart(this.offset); + int end; + if (separatorPosition == -1) { + end = this.toSplit.length(); + this.offset = -1; + } else { + end = separatorPosition; + this.offset = this.separatorEnd(separatorPosition); + } + + if (this.offset != nextStart) { + while(start < end && this.trimmer.matches(this.toSplit.charAt(start))) { + ++start; + } + + while(end > start && this.trimmer.matches(this.toSplit.charAt(end - 1))) { + --end; + } + + if (!this.omitEmptyStrings || start != end) { + if (this.limit == 1) { + end = this.toSplit.length(); + + for(this.offset = -1; end > start && this.trimmer.matches(this.toSplit.charAt(end - 1)); --end) { + } + } else { + --this.limit; + } + + return this.toSplit.subSequence(start, end).toString(); + } + + nextStart = this.offset; + } else { + ++this.offset; + if (this.offset >= this.toSplit.length()) { + this.offset = -1; + } + } + } + + return (String)this.endOfData(); + } + } + } + + private interface Strategy { + Iterator iterator(Splitter var1, CharSequence var2); + } + + @Beta + public static final class MapSplitter { + private static final String INVALID_ENTRY_MESSAGE = "Chunk [%s] is not a valid entry"; + private final Splitter outerSplitter; + private final Splitter entrySplitter; + + private MapSplitter(Splitter outerSplitter, Splitter entrySplitter) { + this.outerSplitter = outerSplitter; + this.entrySplitter = (Splitter)Preconditions.checkNotNull(entrySplitter); + } + + public Map split(CharSequence sequence) { + Map map = new LinkedHashMap(); + Iterator i$ = this.outerSplitter.split(sequence).iterator(); + + while(i$.hasNext()) { + String entry = (String)i$.next(); + Iterator entryFields = this.entrySplitter.splittingIterator(entry); + Preconditions.checkArgument(entryFields.hasNext(), "Chunk [%s] is not a valid entry", entry); + String key = (String)entryFields.next(); + Preconditions.checkArgument(!map.containsKey(key), "Duplicate key [%s] found.", key); + Preconditions.checkArgument(entryFields.hasNext(), "Chunk [%s] is not a valid entry", entry); + String value = (String)entryFields.next(); + map.put(key, value); + Preconditions.checkArgument(!entryFields.hasNext(), "Chunk [%s] is not a valid entry", entry); + } + + return Collections.unmodifiableMap(map); + } + + // $FF: synthetic method + MapSplitter(Splitter x0, Splitter x1, Object x2) { + this(x0, x1); + } + } +} diff --git a/src/main/com/google/common/base/StandardSystemProperty.java b/src/main/com/google/common/base/StandardSystemProperty.java new file mode 100644 index 0000000..ebe0d39 --- /dev/null +++ b/src/main/com/google/common/base/StandardSystemProperty.java @@ -0,0 +1,59 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import javax.annotation.Nullable; + +@Beta +@GwtIncompatible("java.lang.System#getProperty") +public enum StandardSystemProperty { + JAVA_VERSION("java.version"), + JAVA_VENDOR("java.vendor"), + JAVA_VENDOR_URL("java.vendor.url"), + JAVA_HOME("java.home"), + JAVA_VM_SPECIFICATION_VERSION("java.vm.specification.version"), + JAVA_VM_SPECIFICATION_VENDOR("java.vm.specification.vendor"), + JAVA_VM_SPECIFICATION_NAME("java.vm.specification.name"), + JAVA_VM_VERSION("java.vm.version"), + JAVA_VM_VENDOR("java.vm.vendor"), + JAVA_VM_NAME("java.vm.name"), + JAVA_SPECIFICATION_VERSION("java.specification.version"), + JAVA_SPECIFICATION_VENDOR("java.specification.vendor"), + JAVA_SPECIFICATION_NAME("java.specification.name"), + JAVA_CLASS_VERSION("java.class.version"), + JAVA_CLASS_PATH("java.class.path"), + JAVA_LIBRARY_PATH("java.library.path"), + JAVA_IO_TMPDIR("java.io.tmpdir"), + JAVA_COMPILER("java.compiler"), + JAVA_EXT_DIRS("java.ext.dirs"), + OS_NAME("os.name"), + OS_ARCH("os.arch"), + OS_VERSION("os.version"), + FILE_SEPARATOR("file.separator"), + PATH_SEPARATOR("path.separator"), + LINE_SEPARATOR("line.separator"), + USER_NAME("user.name"), + USER_HOME("user.home"), + USER_DIR("user.dir"); + + private final String key; + + private StandardSystemProperty(String key) { + this.key = key; + } + + public String key() { + return this.key; + } + + @Nullable + public String value() { + return System.getProperty(this.key); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.key())); + String var2 = String.valueOf(String.valueOf(this.value())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append("=").append(var2).toString(); + } +} diff --git a/src/main/com/google/common/base/Stopwatch.java b/src/main/com/google/common/base/Stopwatch.java new file mode 100644 index 0000000..1463e0a --- /dev/null +++ b/src/main/com/google/common/base/Stopwatch.java @@ -0,0 +1,123 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.util.concurrent.TimeUnit; + +@Beta +@GwtCompatible( + emulated = true +) +public final class Stopwatch { + private final Ticker ticker; + private boolean isRunning; + private long elapsedNanos; + private long startTick; + + public static Stopwatch createUnstarted() { + return new Stopwatch(); + } + + public static Stopwatch createUnstarted(Ticker ticker) { + return new Stopwatch(ticker); + } + + public static Stopwatch createStarted() { + return (new Stopwatch()).start(); + } + + public static Stopwatch createStarted(Ticker ticker) { + return (new Stopwatch(ticker)).start(); + } + + /** @deprecated */ + @Deprecated + Stopwatch() { + this(Ticker.systemTicker()); + } + + /** @deprecated */ + @Deprecated + Stopwatch(Ticker ticker) { + this.ticker = (Ticker)Preconditions.checkNotNull(ticker, "ticker"); + } + + public boolean isRunning() { + return this.isRunning; + } + + public Stopwatch start() { + Preconditions.checkState(!this.isRunning, "This stopwatch is already running."); + this.isRunning = true; + this.startTick = this.ticker.read(); + return this; + } + + public Stopwatch stop() { + long tick = this.ticker.read(); + Preconditions.checkState(this.isRunning, "This stopwatch is already stopped."); + this.isRunning = false; + this.elapsedNanos += tick - this.startTick; + return this; + } + + public Stopwatch reset() { + this.elapsedNanos = 0L; + this.isRunning = false; + return this; + } + + private long elapsedNanos() { + return this.isRunning ? this.ticker.read() - this.startTick + this.elapsedNanos : this.elapsedNanos; + } + + public long elapsed(TimeUnit desiredUnit) { + return desiredUnit.convert(this.elapsedNanos(), TimeUnit.NANOSECONDS); + } + + @GwtIncompatible("String.format()") + public String toString() { + long nanos = this.elapsedNanos(); + TimeUnit unit = chooseUnit(nanos); + double value = (double)nanos / (double)TimeUnit.NANOSECONDS.convert(1L, unit); + return String.format("%.4g %s", value, abbreviate(unit)); + } + + private static TimeUnit chooseUnit(long nanos) { + if (TimeUnit.DAYS.convert(nanos, TimeUnit.NANOSECONDS) > 0L) { + return TimeUnit.DAYS; + } else if (TimeUnit.HOURS.convert(nanos, TimeUnit.NANOSECONDS) > 0L) { + return TimeUnit.HOURS; + } else if (TimeUnit.MINUTES.convert(nanos, TimeUnit.NANOSECONDS) > 0L) { + return TimeUnit.MINUTES; + } else if (TimeUnit.SECONDS.convert(nanos, TimeUnit.NANOSECONDS) > 0L) { + return TimeUnit.SECONDS; + } else if (TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS) > 0L) { + return TimeUnit.MILLISECONDS; + } else { + return TimeUnit.MICROSECONDS.convert(nanos, TimeUnit.NANOSECONDS) > 0L ? TimeUnit.MICROSECONDS : TimeUnit.NANOSECONDS; + } + } + + private static String abbreviate(TimeUnit unit) { + switch(unit) { + case NANOSECONDS: + return "ns"; + case MICROSECONDS: + return "μs"; + case MILLISECONDS: + return "ms"; + case SECONDS: + return "s"; + case MINUTES: + return "min"; + case HOURS: + return "h"; + case DAYS: + return "d"; + default: + throw new AssertionError(); + } + } +} diff --git a/src/main/com/google/common/base/Strings.java b/src/main/com/google/common/base/Strings.java new file mode 100644 index 0000000..a388786 --- /dev/null +++ b/src/main/com/google/common/base/Strings.java @@ -0,0 +1,119 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Strings { + private Strings() { + } + + public static String nullToEmpty(@Nullable String string) { + return string == null ? "" : string; + } + + @Nullable + public static String emptyToNull(@Nullable String string) { + return isNullOrEmpty(string) ? null : string; + } + + public static boolean isNullOrEmpty(@Nullable String string) { + return string == null || string.length() == 0; + } + + public static String padStart(String string, int minLength, char padChar) { + Preconditions.checkNotNull(string); + if (string.length() >= minLength) { + return string; + } else { + StringBuilder sb = new StringBuilder(minLength); + + for(int i = string.length(); i < minLength; ++i) { + sb.append(padChar); + } + + sb.append(string); + return sb.toString(); + } + } + + public static String padEnd(String string, int minLength, char padChar) { + Preconditions.checkNotNull(string); + if (string.length() >= minLength) { + return string; + } else { + StringBuilder sb = new StringBuilder(minLength); + sb.append(string); + + for(int i = string.length(); i < minLength; ++i) { + sb.append(padChar); + } + + return sb.toString(); + } + } + + public static String repeat(String string, int count) { + Preconditions.checkNotNull(string); + if (count <= 1) { + Preconditions.checkArgument(count >= 0, "invalid count: %s", count); + return count == 0 ? "" : string; + } else { + int len = string.length(); + long longSize = (long)len * (long)count; + int size = (int)longSize; + if ((long)size != longSize) { + throw new ArrayIndexOutOfBoundsException((new StringBuilder(51)).append("Required array size too large: ").append(longSize).toString()); + } else { + char[] array = new char[size]; + string.getChars(0, len, array, 0); + + int n; + for(n = len; n < size - n; n <<= 1) { + System.arraycopy(array, 0, array, n, n); + } + + System.arraycopy(array, 0, array, n, size - n); + return new String(array); + } + } + } + + public static String commonPrefix(CharSequence a, CharSequence b) { + Preconditions.checkNotNull(a); + Preconditions.checkNotNull(b); + int maxPrefixLength = Math.min(a.length(), b.length()); + + int p; + for(p = 0; p < maxPrefixLength && a.charAt(p) == b.charAt(p); ++p) { + } + + if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) { + --p; + } + + return a.subSequence(0, p).toString(); + } + + public static String commonSuffix(CharSequence a, CharSequence b) { + Preconditions.checkNotNull(a); + Preconditions.checkNotNull(b); + int maxSuffixLength = Math.min(a.length(), b.length()); + + int s; + for(s = 0; s < maxSuffixLength && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1); ++s) { + } + + if (validSurrogatePairAt(a, a.length() - s - 1) || validSurrogatePairAt(b, b.length() - s - 1)) { + --s; + } + + return a.subSequence(a.length() - s, a.length()).toString(); + } + + @VisibleForTesting + static boolean validSurrogatePairAt(CharSequence string, int index) { + return index >= 0 && index <= string.length() - 2 && Character.isHighSurrogate(string.charAt(index)) && Character.isLowSurrogate(string.charAt(index + 1)); + } +} diff --git a/src/main/com/google/common/base/Supplier.java b/src/main/com/google/common/base/Supplier.java new file mode 100644 index 0000000..8286831 --- /dev/null +++ b/src/main/com/google/common/base/Supplier.java @@ -0,0 +1,8 @@ +package com.google.common.base; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible +public interface Supplier { + T get(); +} diff --git a/src/main/com/google/common/base/Suppliers.java b/src/main/com/google/common/base/Suppliers.java new file mode 100644 index 0000000..b6196e6 --- /dev/null +++ b/src/main/com/google/common/base/Suppliers.java @@ -0,0 +1,213 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; +import java.io.Serializable; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Suppliers { + private Suppliers() { + } + + public static Supplier compose(Function function, Supplier supplier) { + Preconditions.checkNotNull(function); + Preconditions.checkNotNull(supplier); + return new Suppliers.SupplierComposition(function, supplier); + } + + public static Supplier memoize(Supplier delegate) { + return (Supplier)(delegate instanceof Suppliers.MemoizingSupplier ? delegate : new Suppliers.MemoizingSupplier((Supplier)Preconditions.checkNotNull(delegate))); + } + + public static Supplier memoizeWithExpiration(Supplier delegate, long duration, TimeUnit unit) { + return new Suppliers.ExpiringMemoizingSupplier(delegate, duration, unit); + } + + public static Supplier ofInstance(@Nullable T instance) { + return new Suppliers.SupplierOfInstance(instance); + } + + public static Supplier synchronizedSupplier(Supplier delegate) { + return new Suppliers.ThreadSafeSupplier((Supplier)Preconditions.checkNotNull(delegate)); + } + + @Beta + public static Function, T> supplierFunction() { + Suppliers.SupplierFunction sf = Suppliers.SupplierFunctionImpl.INSTANCE; + return sf; + } + + private static enum SupplierFunctionImpl implements Suppliers.SupplierFunction { + INSTANCE; + + public Object apply(Supplier input) { + return input.get(); + } + + public String toString() { + return "Suppliers.supplierFunction()"; + } + } + + private interface SupplierFunction extends Function, T> { + } + + private static class ThreadSafeSupplier implements Supplier, Serializable { + final Supplier delegate; + private static final long serialVersionUID = 0L; + + ThreadSafeSupplier(Supplier delegate) { + this.delegate = delegate; + } + + public T get() { + synchronized(this.delegate) { + return this.delegate.get(); + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.delegate)); + return (new StringBuilder(32 + var1.length())).append("Suppliers.synchronizedSupplier(").append(var1).append(")").toString(); + } + } + + private static class SupplierOfInstance implements Supplier, Serializable { + final T instance; + private static final long serialVersionUID = 0L; + + SupplierOfInstance(@Nullable T instance) { + this.instance = instance; + } + + public T get() { + return this.instance; + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Suppliers.SupplierOfInstance) { + Suppliers.SupplierOfInstance that = (Suppliers.SupplierOfInstance)obj; + return Objects.equal(this.instance, that.instance); + } else { + return false; + } + } + + public int hashCode() { + return Objects.hashCode(this.instance); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.instance)); + return (new StringBuilder(22 + var1.length())).append("Suppliers.ofInstance(").append(var1).append(")").toString(); + } + } + + @VisibleForTesting + static class ExpiringMemoizingSupplier implements Supplier, Serializable { + final Supplier delegate; + final long durationNanos; + transient volatile T value; + transient volatile long expirationNanos; + private static final long serialVersionUID = 0L; + + ExpiringMemoizingSupplier(Supplier delegate, long duration, TimeUnit unit) { + this.delegate = (Supplier)Preconditions.checkNotNull(delegate); + this.durationNanos = unit.toNanos(duration); + Preconditions.checkArgument(duration > 0L); + } + + public T get() { + long nanos = this.expirationNanos; + long now = Platform.systemNanoTime(); + if (nanos == 0L || now - nanos >= 0L) { + synchronized(this) { + if (nanos == this.expirationNanos) { + T t = this.delegate.get(); + this.value = t; + nanos = now + this.durationNanos; + this.expirationNanos = nanos == 0L ? 1L : nanos; + return t; + } + } + } + + return this.value; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.delegate)); + long var2 = this.durationNanos; + return (new StringBuilder(62 + var1.length())).append("Suppliers.memoizeWithExpiration(").append(var1).append(", ").append(var2).append(", NANOS)").toString(); + } + } + + @VisibleForTesting + static class MemoizingSupplier implements Supplier, Serializable { + final Supplier delegate; + transient volatile boolean initialized; + transient T value; + private static final long serialVersionUID = 0L; + + MemoizingSupplier(Supplier delegate) { + this.delegate = delegate; + } + + public T get() { + if (!this.initialized) { + synchronized(this) { + if (!this.initialized) { + T t = this.delegate.get(); + this.value = t; + this.initialized = true; + return t; + } + } + } + + return this.value; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.delegate)); + return (new StringBuilder(19 + var1.length())).append("Suppliers.memoize(").append(var1).append(")").toString(); + } + } + + private static class SupplierComposition implements Supplier, Serializable { + final Function function; + final Supplier supplier; + private static final long serialVersionUID = 0L; + + SupplierComposition(Function function, Supplier supplier) { + this.function = function; + this.supplier = supplier; + } + + public T get() { + return this.function.apply(this.supplier.get()); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof Suppliers.SupplierComposition)) { + return false; + } else { + Suppliers.SupplierComposition that = (Suppliers.SupplierComposition)obj; + return this.function.equals(that.function) && this.supplier.equals(that.supplier); + } + } + + public int hashCode() { + return Objects.hashCode(this.function, this.supplier); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.function)); + String var2 = String.valueOf(String.valueOf(this.supplier)); + return (new StringBuilder(21 + var1.length() + var2.length())).append("Suppliers.compose(").append(var1).append(", ").append(var2).append(")").toString(); + } + } +} diff --git a/src/main/com/google/common/base/Throwables.java b/src/main/com/google/common/base/Throwables.java new file mode 100644 index 0000000..2ea08ac --- /dev/null +++ b/src/main/com/google/common/base/Throwables.java @@ -0,0 +1,68 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; + +public final class Throwables { + private Throwables() { + } + + public static void propagateIfInstanceOf(@Nullable Throwable throwable, Class declaredType) throws X { + if (throwable != null && declaredType.isInstance(throwable)) { + throw (Throwable)declaredType.cast(throwable); + } + } + + public static void propagateIfPossible(@Nullable Throwable throwable) { + propagateIfInstanceOf(throwable, Error.class); + propagateIfInstanceOf(throwable, RuntimeException.class); + } + + public static void propagateIfPossible(@Nullable Throwable throwable, Class declaredType) throws X { + propagateIfInstanceOf(throwable, declaredType); + propagateIfPossible(throwable); + } + + public static void propagateIfPossible(@Nullable Throwable throwable, Class declaredType1, Class declaredType2) throws X1, X2 { + Preconditions.checkNotNull(declaredType2); + propagateIfInstanceOf(throwable, declaredType1); + propagateIfPossible(throwable, declaredType2); + } + + public static RuntimeException propagate(Throwable throwable) { + propagateIfPossible((Throwable)Preconditions.checkNotNull(throwable)); + throw new RuntimeException(throwable); + } + + public static Throwable getRootCause(Throwable throwable) { + Throwable cause; + while((cause = throwable.getCause()) != null) { + throwable = cause; + } + + return throwable; + } + + @Beta + public static List getCausalChain(Throwable throwable) { + Preconditions.checkNotNull(throwable); + + ArrayList causes; + for(causes = new ArrayList(4); throwable != null; throwable = throwable.getCause()) { + causes.add(throwable); + } + + return Collections.unmodifiableList(causes); + } + + public static String getStackTraceAsString(Throwable throwable) { + StringWriter stringWriter = new StringWriter(); + throwable.printStackTrace(new PrintWriter(stringWriter)); + return stringWriter.toString(); + } +} diff --git a/src/main/com/google/common/base/Ticker.java b/src/main/com/google/common/base/Ticker.java new file mode 100644 index 0000000..e8cc328 --- /dev/null +++ b/src/main/com/google/common/base/Ticker.java @@ -0,0 +1,23 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +@Beta +@GwtCompatible +public abstract class Ticker { + private static final Ticker SYSTEM_TICKER = new Ticker() { + public long read() { + return Platform.systemNanoTime(); + } + }; + + protected Ticker() { + } + + public abstract long read(); + + public static Ticker systemTicker() { + return SYSTEM_TICKER; + } +} diff --git a/src/main/com/google/common/base/Utf8.java b/src/main/com/google/common/base/Utf8.java new file mode 100644 index 0000000..f5f00ac --- /dev/null +++ b/src/main/com/google/common/base/Utf8.java @@ -0,0 +1,123 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +@Beta +@GwtCompatible +public final class Utf8 { + public static int encodedLength(CharSequence sequence) { + int utf16Length = sequence.length(); + int utf8Length = utf16Length; + + int i; + for(i = 0; i < utf16Length && sequence.charAt(i) < 128; ++i) { + } + + while(i < utf16Length) { + char c = sequence.charAt(i); + if (c >= 2048) { + utf8Length += encodedLengthGeneral(sequence, i); + break; + } + + utf8Length += 127 - c >>> 31; + ++i; + } + + if (utf8Length < utf16Length) { + long var6 = (long)utf8Length + 4294967296L; + throw new IllegalArgumentException((new StringBuilder(54)).append("UTF-8 length does not fit in int: ").append(var6).toString()); + } else { + return utf8Length; + } + } + + private static int encodedLengthGeneral(CharSequence sequence, int start) { + int utf16Length = sequence.length(); + int utf8Length = 0; + + for(int i = start; i < utf16Length; ++i) { + char c = sequence.charAt(i); + if (c < 2048) { + utf8Length += 127 - c >>> 31; + } else { + utf8Length += 2; + if ('\ud800' <= c && c <= '\udfff') { + int cp = Character.codePointAt(sequence, i); + if (cp < 65536) { + throw new IllegalArgumentException((new StringBuilder(39)).append("Unpaired surrogate at index ").append(i).toString()); + } + + ++i; + } + } + } + + return utf8Length; + } + + public static boolean isWellFormed(byte[] bytes) { + return isWellFormed(bytes, 0, bytes.length); + } + + public static boolean isWellFormed(byte[] bytes, int off, int len) { + int end = off + len; + Preconditions.checkPositionIndexes(off, end, bytes.length); + + for(int i = off; i < end; ++i) { + if (bytes[i] < 0) { + return isWellFormedSlowPath(bytes, i, end); + } + } + + return true; + } + + private static boolean isWellFormedSlowPath(byte[] bytes, int off, int end) { + int index = off; + + while(true) { + byte byte1; + do { + if (index >= end) { + return true; + } + } while((byte1 = bytes[index++]) >= 0); + + if (byte1 < -32) { + if (index == end) { + return false; + } + + if (byte1 < -62 || bytes[index++] > -65) { + return false; + } + } else { + byte byte2; + if (byte1 < -16) { + if (index + 1 >= end) { + return false; + } + + byte2 = bytes[index++]; + if (byte2 > -65 || byte1 == -32 && byte2 < -96 || byte1 == -19 && -96 <= byte2 || bytes[index++] > -65) { + return false; + } + } else { + if (index + 2 >= end) { + return false; + } + + byte2 = bytes[index++]; + if (byte2 > -65 || (byte1 << 28) + (byte2 - -112) >> 30 != 0 || bytes[index++] > -65 || bytes[index++] > -65) { + return false; + } + } + } + } + } + + private Utf8() { + } +} diff --git a/src/main/com/google/common/base/Verify.java b/src/main/com/google/common/base/Verify.java new file mode 100644 index 0000000..f1a21d5 --- /dev/null +++ b/src/main/com/google/common/base/Verify.java @@ -0,0 +1,33 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public final class Verify { + public static void verify(boolean expression) { + if (!expression) { + throw new VerifyException(); + } + } + + public static void verify(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { + if (!expression) { + throw new VerifyException(Preconditions.format(errorMessageTemplate, errorMessageArgs)); + } + } + + public static T verifyNotNull(@Nullable T reference) { + return verifyNotNull(reference, "expected a non-null reference"); + } + + public static T verifyNotNull(@Nullable T reference, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) { + verify(reference != null, errorMessageTemplate, errorMessageArgs); + return reference; + } + + private Verify() { + } +} diff --git a/src/main/com/google/common/base/VerifyException.java b/src/main/com/google/common/base/VerifyException.java new file mode 100644 index 0000000..cbf27e7 --- /dev/null +++ b/src/main/com/google/common/base/VerifyException.java @@ -0,0 +1,16 @@ +package com.google.common.base; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public class VerifyException extends RuntimeException { + public VerifyException() { + } + + public VerifyException(@Nullable String message) { + super(message); + } +} diff --git a/src/main/com/google/common/base/internal/Finalizer.java b/src/main/com/google/common/base/internal/Finalizer.java new file mode 100644 index 0000000..1f14228 --- /dev/null +++ b/src/main/com/google/common/base/internal/Finalizer.java @@ -0,0 +1,103 @@ +package com.google.common.base.internal; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Finalizer implements Runnable { + private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); + private static final String FINALIZABLE_REFERENCE = "com.google.common.base.FinalizableReference"; + private final WeakReference> finalizableReferenceClassReference; + private final PhantomReference frqReference; + private final ReferenceQueue queue; + private static final Field inheritableThreadLocals = getInheritableThreadLocalsField(); + + public static void startFinalizer(Class finalizableReferenceClass, ReferenceQueue queue, PhantomReference frqReference) { + if (!finalizableReferenceClass.getName().equals("com.google.common.base.FinalizableReference")) { + throw new IllegalArgumentException("Expected com.google.common.base.FinalizableReference."); + } else { + 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, (Object)null); + } + } catch (Throwable var6) { + logger.log(Level.INFO, "Failed to clear thread local values inherited by reference finalizer thread.", var6); + } + + thread.start(); + } + } + + private Finalizer(Class finalizableReferenceClass, ReferenceQueue queue, PhantomReference frqReference) { + this.queue = queue; + this.finalizableReferenceClassReference = new WeakReference(finalizableReferenceClass); + this.frqReference = frqReference; + } + + public void run() { + while(true) { + try { + if (!this.cleanUp(this.queue.remove())) { + return; + } + } catch (InterruptedException var2) { + } + } + } + + private boolean cleanUp(Reference reference) { + Method finalizeReferentMethod = this.getFinalizeReferentMethod(); + if (finalizeReferentMethod == null) { + return false; + } else { + do { + reference.clear(); + if (reference == this.frqReference) { + return false; + } + + try { + finalizeReferentMethod.invoke(reference); + } catch (Throwable var4) { + logger.log(Level.SEVERE, "Error cleaning up after reference.", var4); + } + } while((reference = this.queue.poll()) != null); + + return true; + } + } + + private Method getFinalizeReferentMethod() { + Class finalizableReferenceClass = (Class)this.finalizableReferenceClassReference.get(); + if (finalizableReferenceClass == null) { + return null; + } else { + try { + return finalizableReferenceClass.getMethod("finalizeReferent"); + } catch (NoSuchMethodException var3) { + throw new AssertionError(var3); + } + } + } + + public static Field getInheritableThreadLocalsField() { + try { + Field inheritableThreadLocals = Thread.class.getDeclaredField("inheritableThreadLocals"); + inheritableThreadLocals.setAccessible(true); + return inheritableThreadLocals; + } catch (Throwable var1) { + logger.log(Level.INFO, "Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will inherit thread local values."); + return null; + } + } +} diff --git a/src/main/com/google/common/base/package-info.java b/src/main/com/google/common/base/package-info.java new file mode 100644 index 0000000..2a5516d --- /dev/null +++ b/src/main/com/google/common/base/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.base; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/cache/AbstractCache.java b/src/main/com/google/common/cache/AbstractCache.java new file mode 100644 index 0000000..2680cac --- /dev/null +++ b/src/main/com/google/common/cache/AbstractCache.java @@ -0,0 +1,148 @@ +package com.google.common.cache; + +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 java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; + +@Beta +@GwtCompatible +public abstract class AbstractCache implements Cache { + protected AbstractCache() { + } + + public V get(K key, Callable valueLoader) throws ExecutionException { + throw new UnsupportedOperationException(); + } + + public ImmutableMap getAllPresent(Iterable keys) { + Map result = Maps.newLinkedHashMap(); + Iterator i$ = keys.iterator(); + + while(i$.hasNext()) { + Object key = i$.next(); + if (!result.containsKey(key)) { + V value = this.getIfPresent(key); + if (value != null) { + result.put(key, value); + } + } + } + + return ImmutableMap.copyOf(result); + } + + public void put(K key, V value) { + throw new UnsupportedOperationException(); + } + + public void putAll(Map m) { + Iterator i$ = m.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + this.put(entry.getKey(), entry.getValue()); + } + + } + + public void cleanUp() { + } + + public long size() { + throw new UnsupportedOperationException(); + } + + public void invalidate(Object key) { + throw new UnsupportedOperationException(); + } + + public void invalidateAll(Iterable keys) { + Iterator i$ = keys.iterator(); + + while(i$.hasNext()) { + Object key = i$.next(); + this.invalidate(key); + } + + } + + public void invalidateAll() { + throw new UnsupportedOperationException(); + } + + public CacheStats stats() { + throw new UnsupportedOperationException(); + } + + public ConcurrentMap asMap() { + throw new UnsupportedOperationException(); + } + + @Beta + public static final class SimpleStatsCounter implements AbstractCache.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(); + + public void recordHits(int count) { + this.hitCount.add((long)count); + } + + public void recordMisses(int count) { + this.missCount.add((long)count); + } + + public void recordLoadSuccess(long loadTime) { + this.loadSuccessCount.increment(); + this.totalLoadTime.add(loadTime); + } + + public void recordLoadException(long loadTime) { + this.loadExceptionCount.increment(); + this.totalLoadTime.add(loadTime); + } + + public void recordEviction() { + this.evictionCount.increment(); + } + + public CacheStats snapshot() { + return new CacheStats(this.hitCount.sum(), this.missCount.sum(), this.loadSuccessCount.sum(), this.loadExceptionCount.sum(), this.totalLoadTime.sum(), this.evictionCount.sum()); + } + + public void incrementBy(AbstractCache.StatsCounter other) { + CacheStats otherStats = other.snapshot(); + this.hitCount.add(otherStats.hitCount()); + this.missCount.add(otherStats.missCount()); + this.loadSuccessCount.add(otherStats.loadSuccessCount()); + this.loadExceptionCount.add(otherStats.loadExceptionCount()); + this.totalLoadTime.add(otherStats.totalLoadTime()); + this.evictionCount.add(otherStats.evictionCount()); + } + } + + @Beta + public interface StatsCounter { + void recordHits(int var1); + + void recordMisses(int var1); + + void recordLoadSuccess(long var1); + + void recordLoadException(long var1); + + void recordEviction(); + + CacheStats snapshot(); + } +} diff --git a/src/main/com/google/common/cache/AbstractLoadingCache.java b/src/main/com/google/common/cache/AbstractLoadingCache.java new file mode 100644 index 0000000..8c54601 --- /dev/null +++ b/src/main/com/google/common/cache/AbstractLoadingCache.java @@ -0,0 +1,45 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.UncheckedExecutionException; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +@Beta +public abstract class AbstractLoadingCache extends AbstractCache implements LoadingCache { + protected AbstractLoadingCache() { + } + + public V getUnchecked(K key) { + try { + return this.get(key); + } catch (ExecutionException var3) { + throw new UncheckedExecutionException(var3.getCause()); + } + } + + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + Map result = Maps.newLinkedHashMap(); + Iterator i$ = keys.iterator(); + + while(i$.hasNext()) { + K key = i$.next(); + if (!result.containsKey(key)) { + result.put(key, this.get(key)); + } + } + + return ImmutableMap.copyOf(result); + } + + public final V apply(K key) { + return this.getUnchecked(key); + } + + public void refresh(K key) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/cache/Cache.java b/src/main/com/google/common/cache/Cache.java new file mode 100644 index 0000000..fa7cb50 --- /dev/null +++ b/src/main/com/google/common/cache/Cache.java @@ -0,0 +1,39 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +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; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public interface Cache { + @Nullable + V getIfPresent(Object var1); + + V get(K var1, Callable var2) throws ExecutionException; + + ImmutableMap getAllPresent(Iterable var1); + + void put(K var1, V var2); + + void putAll(Map var1); + + void invalidate(Object var1); + + void invalidateAll(Iterable var1); + + void invalidateAll(); + + long size(); + + CacheStats stats(); + + ConcurrentMap asMap(); + + void cleanUp(); +} diff --git a/src/main/com/google/common/cache/CacheBuilder.java b/src/main/com/google/common/cache/CacheBuilder.java new file mode 100644 index 0000000..51a6c0d --- /dev/null +++ b/src/main/com/google/common/cache/CacheBuilder.java @@ -0,0 +1,388 @@ +package com.google.common.cache; + +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.Equivalence; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.base.Ticker; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.CheckReturnValue; + +@GwtCompatible( + emulated = true +) +public final class CacheBuilder { + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int DEFAULT_CONCURRENCY_LEVEL = 4; + private static final int DEFAULT_EXPIRATION_NANOS = 0; + private static final int DEFAULT_REFRESH_NANOS = 0; + static final Supplier NULL_STATS_COUNTER = Suppliers.ofInstance(new AbstractCache.StatsCounter() { + public void recordHits(int count) { + } + + public void recordMisses(int count) { + } + + public void recordLoadSuccess(long loadTime) { + } + + public void recordLoadException(long loadTime) { + } + + public void recordEviction() { + } + + public CacheStats snapshot() { + return CacheBuilder.EMPTY_STATS; + } + }); + static final CacheStats EMPTY_STATS = new CacheStats(0L, 0L, 0L, 0L, 0L, 0L); + static final Supplier CACHE_STATS_COUNTER = new Supplier() { + public AbstractCache.StatsCounter get() { + return new AbstractCache.SimpleStatsCounter(); + } + }; + static final Ticker NULL_TICKER = new Ticker() { + public long read() { + return 0L; + } + }; + private static final Logger logger = Logger.getLogger(CacheBuilder.class.getName()); + static final int UNSET_INT = -1; + boolean strictParsing = true; + int initialCapacity = -1; + int concurrencyLevel = -1; + long maximumSize = -1L; + long maximumWeight = -1L; + Weigher weigher; + LocalCache.Strength keyStrength; + LocalCache.Strength valueStrength; + long expireAfterWriteNanos = -1L; + long expireAfterAccessNanos = -1L; + long refreshNanos = -1L; + Equivalence keyEquivalence; + Equivalence valueEquivalence; + RemovalListener removalListener; + Ticker ticker; + Supplier statsCounterSupplier; + + CacheBuilder() { + this.statsCounterSupplier = NULL_STATS_COUNTER; + } + + public static CacheBuilder newBuilder() { + return new CacheBuilder(); + } + + @Beta + @GwtIncompatible("To be supported") + public static CacheBuilder from(CacheBuilderSpec spec) { + return spec.toCacheBuilder().lenientParsing(); + } + + @Beta + @GwtIncompatible("To be supported") + public static CacheBuilder from(String spec) { + return from(CacheBuilderSpec.parse(spec)); + } + + @GwtIncompatible("To be supported") + CacheBuilder lenientParsing() { + this.strictParsing = false; + return this; + } + + @GwtIncompatible("To be supported") + CacheBuilder keyEquivalence(Equivalence equivalence) { + Preconditions.checkState(this.keyEquivalence == null, "key equivalence was already set to %s", this.keyEquivalence); + this.keyEquivalence = (Equivalence)Preconditions.checkNotNull(equivalence); + return this; + } + + Equivalence getKeyEquivalence() { + return (Equivalence)MoreObjects.firstNonNull(this.keyEquivalence, this.getKeyStrength().defaultEquivalence()); + } + + @GwtIncompatible("To be supported") + CacheBuilder valueEquivalence(Equivalence equivalence) { + Preconditions.checkState(this.valueEquivalence == null, "value equivalence was already set to %s", this.valueEquivalence); + this.valueEquivalence = (Equivalence)Preconditions.checkNotNull(equivalence); + return this; + } + + Equivalence getValueEquivalence() { + return (Equivalence)MoreObjects.firstNonNull(this.valueEquivalence, this.getValueStrength().defaultEquivalence()); + } + + public CacheBuilder initialCapacity(int initialCapacity) { + Preconditions.checkState(this.initialCapacity == -1, "initial capacity was already set to %s", this.initialCapacity); + Preconditions.checkArgument(initialCapacity >= 0); + this.initialCapacity = initialCapacity; + return this; + } + + int getInitialCapacity() { + return this.initialCapacity == -1 ? 16 : this.initialCapacity; + } + + public CacheBuilder concurrencyLevel(int concurrencyLevel) { + Preconditions.checkState(this.concurrencyLevel == -1, "concurrency level was already set to %s", this.concurrencyLevel); + Preconditions.checkArgument(concurrencyLevel > 0); + this.concurrencyLevel = concurrencyLevel; + return this; + } + + int getConcurrencyLevel() { + return this.concurrencyLevel == -1 ? 4 : this.concurrencyLevel; + } + + public CacheBuilder maximumSize(long size) { + Preconditions.checkState(this.maximumSize == -1L, "maximum size was already set to %s", this.maximumSize); + Preconditions.checkState(this.maximumWeight == -1L, "maximum weight was already set to %s", this.maximumWeight); + Preconditions.checkState(this.weigher == null, "maximum size can not be combined with weigher"); + Preconditions.checkArgument(size >= 0L, "maximum size must not be negative"); + this.maximumSize = size; + return this; + } + + @GwtIncompatible("To be supported") + public CacheBuilder maximumWeight(long weight) { + Preconditions.checkState(this.maximumWeight == -1L, "maximum weight was already set to %s", this.maximumWeight); + Preconditions.checkState(this.maximumSize == -1L, "maximum size was already set to %s", this.maximumSize); + this.maximumWeight = weight; + Preconditions.checkArgument(weight >= 0L, "maximum weight must not be negative"); + return this; + } + + @GwtIncompatible("To be supported") + public CacheBuilder weigher(Weigher weigher) { + Preconditions.checkState(this.weigher == null); + if (this.strictParsing) { + Preconditions.checkState(this.maximumSize == -1L, "weigher can not be combined with maximum size", this.maximumSize); + } + + this.weigher = (Weigher)Preconditions.checkNotNull(weigher); + return this; + } + + long getMaximumWeight() { + if (this.expireAfterWriteNanos != 0L && this.expireAfterAccessNanos != 0L) { + return this.weigher == null ? this.maximumSize : this.maximumWeight; + } else { + return 0L; + } + } + + Weigher getWeigher() { + return (Weigher)MoreObjects.firstNonNull(this.weigher, CacheBuilder.OneWeigher.INSTANCE); + } + + @GwtIncompatible("java.lang.ref.WeakReference") + public CacheBuilder weakKeys() { + return this.setKeyStrength(LocalCache.Strength.WEAK); + } + + CacheBuilder setKeyStrength(LocalCache.Strength strength) { + Preconditions.checkState(this.keyStrength == null, "Key strength was already set to %s", this.keyStrength); + this.keyStrength = (LocalCache.Strength)Preconditions.checkNotNull(strength); + return this; + } + + LocalCache.Strength getKeyStrength() { + return (LocalCache.Strength)MoreObjects.firstNonNull(this.keyStrength, LocalCache.Strength.STRONG); + } + + @GwtIncompatible("java.lang.ref.WeakReference") + public CacheBuilder weakValues() { + return this.setValueStrength(LocalCache.Strength.WEAK); + } + + @GwtIncompatible("java.lang.ref.SoftReference") + public CacheBuilder softValues() { + return this.setValueStrength(LocalCache.Strength.SOFT); + } + + CacheBuilder setValueStrength(LocalCache.Strength strength) { + Preconditions.checkState(this.valueStrength == null, "Value strength was already set to %s", this.valueStrength); + this.valueStrength = (LocalCache.Strength)Preconditions.checkNotNull(strength); + return this; + } + + LocalCache.Strength getValueStrength() { + return (LocalCache.Strength)MoreObjects.firstNonNull(this.valueStrength, LocalCache.Strength.STRONG); + } + + public CacheBuilder expireAfterWrite(long duration, TimeUnit unit) { + Preconditions.checkState(this.expireAfterWriteNanos == -1L, "expireAfterWrite was already set to %s ns", this.expireAfterWriteNanos); + Preconditions.checkArgument(duration >= 0L, "duration cannot be negative: %s %s", duration, unit); + this.expireAfterWriteNanos = unit.toNanos(duration); + return this; + } + + long getExpireAfterWriteNanos() { + return this.expireAfterWriteNanos == -1L ? 0L : this.expireAfterWriteNanos; + } + + public CacheBuilder expireAfterAccess(long duration, TimeUnit unit) { + Preconditions.checkState(this.expireAfterAccessNanos == -1L, "expireAfterAccess was already set to %s ns", this.expireAfterAccessNanos); + Preconditions.checkArgument(duration >= 0L, "duration cannot be negative: %s %s", duration, unit); + this.expireAfterAccessNanos = unit.toNanos(duration); + return this; + } + + long getExpireAfterAccessNanos() { + return this.expireAfterAccessNanos == -1L ? 0L : this.expireAfterAccessNanos; + } + + @Beta + @GwtIncompatible("To be supported (synchronously).") + public CacheBuilder refreshAfterWrite(long duration, TimeUnit unit) { + Preconditions.checkNotNull(unit); + Preconditions.checkState(this.refreshNanos == -1L, "refresh was already set to %s ns", this.refreshNanos); + Preconditions.checkArgument(duration > 0L, "duration must be positive: %s %s", duration, unit); + this.refreshNanos = unit.toNanos(duration); + return this; + } + + long getRefreshNanos() { + return this.refreshNanos == -1L ? 0L : this.refreshNanos; + } + + public CacheBuilder ticker(Ticker ticker) { + Preconditions.checkState(this.ticker == null); + this.ticker = (Ticker)Preconditions.checkNotNull(ticker); + return this; + } + + Ticker getTicker(boolean recordsTime) { + if (this.ticker != null) { + return this.ticker; + } else { + return recordsTime ? Ticker.systemTicker() : NULL_TICKER; + } + } + + @CheckReturnValue + public CacheBuilder removalListener(RemovalListener listener) { + Preconditions.checkState(this.removalListener == null); + this.removalListener = (RemovalListener)Preconditions.checkNotNull(listener); + return this; + } + + RemovalListener getRemovalListener() { + return (RemovalListener)MoreObjects.firstNonNull(this.removalListener, CacheBuilder.NullListener.INSTANCE); + } + + public CacheBuilder recordStats() { + this.statsCounterSupplier = CACHE_STATS_COUNTER; + return this; + } + + boolean isRecordingStats() { + return this.statsCounterSupplier == CACHE_STATS_COUNTER; + } + + Supplier getStatsCounterSupplier() { + return this.statsCounterSupplier; + } + + public LoadingCache build(CacheLoader loader) { + this.checkWeightWithWeigher(); + return new LocalCache.LocalLoadingCache(this, loader); + } + + public Cache build() { + this.checkWeightWithWeigher(); + this.checkNonLoadingCache(); + return new LocalCache.LocalManualCache(this); + } + + private void checkNonLoadingCache() { + Preconditions.checkState(this.refreshNanos == -1L, "refreshAfterWrite requires a LoadingCache"); + } + + private void checkWeightWithWeigher() { + if (this.weigher == null) { + Preconditions.checkState(this.maximumWeight == -1L, "maximumWeight requires weigher"); + } else if (this.strictParsing) { + Preconditions.checkState(this.maximumWeight != -1L, "weigher requires maximumWeight"); + } else if (this.maximumWeight == -1L) { + logger.log(Level.WARNING, "ignoring weigher specified without maximumWeight"); + } + + } + + public String toString() { + MoreObjects.ToStringHelper s = MoreObjects.toStringHelper((Object)this); + if (this.initialCapacity != -1) { + s.add("initialCapacity", this.initialCapacity); + } + + if (this.concurrencyLevel != -1) { + s.add("concurrencyLevel", this.concurrencyLevel); + } + + if (this.maximumSize != -1L) { + s.add("maximumSize", this.maximumSize); + } + + if (this.maximumWeight != -1L) { + s.add("maximumWeight", this.maximumWeight); + } + + long var2; + if (this.expireAfterWriteNanos != -1L) { + var2 = this.expireAfterWriteNanos; + s.add("expireAfterWrite", (new StringBuilder(22)).append(var2).append("ns").toString()); + } + + if (this.expireAfterAccessNanos != -1L) { + var2 = this.expireAfterAccessNanos; + s.add("expireAfterAccess", (new StringBuilder(22)).append(var2).append("ns").toString()); + } + + if (this.keyStrength != null) { + s.add("keyStrength", Ascii.toLowerCase(this.keyStrength.toString())); + } + + if (this.valueStrength != null) { + s.add("valueStrength", Ascii.toLowerCase(this.valueStrength.toString())); + } + + if (this.keyEquivalence != null) { + s.addValue("keyEquivalence"); + } + + if (this.valueEquivalence != null) { + s.addValue("valueEquivalence"); + } + + if (this.removalListener != null) { + s.addValue("removalListener"); + } + + return s.toString(); + } + + static enum OneWeigher implements Weigher { + INSTANCE; + + public int weigh(Object key, Object value) { + return 1; + } + } + + static enum NullListener implements RemovalListener { + INSTANCE; + + public void onRemoval(RemovalNotification notification) { + } + } +} diff --git a/src/main/com/google/common/cache/CacheBuilderSpec.java b/src/main/com/google/common/cache/CacheBuilderSpec.java new file mode 100644 index 0000000..76f7bdb --- /dev/null +++ b/src/main/com/google/common/cache/CacheBuilderSpec.java @@ -0,0 +1,325 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + +@Beta +public final class CacheBuilderSpec { + private static final Splitter KEYS_SPLITTER = Splitter.on(',').trimResults(); + private static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').trimResults(); + private static final ImmutableMap VALUE_PARSERS; + @VisibleForTesting + Integer initialCapacity; + @VisibleForTesting + Long maximumSize; + @VisibleForTesting + Long maximumWeight; + @VisibleForTesting + Integer concurrencyLevel; + @VisibleForTesting + LocalCache.Strength keyStrength; + @VisibleForTesting + LocalCache.Strength valueStrength; + @VisibleForTesting + Boolean recordStats; + @VisibleForTesting + long writeExpirationDuration; + @VisibleForTesting + TimeUnit writeExpirationTimeUnit; + @VisibleForTesting + long accessExpirationDuration; + @VisibleForTesting + TimeUnit accessExpirationTimeUnit; + @VisibleForTesting + long refreshDuration; + @VisibleForTesting + TimeUnit refreshTimeUnit; + private final String specification; + + private CacheBuilderSpec(String specification) { + this.specification = specification; + } + + public static CacheBuilderSpec parse(String cacheBuilderSpecification) { + CacheBuilderSpec spec = new CacheBuilderSpec(cacheBuilderSpecification); + if (!cacheBuilderSpecification.isEmpty()) { + Iterator i$ = KEYS_SPLITTER.split(cacheBuilderSpecification).iterator(); + + while(i$.hasNext()) { + String keyValuePair = (String)i$.next(); + List keyAndValue = ImmutableList.copyOf(KEY_VALUE_SPLITTER.split(keyValuePair)); + Preconditions.checkArgument(!keyAndValue.isEmpty(), "blank key-value pair"); + Preconditions.checkArgument(keyAndValue.size() <= 2, "key-value pair %s with more than one equals sign", keyValuePair); + String key = (String)keyAndValue.get(0); + CacheBuilderSpec.ValueParser valueParser = (CacheBuilderSpec.ValueParser)VALUE_PARSERS.get(key); + Preconditions.checkArgument(valueParser != null, "unknown key %s", key); + String value = keyAndValue.size() == 1 ? null : (String)keyAndValue.get(1); + valueParser.parse(spec, key, value); + } + } + + return spec; + } + + public static CacheBuilderSpec disableCaching() { + return parse("maximumSize=0"); + } + + CacheBuilder toCacheBuilder() { + CacheBuilder builder = CacheBuilder.newBuilder(); + if (this.initialCapacity != null) { + builder.initialCapacity(this.initialCapacity); + } + + if (this.maximumSize != null) { + builder.maximumSize(this.maximumSize); + } + + if (this.maximumWeight != null) { + builder.maximumWeight(this.maximumWeight); + } + + if (this.concurrencyLevel != null) { + builder.concurrencyLevel(this.concurrencyLevel); + } + + if (this.keyStrength != null) { + switch(this.keyStrength) { + case WEAK: + builder.weakKeys(); + break; + default: + throw new AssertionError(); + } + } + + if (this.valueStrength != null) { + switch(this.valueStrength) { + case WEAK: + builder.weakValues(); + break; + case SOFT: + builder.softValues(); + break; + default: + throw new AssertionError(); + } + } + + if (this.recordStats != null && this.recordStats) { + builder.recordStats(); + } + + if (this.writeExpirationTimeUnit != null) { + builder.expireAfterWrite(this.writeExpirationDuration, this.writeExpirationTimeUnit); + } + + if (this.accessExpirationTimeUnit != null) { + builder.expireAfterAccess(this.accessExpirationDuration, this.accessExpirationTimeUnit); + } + + if (this.refreshTimeUnit != null) { + builder.refreshAfterWrite(this.refreshDuration, this.refreshTimeUnit); + } + + return builder; + } + + public String toParsableString() { + return this.specification; + } + + public String toString() { + return MoreObjects.toStringHelper((Object)this).addValue(this.toParsableString()).toString(); + } + + public int hashCode() { + return Objects.hashCode(this.initialCapacity, this.maximumSize, this.maximumWeight, this.concurrencyLevel, this.keyStrength, this.valueStrength, this.recordStats, durationInNanos(this.writeExpirationDuration, this.writeExpirationTimeUnit), durationInNanos(this.accessExpirationDuration, this.accessExpirationTimeUnit), durationInNanos(this.refreshDuration, this.refreshTimeUnit)); + } + + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } else if (!(obj instanceof CacheBuilderSpec)) { + return false; + } else { + CacheBuilderSpec that = (CacheBuilderSpec)obj; + return Objects.equal(this.initialCapacity, that.initialCapacity) && Objects.equal(this.maximumSize, that.maximumSize) && Objects.equal(this.maximumWeight, that.maximumWeight) && Objects.equal(this.concurrencyLevel, that.concurrencyLevel) && Objects.equal(this.keyStrength, that.keyStrength) && Objects.equal(this.valueStrength, that.valueStrength) && Objects.equal(this.recordStats, that.recordStats) && Objects.equal(durationInNanos(this.writeExpirationDuration, this.writeExpirationTimeUnit), durationInNanos(that.writeExpirationDuration, that.writeExpirationTimeUnit)) && Objects.equal(durationInNanos(this.accessExpirationDuration, this.accessExpirationTimeUnit), durationInNanos(that.accessExpirationDuration, that.accessExpirationTimeUnit)) && Objects.equal(durationInNanos(this.refreshDuration, this.refreshTimeUnit), durationInNanos(that.refreshDuration, that.refreshTimeUnit)); + } + } + + @Nullable + private static Long durationInNanos(long duration, @Nullable TimeUnit unit) { + return unit == null ? null : unit.toNanos(duration); + } + + static { + VALUE_PARSERS = ImmutableMap.builder().put("initialCapacity", new CacheBuilderSpec.InitialCapacityParser()).put("maximumSize", new CacheBuilderSpec.MaximumSizeParser()).put("maximumWeight", new CacheBuilderSpec.MaximumWeightParser()).put("concurrencyLevel", new CacheBuilderSpec.ConcurrencyLevelParser()).put("weakKeys", new CacheBuilderSpec.KeyStrengthParser(LocalCache.Strength.WEAK)).put("softValues", new CacheBuilderSpec.ValueStrengthParser(LocalCache.Strength.SOFT)).put("weakValues", new CacheBuilderSpec.ValueStrengthParser(LocalCache.Strength.WEAK)).put("recordStats", new CacheBuilderSpec.RecordStatsParser()).put("expireAfterAccess", new CacheBuilderSpec.AccessDurationParser()).put("expireAfterWrite", new CacheBuilderSpec.WriteDurationParser()).put("refreshAfterWrite", new CacheBuilderSpec.RefreshDurationParser()).put("refreshInterval", new CacheBuilderSpec.RefreshDurationParser()).build(); + } + + static class RefreshDurationParser extends CacheBuilderSpec.DurationParser { + protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { + Preconditions.checkArgument(spec.refreshTimeUnit == null, "refreshAfterWrite already set"); + spec.refreshDuration = duration; + spec.refreshTimeUnit = unit; + } + } + + static class WriteDurationParser extends CacheBuilderSpec.DurationParser { + protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { + Preconditions.checkArgument(spec.writeExpirationTimeUnit == null, "expireAfterWrite already set"); + spec.writeExpirationDuration = duration; + spec.writeExpirationTimeUnit = unit; + } + } + + static class AccessDurationParser extends CacheBuilderSpec.DurationParser { + protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) { + Preconditions.checkArgument(spec.accessExpirationTimeUnit == null, "expireAfterAccess already set"); + spec.accessExpirationDuration = duration; + spec.accessExpirationTimeUnit = unit; + } + } + + abstract static class DurationParser implements CacheBuilderSpec.ValueParser { + protected abstract void parseDuration(CacheBuilderSpec var1, long var2, TimeUnit var4); + + public void parse(CacheBuilderSpec spec, String key, String value) { + Preconditions.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)); + this.parseDuration(spec, duration, timeUnit); + } catch (NumberFormatException var8) { + throw new IllegalArgumentException(String.format("key %s value set to %s, must be integer", key, value)); + } + } + } + + static class RecordStatsParser implements CacheBuilderSpec.ValueParser { + public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { + Preconditions.checkArgument(value == null, "recordStats does not take values"); + Preconditions.checkArgument(spec.recordStats == null, "recordStats already set"); + spec.recordStats = true; + } + } + + static class ValueStrengthParser implements CacheBuilderSpec.ValueParser { + private final LocalCache.Strength strength; + + public ValueStrengthParser(LocalCache.Strength strength) { + this.strength = strength; + } + + public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { + Preconditions.checkArgument(value == null, "key %s does not take values", key); + Preconditions.checkArgument(spec.valueStrength == null, "%s was already set to %s", key, spec.valueStrength); + spec.valueStrength = this.strength; + } + } + + static class KeyStrengthParser implements CacheBuilderSpec.ValueParser { + private final LocalCache.Strength strength; + + public KeyStrengthParser(LocalCache.Strength strength) { + this.strength = strength; + } + + public void parse(CacheBuilderSpec spec, String key, @Nullable String value) { + Preconditions.checkArgument(value == null, "key %s does not take values", key); + Preconditions.checkArgument(spec.keyStrength == null, "%s was already set to %s", key, spec.keyStrength); + spec.keyStrength = this.strength; + } + } + + static class ConcurrencyLevelParser extends CacheBuilderSpec.IntegerParser { + protected void parseInteger(CacheBuilderSpec spec, int value) { + Preconditions.checkArgument(spec.concurrencyLevel == null, "concurrency level was already set to ", spec.concurrencyLevel); + spec.concurrencyLevel = value; + } + } + + static class MaximumWeightParser extends CacheBuilderSpec.LongParser { + protected void parseLong(CacheBuilderSpec spec, long value) { + Preconditions.checkArgument(spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight); + Preconditions.checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize); + spec.maximumWeight = value; + } + } + + static class MaximumSizeParser extends CacheBuilderSpec.LongParser { + protected void parseLong(CacheBuilderSpec spec, long value) { + Preconditions.checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize); + Preconditions.checkArgument(spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight); + spec.maximumSize = value; + } + } + + static class InitialCapacityParser extends CacheBuilderSpec.IntegerParser { + protected void parseInteger(CacheBuilderSpec spec, int value) { + Preconditions.checkArgument(spec.initialCapacity == null, "initial capacity was already set to ", spec.initialCapacity); + spec.initialCapacity = value; + } + } + + abstract static class LongParser implements CacheBuilderSpec.ValueParser { + protected abstract void parseLong(CacheBuilderSpec var1, long var2); + + public void parse(CacheBuilderSpec spec, String key, String value) { + Preconditions.checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key); + + try { + this.parseLong(spec, Long.parseLong(value)); + } catch (NumberFormatException var5) { + throw new IllegalArgumentException(String.format("key %s value set to %s, must be integer", key, value), var5); + } + } + } + + abstract static class IntegerParser implements CacheBuilderSpec.ValueParser { + protected abstract void parseInteger(CacheBuilderSpec var1, int var2); + + public void parse(CacheBuilderSpec spec, String key, String value) { + Preconditions.checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key); + + try { + this.parseInteger(spec, Integer.parseInt(value)); + } catch (NumberFormatException var5) { + throw new IllegalArgumentException(String.format("key %s value set to %s, must be integer", key, value), var5); + } + } + } + + private interface ValueParser { + void parse(CacheBuilderSpec var1, String var2, @Nullable String var3); + } +} diff --git a/src/main/com/google/common/cache/CacheLoader.java b/src/main/com/google/common/cache/CacheLoader.java new file mode 100644 index 0000000..236dc6f --- /dev/null +++ b/src/main/com/google/common/cache/CacheLoader.java @@ -0,0 +1,108 @@ +package com.google.common.cache; + +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.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableFutureTask; +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; + +@GwtCompatible( + emulated = true +) +public abstract class CacheLoader { + protected CacheLoader() { + } + + public abstract V load(K var1) throws Exception; + + @GwtIncompatible("Futures") + public ListenableFuture reload(K key, V oldValue) throws Exception { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(oldValue); + return Futures.immediateFuture(this.load(key)); + } + + public Map loadAll(Iterable keys) throws Exception { + throw new CacheLoader.UnsupportedLoadingOperationException(); + } + + @Beta + public static CacheLoader from(Function function) { + return new CacheLoader.FunctionToCacheLoader(function); + } + + @Beta + public static CacheLoader from(Supplier supplier) { + return new CacheLoader.SupplierToCacheLoader(supplier); + } + + @Beta + @GwtIncompatible("Executor + Futures") + public static CacheLoader asyncReloading(final CacheLoader loader, final Executor executor) { + Preconditions.checkNotNull(loader); + Preconditions.checkNotNull(executor); + return new CacheLoader() { + public V load(K key) throws Exception { + return loader.load(key); + } + + public ListenableFuture reload(final K key, final V oldValue) throws Exception { + ListenableFutureTask task = ListenableFutureTask.create(new Callable() { + public V call() throws Exception { + return loader.reload(key, oldValue).get(); + } + }); + executor.execute(task); + return task; + } + + public Map loadAll(Iterable keys) throws Exception { + return loader.loadAll(keys); + } + }; + } + + public static final class InvalidCacheLoadException extends RuntimeException { + public InvalidCacheLoadException(String message) { + super(message); + } + } + + static final class UnsupportedLoadingOperationException extends UnsupportedOperationException { + } + + private static final class SupplierToCacheLoader extends CacheLoader implements Serializable { + private final Supplier computingSupplier; + private static final long serialVersionUID = 0L; + + public SupplierToCacheLoader(Supplier computingSupplier) { + this.computingSupplier = (Supplier)Preconditions.checkNotNull(computingSupplier); + } + + public V load(Object key) { + Preconditions.checkNotNull(key); + return this.computingSupplier.get(); + } + } + + private static final class FunctionToCacheLoader extends CacheLoader implements Serializable { + private final Function computingFunction; + private static final long serialVersionUID = 0L; + + public FunctionToCacheLoader(Function computingFunction) { + this.computingFunction = (Function)Preconditions.checkNotNull(computingFunction); + } + + public V load(K key) { + return this.computingFunction.apply(Preconditions.checkNotNull(key)); + } + } +} diff --git a/src/main/com/google/common/cache/CacheStats.java b/src/main/com/google/common/cache/CacheStats.java new file mode 100644 index 0000000..e464e8d --- /dev/null +++ b/src/main/com/google/common/cache/CacheStats.java @@ -0,0 +1,111 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public final class CacheStats { + private final long hitCount; + private final long missCount; + private final long loadSuccessCount; + private final long loadExceptionCount; + private final long totalLoadTime; + private final long evictionCount; + + public CacheStats(long hitCount, long missCount, long loadSuccessCount, long loadExceptionCount, long totalLoadTime, long evictionCount) { + Preconditions.checkArgument(hitCount >= 0L); + Preconditions.checkArgument(missCount >= 0L); + Preconditions.checkArgument(loadSuccessCount >= 0L); + Preconditions.checkArgument(loadExceptionCount >= 0L); + Preconditions.checkArgument(totalLoadTime >= 0L); + Preconditions.checkArgument(evictionCount >= 0L); + this.hitCount = hitCount; + this.missCount = missCount; + this.loadSuccessCount = loadSuccessCount; + this.loadExceptionCount = loadExceptionCount; + this.totalLoadTime = totalLoadTime; + this.evictionCount = evictionCount; + } + + public long requestCount() { + return this.hitCount + this.missCount; + } + + public long hitCount() { + return this.hitCount; + } + + public double hitRate() { + long requestCount = this.requestCount(); + return requestCount == 0L ? 1.0D : (double)this.hitCount / (double)requestCount; + } + + public long missCount() { + return this.missCount; + } + + public double missRate() { + long requestCount = this.requestCount(); + return requestCount == 0L ? 0.0D : (double)this.missCount / (double)requestCount; + } + + public long loadCount() { + return this.loadSuccessCount + this.loadExceptionCount; + } + + public long loadSuccessCount() { + return this.loadSuccessCount; + } + + public long loadExceptionCount() { + return this.loadExceptionCount; + } + + public double loadExceptionRate() { + long totalLoadCount = this.loadSuccessCount + this.loadExceptionCount; + return totalLoadCount == 0L ? 0.0D : (double)this.loadExceptionCount / (double)totalLoadCount; + } + + public long totalLoadTime() { + return this.totalLoadTime; + } + + public double averageLoadPenalty() { + long totalLoadCount = this.loadSuccessCount + this.loadExceptionCount; + return totalLoadCount == 0L ? 0.0D : (double)this.totalLoadTime / (double)totalLoadCount; + } + + public long evictionCount() { + return this.evictionCount; + } + + public CacheStats minus(CacheStats other) { + return new CacheStats(Math.max(0L, this.hitCount - other.hitCount), Math.max(0L, this.missCount - other.missCount), Math.max(0L, this.loadSuccessCount - other.loadSuccessCount), Math.max(0L, this.loadExceptionCount - other.loadExceptionCount), Math.max(0L, this.totalLoadTime - other.totalLoadTime), Math.max(0L, this.evictionCount - other.evictionCount)); + } + + public CacheStats plus(CacheStats other) { + return new CacheStats(this.hitCount + other.hitCount, this.missCount + other.missCount, this.loadSuccessCount + other.loadSuccessCount, this.loadExceptionCount + other.loadExceptionCount, this.totalLoadTime + other.totalLoadTime, this.evictionCount + other.evictionCount); + } + + public int hashCode() { + return Objects.hashCode(this.hitCount, this.missCount, this.loadSuccessCount, this.loadExceptionCount, this.totalLoadTime, this.evictionCount); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof CacheStats)) { + return false; + } else { + CacheStats other = (CacheStats)object; + return this.hitCount == other.hitCount && this.missCount == other.missCount && this.loadSuccessCount == other.loadSuccessCount && this.loadExceptionCount == other.loadExceptionCount && this.totalLoadTime == other.totalLoadTime && this.evictionCount == other.evictionCount; + } + } + + public String toString() { + return MoreObjects.toStringHelper((Object)this).add("hitCount", this.hitCount).add("missCount", this.missCount).add("loadSuccessCount", this.loadSuccessCount).add("loadExceptionCount", this.loadExceptionCount).add("totalLoadTime", this.totalLoadTime).add("evictionCount", this.evictionCount).toString(); + } +} diff --git a/src/main/com/google/common/cache/ForwardingCache.java b/src/main/com/google/common/cache/ForwardingCache.java new file mode 100644 index 0000000..27f5b65 --- /dev/null +++ b/src/main/com/google/common/cache/ForwardingCache.java @@ -0,0 +1,81 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +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; +import javax.annotation.Nullable; + +@Beta +public abstract class ForwardingCache extends ForwardingObject implements Cache { + protected ForwardingCache() { + } + + protected abstract Cache delegate(); + + @Nullable + public V getIfPresent(Object key) { + return this.delegate().getIfPresent(key); + } + + public V get(K key, Callable valueLoader) throws ExecutionException { + return this.delegate().get(key, valueLoader); + } + + public ImmutableMap getAllPresent(Iterable keys) { + return this.delegate().getAllPresent(keys); + } + + public void put(K key, V value) { + this.delegate().put(key, value); + } + + public void putAll(Map m) { + this.delegate().putAll(m); + } + + public void invalidate(Object key) { + this.delegate().invalidate(key); + } + + public void invalidateAll(Iterable keys) { + this.delegate().invalidateAll(keys); + } + + public void invalidateAll() { + this.delegate().invalidateAll(); + } + + public long size() { + return this.delegate().size(); + } + + public CacheStats stats() { + return this.delegate().stats(); + } + + public ConcurrentMap asMap() { + return this.delegate().asMap(); + } + + public void cleanUp() { + this.delegate().cleanUp(); + } + + @Beta + public abstract static class SimpleForwardingCache extends ForwardingCache { + private final Cache delegate; + + protected SimpleForwardingCache(Cache delegate) { + this.delegate = (Cache)Preconditions.checkNotNull(delegate); + } + + protected final Cache delegate() { + return this.delegate; + } + } +} diff --git a/src/main/com/google/common/cache/ForwardingLoadingCache.java b/src/main/com/google/common/cache/ForwardingLoadingCache.java new file mode 100644 index 0000000..b929dd5 --- /dev/null +++ b/src/main/com/google/common/cache/ForwardingLoadingCache.java @@ -0,0 +1,47 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import java.util.concurrent.ExecutionException; + +@Beta +public abstract class ForwardingLoadingCache extends ForwardingCache implements LoadingCache { + protected ForwardingLoadingCache() { + } + + protected abstract LoadingCache delegate(); + + public V get(K key) throws ExecutionException { + return this.delegate().get(key); + } + + public V getUnchecked(K key) { + return this.delegate().getUnchecked(key); + } + + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + return this.delegate().getAll(keys); + } + + public V apply(K key) { + return this.delegate().apply(key); + } + + public void refresh(K key) { + this.delegate().refresh(key); + } + + @Beta + public abstract static class SimpleForwardingLoadingCache extends ForwardingLoadingCache { + private final LoadingCache delegate; + + protected SimpleForwardingLoadingCache(LoadingCache delegate) { + this.delegate = (LoadingCache)Preconditions.checkNotNull(delegate); + } + + protected final LoadingCache delegate() { + return this.delegate; + } + } +} diff --git a/src/main/com/google/common/cache/LoadingCache.java b/src/main/com/google/common/cache/LoadingCache.java new file mode 100644 index 0000000..4ca85ac --- /dev/null +++ b/src/main/com/google/common/cache/LoadingCache.java @@ -0,0 +1,26 @@ +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 java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; + +@Beta +@GwtCompatible +public interface LoadingCache extends Cache, Function { + V get(K var1) throws ExecutionException; + + V getUnchecked(K var1); + + ImmutableMap getAll(Iterable var1) throws ExecutionException; + + /** @deprecated */ + @Deprecated + V apply(K var1); + + void refresh(K var1); + + ConcurrentMap asMap(); +} diff --git a/src/main/com/google/common/cache/LocalCache.java b/src/main/com/google/common/cache/LocalCache.java new file mode 100644 index 0000000..ffcf749 --- /dev/null +++ b/src/main/com/google/common/cache/LocalCache.java @@ -0,0 +1,3792 @@ +package com.google.common.cache; + +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.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.base.Ticker; +import com.google.common.collect.AbstractSequentialIterator; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.primitives.Ints; +import com.google.common.util.concurrent.ExecutionError; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +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; +import java.io.Serializable; +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; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +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; + +@GwtCompatible( + emulated = true +) +class LocalCache extends AbstractMap implements ConcurrentMap { + static final int MAXIMUM_CAPACITY = 1073741824; + static final int MAX_SEGMENTS = 65536; + static final int CONTAINS_VALUE_RETRIES = 3; + static final int DRAIN_THRESHOLD = 63; + static final int DRAIN_MAX = 16; + static final Logger logger = Logger.getLogger(LocalCache.class.getName()); + final int segmentMask; + final int segmentShift; + final LocalCache.Segment[] segments; + final int concurrencyLevel; + final Equivalence keyEquivalence; + final Equivalence valueEquivalence; + final LocalCache.Strength keyStrength; + final LocalCache.Strength valueStrength; + final long maxWeight; + final Weigher weigher; + final long expireAfterAccessNanos; + final long expireAfterWriteNanos; + final long refreshNanos; + final Queue> removalNotificationQueue; + final RemovalListener removalListener; + final Ticker ticker; + final LocalCache.EntryFactory entryFactory; + final AbstractCache.StatsCounter globalStatsCounter; + @Nullable + final CacheLoader defaultLoader; + static final LocalCache.ValueReference UNSET = new LocalCache.ValueReference() { + public Object get() { + return null; + } + + public int getWeight() { + return 0; + } + + public LocalCache.ReferenceEntry getEntry() { + return null; + } + + public LocalCache.ValueReference copyFor(ReferenceQueue queue, @Nullable Object value, LocalCache.ReferenceEntry entry) { + return this; + } + + public boolean isLoading() { + return false; + } + + public boolean isActive() { + return false; + } + + public Object waitForValue() { + return null; + } + + public void notifyNewValue(Object newValue) { + } + }; + static final Queue DISCARDING_QUEUE = new AbstractQueue() { + public boolean offer(Object o) { + return true; + } + + public Object peek() { + return null; + } + + public Object poll() { + return null; + } + + public int size() { + return 0; + } + + public Iterator iterator() { + return ImmutableSet.of().iterator(); + } + }; + Set keySet; + Collection values; + Set> entrySet; + + LocalCache(CacheBuilder builder, @Nullable CacheLoader loader) { + this.concurrencyLevel = Math.min(builder.getConcurrencyLevel(), 65536); + this.keyStrength = builder.getKeyStrength(); + this.valueStrength = builder.getValueStrength(); + this.keyEquivalence = builder.getKeyEquivalence(); + this.valueEquivalence = builder.getValueEquivalence(); + this.maxWeight = builder.getMaximumWeight(); + this.weigher = builder.getWeigher(); + this.expireAfterAccessNanos = builder.getExpireAfterAccessNanos(); + this.expireAfterWriteNanos = builder.getExpireAfterWriteNanos(); + this.refreshNanos = builder.getRefreshNanos(); + this.removalListener = builder.getRemovalListener(); + this.removalNotificationQueue = (Queue)(this.removalListener == CacheBuilder.NullListener.INSTANCE ? discardingQueue() : new ConcurrentLinkedQueue()); + this.ticker = builder.getTicker(this.recordsTime()); + this.entryFactory = LocalCache.EntryFactory.getFactory(this.keyStrength, this.usesAccessEntries(), this.usesWriteEntries()); + this.globalStatsCounter = (AbstractCache.StatsCounter)builder.getStatsCounterSupplier().get(); + this.defaultLoader = loader; + int initialCapacity = Math.min(builder.getInitialCapacity(), 1073741824); + if (this.evictsBySize() && !this.customWeigher()) { + initialCapacity = Math.min(initialCapacity, (int)this.maxWeight); + } + + int segmentShift = 0; + + int segmentCount; + for(segmentCount = 1; segmentCount < this.concurrencyLevel && (!this.evictsBySize() || (long)(segmentCount * 20) <= this.maxWeight); segmentCount <<= 1) { + ++segmentShift; + } + + this.segmentShift = 32 - segmentShift; + this.segmentMask = segmentCount - 1; + this.segments = this.newSegmentArray(segmentCount); + int segmentCapacity = initialCapacity / segmentCount; + if (segmentCapacity * segmentCount < initialCapacity) { + ++segmentCapacity; + } + + int segmentSize; + for(segmentSize = 1; segmentSize < segmentCapacity; segmentSize <<= 1) { + } + + if (this.evictsBySize()) { + long maxSegmentWeight = this.maxWeight / (long)segmentCount + 1L; + long remainder = this.maxWeight % (long)segmentCount; + + for(int i = 0; i < this.segments.length; ++i) { + if ((long)i == remainder) { + --maxSegmentWeight; + } + + this.segments[i] = this.createSegment(segmentSize, maxSegmentWeight, (AbstractCache.StatsCounter)builder.getStatsCounterSupplier().get()); + } + } else { + for(int i = 0; i < this.segments.length; ++i) { + this.segments[i] = this.createSegment(segmentSize, -1L, (AbstractCache.StatsCounter)builder.getStatsCounterSupplier().get()); + } + } + + } + + boolean evictsBySize() { + return this.maxWeight >= 0L; + } + + boolean customWeigher() { + return this.weigher != CacheBuilder.OneWeigher.INSTANCE; + } + + boolean expires() { + return this.expiresAfterWrite() || this.expiresAfterAccess(); + } + + boolean expiresAfterWrite() { + return this.expireAfterWriteNanos > 0L; + } + + boolean expiresAfterAccess() { + return this.expireAfterAccessNanos > 0L; + } + + boolean refreshes() { + return this.refreshNanos > 0L; + } + + boolean usesAccessQueue() { + return this.expiresAfterAccess() || this.evictsBySize(); + } + + boolean usesWriteQueue() { + return this.expiresAfterWrite(); + } + + boolean recordsWrite() { + return this.expiresAfterWrite() || this.refreshes(); + } + + boolean recordsAccess() { + return this.expiresAfterAccess(); + } + + boolean recordsTime() { + return this.recordsWrite() || this.recordsAccess(); + } + + boolean usesWriteEntries() { + return this.usesWriteQueue() || this.recordsWrite(); + } + + boolean usesAccessEntries() { + return this.usesAccessQueue() || this.recordsAccess(); + } + + boolean usesKeyReferences() { + return this.keyStrength != LocalCache.Strength.STRONG; + } + + boolean usesValueReferences() { + return this.valueStrength != LocalCache.Strength.STRONG; + } + + static LocalCache.ValueReference unset() { + return UNSET; + } + + static LocalCache.ReferenceEntry nullEntry() { + return LocalCache.NullEntry.INSTANCE; + } + + static Queue discardingQueue() { + return DISCARDING_QUEUE; + } + + static int rehash(int h) { + h += h << 15 ^ -12931; + h ^= h >>> 10; + h += h << 3; + h ^= h >>> 6; + h += (h << 2) + (h << 14); + return h ^ h >>> 16; + } + + @VisibleForTesting + LocalCache.ReferenceEntry newEntry(K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + LocalCache.Segment segment = this.segmentFor(hash); + segment.lock(); + + LocalCache.ReferenceEntry var5; + try { + var5 = segment.newEntry(key, hash, next); + } finally { + segment.unlock(); + } + + return var5; + } + + @VisibleForTesting + LocalCache.ReferenceEntry copyEntry(LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + int hash = original.getHash(); + return this.segmentFor(hash).copyEntry(original, newNext); + } + + @VisibleForTesting + LocalCache.ValueReference newValueReference(LocalCache.ReferenceEntry entry, V value, int weight) { + int hash = entry.getHash(); + return this.valueStrength.referenceValue(this.segmentFor(hash), entry, Preconditions.checkNotNull(value), weight); + } + + int hash(@Nullable Object key) { + int h = this.keyEquivalence.hash(key); + return rehash(h); + } + + void reclaimValue(LocalCache.ValueReference valueReference) { + LocalCache.ReferenceEntry entry = valueReference.getEntry(); + int hash = entry.getHash(); + this.segmentFor(hash).reclaimValue(entry.getKey(), hash, valueReference); + } + + void reclaimKey(LocalCache.ReferenceEntry entry) { + int hash = entry.getHash(); + this.segmentFor(hash).reclaimKey(entry, hash); + } + + @VisibleForTesting + boolean isLive(LocalCache.ReferenceEntry entry, long now) { + return this.segmentFor(entry.getHash()).getLiveValue(entry, now) != null; + } + + LocalCache.Segment segmentFor(int hash) { + return this.segments[hash >>> this.segmentShift & this.segmentMask]; + } + + LocalCache.Segment createSegment(int initialCapacity, long maxSegmentWeight, AbstractCache.StatsCounter statsCounter) { + return new LocalCache.Segment(this, initialCapacity, maxSegmentWeight, statsCounter); + } + + @Nullable + V getLiveValue(LocalCache.ReferenceEntry entry, long now) { + if (entry.getKey() == null) { + return null; + } else { + V value = entry.getValueReference().get(); + if (value == null) { + return null; + } else { + return this.isExpired(entry, now) ? null : value; + } + } + } + + boolean isExpired(LocalCache.ReferenceEntry entry, long now) { + Preconditions.checkNotNull(entry); + if (this.expiresAfterAccess() && now - entry.getAccessTime() >= this.expireAfterAccessNanos) { + return true; + } else { + return this.expiresAfterWrite() && now - entry.getWriteTime() >= this.expireAfterWriteNanos; + } + } + + static void connectAccessOrder(LocalCache.ReferenceEntry previous, LocalCache.ReferenceEntry next) { + previous.setNextInAccessQueue(next); + next.setPreviousInAccessQueue(previous); + } + + static void nullifyAccessOrder(LocalCache.ReferenceEntry nulled) { + LocalCache.ReferenceEntry nullEntry = nullEntry(); + nulled.setNextInAccessQueue(nullEntry); + nulled.setPreviousInAccessQueue(nullEntry); + } + + static void connectWriteOrder(LocalCache.ReferenceEntry previous, LocalCache.ReferenceEntry next) { + previous.setNextInWriteQueue(next); + next.setPreviousInWriteQueue(previous); + } + + static void nullifyWriteOrder(LocalCache.ReferenceEntry nulled) { + LocalCache.ReferenceEntry nullEntry = nullEntry(); + nulled.setNextInWriteQueue(nullEntry); + nulled.setPreviousInWriteQueue(nullEntry); + } + + void processPendingNotifications() { + RemovalNotification notification; + while((notification = (RemovalNotification)this.removalNotificationQueue.poll()) != null) { + try { + this.removalListener.onRemoval(notification); + } catch (Throwable var3) { + logger.log(Level.WARNING, "Exception thrown by removal listener", var3); + } + } + + } + + final LocalCache.Segment[] newSegmentArray(int ssize) { + return new LocalCache.Segment[ssize]; + } + + public void cleanUp() { + LocalCache.Segment[] arr$ = this.segments; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + LocalCache.Segment segment = arr$[i$]; + segment.cleanUp(); + } + + } + + public boolean isEmpty() { + long sum = 0L; + LocalCache.Segment[] segments = this.segments; + + int i; + for(i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + + sum += (long)segments[i].modCount; + } + + if (sum != 0L) { + for(i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + + sum -= (long)segments[i].modCount; + } + + if (sum != 0L) { + return false; + } + } + + return true; + } + + long longSize() { + LocalCache.Segment[] segments = this.segments; + long sum = 0L; + + for(int i = 0; i < segments.length; ++i) { + sum += (long)segments[i].count; + } + + return sum; + } + + public int size() { + return Ints.saturatedCast(this.longSize()); + } + + @Nullable + public V get(@Nullable Object key) { + if (key == null) { + return null; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).get(key, hash); + } + } + + @Nullable + public V getIfPresent(Object key) { + int hash = this.hash(Preconditions.checkNotNull(key)); + V value = this.segmentFor(hash).get(key, hash); + if (value == null) { + this.globalStatsCounter.recordMisses(1); + } else { + this.globalStatsCounter.recordHits(1); + } + + return value; + } + + V get(K key, CacheLoader loader) throws ExecutionException { + int hash = this.hash(Preconditions.checkNotNull(key)); + return this.segmentFor(hash).get(key, hash, loader); + } + + V getOrLoad(K key) throws ExecutionException { + return this.get(key, this.defaultLoader); + } + + ImmutableMap getAllPresent(Iterable keys) { + int hits = 0; + int misses = 0; + Map result = Maps.newLinkedHashMap(); + Iterator i$ = keys.iterator(); + + while(i$.hasNext()) { + Object key = i$.next(); + V value = this.get(key); + if (value == null) { + ++misses; + } else { + result.put(key, value); + ++hits; + } + } + + this.globalStatsCounter.recordHits(hits); + this.globalStatsCounter.recordMisses(misses); + return ImmutableMap.copyOf(result); + } + + ImmutableMap getAll(Iterable keys) throws ExecutionException { + int hits = 0; + int misses = 0; + Map result = Maps.newLinkedHashMap(); + Set keysToLoad = Sets.newLinkedHashSet(); + Iterator i$ = keys.iterator(); + + Object key; + while(i$.hasNext()) { + K key = i$.next(); + key = this.get(key); + if (!result.containsKey(key)) { + result.put(key, key); + if (key == null) { + ++misses; + keysToLoad.add(key); + } else { + ++hits; + } + } + } + + ImmutableMap var17; + try { + if (!keysToLoad.isEmpty()) { + Iterator i$; + try { + Map newEntries = this.loadAll(keysToLoad, this.defaultLoader); + i$ = keysToLoad.iterator(); + + while(i$.hasNext()) { + key = i$.next(); + V value = newEntries.get(key); + if (value == null) { + String var10 = String.valueOf(String.valueOf(key)); + throw new CacheLoader.InvalidCacheLoadException((new StringBuilder(37 + var10.length())).append("loadAll failed to return a value for ").append(var10).toString()); + } + + result.put(key, value); + } + } catch (CacheLoader.UnsupportedLoadingOperationException var14) { + i$ = keysToLoad.iterator(); + + while(i$.hasNext()) { + key = i$.next(); + --misses; + result.put(key, this.get(key, this.defaultLoader)); + } + } + } + + var17 = ImmutableMap.copyOf(result); + } finally { + this.globalStatsCounter.recordHits(hits); + this.globalStatsCounter.recordMisses(misses); + } + + return var17; + } + + @Nullable + Map loadAll(Set keys, CacheLoader loader) throws ExecutionException { + Preconditions.checkNotNull(loader); + Preconditions.checkNotNull(keys); + Stopwatch stopwatch = Stopwatch.createStarted(); + boolean success = false; + + Map result; + try { + Map map = loader.loadAll(keys); + result = map; + success = true; + } catch (CacheLoader.UnsupportedLoadingOperationException var17) { + success = true; + throw var17; + } catch (InterruptedException var18) { + Thread.currentThread().interrupt(); + throw new ExecutionException(var18); + } catch (RuntimeException var19) { + throw new UncheckedExecutionException(var19); + } catch (Exception var20) { + throw new ExecutionException(var20); + } catch (Error var21) { + throw new ExecutionError(var21); + } finally { + if (!success) { + this.globalStatsCounter.recordLoadException(stopwatch.elapsed(TimeUnit.NANOSECONDS)); + } + + } + + if (result == null) { + this.globalStatsCounter.recordLoadException(stopwatch.elapsed(TimeUnit.NANOSECONDS)); + String var24 = String.valueOf(String.valueOf(loader)); + throw new CacheLoader.InvalidCacheLoadException((new StringBuilder(31 + var24.length())).append(var24).append(" returned null map from loadAll").toString()); + } else { + stopwatch.stop(); + boolean nullsPresent = false; + Iterator i$ = result.entrySet().iterator(); + + while(true) { + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + K key = entry.getKey(); + V value = entry.getValue(); + if (key != null && value != null) { + this.put(key, value); + } else { + nullsPresent = true; + } + } + + if (nullsPresent) { + this.globalStatsCounter.recordLoadException(stopwatch.elapsed(TimeUnit.NANOSECONDS)); + String var25 = String.valueOf(String.valueOf(loader)); + throw new CacheLoader.InvalidCacheLoadException((new StringBuilder(42 + var25.length())).append(var25).append(" returned null keys or values from loadAll").toString()); + } + + this.globalStatsCounter.recordLoadSuccess(stopwatch.elapsed(TimeUnit.NANOSECONDS)); + return result; + } + } + } + + LocalCache.ReferenceEntry getEntry(@Nullable Object key) { + if (key == null) { + return null; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).getEntry(key, hash); + } + } + + void refresh(K key) { + int hash = this.hash(Preconditions.checkNotNull(key)); + this.segmentFor(hash).refresh(key, hash, this.defaultLoader, false); + } + + public boolean containsKey(@Nullable Object key) { + if (key == null) { + return false; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).containsKey(key, hash); + } + } + + public boolean containsValue(@Nullable Object value) { + if (value == null) { + return false; + } else { + long now = this.ticker.read(); + LocalCache.Segment[] segments = this.segments; + long last = -1L; + + for(int i = 0; i < 3; ++i) { + long sum = 0L; + LocalCache.Segment[] arr$ = segments; + int len$ = segments.length; + + for(int i$ = 0; i$ < len$; ++i$) { + LocalCache.Segment segment = arr$[i$]; + int c = segment.count; + AtomicReferenceArray> table = segment.table; + + for(int j = 0; j < table.length(); ++j) { + for(LocalCache.ReferenceEntry e = (LocalCache.ReferenceEntry)table.get(j); e != null; e = e.getNext()) { + V v = segment.getLiveValue(e, now); + if (v != null && this.valueEquivalence.equivalent(value, v)) { + return true; + } + } + } + + sum += (long)segment.modCount; + } + + if (sum == last) { + break; + } + + last = sum; + } + + return false; + } + } + + public V put(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + int hash = this.hash(key); + return this.segmentFor(hash).put(key, hash, value, false); + } + + public V putIfAbsent(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + int hash = this.hash(key); + return this.segmentFor(hash).put(key, hash, value, true); + } + + public void putAll(Map m) { + Iterator i$ = m.entrySet().iterator(); + + while(i$.hasNext()) { + Entry e = (Entry)i$.next(); + this.put(e.getKey(), e.getValue()); + } + + } + + public V remove(@Nullable Object key) { + if (key == null) { + return null; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).remove(key, hash); + } + } + + public boolean remove(@Nullable Object key, @Nullable Object value) { + if (key != null && value != null) { + int hash = this.hash(key); + return this.segmentFor(hash).remove(key, hash, value); + } else { + return false; + } + } + + public boolean replace(K key, @Nullable V oldValue, V newValue) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(newValue); + if (oldValue == null) { + return false; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).replace(key, hash, oldValue, newValue); + } + } + + public V replace(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + int hash = this.hash(key); + return this.segmentFor(hash).replace(key, hash, value); + } + + public void clear() { + LocalCache.Segment[] arr$ = this.segments; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + LocalCache.Segment segment = arr$[i$]; + segment.clear(); + } + + } + + void invalidateAll(Iterable keys) { + Iterator i$ = keys.iterator(); + + while(i$.hasNext()) { + Object key = i$.next(); + this.remove(key); + } + + } + + public Set keySet() { + Set ks = this.keySet; + return ks != null ? ks : (this.keySet = new LocalCache.KeySet(this)); + } + + public Collection values() { + Collection vs = this.values; + return vs != null ? vs : (this.values = new LocalCache.Values(this)); + } + + @GwtIncompatible("Not supported.") + public Set> entrySet() { + Set> es = this.entrySet; + return es != null ? es : (this.entrySet = new LocalCache.EntrySet(this)); + } + + static class LocalLoadingCache extends LocalCache.LocalManualCache implements LoadingCache { + private static final long serialVersionUID = 1L; + + LocalLoadingCache(CacheBuilder builder, CacheLoader loader) { + super(new LocalCache(builder, (CacheLoader)Preconditions.checkNotNull(loader)), null); + } + + public V get(K key) throws ExecutionException { + return this.localCache.getOrLoad(key); + } + + public V getUnchecked(K key) { + try { + return this.get(key); + } catch (ExecutionException var3) { + throw new UncheckedExecutionException(var3.getCause()); + } + } + + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + return this.localCache.getAll(keys); + } + + public void refresh(K key) { + this.localCache.refresh(key); + } + + public final V apply(K key) { + return this.getUnchecked(key); + } + + Object writeReplace() { + return new LocalCache.LoadingSerializationProxy(this.localCache); + } + } + + static class LocalManualCache implements Cache, Serializable { + final LocalCache localCache; + private static final long serialVersionUID = 1L; + + LocalManualCache(CacheBuilder builder) { + this(new LocalCache(builder, (CacheLoader)null)); + } + + private LocalManualCache(LocalCache localCache) { + this.localCache = localCache; + } + + @Nullable + public V getIfPresent(Object key) { + return this.localCache.getIfPresent(key); + } + + public V get(K key, final Callable valueLoader) throws ExecutionException { + Preconditions.checkNotNull(valueLoader); + return this.localCache.get(key, new CacheLoader() { + public V load(Object key) throws Exception { + return valueLoader.call(); + } + }); + } + + public ImmutableMap getAllPresent(Iterable keys) { + return this.localCache.getAllPresent(keys); + } + + public void put(K key, V value) { + this.localCache.put(key, value); + } + + public void putAll(Map m) { + this.localCache.putAll(m); + } + + public void invalidate(Object key) { + Preconditions.checkNotNull(key); + this.localCache.remove(key); + } + + public void invalidateAll(Iterable keys) { + this.localCache.invalidateAll(keys); + } + + public void invalidateAll() { + this.localCache.clear(); + } + + public long size() { + return this.localCache.longSize(); + } + + public ConcurrentMap asMap() { + return this.localCache; + } + + public CacheStats stats() { + AbstractCache.SimpleStatsCounter aggregator = new AbstractCache.SimpleStatsCounter(); + aggregator.incrementBy(this.localCache.globalStatsCounter); + LocalCache.Segment[] arr$ = this.localCache.segments; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + LocalCache.Segment segment = arr$[i$]; + aggregator.incrementBy(segment.statsCounter); + } + + return aggregator.snapshot(); + } + + public void cleanUp() { + this.localCache.cleanUp(); + } + + Object writeReplace() { + return new LocalCache.ManualSerializationProxy(this.localCache); + } + + // $FF: synthetic method + LocalManualCache(LocalCache x0, Object x1) { + this(x0); + } + } + + static final class LoadingSerializationProxy extends LocalCache.ManualSerializationProxy implements LoadingCache, Serializable { + private static final long serialVersionUID = 1L; + transient LoadingCache autoDelegate; + + LoadingSerializationProxy(LocalCache cache) { + super(cache); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + CacheBuilder builder = this.recreateCacheBuilder(); + this.autoDelegate = builder.build(this.loader); + } + + public V get(K key) throws ExecutionException { + return this.autoDelegate.get(key); + } + + public V getUnchecked(K key) { + return this.autoDelegate.getUnchecked(key); + } + + public ImmutableMap getAll(Iterable keys) throws ExecutionException { + return this.autoDelegate.getAll(keys); + } + + public final V apply(K key) { + return this.autoDelegate.apply(key); + } + + public void refresh(K key) { + this.autoDelegate.refresh(key); + } + + private Object readResolve() { + return this.autoDelegate; + } + } + + static class ManualSerializationProxy extends ForwardingCache implements Serializable { + private static final long serialVersionUID = 1L; + final LocalCache.Strength keyStrength; + final LocalCache.Strength valueStrength; + final Equivalence keyEquivalence; + final Equivalence valueEquivalence; + final long expireAfterWriteNanos; + final long expireAfterAccessNanos; + final long maxWeight; + final Weigher weigher; + final int concurrencyLevel; + final RemovalListener removalListener; + final Ticker ticker; + final CacheLoader loader; + transient Cache delegate; + + ManualSerializationProxy(LocalCache cache) { + this(cache.keyStrength, cache.valueStrength, cache.keyEquivalence, cache.valueEquivalence, cache.expireAfterWriteNanos, cache.expireAfterAccessNanos, cache.maxWeight, cache.weigher, cache.concurrencyLevel, cache.removalListener, cache.ticker, cache.defaultLoader); + } + + private ManualSerializationProxy(LocalCache.Strength keyStrength, LocalCache.Strength valueStrength, Equivalence keyEquivalence, Equivalence valueEquivalence, long expireAfterWriteNanos, long expireAfterAccessNanos, long maxWeight, Weigher weigher, int concurrencyLevel, RemovalListener removalListener, Ticker ticker, CacheLoader loader) { + this.keyStrength = keyStrength; + this.valueStrength = valueStrength; + this.keyEquivalence = keyEquivalence; + this.valueEquivalence = valueEquivalence; + this.expireAfterWriteNanos = expireAfterWriteNanos; + this.expireAfterAccessNanos = expireAfterAccessNanos; + this.maxWeight = maxWeight; + this.weigher = weigher; + this.concurrencyLevel = concurrencyLevel; + this.removalListener = removalListener; + this.ticker = ticker != Ticker.systemTicker() && ticker != CacheBuilder.NULL_TICKER ? ticker : null; + this.loader = loader; + } + + CacheBuilder recreateCacheBuilder() { + CacheBuilder builder = CacheBuilder.newBuilder().setKeyStrength(this.keyStrength).setValueStrength(this.valueStrength).keyEquivalence(this.keyEquivalence).valueEquivalence(this.valueEquivalence).concurrencyLevel(this.concurrencyLevel).removalListener(this.removalListener); + builder.strictParsing = false; + if (this.expireAfterWriteNanos > 0L) { + builder.expireAfterWrite(this.expireAfterWriteNanos, TimeUnit.NANOSECONDS); + } + + if (this.expireAfterAccessNanos > 0L) { + builder.expireAfterAccess(this.expireAfterAccessNanos, TimeUnit.NANOSECONDS); + } + + if (this.weigher != CacheBuilder.OneWeigher.INSTANCE) { + builder.weigher(this.weigher); + if (this.maxWeight != -1L) { + builder.maximumWeight(this.maxWeight); + } + } else if (this.maxWeight != -1L) { + builder.maximumSize(this.maxWeight); + } + + if (this.ticker != null) { + builder.ticker(this.ticker); + } + + return builder; + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + CacheBuilder builder = this.recreateCacheBuilder(); + this.delegate = builder.build(); + } + + private Object readResolve() { + return this.delegate; + } + + protected Cache delegate() { + return this.delegate; + } + } + + final class EntrySet extends LocalCache.AbstractCacheSet> { + EntrySet(ConcurrentMap map) { + super(map); + } + + public Iterator> iterator() { + return LocalCache.this.new EntryIterator(); + } + + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } else { + Entry e = (Entry)o; + Object key = e.getKey(); + if (key == null) { + return false; + } else { + V v = LocalCache.this.get(key); + return v != null && LocalCache.this.valueEquivalence.equivalent(e.getValue(), v); + } + } + } + + public boolean remove(Object o) { + if (!(o instanceof Entry)) { + return false; + } else { + Entry e = (Entry)o; + Object key = e.getKey(); + return key != null && LocalCache.this.remove(key, e.getValue()); + } + } + } + + final class Values extends AbstractCollection { + private final ConcurrentMap map; + + Values(ConcurrentMap map) { + this.map = map; + } + + public int size() { + return this.map.size(); + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + public void clear() { + this.map.clear(); + } + + public Iterator iterator() { + return LocalCache.this.new ValueIterator(); + } + + public boolean contains(Object o) { + return this.map.containsValue(o); + } + } + + final class KeySet extends LocalCache.AbstractCacheSet { + KeySet(ConcurrentMap map) { + super(map); + } + + public Iterator iterator() { + return LocalCache.this.new KeyIterator(); + } + + public boolean contains(Object o) { + return this.map.containsKey(o); + } + + public boolean remove(Object o) { + return this.map.remove(o) != null; + } + } + + abstract class AbstractCacheSet extends AbstractSet { + final ConcurrentMap map; + + AbstractCacheSet(ConcurrentMap map) { + this.map = map; + } + + public int size() { + return this.map.size(); + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + public void clear() { + this.map.clear(); + } + } + + final class EntryIterator extends LocalCache.HashIterator> { + EntryIterator() { + super(); + } + + public Entry next() { + return this.nextEntry(); + } + } + + final class WriteThroughEntry implements Entry { + final K key; + V value; + + WriteThroughEntry(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return this.key; + } + + public V getValue() { + return this.value; + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof Entry)) { + return false; + } else { + Entry that = (Entry)object; + return this.key.equals(that.getKey()) && this.value.equals(that.getValue()); + } + } + + public int hashCode() { + return this.key.hashCode() ^ this.value.hashCode(); + } + + public V setValue(V newValue) { + throw new UnsupportedOperationException(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.getKey())); + String var2 = String.valueOf(String.valueOf(this.getValue())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append("=").append(var2).toString(); + } + } + + final class ValueIterator extends LocalCache.HashIterator { + ValueIterator() { + super(); + } + + public V next() { + return this.nextEntry().getValue(); + } + } + + final class KeyIterator extends LocalCache.HashIterator { + KeyIterator() { + super(); + } + + public K next() { + return this.nextEntry().getKey(); + } + } + + abstract class HashIterator implements Iterator { + int nextSegmentIndex; + int nextTableIndex; + LocalCache.Segment currentSegment; + AtomicReferenceArray> currentTable; + LocalCache.ReferenceEntry nextEntry; + LocalCache.WriteThroughEntry nextExternal; + LocalCache.WriteThroughEntry lastReturned; + + HashIterator() { + this.nextSegmentIndex = LocalCache.this.segments.length - 1; + this.nextTableIndex = -1; + this.advance(); + } + + public abstract T next(); + + final void advance() { + this.nextExternal = null; + if (!this.nextInChain()) { + if (!this.nextInTable()) { + while(this.nextSegmentIndex >= 0) { + this.currentSegment = LocalCache.this.segments[this.nextSegmentIndex--]; + if (this.currentSegment.count != 0) { + this.currentTable = this.currentSegment.table; + this.nextTableIndex = this.currentTable.length() - 1; + if (this.nextInTable()) { + return; + } + } + } + + } + } + } + + boolean nextInChain() { + if (this.nextEntry != null) { + for(this.nextEntry = this.nextEntry.getNext(); this.nextEntry != null; this.nextEntry = this.nextEntry.getNext()) { + if (this.advanceTo(this.nextEntry)) { + return true; + } + } + } + + return false; + } + + boolean nextInTable() { + while(true) { + if (this.nextTableIndex >= 0) { + if ((this.nextEntry = (LocalCache.ReferenceEntry)this.currentTable.get(this.nextTableIndex--)) == null || !this.advanceTo(this.nextEntry) && !this.nextInChain()) { + continue; + } + + return true; + } + + return false; + } + } + + boolean advanceTo(LocalCache.ReferenceEntry entry) { + boolean var6; + try { + long now = LocalCache.this.ticker.read(); + K key = entry.getKey(); + V value = LocalCache.this.getLiveValue(entry, now); + if (value == null) { + var6 = false; + return var6; + } + + this.nextExternal = LocalCache.this.new WriteThroughEntry(key, value); + var6 = true; + } finally { + this.currentSegment.postReadCleanup(); + } + + return var6; + } + + public boolean hasNext() { + return this.nextExternal != null; + } + + LocalCache.WriteThroughEntry nextEntry() { + if (this.nextExternal == null) { + throw new NoSuchElementException(); + } else { + this.lastReturned = this.nextExternal; + this.advance(); + return this.lastReturned; + } + } + + public void remove() { + Preconditions.checkState(this.lastReturned != null); + LocalCache.this.remove(this.lastReturned.getKey()); + this.lastReturned = null; + } + } + + static final class AccessQueue extends AbstractQueue> { + final LocalCache.ReferenceEntry head = new LocalCache.AbstractReferenceEntry() { + LocalCache.ReferenceEntry nextAccess = this; + LocalCache.ReferenceEntry previousAccess = this; + + public long getAccessTime() { + return Long.MAX_VALUE; + } + + public void setAccessTime(long time) { + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + return this.nextAccess; + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + this.nextAccess = next; + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + return this.previousAccess; + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + this.previousAccess = previous; + } + }; + + public boolean offer(LocalCache.ReferenceEntry entry) { + LocalCache.connectAccessOrder(entry.getPreviousInAccessQueue(), entry.getNextInAccessQueue()); + LocalCache.connectAccessOrder(this.head.getPreviousInAccessQueue(), entry); + LocalCache.connectAccessOrder(entry, this.head); + return true; + } + + public LocalCache.ReferenceEntry peek() { + LocalCache.ReferenceEntry next = this.head.getNextInAccessQueue(); + return next == this.head ? null : next; + } + + public LocalCache.ReferenceEntry poll() { + LocalCache.ReferenceEntry next = this.head.getNextInAccessQueue(); + if (next == this.head) { + return null; + } else { + this.remove(next); + return next; + } + } + + public boolean remove(Object o) { + LocalCache.ReferenceEntry e = (LocalCache.ReferenceEntry)o; + LocalCache.ReferenceEntry previous = e.getPreviousInAccessQueue(); + LocalCache.ReferenceEntry next = e.getNextInAccessQueue(); + LocalCache.connectAccessOrder(previous, next); + LocalCache.nullifyAccessOrder(e); + return next != LocalCache.NullEntry.INSTANCE; + } + + public boolean contains(Object o) { + LocalCache.ReferenceEntry e = (LocalCache.ReferenceEntry)o; + return e.getNextInAccessQueue() != LocalCache.NullEntry.INSTANCE; + } + + public boolean isEmpty() { + return this.head.getNextInAccessQueue() == this.head; + } + + public int size() { + int size = 0; + + for(LocalCache.ReferenceEntry e = this.head.getNextInAccessQueue(); e != this.head; e = e.getNextInAccessQueue()) { + ++size; + } + + return size; + } + + public void clear() { + LocalCache.ReferenceEntry next; + for(LocalCache.ReferenceEntry e = this.head.getNextInAccessQueue(); e != this.head; e = next) { + next = e.getNextInAccessQueue(); + LocalCache.nullifyAccessOrder(e); + } + + this.head.setNextInAccessQueue(this.head); + this.head.setPreviousInAccessQueue(this.head); + } + + public Iterator> iterator() { + return new AbstractSequentialIterator>(this.peek()) { + protected LocalCache.ReferenceEntry computeNext(LocalCache.ReferenceEntry previous) { + LocalCache.ReferenceEntry next = previous.getNextInAccessQueue(); + return next == AccessQueue.this.head ? null : next; + } + }; + } + } + + static final class WriteQueue extends AbstractQueue> { + final LocalCache.ReferenceEntry head = new LocalCache.AbstractReferenceEntry() { + LocalCache.ReferenceEntry nextWrite = this; + LocalCache.ReferenceEntry previousWrite = this; + + public long getWriteTime() { + return Long.MAX_VALUE; + } + + public void setWriteTime(long time) { + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + return this.nextWrite; + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + this.nextWrite = next; + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + return this.previousWrite; + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + this.previousWrite = previous; + } + }; + + public boolean offer(LocalCache.ReferenceEntry entry) { + LocalCache.connectWriteOrder(entry.getPreviousInWriteQueue(), entry.getNextInWriteQueue()); + LocalCache.connectWriteOrder(this.head.getPreviousInWriteQueue(), entry); + LocalCache.connectWriteOrder(entry, this.head); + return true; + } + + public LocalCache.ReferenceEntry peek() { + LocalCache.ReferenceEntry next = this.head.getNextInWriteQueue(); + return next == this.head ? null : next; + } + + public LocalCache.ReferenceEntry poll() { + LocalCache.ReferenceEntry next = this.head.getNextInWriteQueue(); + if (next == this.head) { + return null; + } else { + this.remove(next); + return next; + } + } + + public boolean remove(Object o) { + LocalCache.ReferenceEntry e = (LocalCache.ReferenceEntry)o; + LocalCache.ReferenceEntry previous = e.getPreviousInWriteQueue(); + LocalCache.ReferenceEntry next = e.getNextInWriteQueue(); + LocalCache.connectWriteOrder(previous, next); + LocalCache.nullifyWriteOrder(e); + return next != LocalCache.NullEntry.INSTANCE; + } + + public boolean contains(Object o) { + LocalCache.ReferenceEntry e = (LocalCache.ReferenceEntry)o; + return e.getNextInWriteQueue() != LocalCache.NullEntry.INSTANCE; + } + + public boolean isEmpty() { + return this.head.getNextInWriteQueue() == this.head; + } + + public int size() { + int size = 0; + + for(LocalCache.ReferenceEntry e = this.head.getNextInWriteQueue(); e != this.head; e = e.getNextInWriteQueue()) { + ++size; + } + + return size; + } + + public void clear() { + LocalCache.ReferenceEntry next; + for(LocalCache.ReferenceEntry e = this.head.getNextInWriteQueue(); e != this.head; e = next) { + next = e.getNextInWriteQueue(); + LocalCache.nullifyWriteOrder(e); + } + + this.head.setNextInWriteQueue(this.head); + this.head.setPreviousInWriteQueue(this.head); + } + + public Iterator> iterator() { + return new AbstractSequentialIterator>(this.peek()) { + protected LocalCache.ReferenceEntry computeNext(LocalCache.ReferenceEntry previous) { + LocalCache.ReferenceEntry next = previous.getNextInWriteQueue(); + return next == WriteQueue.this.head ? null : next; + } + }; + } + } + + static class LoadingValueReference implements LocalCache.ValueReference { + volatile LocalCache.ValueReference oldValue; + final SettableFuture futureValue; + final Stopwatch stopwatch; + + public LoadingValueReference() { + this(LocalCache.unset()); + } + + public LoadingValueReference(LocalCache.ValueReference oldValue) { + this.futureValue = SettableFuture.create(); + this.stopwatch = Stopwatch.createUnstarted(); + this.oldValue = oldValue; + } + + public boolean isLoading() { + return true; + } + + public boolean isActive() { + return this.oldValue.isActive(); + } + + public int getWeight() { + return this.oldValue.getWeight(); + } + + public boolean set(@Nullable V newValue) { + return this.futureValue.set(newValue); + } + + public boolean setException(Throwable t) { + return this.futureValue.setException(t); + } + + private ListenableFuture fullyFailedFuture(Throwable t) { + return Futures.immediateFailedFuture(t); + } + + public void notifyNewValue(@Nullable V newValue) { + if (newValue != null) { + this.set(newValue); + } else { + this.oldValue = LocalCache.unset(); + } + + } + + public ListenableFuture loadFuture(K key, CacheLoader loader) { + this.stopwatch.start(); + Object previousValue = this.oldValue.get(); + + try { + if (previousValue == null) { + V newValue = loader.load(key); + return (ListenableFuture)(this.set(newValue) ? this.futureValue : Futures.immediateFuture(newValue)); + } else { + ListenableFuture newValue = loader.reload(key, previousValue); + return newValue == null ? Futures.immediateFuture((Object)null) : Futures.transform(newValue, new Function() { + public V apply(V newValue) { + LoadingValueReference.this.set(newValue); + return newValue; + } + }); + } + } catch (Throwable var5) { + if (var5 instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + + return (ListenableFuture)(this.setException(var5) ? this.futureValue : this.fullyFailedFuture(var5)); + } + } + + public long elapsedNanos() { + return this.stopwatch.elapsed(TimeUnit.NANOSECONDS); + } + + public V waitForValue() throws ExecutionException { + return Uninterruptibles.getUninterruptibly(this.futureValue); + } + + public V get() { + return this.oldValue.get(); + } + + public LocalCache.ValueReference getOldValue() { + return this.oldValue; + } + + public LocalCache.ReferenceEntry getEntry() { + return null; + } + + public LocalCache.ValueReference copyFor(ReferenceQueue queue, @Nullable V value, LocalCache.ReferenceEntry entry) { + return this; + } + } + + static class Segment extends ReentrantLock { + final LocalCache map; + volatile int count; + @GuardedBy("this") + long totalWeight; + int modCount; + int threshold; + volatile AtomicReferenceArray> table; + final long maxSegmentWeight; + final ReferenceQueue keyReferenceQueue; + final ReferenceQueue valueReferenceQueue; + final Queue> recencyQueue; + final AtomicInteger readCount = new AtomicInteger(); + @GuardedBy("this") + final Queue> writeQueue; + @GuardedBy("this") + final Queue> accessQueue; + final AbstractCache.StatsCounter statsCounter; + + Segment(LocalCache map, int initialCapacity, long maxSegmentWeight, AbstractCache.StatsCounter statsCounter) { + this.map = map; + this.maxSegmentWeight = maxSegmentWeight; + this.statsCounter = (AbstractCache.StatsCounter)Preconditions.checkNotNull(statsCounter); + this.initTable(this.newEntryArray(initialCapacity)); + this.keyReferenceQueue = map.usesKeyReferences() ? new ReferenceQueue() : null; + this.valueReferenceQueue = map.usesValueReferences() ? new ReferenceQueue() : null; + this.recencyQueue = (Queue)(map.usesAccessQueue() ? new ConcurrentLinkedQueue() : LocalCache.discardingQueue()); + this.writeQueue = (Queue)(map.usesWriteQueue() ? new LocalCache.WriteQueue() : LocalCache.discardingQueue()); + this.accessQueue = (Queue)(map.usesAccessQueue() ? new LocalCache.AccessQueue() : LocalCache.discardingQueue()); + } + + AtomicReferenceArray> newEntryArray(int size) { + return new AtomicReferenceArray(size); + } + + void initTable(AtomicReferenceArray> newTable) { + this.threshold = newTable.length() * 3 / 4; + if (!this.map.customWeigher() && (long)this.threshold == this.maxSegmentWeight) { + ++this.threshold; + } + + this.table = newTable; + } + + @GuardedBy("this") + LocalCache.ReferenceEntry newEntry(K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return this.map.entryFactory.newEntry(this, Preconditions.checkNotNull(key), hash, next); + } + + @GuardedBy("this") + LocalCache.ReferenceEntry copyEntry(LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + if (original.getKey() == null) { + return null; + } else { + LocalCache.ValueReference valueReference = original.getValueReference(); + V value = valueReference.get(); + if (value == null && valueReference.isActive()) { + return null; + } else { + LocalCache.ReferenceEntry newEntry = this.map.entryFactory.copyEntry(this, original, newNext); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + return newEntry; + } + } + } + + @GuardedBy("this") + void setValue(LocalCache.ReferenceEntry entry, K key, V value, long now) { + LocalCache.ValueReference previous = entry.getValueReference(); + int weight = this.map.weigher.weigh(key, value); + Preconditions.checkState(weight >= 0, "Weights must be non-negative"); + LocalCache.ValueReference valueReference = this.map.valueStrength.referenceValue(this, entry, value, weight); + entry.setValueReference(valueReference); + this.recordWrite(entry, weight, now); + previous.notifyNewValue(value); + } + + V get(K key, int hash, CacheLoader loader) throws ExecutionException { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(loader); + + try { + if (this.count != 0) { + LocalCache.ReferenceEntry e = this.getEntry(key, hash); + if (e != null) { + long now = this.map.ticker.read(); + V value = this.getLiveValue(e, now); + if (value != null) { + this.recordRead(e, now); + this.statsCounter.recordHits(1); + Object var17 = this.scheduleRefresh(e, key, hash, value, now, loader); + return var17; + } + + LocalCache.ValueReference valueReference = e.getValueReference(); + if (valueReference.isLoading()) { + Object var9 = this.waitForLoadingValue(e, key, valueReference); + return var9; + } + } + } + + Object var15 = this.lockedGetOrLoad(key, hash, loader); + return var15; + } catch (ExecutionException var13) { + Throwable cause = var13.getCause(); + if (cause instanceof Error) { + throw new ExecutionError((Error)cause); + } else if (cause instanceof RuntimeException) { + throw new UncheckedExecutionException(cause); + } else { + throw var13; + } + } finally { + this.postReadCleanup(); + } + } + + V lockedGetOrLoad(K key, int hash, CacheLoader loader) throws ExecutionException { + LocalCache.ValueReference valueReference = null; + LocalCache.LoadingValueReference loadingValueReference = null; + boolean createNewEntry = true; + this.lock(); + + LocalCache.ReferenceEntry e; + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + valueReference = e.getValueReference(); + if (valueReference.isLoading()) { + createNewEntry = false; + } else { + V value = valueReference.get(); + if (value == null) { + this.enqueueNotification(entryKey, hash, valueReference, RemovalCause.COLLECTED); + } else { + if (!this.map.isExpired(e, now)) { + this.recordLockedRead(e, now); + this.statsCounter.recordHits(1); + Object var16 = value; + return var16; + } + + this.enqueueNotification(entryKey, hash, valueReference, RemovalCause.EXPIRED); + } + + this.writeQueue.remove(e); + this.accessQueue.remove(e); + this.count = newCount; + } + break; + } + } + + if (createNewEntry) { + loadingValueReference = new LocalCache.LoadingValueReference(); + if (e == null) { + e = this.newEntry(key, hash, first); + e.setValueReference(loadingValueReference); + table.set(index, e); + } else { + e.setValueReference(loadingValueReference); + } + } + } finally { + this.unlock(); + this.postWriteCleanup(); + } + + if (createNewEntry) { + Object var9; + try { + synchronized(e) { + var9 = this.loadSync(key, hash, loadingValueReference, loader); + } + } finally { + this.statsCounter.recordMisses(1); + } + + return var9; + } else { + return this.waitForLoadingValue(e, key, valueReference); + } + } + + V waitForLoadingValue(LocalCache.ReferenceEntry e, K key, LocalCache.ValueReference valueReference) throws ExecutionException { + if (!valueReference.isLoading()) { + throw new AssertionError(); + } else { + Preconditions.checkState(!Thread.holdsLock(e), "Recursive load of: %s", key); + + Object var7; + try { + V value = valueReference.waitForValue(); + if (value == null) { + String var11 = String.valueOf(String.valueOf(key)); + throw new CacheLoader.InvalidCacheLoadException((new StringBuilder(35 + var11.length())).append("CacheLoader returned null for key ").append(var11).append(".").toString()); + } + + long now = this.map.ticker.read(); + this.recordRead(e, now); + var7 = value; + } finally { + this.statsCounter.recordMisses(1); + } + + return var7; + } + } + + V loadSync(K key, int hash, LocalCache.LoadingValueReference loadingValueReference, CacheLoader loader) throws ExecutionException { + ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); + return this.getAndRecordStats(key, hash, loadingValueReference, loadingFuture); + } + + ListenableFuture loadAsync(final K key, final int hash, final LocalCache.LoadingValueReference loadingValueReference, CacheLoader loader) { + final ListenableFuture loadingFuture = loadingValueReference.loadFuture(key, loader); + loadingFuture.addListener(new Runnable() { + public void run() { + try { + Object var1 = Segment.this.getAndRecordStats(key, hash, loadingValueReference, loadingFuture); + } catch (Throwable var2) { + LocalCache.logger.log(Level.WARNING, "Exception thrown during refresh", var2); + loadingValueReference.setException(var2); + } + + } + }, MoreExecutors.directExecutor()); + return loadingFuture; + } + + V getAndRecordStats(K key, int hash, LocalCache.LoadingValueReference loadingValueReference, ListenableFuture newValue) throws ExecutionException { + Object value = null; + + Object var6; + try { + value = Uninterruptibles.getUninterruptibly(newValue); + if (value == null) { + String var10 = String.valueOf(String.valueOf(key)); + throw new CacheLoader.InvalidCacheLoadException((new StringBuilder(35 + var10.length())).append("CacheLoader returned null for key ").append(var10).append(".").toString()); + } + + this.statsCounter.recordLoadSuccess(loadingValueReference.elapsedNanos()); + this.storeLoadedValue(key, hash, loadingValueReference, value); + var6 = value; + } finally { + if (value == null) { + this.statsCounter.recordLoadException(loadingValueReference.elapsedNanos()); + this.removeLoadingValue(key, hash, loadingValueReference); + } + + } + + return var6; + } + + V scheduleRefresh(LocalCache.ReferenceEntry entry, K key, int hash, V oldValue, long now, CacheLoader loader) { + if (this.map.refreshes() && now - entry.getWriteTime() > this.map.refreshNanos && !entry.getValueReference().isLoading()) { + V newValue = this.refresh(key, hash, loader, true); + if (newValue != null) { + return newValue; + } + } + + return oldValue; + } + + @Nullable + V refresh(K key, int hash, CacheLoader loader, boolean checkTime) { + LocalCache.LoadingValueReference loadingValueReference = this.insertLoadingValueReference(key, hash, checkTime); + if (loadingValueReference == null) { + return null; + } else { + ListenableFuture result = this.loadAsync(key, hash, loadingValueReference, loader); + if (result.isDone()) { + try { + return Uninterruptibles.getUninterruptibly(result); + } catch (Throwable var8) { + } + } + + return null; + } + } + + @Nullable + LocalCache.LoadingValueReference insertLoadingValueReference(K key, int hash, boolean checkTime) { + LocalCache.ReferenceEntry e = null; + this.lock(); + + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference valueReference = e.getValueReference(); + LocalCache.LoadingValueReference loadingValueReference; + if (!valueReference.isLoading() && (!checkTime || now - e.getWriteTime() >= this.map.refreshNanos)) { + ++this.modCount; + loadingValueReference = new LocalCache.LoadingValueReference(valueReference); + e.setValueReference(loadingValueReference); + LocalCache.LoadingValueReference var13 = loadingValueReference; + return var13; + } + + loadingValueReference = null; + return loadingValueReference; + } + } + + ++this.modCount; + LocalCache.LoadingValueReference loadingValueReference = new LocalCache.LoadingValueReference(); + e = this.newEntry(key, hash, first); + e.setValueReference(loadingValueReference); + table.set(index, e); + LocalCache.LoadingValueReference var18 = loadingValueReference; + return var18; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + void tryDrainReferenceQueues() { + if (this.tryLock()) { + try { + this.drainReferenceQueues(); + } finally { + this.unlock(); + } + } + + } + + @GuardedBy("this") + void drainReferenceQueues() { + if (this.map.usesKeyReferences()) { + this.drainKeyReferenceQueue(); + } + + if (this.map.usesValueReferences()) { + this.drainValueReferenceQueue(); + } + + } + + @GuardedBy("this") + void drainKeyReferenceQueue() { + int i = 0; + + Reference ref; + while((ref = this.keyReferenceQueue.poll()) != null) { + LocalCache.ReferenceEntry entry = (LocalCache.ReferenceEntry)ref; + this.map.reclaimKey(entry); + ++i; + if (i == 16) { + break; + } + } + + } + + @GuardedBy("this") + void drainValueReferenceQueue() { + int i = 0; + + Reference ref; + while((ref = this.valueReferenceQueue.poll()) != null) { + LocalCache.ValueReference valueReference = (LocalCache.ValueReference)ref; + this.map.reclaimValue(valueReference); + ++i; + if (i == 16) { + break; + } + } + + } + + void clearReferenceQueues() { + if (this.map.usesKeyReferences()) { + this.clearKeyReferenceQueue(); + } + + if (this.map.usesValueReferences()) { + this.clearValueReferenceQueue(); + } + + } + + void clearKeyReferenceQueue() { + while(this.keyReferenceQueue.poll() != null) { + } + + } + + void clearValueReferenceQueue() { + while(this.valueReferenceQueue.poll() != null) { + } + + } + + void recordRead(LocalCache.ReferenceEntry entry, long now) { + if (this.map.recordsAccess()) { + entry.setAccessTime(now); + } + + this.recencyQueue.add(entry); + } + + @GuardedBy("this") + void recordLockedRead(LocalCache.ReferenceEntry entry, long now) { + if (this.map.recordsAccess()) { + entry.setAccessTime(now); + } + + this.accessQueue.add(entry); + } + + @GuardedBy("this") + void recordWrite(LocalCache.ReferenceEntry entry, int weight, long now) { + this.drainRecencyQueue(); + this.totalWeight += (long)weight; + if (this.map.recordsAccess()) { + entry.setAccessTime(now); + } + + if (this.map.recordsWrite()) { + entry.setWriteTime(now); + } + + this.accessQueue.add(entry); + this.writeQueue.add(entry); + } + + @GuardedBy("this") + void drainRecencyQueue() { + LocalCache.ReferenceEntry e; + while((e = (LocalCache.ReferenceEntry)this.recencyQueue.poll()) != null) { + if (this.accessQueue.contains(e)) { + this.accessQueue.add(e); + } + } + + } + + void tryExpireEntries(long now) { + if (this.tryLock()) { + try { + this.expireEntries(now); + } finally { + this.unlock(); + } + } + + } + + @GuardedBy("this") + void expireEntries(long now) { + this.drainRecencyQueue(); + + LocalCache.ReferenceEntry e; + while((e = (LocalCache.ReferenceEntry)this.writeQueue.peek()) != null && this.map.isExpired(e, now)) { + if (!this.removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + + while((e = (LocalCache.ReferenceEntry)this.accessQueue.peek()) != null && this.map.isExpired(e, now)) { + if (!this.removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + + } + + @GuardedBy("this") + void enqueueNotification(LocalCache.ReferenceEntry entry, RemovalCause cause) { + this.enqueueNotification(entry.getKey(), entry.getHash(), entry.getValueReference(), cause); + } + + @GuardedBy("this") + void enqueueNotification(@Nullable K key, int hash, LocalCache.ValueReference valueReference, RemovalCause cause) { + this.totalWeight -= (long)valueReference.getWeight(); + if (cause.wasEvicted()) { + this.statsCounter.recordEviction(); + } + + if (this.map.removalNotificationQueue != LocalCache.DISCARDING_QUEUE) { + V value = valueReference.get(); + RemovalNotification notification = new RemovalNotification(key, value, cause); + this.map.removalNotificationQueue.offer(notification); + } + + } + + @GuardedBy("this") + void evictEntries() { + if (this.map.evictsBySize()) { + this.drainRecencyQueue(); + + LocalCache.ReferenceEntry e; + do { + if (this.totalWeight <= this.maxSegmentWeight) { + return; + } + + e = this.getNextEvictable(); + } while(this.removeEntry(e, e.getHash(), RemovalCause.SIZE)); + + throw new AssertionError(); + } + } + + @GuardedBy("this") + LocalCache.ReferenceEntry getNextEvictable() { + Iterator i$ = this.accessQueue.iterator(); + + LocalCache.ReferenceEntry e; + int weight; + do { + if (!i$.hasNext()) { + throw new AssertionError(); + } + + e = (LocalCache.ReferenceEntry)i$.next(); + weight = e.getValueReference().getWeight(); + } while(weight <= 0); + + return e; + } + + LocalCache.ReferenceEntry getFirst(int hash) { + AtomicReferenceArray> table = this.table; + return (LocalCache.ReferenceEntry)table.get(hash & table.length() - 1); + } + + @Nullable + LocalCache.ReferenceEntry getEntry(Object key, int hash) { + for(LocalCache.ReferenceEntry e = this.getFirst(hash); e != null; e = e.getNext()) { + if (e.getHash() == hash) { + K entryKey = e.getKey(); + if (entryKey == null) { + this.tryDrainReferenceQueues(); + } else if (this.map.keyEquivalence.equivalent(key, entryKey)) { + return e; + } + } + } + + return null; + } + + @Nullable + LocalCache.ReferenceEntry getLiveEntry(Object key, int hash, long now) { + LocalCache.ReferenceEntry e = this.getEntry(key, hash); + if (e == null) { + return null; + } else if (this.map.isExpired(e, now)) { + this.tryExpireEntries(now); + return null; + } else { + return e; + } + } + + V getLiveValue(LocalCache.ReferenceEntry entry, long now) { + if (entry.getKey() == null) { + this.tryDrainReferenceQueues(); + return null; + } else { + V value = entry.getValueReference().get(); + if (value == null) { + this.tryDrainReferenceQueues(); + return null; + } else if (this.map.isExpired(entry, now)) { + this.tryExpireEntries(now); + return null; + } else { + return value; + } + } + } + + @Nullable + V get(Object key, int hash) { + try { + if (this.count != 0) { + long now = this.map.ticker.read(); + LocalCache.ReferenceEntry e = this.getLiveEntry(key, hash, now); + Object value; + if (e == null) { + value = null; + return value; + } + + value = e.getValueReference().get(); + if (value != null) { + this.recordRead(e, now); + Object var7 = this.scheduleRefresh(e, e.getKey(), hash, value, now, this.map.defaultLoader); + return var7; + } + + this.tryDrainReferenceQueues(); + } + + Object var11 = null; + return var11; + } finally { + this.postReadCleanup(); + } + } + + boolean containsKey(Object key, int hash) { + boolean var6; + try { + if (this.count == 0) { + boolean var10 = false; + return var10; + } + + long now = this.map.ticker.read(); + LocalCache.ReferenceEntry e = this.getLiveEntry(key, hash, now); + if (e != null) { + var6 = e.getValueReference().get() != null; + return var6; + } + + var6 = false; + } finally { + this.postReadCleanup(); + } + + return var6; + } + + @VisibleForTesting + boolean containsValue(Object value) { + boolean var13; + try { + if (this.count != 0) { + long now = this.map.ticker.read(); + AtomicReferenceArray> table = this.table; + int length = table.length(); + + for(int i = 0; i < length; ++i) { + for(LocalCache.ReferenceEntry e = (LocalCache.ReferenceEntry)table.get(i); e != null; e = e.getNext()) { + V entryValue = this.getLiveValue(e, now); + if (entryValue != null && this.map.valueEquivalence.equivalent(value, entryValue)) { + boolean var9 = true; + return var9; + } + } + } + } + + var13 = false; + } finally { + this.postReadCleanup(); + } + + return var13; + } + + @Nullable + V put(K key, int hash, V value, boolean onlyIfAbsent) { + this.lock(); + + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + int newCount = this.count + 1; + if (newCount > this.threshold) { + this.expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + LocalCache.ReferenceEntry e; + Object entryKey; + for(e = first; e != null; e = e.getNext()) { + entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + Object var15; + if (entryValue == null) { + ++this.modCount; + if (valueReference.isActive()) { + this.enqueueNotification(key, hash, valueReference, RemovalCause.COLLECTED); + this.setValue(e, key, value, now); + newCount = this.count; + } else { + this.setValue(e, key, value, now); + newCount = this.count + 1; + } + + this.count = newCount; + this.evictEntries(); + var15 = null; + return var15; + } + + if (!onlyIfAbsent) { + ++this.modCount; + this.enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + this.setValue(e, key, value, now); + this.evictEntries(); + var15 = entryValue; + return var15; + } + + this.recordLockedRead(e, now); + var15 = entryValue; + return var15; + } + } + + ++this.modCount; + e = this.newEntry(key, hash, first); + this.setValue(e, key, value, now); + table.set(index, e); + newCount = this.count + 1; + this.count = newCount; + this.evictEntries(); + entryKey = null; + return entryKey; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + @GuardedBy("this") + void expand() { + AtomicReferenceArray> oldTable = this.table; + int oldCapacity = oldTable.length(); + if (oldCapacity < 1073741824) { + int newCount = this.count; + AtomicReferenceArray> newTable = this.newEntryArray(oldCapacity << 1); + this.threshold = newTable.length() * 3 / 4; + int newMask = newTable.length() - 1; + + for(int oldIndex = 0; oldIndex < oldCapacity; ++oldIndex) { + LocalCache.ReferenceEntry head = (LocalCache.ReferenceEntry)oldTable.get(oldIndex); + if (head != null) { + LocalCache.ReferenceEntry next = head.getNext(); + int headIndex = head.getHash() & newMask; + if (next == null) { + newTable.set(headIndex, head); + } else { + LocalCache.ReferenceEntry tail = head; + int tailIndex = headIndex; + + LocalCache.ReferenceEntry e; + int newIndex; + for(e = next; e != null; e = e.getNext()) { + newIndex = e.getHash() & newMask; + if (newIndex != tailIndex) { + tailIndex = newIndex; + tail = e; + } + } + + newTable.set(tailIndex, tail); + + for(e = head; e != tail; e = e.getNext()) { + newIndex = e.getHash() & newMask; + LocalCache.ReferenceEntry newNext = (LocalCache.ReferenceEntry)newTable.get(newIndex); + LocalCache.ReferenceEntry newFirst = this.copyEntry(e, newNext); + if (newFirst != null) { + newTable.set(newIndex, newFirst); + } else { + this.removeCollectedEntry(e); + --newCount; + } + } + } + } + } + + this.table = newTable; + this.count = newCount; + } + } + + boolean replace(K key, int hash, V oldValue, V newValue) { + this.lock(); + + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(LocalCache.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + boolean var14; + if (entryValue == null) { + if (valueReference.isActive()) { + int newCount = this.count - 1; + ++this.modCount; + LocalCache.ReferenceEntry newFirst = this.removeValueFromChain(first, e, entryKey, hash, valueReference, RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + } + + var14 = false; + return var14; + } + + if (!this.map.valueEquivalence.equivalent(oldValue, entryValue)) { + this.recordLockedRead(e, now); + var14 = false; + return var14; + } + + ++this.modCount; + this.enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + this.setValue(e, key, newValue, now); + this.evictEntries(); + var14 = true; + return var14; + } + } + + boolean var19 = false; + return var19; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + @Nullable + V replace(K key, int hash, V newValue) { + this.lock(); + + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + LocalCache.ReferenceEntry e; + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + Object var18; + if (entryValue != null) { + ++this.modCount; + this.enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + this.setValue(e, key, newValue, now); + this.evictEntries(); + var18 = entryValue; + return var18; + } + + if (valueReference.isActive()) { + int newCount = this.count - 1; + ++this.modCount; + LocalCache.ReferenceEntry newFirst = this.removeValueFromChain(first, e, entryKey, hash, valueReference, RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + } + + var18 = null; + return var18; + } + } + + e = null; + return e; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + @Nullable + V remove(Object key, int hash) { + this.lock(); + + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + LocalCache.ReferenceEntry e; + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + RemovalCause cause; + LocalCache.ReferenceEntry newFirst; + if (entryValue != null) { + cause = RemovalCause.EXPLICIT; + } else { + if (!valueReference.isActive()) { + newFirst = null; + return newFirst; + } + + cause = RemovalCause.COLLECTED; + } + + ++this.modCount; + newFirst = this.removeValueFromChain(first, e, entryKey, hash, valueReference, cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + Object var15 = entryValue; + return var15; + } + } + + e = null; + return e; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + boolean storeLoadedValue(K key, int hash, LocalCache.LoadingValueReference oldValueReference, V newValue) { + this.lock(); + + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + int newCount = this.count + 1; + if (newCount > this.threshold) { + this.expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + LocalCache.ReferenceEntry e; + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + boolean var15; + if (oldValueReference == valueReference || entryValue == null && valueReference != LocalCache.UNSET) { + ++this.modCount; + if (oldValueReference.isActive()) { + RemovalCause cause = entryValue == null ? RemovalCause.COLLECTED : RemovalCause.REPLACED; + this.enqueueNotification(key, hash, oldValueReference, cause); + --newCount; + } + + this.setValue(e, key, newValue, now); + this.count = newCount; + this.evictEntries(); + var15 = true; + return var15; + } + + LocalCache.ValueReference valueReference = new LocalCache.WeightedStrongValueReference(newValue, 0); + this.enqueueNotification(key, hash, valueReference, RemovalCause.REPLACED); + var15 = false; + return var15; + } + } + + ++this.modCount; + e = this.newEntry(key, hash, first); + this.setValue(e, key, newValue, now); + table.set(index, e); + this.count = newCount; + this.evictEntries(); + boolean var19 = true; + return var19; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + boolean remove(Object key, int hash, Object value) { + this.lock(); + + try { + long now = this.map.ticker.read(); + this.preWriteCleanup(now); + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(LocalCache.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + RemovalCause cause; + if (this.map.valueEquivalence.equivalent(value, entryValue)) { + cause = RemovalCause.EXPLICIT; + } else { + if (entryValue != null || !valueReference.isActive()) { + boolean var21 = false; + return var21; + } + + cause = RemovalCause.COLLECTED; + } + + ++this.modCount; + LocalCache.ReferenceEntry newFirst = this.removeValueFromChain(first, e, entryKey, hash, valueReference, cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + boolean var16 = cause == RemovalCause.EXPLICIT; + return var16; + } + } + + boolean var20 = false; + return var20; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + void clear() { + if (this.count != 0) { + this.lock(); + + try { + AtomicReferenceArray> table = this.table; + + int i; + for(i = 0; i < table.length(); ++i) { + for(LocalCache.ReferenceEntry e = (LocalCache.ReferenceEntry)table.get(i); e != null; e = e.getNext()) { + if (e.getValueReference().isActive()) { + this.enqueueNotification(e, RemovalCause.EXPLICIT); + } + } + } + + for(i = 0; i < table.length(); ++i) { + table.set(i, (Object)null); + } + + this.clearReferenceQueues(); + this.writeQueue.clear(); + this.accessQueue.clear(); + this.readCount.set(0); + ++this.modCount; + this.count = 0; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + } + + @Nullable + @GuardedBy("this") + LocalCache.ReferenceEntry removeValueFromChain(LocalCache.ReferenceEntry first, LocalCache.ReferenceEntry entry, @Nullable K key, int hash, LocalCache.ValueReference valueReference, RemovalCause cause) { + this.enqueueNotification(key, hash, valueReference, cause); + this.writeQueue.remove(entry); + this.accessQueue.remove(entry); + if (valueReference.isLoading()) { + valueReference.notifyNewValue((Object)null); + return first; + } else { + return this.removeEntryFromChain(first, entry); + } + } + + @Nullable + @GuardedBy("this") + LocalCache.ReferenceEntry removeEntryFromChain(LocalCache.ReferenceEntry first, LocalCache.ReferenceEntry entry) { + int newCount = this.count; + LocalCache.ReferenceEntry newFirst = entry.getNext(); + + for(LocalCache.ReferenceEntry e = first; e != entry; e = e.getNext()) { + LocalCache.ReferenceEntry next = this.copyEntry(e, newFirst); + if (next != null) { + newFirst = next; + } else { + this.removeCollectedEntry(e); + --newCount; + } + } + + this.count = newCount; + return newFirst; + } + + @GuardedBy("this") + void removeCollectedEntry(LocalCache.ReferenceEntry entry) { + this.enqueueNotification(entry, RemovalCause.COLLECTED); + this.writeQueue.remove(entry); + this.accessQueue.remove(entry); + } + + boolean reclaimKey(LocalCache.ReferenceEntry entry, int hash) { + this.lock(); + + try { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(LocalCache.ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++this.modCount; + LocalCache.ReferenceEntry newFirst = this.removeValueFromChain(first, e, e.getKey(), hash, e.getValueReference(), RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + boolean var9 = true; + return var9; + } + } + + boolean var13 = false; + return var13; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + boolean reclaimValue(K key, int hash, LocalCache.ValueReference valueReference) { + this.lock(); + + try { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(LocalCache.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference v = e.getValueReference(); + if (v != valueReference) { + boolean var17 = false; + return var17; + } + + ++this.modCount; + LocalCache.ReferenceEntry newFirst = this.removeValueFromChain(first, e, entryKey, hash, valueReference, RemovalCause.COLLECTED); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + boolean var12 = true; + return var12; + } + } + + boolean var16 = false; + return var16; + } finally { + this.unlock(); + if (!this.isHeldByCurrentThread()) { + this.postWriteCleanup(); + } + + } + } + + boolean removeLoadingValue(K key, int hash, LocalCache.LoadingValueReference valueReference) { + this.lock(); + + try { + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(LocalCache.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + LocalCache.ValueReference v = e.getValueReference(); + boolean var14; + if (v != valueReference) { + var14 = false; + return var14; + } + + if (valueReference.isActive()) { + e.setValueReference(valueReference.getOldValue()); + } else { + LocalCache.ReferenceEntry newFirst = this.removeEntryFromChain(first, e); + table.set(index, newFirst); + } + + var14 = true; + return var14; + } + } + + boolean var15 = false; + return var15; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + @GuardedBy("this") + boolean removeEntry(LocalCache.ReferenceEntry entry, int hash, RemovalCause cause) { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + LocalCache.ReferenceEntry first = (LocalCache.ReferenceEntry)table.get(index); + + for(LocalCache.ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++this.modCount; + LocalCache.ReferenceEntry newFirst = this.removeValueFromChain(first, e, e.getKey(), hash, e.getValueReference(), cause); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + return true; + } + } + + return false; + } + + void postReadCleanup() { + if ((this.readCount.incrementAndGet() & 63) == 0) { + this.cleanUp(); + } + + } + + @GuardedBy("this") + void preWriteCleanup(long now) { + this.runLockedCleanup(now); + } + + void postWriteCleanup() { + this.runUnlockedCleanup(); + } + + void cleanUp() { + long now = this.map.ticker.read(); + this.runLockedCleanup(now); + this.runUnlockedCleanup(); + } + + void runLockedCleanup(long now) { + if (this.tryLock()) { + try { + this.drainReferenceQueues(); + this.expireEntries(now); + this.readCount.set(0); + } finally { + this.unlock(); + } + } + + } + + void runUnlockedCleanup() { + if (!this.isHeldByCurrentThread()) { + this.map.processPendingNotifications(); + } + + } + } + + static final class WeightedStrongValueReference extends LocalCache.StrongValueReference { + final int weight; + + WeightedStrongValueReference(V referent, int weight) { + super(referent); + this.weight = weight; + } + + public int getWeight() { + return this.weight; + } + } + + static final class WeightedSoftValueReference extends LocalCache.SoftValueReference { + final int weight; + + WeightedSoftValueReference(ReferenceQueue queue, V referent, LocalCache.ReferenceEntry entry, int weight) { + super(queue, referent, entry); + this.weight = weight; + } + + public int getWeight() { + return this.weight; + } + + public LocalCache.ValueReference copyFor(ReferenceQueue queue, V value, LocalCache.ReferenceEntry entry) { + return new LocalCache.WeightedSoftValueReference(queue, value, entry, this.weight); + } + } + + static final class WeightedWeakValueReference extends LocalCache.WeakValueReference { + final int weight; + + WeightedWeakValueReference(ReferenceQueue queue, V referent, LocalCache.ReferenceEntry entry, int weight) { + super(queue, referent, entry); + this.weight = weight; + } + + public int getWeight() { + return this.weight; + } + + public LocalCache.ValueReference copyFor(ReferenceQueue queue, V value, LocalCache.ReferenceEntry entry) { + return new LocalCache.WeightedWeakValueReference(queue, value, entry, this.weight); + } + } + + static class StrongValueReference implements LocalCache.ValueReference { + final V referent; + + StrongValueReference(V referent) { + this.referent = referent; + } + + public V get() { + return this.referent; + } + + public int getWeight() { + return 1; + } + + public LocalCache.ReferenceEntry getEntry() { + return null; + } + + public LocalCache.ValueReference copyFor(ReferenceQueue queue, V value, LocalCache.ReferenceEntry entry) { + return this; + } + + public boolean isLoading() { + return false; + } + + public boolean isActive() { + return true; + } + + public V waitForValue() { + return this.get(); + } + + public void notifyNewValue(V newValue) { + } + } + + static class SoftValueReference extends SoftReference implements LocalCache.ValueReference { + final LocalCache.ReferenceEntry entry; + + SoftValueReference(ReferenceQueue queue, V referent, LocalCache.ReferenceEntry entry) { + super(referent, queue); + this.entry = entry; + } + + public int getWeight() { + return 1; + } + + public LocalCache.ReferenceEntry getEntry() { + return this.entry; + } + + public void notifyNewValue(V newValue) { + } + + public LocalCache.ValueReference copyFor(ReferenceQueue queue, V value, LocalCache.ReferenceEntry entry) { + return new LocalCache.SoftValueReference(queue, value, entry); + } + + public boolean isLoading() { + return false; + } + + public boolean isActive() { + return true; + } + + public V waitForValue() { + return this.get(); + } + } + + static class WeakValueReference extends WeakReference implements LocalCache.ValueReference { + final LocalCache.ReferenceEntry entry; + + WeakValueReference(ReferenceQueue queue, V referent, LocalCache.ReferenceEntry entry) { + super(referent, queue); + this.entry = entry; + } + + public int getWeight() { + return 1; + } + + public LocalCache.ReferenceEntry getEntry() { + return this.entry; + } + + public void notifyNewValue(V newValue) { + } + + public LocalCache.ValueReference copyFor(ReferenceQueue queue, V value, LocalCache.ReferenceEntry entry) { + return new LocalCache.WeakValueReference(queue, value, entry); + } + + public boolean isLoading() { + return false; + } + + public boolean isActive() { + return true; + } + + public V waitForValue() { + return this.get(); + } + } + + static final class WeakAccessWriteEntry extends LocalCache.WeakEntry { + volatile long accessTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextAccess = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousAccess = LocalCache.nullEntry(); + volatile long writeTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextWrite = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousWrite = LocalCache.nullEntry(); + + WeakAccessWriteEntry(ReferenceQueue queue, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public long getAccessTime() { + return this.accessTime; + } + + public void setAccessTime(long time) { + this.accessTime = time; + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + return this.nextAccess; + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + this.nextAccess = next; + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + return this.previousAccess; + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + this.previousAccess = previous; + } + + public long getWriteTime() { + return this.writeTime; + } + + public void setWriteTime(long time) { + this.writeTime = time; + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + return this.nextWrite; + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + this.nextWrite = next; + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + return this.previousWrite; + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class WeakWriteEntry extends LocalCache.WeakEntry { + volatile long writeTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextWrite = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousWrite = LocalCache.nullEntry(); + + WeakWriteEntry(ReferenceQueue queue, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public long getWriteTime() { + return this.writeTime; + } + + public void setWriteTime(long time) { + this.writeTime = time; + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + return this.nextWrite; + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + this.nextWrite = next; + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + return this.previousWrite; + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class WeakAccessEntry extends LocalCache.WeakEntry { + volatile long accessTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextAccess = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousAccess = LocalCache.nullEntry(); + + WeakAccessEntry(ReferenceQueue queue, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public long getAccessTime() { + return this.accessTime; + } + + public void setAccessTime(long time) { + this.accessTime = time; + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + return this.nextAccess; + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + this.nextAccess = next; + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + return this.previousAccess; + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + this.previousAccess = previous; + } + } + + static class WeakEntry extends WeakReference implements LocalCache.ReferenceEntry { + final int hash; + final LocalCache.ReferenceEntry next; + volatile LocalCache.ValueReference valueReference = LocalCache.unset(); + + WeakEntry(ReferenceQueue queue, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + super(key, queue); + this.hash = hash; + this.next = next; + } + + public K getKey() { + return this.get(); + } + + public long getAccessTime() { + throw new UnsupportedOperationException(); + } + + public void setAccessTime(long time) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + throw new UnsupportedOperationException(); + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + throw new UnsupportedOperationException(); + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public long getWriteTime() { + throw new UnsupportedOperationException(); + } + + public void setWriteTime(long time) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + throw new UnsupportedOperationException(); + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + throw new UnsupportedOperationException(); + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ValueReference getValueReference() { + return this.valueReference; + } + + public void setValueReference(LocalCache.ValueReference valueReference) { + this.valueReference = valueReference; + } + + public int getHash() { + return this.hash; + } + + public LocalCache.ReferenceEntry getNext() { + return this.next; + } + } + + static final class StrongAccessWriteEntry extends LocalCache.StrongEntry { + volatile long accessTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextAccess = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousAccess = LocalCache.nullEntry(); + volatile long writeTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextWrite = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousWrite = LocalCache.nullEntry(); + + StrongAccessWriteEntry(K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + super(key, hash, next); + } + + public long getAccessTime() { + return this.accessTime; + } + + public void setAccessTime(long time) { + this.accessTime = time; + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + return this.nextAccess; + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + this.nextAccess = next; + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + return this.previousAccess; + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + this.previousAccess = previous; + } + + public long getWriteTime() { + return this.writeTime; + } + + public void setWriteTime(long time) { + this.writeTime = time; + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + return this.nextWrite; + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + this.nextWrite = next; + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + return this.previousWrite; + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class StrongWriteEntry extends LocalCache.StrongEntry { + volatile long writeTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextWrite = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousWrite = LocalCache.nullEntry(); + + StrongWriteEntry(K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + super(key, hash, next); + } + + public long getWriteTime() { + return this.writeTime; + } + + public void setWriteTime(long time) { + this.writeTime = time; + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + return this.nextWrite; + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + this.nextWrite = next; + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + return this.previousWrite; + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + this.previousWrite = previous; + } + } + + static final class StrongAccessEntry extends LocalCache.StrongEntry { + volatile long accessTime = Long.MAX_VALUE; + LocalCache.ReferenceEntry nextAccess = LocalCache.nullEntry(); + LocalCache.ReferenceEntry previousAccess = LocalCache.nullEntry(); + + StrongAccessEntry(K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + super(key, hash, next); + } + + public long getAccessTime() { + return this.accessTime; + } + + public void setAccessTime(long time) { + this.accessTime = time; + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + return this.nextAccess; + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + this.nextAccess = next; + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + return this.previousAccess; + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + this.previousAccess = previous; + } + } + + static class StrongEntry extends LocalCache.AbstractReferenceEntry { + final K key; + final int hash; + final LocalCache.ReferenceEntry next; + volatile LocalCache.ValueReference valueReference = LocalCache.unset(); + + StrongEntry(K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + this.key = key; + this.hash = hash; + this.next = next; + } + + public K getKey() { + return this.key; + } + + public LocalCache.ValueReference getValueReference() { + return this.valueReference; + } + + public void setValueReference(LocalCache.ValueReference valueReference) { + this.valueReference = valueReference; + } + + public int getHash() { + return this.hash; + } + + public LocalCache.ReferenceEntry getNext() { + return this.next; + } + } + + abstract static class AbstractReferenceEntry implements LocalCache.ReferenceEntry { + public LocalCache.ValueReference getValueReference() { + throw new UnsupportedOperationException(); + } + + public void setValueReference(LocalCache.ValueReference valueReference) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getNext() { + throw new UnsupportedOperationException(); + } + + public int getHash() { + throw new UnsupportedOperationException(); + } + + public K getKey() { + throw new UnsupportedOperationException(); + } + + public long getAccessTime() { + throw new UnsupportedOperationException(); + } + + public void setAccessTime(long time) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + throw new UnsupportedOperationException(); + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + throw new UnsupportedOperationException(); + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public long getWriteTime() { + throw new UnsupportedOperationException(); + } + + public void setWriteTime(long time) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + throw new UnsupportedOperationException(); + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + throw new UnsupportedOperationException(); + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + } + + private static enum NullEntry implements LocalCache.ReferenceEntry { + INSTANCE; + + public LocalCache.ValueReference getValueReference() { + return null; + } + + public void setValueReference(LocalCache.ValueReference valueReference) { + } + + public LocalCache.ReferenceEntry getNext() { + return null; + } + + public int getHash() { + return 0; + } + + public Object getKey() { + return null; + } + + public long getAccessTime() { + return 0L; + } + + public void setAccessTime(long time) { + } + + public LocalCache.ReferenceEntry getNextInAccessQueue() { + return this; + } + + public void setNextInAccessQueue(LocalCache.ReferenceEntry next) { + } + + public LocalCache.ReferenceEntry getPreviousInAccessQueue() { + return this; + } + + public void setPreviousInAccessQueue(LocalCache.ReferenceEntry previous) { + } + + public long getWriteTime() { + return 0L; + } + + public void setWriteTime(long time) { + } + + public LocalCache.ReferenceEntry getNextInWriteQueue() { + return this; + } + + public void setNextInWriteQueue(LocalCache.ReferenceEntry next) { + } + + public LocalCache.ReferenceEntry getPreviousInWriteQueue() { + return this; + } + + public void setPreviousInWriteQueue(LocalCache.ReferenceEntry previous) { + } + } + + interface ReferenceEntry { + LocalCache.ValueReference getValueReference(); + + void setValueReference(LocalCache.ValueReference var1); + + @Nullable + LocalCache.ReferenceEntry getNext(); + + int getHash(); + + @Nullable + K getKey(); + + long getAccessTime(); + + void setAccessTime(long var1); + + LocalCache.ReferenceEntry getNextInAccessQueue(); + + void setNextInAccessQueue(LocalCache.ReferenceEntry var1); + + LocalCache.ReferenceEntry getPreviousInAccessQueue(); + + void setPreviousInAccessQueue(LocalCache.ReferenceEntry var1); + + long getWriteTime(); + + void setWriteTime(long var1); + + LocalCache.ReferenceEntry getNextInWriteQueue(); + + void setNextInWriteQueue(LocalCache.ReferenceEntry var1); + + LocalCache.ReferenceEntry getPreviousInWriteQueue(); + + void setPreviousInWriteQueue(LocalCache.ReferenceEntry var1); + } + + interface ValueReference { + @Nullable + V get(); + + V waitForValue() throws ExecutionException; + + int getWeight(); + + @Nullable + LocalCache.ReferenceEntry getEntry(); + + LocalCache.ValueReference copyFor(ReferenceQueue var1, @Nullable V var2, LocalCache.ReferenceEntry var3); + + void notifyNewValue(@Nullable V var1); + + boolean isLoading(); + + boolean isActive(); + } + + static enum EntryFactory { + STRONG { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.StrongEntry(key, hash, next); + } + }, + STRONG_ACCESS { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.StrongAccessEntry(key, hash, next); + } + + LocalCache.ReferenceEntry copyEntry(LocalCache.Segment segment, LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + LocalCache.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyAccessEntry(original, newEntry); + return newEntry; + } + }, + STRONG_WRITE { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.StrongWriteEntry(key, hash, next); + } + + LocalCache.ReferenceEntry copyEntry(LocalCache.Segment segment, LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + LocalCache.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyWriteEntry(original, newEntry); + return newEntry; + } + }, + STRONG_ACCESS_WRITE { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.StrongAccessWriteEntry(key, hash, next); + } + + LocalCache.ReferenceEntry copyEntry(LocalCache.Segment segment, LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + LocalCache.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyAccessEntry(original, newEntry); + this.copyWriteEntry(original, newEntry); + return newEntry; + } + }, + WEAK { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.WeakEntry(segment.keyReferenceQueue, key, hash, next); + } + }, + WEAK_ACCESS { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.WeakAccessEntry(segment.keyReferenceQueue, key, hash, next); + } + + LocalCache.ReferenceEntry copyEntry(LocalCache.Segment segment, LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + LocalCache.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyAccessEntry(original, newEntry); + return newEntry; + } + }, + WEAK_WRITE { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.WeakWriteEntry(segment.keyReferenceQueue, key, hash, next); + } + + LocalCache.ReferenceEntry copyEntry(LocalCache.Segment segment, LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + LocalCache.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyWriteEntry(original, newEntry); + return newEntry; + } + }, + WEAK_ACCESS_WRITE { + LocalCache.ReferenceEntry newEntry(LocalCache.Segment segment, K key, int hash, @Nullable LocalCache.ReferenceEntry next) { + return new LocalCache.WeakAccessWriteEntry(segment.keyReferenceQueue, key, hash, next); + } + + LocalCache.ReferenceEntry copyEntry(LocalCache.Segment segment, LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + LocalCache.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyAccessEntry(original, newEntry); + this.copyWriteEntry(original, newEntry); + return newEntry; + } + }; + + static final int ACCESS_MASK = 1; + static final int WRITE_MASK = 2; + static final int WEAK_MASK = 4; + static final LocalCache.EntryFactory[] factories = new LocalCache.EntryFactory[]{STRONG, STRONG_ACCESS, STRONG_WRITE, STRONG_ACCESS_WRITE, WEAK, WEAK_ACCESS, WEAK_WRITE, WEAK_ACCESS_WRITE}; + + private EntryFactory() { + } + + static LocalCache.EntryFactory getFactory(LocalCache.Strength keyStrength, boolean usesAccessQueue, boolean usesWriteQueue) { + int flags = (keyStrength == LocalCache.Strength.WEAK ? 4 : 0) | (usesAccessQueue ? 1 : 0) | (usesWriteQueue ? 2 : 0); + return factories[flags]; + } + + abstract LocalCache.ReferenceEntry newEntry(LocalCache.Segment var1, K var2, int var3, @Nullable LocalCache.ReferenceEntry var4); + + LocalCache.ReferenceEntry copyEntry(LocalCache.Segment segment, LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newNext) { + return this.newEntry(segment, original.getKey(), original.getHash(), newNext); + } + + void copyAccessEntry(LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newEntry) { + newEntry.setAccessTime(original.getAccessTime()); + LocalCache.connectAccessOrder(original.getPreviousInAccessQueue(), newEntry); + LocalCache.connectAccessOrder(newEntry, original.getNextInAccessQueue()); + LocalCache.nullifyAccessOrder(original); + } + + void copyWriteEntry(LocalCache.ReferenceEntry original, LocalCache.ReferenceEntry newEntry) { + newEntry.setWriteTime(original.getWriteTime()); + LocalCache.connectWriteOrder(original.getPreviousInWriteQueue(), newEntry); + LocalCache.connectWriteOrder(newEntry, original.getNextInWriteQueue()); + LocalCache.nullifyWriteOrder(original); + } + + // $FF: synthetic method + EntryFactory(Object x2) { + this(); + } + } + + static enum Strength { + STRONG { + LocalCache.ValueReference referenceValue(LocalCache.Segment segment, LocalCache.ReferenceEntry entry, V value, int weight) { + return (LocalCache.ValueReference)(weight == 1 ? new LocalCache.StrongValueReference(value) : new LocalCache.WeightedStrongValueReference(value, weight)); + } + + Equivalence defaultEquivalence() { + return Equivalence.equals(); + } + }, + SOFT { + LocalCache.ValueReference referenceValue(LocalCache.Segment segment, LocalCache.ReferenceEntry entry, V value, int weight) { + return (LocalCache.ValueReference)(weight == 1 ? new LocalCache.SoftValueReference(segment.valueReferenceQueue, value, entry) : new LocalCache.WeightedSoftValueReference(segment.valueReferenceQueue, value, entry, weight)); + } + + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }, + WEAK { + LocalCache.ValueReference referenceValue(LocalCache.Segment segment, LocalCache.ReferenceEntry entry, V value, int weight) { + return (LocalCache.ValueReference)(weight == 1 ? new LocalCache.WeakValueReference(segment.valueReferenceQueue, value, entry) : new LocalCache.WeightedWeakValueReference(segment.valueReferenceQueue, value, entry, weight)); + } + + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }; + + private Strength() { + } + + abstract LocalCache.ValueReference referenceValue(LocalCache.Segment var1, LocalCache.ReferenceEntry var2, V var3, int var4); + + abstract Equivalence defaultEquivalence(); + + // $FF: synthetic method + Strength(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/cache/LongAddable.java b/src/main/com/google/common/cache/LongAddable.java new file mode 100644 index 0000000..7a0d455 --- /dev/null +++ b/src/main/com/google/common/cache/LongAddable.java @@ -0,0 +1,12 @@ +package com.google.common.cache; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible +interface LongAddable { + void increment(); + + void add(long var1); + + long sum(); +} diff --git a/src/main/com/google/common/cache/LongAddables.java b/src/main/com/google/common/cache/LongAddables.java new file mode 100644 index 0000000..4588a6f --- /dev/null +++ b/src/main/com/google/common/cache/LongAddables.java @@ -0,0 +1,58 @@ +package com.google.common.cache; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Supplier; +import java.util.concurrent.atomic.AtomicLong; + +@GwtCompatible( + emulated = true +) +final class LongAddables { + private static final Supplier SUPPLIER; + + public static LongAddable create() { + return (LongAddable)SUPPLIER.get(); + } + + static { + Supplier supplier; + try { + new LongAdder(); + supplier = new Supplier() { + public LongAddable get() { + return new LongAdder(); + } + }; + } catch (Throwable var2) { + supplier = new Supplier() { + public LongAddable get() { + return new LongAddables.PureJavaLongAddable(); + } + }; + } + + SUPPLIER = supplier; + } + + private static final class PureJavaLongAddable extends AtomicLong implements LongAddable { + private PureJavaLongAddable() { + } + + public void increment() { + this.getAndIncrement(); + } + + public void add(long x) { + this.getAndAdd(x); + } + + public long sum() { + return this.get(); + } + + // $FF: synthetic method + PureJavaLongAddable(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/cache/LongAdder.java b/src/main/com/google/common/cache/LongAdder.java new file mode 100644 index 0000000..aece034 --- /dev/null +++ b/src/main/com/google/common/cache/LongAdder.java @@ -0,0 +1,117 @@ +package com.google.common.cache; + +import com.google.common.annotations.GwtCompatible; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +@GwtCompatible( + emulated = true +) +final class LongAdder extends Striped64 implements Serializable, LongAddable { + private static final long serialVersionUID = 7249069246863182397L; + + final long fn(long v, long x) { + return v + x; + } + + public LongAdder() { + } + + public void add(long x) { + Striped64.Cell[] as; + long b; + if ((as = this.cells) != null || !this.casBase(b = this.base, b + x)) { + boolean uncontended = true; + long v; + int[] hc; + Striped64.Cell a; + int n; + if ((hc = (int[])threadHashCode.get()) == null || as == null || (n = as.length) < 1 || (a = as[n - 1 & hc[0]]) == null || !(uncontended = a.cas(v = a.value, v + x))) { + this.retryUpdate(x, hc, uncontended); + } + } + + } + + public void increment() { + this.add(1L); + } + + public void decrement() { + this.add(-1L); + } + + public long sum() { + long sum = this.base; + Striped64.Cell[] as = this.cells; + if (as != null) { + int n = as.length; + + for(int i = 0; i < n; ++i) { + Striped64.Cell a = as[i]; + if (a != null) { + sum += a.value; + } + } + } + + return sum; + } + + public void reset() { + this.internalReset(0L); + } + + public long sumThenReset() { + long sum = this.base; + Striped64.Cell[] as = this.cells; + this.base = 0L; + if (as != null) { + int n = as.length; + + for(int i = 0; i < n; ++i) { + Striped64.Cell a = as[i]; + if (a != null) { + sum += a.value; + a.value = 0L; + } + } + } + + return sum; + } + + public String toString() { + return Long.toString(this.sum()); + } + + public long longValue() { + return this.sum(); + } + + public int intValue() { + return (int)this.sum(); + } + + public float floatValue() { + return (float)this.sum(); + } + + public double doubleValue() { + return (double)this.sum(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeLong(this.sum()); + } + + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + this.busy = 0; + this.cells = null; + this.base = s.readLong(); + } +} diff --git a/src/main/com/google/common/cache/RemovalCause.java b/src/main/com/google/common/cache/RemovalCause.java new file mode 100644 index 0000000..fc16015 --- /dev/null +++ b/src/main/com/google/common/cache/RemovalCause.java @@ -0,0 +1,44 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +@Beta +@GwtCompatible +public enum RemovalCause { + EXPLICIT { + boolean wasEvicted() { + return false; + } + }, + REPLACED { + boolean wasEvicted() { + return false; + } + }, + COLLECTED { + boolean wasEvicted() { + return true; + } + }, + EXPIRED { + boolean wasEvicted() { + return true; + } + }, + SIZE { + boolean wasEvicted() { + return true; + } + }; + + private RemovalCause() { + } + + abstract boolean wasEvicted(); + + // $FF: synthetic method + RemovalCause(Object x2) { + this(); + } +} diff --git a/src/main/com/google/common/cache/RemovalListener.java b/src/main/com/google/common/cache/RemovalListener.java new file mode 100644 index 0000000..99c51ff --- /dev/null +++ b/src/main/com/google/common/cache/RemovalListener.java @@ -0,0 +1,10 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +@Beta +@GwtCompatible +public interface RemovalListener { + void onRemoval(RemovalNotification var1); +} diff --git a/src/main/com/google/common/cache/RemovalListeners.java b/src/main/com/google/common/cache/RemovalListeners.java new file mode 100644 index 0000000..a980f26 --- /dev/null +++ b/src/main/com/google/common/cache/RemovalListeners.java @@ -0,0 +1,25 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.concurrent.Executor; + +@Beta +public final class RemovalListeners { + private RemovalListeners() { + } + + public static RemovalListener asynchronous(final RemovalListener listener, final Executor executor) { + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(executor); + return new RemovalListener() { + public void onRemoval(final RemovalNotification notification) { + executor.execute(new Runnable() { + public void run() { + listener.onRemoval(notification); + } + }); + } + }; + } +} diff --git a/src/main/com/google/common/cache/RemovalNotification.java b/src/main/com/google/common/cache/RemovalNotification.java new file mode 100644 index 0000000..798d9e5 --- /dev/null +++ b/src/main/com/google/common/cache/RemovalNotification.java @@ -0,0 +1,68 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public final class RemovalNotification implements Entry { + @Nullable + private final K key; + @Nullable + private final V value; + private final RemovalCause cause; + private static final long serialVersionUID = 0L; + + RemovalNotification(@Nullable K key, @Nullable V value, RemovalCause cause) { + this.key = key; + this.value = value; + this.cause = (RemovalCause)Preconditions.checkNotNull(cause); + } + + public RemovalCause getCause() { + return this.cause; + } + + public boolean wasEvicted() { + return this.cause.wasEvicted(); + } + + @Nullable + public K getKey() { + return this.key; + } + + @Nullable + public V getValue() { + return this.value; + } + + public final V setValue(V value) { + throw new UnsupportedOperationException(); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof Entry)) { + return false; + } else { + Entry that = (Entry)object; + return Objects.equal(this.getKey(), that.getKey()) && Objects.equal(this.getValue(), that.getValue()); + } + } + + public int hashCode() { + K k = this.getKey(); + V v = this.getValue(); + return (k == null ? 0 : k.hashCode()) ^ (v == null ? 0 : v.hashCode()); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.getKey())); + String var2 = String.valueOf(String.valueOf(this.getValue())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append("=").append(var2).toString(); + } +} diff --git a/src/main/com/google/common/cache/Striped64.java b/src/main/com/google/common/cache/Striped64.java new file mode 100644 index 0000000..8afe5b2 --- /dev/null +++ b/src/main/com/google/common/cache/Striped64.java @@ -0,0 +1,229 @@ +package com.google.common.cache; + +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Random; +import sun.misc.Unsafe; + +abstract class Striped64 extends Number { + static final ThreadLocal threadHashCode = new ThreadLocal(); + static final Random rng = new Random(); + static final int NCPU = Runtime.getRuntime().availableProcessors(); + transient volatile Striped64.Cell[] cells; + transient volatile long base; + transient volatile int busy; + private static final Unsafe UNSAFE; + private static final long baseOffset; + private static final long busyOffset; + + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + } + + final boolean casBusy() { + return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + } + + abstract long fn(long var1, long var3); + + final void retryUpdate(long x, int[] hc, boolean wasUncontended) { + int h; + if (hc == null) { + threadHashCode.set(hc = new int[1]); + int r = rng.nextInt(); + h = hc[0] = r == 0 ? 1 : r; + } else { + h = hc[0]; + } + + boolean collide = false; + + while(true) { + Striped64.Cell[] as; + int n; + long v; + if ((as = this.cells) != null && (n = as.length) > 0) { + Striped64.Cell a; + if ((a = as[n - 1 & h]) == null) { + if (this.busy == 0) { + Striped64.Cell r = new Striped64.Cell(x); + if (this.busy == 0 && this.casBusy()) { + boolean created = false; + + try { + Striped64.Cell[] rs; + int m; + int j; + if ((rs = this.cells) != null && (m = rs.length) > 0 && rs[j = m - 1 & h] == null) { + rs[j] = r; + created = true; + } + } finally { + this.busy = 0; + } + + if (created) { + break; + } + continue; + } + } + + collide = false; + } else if (!wasUncontended) { + wasUncontended = true; + } else { + if (a.cas(v = a.value, this.fn(v, x))) { + break; + } + + if (n < NCPU && this.cells == as) { + if (!collide) { + collide = true; + } else if (this.busy == 0 && this.casBusy()) { + try { + if (this.cells == as) { + Striped64.Cell[] rs = new Striped64.Cell[n << 1]; + + for(int i = 0; i < n; ++i) { + rs[i] = as[i]; + } + + this.cells = rs; + } + } finally { + this.busy = 0; + } + + collide = false; + continue; + } + } else { + collide = false; + } + } + + h ^= h << 13; + h ^= h >>> 17; + h ^= h << 5; + hc[0] = h; + } else if (this.busy == 0 && this.cells == as && this.casBusy()) { + boolean init = false; + + try { + if (this.cells == as) { + Striped64.Cell[] rs = new Striped64.Cell[2]; + rs[h & 1] = new Striped64.Cell(x); + this.cells = rs; + init = true; + } + } finally { + this.busy = 0; + } + + if (init) { + break; + } + } else if (this.casBase(v = this.base, this.fn(v, x))) { + break; + } + } + + } + + final void internalReset(long initialValue) { + Striped64.Cell[] as = this.cells; + this.base = initialValue; + if (as != null) { + int n = as.length; + + for(int i = 0; i < n; ++i) { + Striped64.Cell a = as[i]; + if (a != null) { + a.value = initialValue; + } + } + } + + } + + private static Unsafe getUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException var2) { + try { + return (Unsafe)AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Unsafe run() throws Exception { + Class k = Unsafe.class; + Field[] arr$ = k.getDeclaredFields(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Field f = arr$[i$]; + f.setAccessible(true); + Object x = f.get((Object)null); + if (k.isInstance(x)) { + return (Unsafe)k.cast(x); + } + } + + throw new NoSuchFieldError("the Unsafe"); + } + }); + } catch (PrivilegedActionException var1) { + throw new RuntimeException("Could not initialize intrinsics", var1.getCause()); + } + } + } + + static { + try { + UNSAFE = getUnsafe(); + Class sk = Striped64.class; + baseOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); + busyOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); + } catch (Exception var1) { + throw new Error(var1); + } + } + + static final class Cell { + volatile long p0; + volatile long p1; + volatile long p2; + volatile long p3; + volatile long p4; + volatile long p5; + volatile long p6; + volatile long value; + volatile long q0; + volatile long q1; + volatile long q2; + volatile long q3; + volatile long q4; + volatile long q5; + volatile long q6; + private static final Unsafe UNSAFE; + private static final long valueOffset; + + Cell(long x) { + this.value = x; + } + + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + static { + try { + UNSAFE = Striped64.getUnsafe(); + Class ak = Striped64.Cell.class; + valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value")); + } catch (Exception var1) { + throw new Error(var1); + } + } + } +} diff --git a/src/main/com/google/common/cache/Weigher.java b/src/main/com/google/common/cache/Weigher.java new file mode 100644 index 0000000..7178bf5 --- /dev/null +++ b/src/main/com/google/common/cache/Weigher.java @@ -0,0 +1,10 @@ +package com.google.common.cache; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +@Beta +@GwtCompatible +public interface Weigher { + int weigh(K var1, V var2); +} diff --git a/src/main/com/google/common/cache/package-info.java b/src/main/com/google/common/cache/package-info.java new file mode 100644 index 0000000..7c61c84 --- /dev/null +++ b/src/main/com/google/common/cache/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.cache; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/collect/AbstractBiMap.java b/src/main/com/google/common/collect/AbstractBiMap.java new file mode 100644 index 0000000..224b673 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractBiMap.java @@ -0,0 +1,364 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +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.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +abstract class AbstractBiMap extends ForwardingMap implements BiMap, Serializable { + private transient Map delegate; + transient AbstractBiMap inverse; + private transient Set keySet; + private transient Set valueSet; + private transient Set> entrySet; + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0L; + + AbstractBiMap(Map forward, Map backward) { + this.setDelegates(forward, backward); + } + + private AbstractBiMap(Map backward, AbstractBiMap forward) { + this.delegate = backward; + this.inverse = forward; + } + + protected Map delegate() { + return this.delegate; + } + + K checkKey(@Nullable K key) { + return key; + } + + V checkValue(@Nullable V value) { + return value; + } + + void setDelegates(Map forward, Map backward) { + Preconditions.checkState(this.delegate == null); + Preconditions.checkState(this.inverse == null); + Preconditions.checkArgument(forward.isEmpty()); + Preconditions.checkArgument(backward.isEmpty()); + Preconditions.checkArgument(forward != backward); + this.delegate = forward; + this.inverse = new AbstractBiMap.Inverse(backward, this); + } + + void setInverse(AbstractBiMap inverse) { + this.inverse = inverse; + } + + public boolean containsValue(@Nullable Object value) { + return this.inverse.containsKey(value); + } + + public V put(@Nullable K key, @Nullable V value) { + return this.putInBothMaps(key, value, false); + } + + public V forcePut(@Nullable K key, @Nullable V value) { + return this.putInBothMaps(key, value, true); + } + + private V putInBothMaps(@Nullable K key, @Nullable V value, boolean force) { + this.checkKey(key); + this.checkValue(value); + boolean containedKey = this.containsKey(key); + if (containedKey && Objects.equal(value, this.get(key))) { + return value; + } else { + if (force) { + this.inverse().remove(value); + } else { + Preconditions.checkArgument(!this.containsValue(value), "value already present: %s", value); + } + + V oldValue = this.delegate.put(key, value); + this.updateInverseMap(key, containedKey, oldValue, value); + return oldValue; + } + } + + private void updateInverseMap(K key, boolean containedKey, V oldValue, V newValue) { + if (containedKey) { + this.removeFromInverseMap(oldValue); + } + + this.inverse.delegate.put(newValue, key); + } + + public V remove(@Nullable Object key) { + return this.containsKey(key) ? this.removeFromBothMaps(key) : null; + } + + private V removeFromBothMaps(Object key) { + V oldValue = this.delegate.remove(key); + this.removeFromInverseMap(oldValue); + return oldValue; + } + + private void removeFromInverseMap(V oldValue) { + this.inverse.delegate.remove(oldValue); + } + + public void putAll(Map map) { + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + this.put(entry.getKey(), entry.getValue()); + } + + } + + public void clear() { + this.delegate.clear(); + this.inverse.delegate.clear(); + } + + public BiMap inverse() { + return this.inverse; + } + + public Set keySet() { + Set result = this.keySet; + return result == null ? (this.keySet = new AbstractBiMap.KeySet()) : result; + } + + public Set values() { + Set result = this.valueSet; + return result == null ? (this.valueSet = new AbstractBiMap.ValueSet()) : result; + } + + public Set> entrySet() { + Set> result = this.entrySet; + return result == null ? (this.entrySet = new AbstractBiMap.EntrySet()) : result; + } + + // $FF: synthetic method + AbstractBiMap(Map x0, AbstractBiMap x1, Object x2) { + this(x0, x1); + } + + private static class Inverse extends AbstractBiMap { + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0L; + + private Inverse(Map backward, AbstractBiMap forward) { + super(backward, forward, null); + } + + K checkKey(K key) { + return this.inverse.checkValue(key); + } + + V checkValue(V value) { + return this.inverse.checkKey(value); + } + + @GwtIncompatible("java.io.ObjectOuputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.inverse()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.setInverse((AbstractBiMap)stream.readObject()); + } + + @GwtIncompatible("Not needed in the emulated source.") + Object readResolve() { + return this.inverse().inverse(); + } + + // $FF: synthetic method + Inverse(Map x0, AbstractBiMap x1, Object x2) { + this(x0, x1); + } + } + + private class EntrySet extends ForwardingSet> { + final Set> esDelegate; + + private EntrySet() { + this.esDelegate = AbstractBiMap.this.delegate.entrySet(); + } + + protected Set> delegate() { + return this.esDelegate; + } + + public void clear() { + AbstractBiMap.this.clear(); + } + + public boolean remove(Object object) { + if (!this.esDelegate.contains(object)) { + return false; + } else { + Entry entry = (Entry)object; + AbstractBiMap.this.inverse.delegate.remove(entry.getValue()); + this.esDelegate.remove(entry); + return true; + } + } + + public Iterator> iterator() { + final Iterator> iterator = this.esDelegate.iterator(); + return new Iterator>() { + Entry entry; + + public boolean hasNext() { + return iterator.hasNext(); + } + + public Entry next() { + this.entry = (Entry)iterator.next(); + final Entry finalEntry = this.entry; + return new ForwardingMapEntry() { + protected Entry delegate() { + return finalEntry; + } + + public V setValue(V value) { + Preconditions.checkState(EntrySet.this.contains(this), "entry no longer in map"); + if (Objects.equal(value, this.getValue())) { + return value; + } else { + Preconditions.checkArgument(!AbstractBiMap.this.containsValue(value), "value already present: %s", value); + V oldValue = finalEntry.setValue(value); + Preconditions.checkState(Objects.equal(value, AbstractBiMap.this.get(this.getKey())), "entry no longer in map"); + AbstractBiMap.this.updateInverseMap(this.getKey(), true, oldValue, value); + return oldValue; + } + } + }; + } + + public void remove() { + CollectPreconditions.checkRemove(this.entry != null); + V value = this.entry.getValue(); + iterator.remove(); + AbstractBiMap.this.removeFromInverseMap(value); + } + }; + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + + public boolean contains(Object o) { + return Maps.containsEntryImpl(this.delegate(), o); + } + + public boolean containsAll(Collection c) { + return this.standardContainsAll(c); + } + + public boolean removeAll(Collection c) { + return this.standardRemoveAll(c); + } + + public boolean retainAll(Collection c) { + return this.standardRetainAll(c); + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } + + private class ValueSet extends ForwardingSet { + final Set valuesDelegate; + + private ValueSet() { + this.valuesDelegate = AbstractBiMap.this.inverse.keySet(); + } + + protected Set delegate() { + return this.valuesDelegate; + } + + public Iterator iterator() { + return Maps.valueIterator(AbstractBiMap.this.entrySet().iterator()); + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + + public String toString() { + return this.standardToString(); + } + + // $FF: synthetic method + ValueSet(Object x1) { + this(); + } + } + + private class KeySet extends ForwardingSet { + private KeySet() { + } + + protected Set delegate() { + return AbstractBiMap.this.delegate.keySet(); + } + + public void clear() { + AbstractBiMap.this.clear(); + } + + public boolean remove(Object key) { + if (!this.contains(key)) { + return false; + } else { + AbstractBiMap.this.removeFromBothMaps(key); + return true; + } + } + + public boolean removeAll(Collection keysToRemove) { + return this.standardRemoveAll(keysToRemove); + } + + public boolean retainAll(Collection keysToRetain) { + return this.standardRetainAll(keysToRetain); + } + + public Iterator iterator() { + return Maps.keyIterator(AbstractBiMap.this.entrySet().iterator()); + } + + // $FF: synthetic method + KeySet(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/AbstractIndexedListIterator.java b/src/main/com/google/common/collect/AbstractIndexedListIterator.java new file mode 100644 index 0000000..ce8d36b --- /dev/null +++ b/src/main/com/google/common/collect/AbstractIndexedListIterator.java @@ -0,0 +1,55 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.NoSuchElementException; + +@GwtCompatible +abstract class AbstractIndexedListIterator extends UnmodifiableListIterator { + private final int size; + private int position; + + protected abstract E get(int var1); + + protected AbstractIndexedListIterator(int size) { + this(size, 0); + } + + protected AbstractIndexedListIterator(int size, int position) { + Preconditions.checkPositionIndex(position, size); + this.size = size; + this.position = position; + } + + public final boolean hasNext() { + return this.position < this.size; + } + + public final E next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + return this.get(this.position++); + } + } + + public final int nextIndex() { + return this.position; + } + + public final boolean hasPrevious() { + return this.position > 0; + } + + public final E previous() { + if (!this.hasPrevious()) { + throw new NoSuchElementException(); + } else { + return this.get(--this.position); + } + } + + public final int previousIndex() { + return this.position - 1; + } +} diff --git a/src/main/com/google/common/collect/AbstractIterator.java b/src/main/com/google/common/collect/AbstractIterator.java new file mode 100644 index 0000000..daa7e17 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractIterator.java @@ -0,0 +1,71 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.NoSuchElementException; + +@GwtCompatible +public abstract class AbstractIterator extends UnmodifiableIterator { + private AbstractIterator.State state; + private T next; + + protected AbstractIterator() { + this.state = AbstractIterator.State.NOT_READY; + } + + protected abstract T computeNext(); + + protected final T endOfData() { + this.state = AbstractIterator.State.DONE; + return null; + } + + public final boolean hasNext() { + Preconditions.checkState(this.state != AbstractIterator.State.FAILED); + switch(this.state) { + case DONE: + return false; + case READY: + return true; + default: + return this.tryToComputeNext(); + } + } + + private boolean tryToComputeNext() { + this.state = AbstractIterator.State.FAILED; + this.next = this.computeNext(); + if (this.state != AbstractIterator.State.DONE) { + this.state = AbstractIterator.State.READY; + return true; + } else { + return false; + } + } + + public final T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + this.state = AbstractIterator.State.NOT_READY; + T result = this.next; + this.next = null; + return result; + } + } + + public final T peek() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + return this.next; + } + } + + private static enum State { + READY, + NOT_READY, + DONE, + FAILED; + } +} diff --git a/src/main/com/google/common/collect/AbstractListMultimap.java b/src/main/com/google/common/collect/AbstractListMultimap.java new file mode 100644 index 0000000..0d4af4a --- /dev/null +++ b/src/main/com/google/common/collect/AbstractListMultimap.java @@ -0,0 +1,46 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class AbstractListMultimap extends AbstractMapBasedMultimap implements ListMultimap { + private static final long serialVersionUID = 6588350623831699109L; + + protected AbstractListMultimap(Map> map) { + super(map); + } + + abstract List createCollection(); + + List createUnmodifiableEmptyCollection() { + return ImmutableList.of(); + } + + public List get(@Nullable K key) { + return (List)super.get(key); + } + + public List removeAll(@Nullable Object key) { + return (List)super.removeAll(key); + } + + public List replaceValues(@Nullable K key, Iterable values) { + return (List)super.replaceValues(key, values); + } + + public boolean put(@Nullable K key, @Nullable V value) { + return super.put(key, value); + } + + public Map> asMap() { + return super.asMap(); + } + + public boolean equals(@Nullable Object object) { + return super.equals(object); + } +} diff --git a/src/main/com/google/common/collect/AbstractMapBasedMultimap.java b/src/main/com/google/common/collect/AbstractMapBasedMultimap.java new file mode 100644 index 0000000..d56b6d0 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractMapBasedMultimap.java @@ -0,0 +1,1183 @@ +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.io.Serializable; +import java.util.AbstractCollection; +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 java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +abstract class AbstractMapBasedMultimap extends AbstractMultimap implements Serializable { + private transient Map> map; + private transient int totalSize; + private static final long serialVersionUID = 2447537837011683357L; + + protected AbstractMapBasedMultimap(Map> map) { + Preconditions.checkArgument(map.isEmpty()); + this.map = map; + } + + final void setMap(Map> map) { + this.map = map; + this.totalSize = 0; + + Collection values; + for(Iterator i$ = map.values().iterator(); i$.hasNext(); this.totalSize += values.size()) { + values = (Collection)i$.next(); + Preconditions.checkArgument(!values.isEmpty()); + } + + } + + Collection createUnmodifiableEmptyCollection() { + return this.unmodifiableCollectionSubclass(this.createCollection()); + } + + abstract Collection createCollection(); + + Collection createCollection(@Nullable K key) { + return this.createCollection(); + } + + Map> backingMap() { + return this.map; + } + + public int size() { + return this.totalSize; + } + + public boolean containsKey(@Nullable Object key) { + return this.map.containsKey(key); + } + + public boolean put(@Nullable K key, @Nullable V value) { + Collection collection = (Collection)this.map.get(key); + if (collection == null) { + collection = this.createCollection(key); + if (collection.add(value)) { + ++this.totalSize; + this.map.put(key, collection); + return true; + } else { + throw new AssertionError("New Collection violated the Collection spec"); + } + } else if (collection.add(value)) { + ++this.totalSize; + return true; + } else { + return false; + } + } + + private Collection getOrCreateCollection(@Nullable K key) { + Collection collection = (Collection)this.map.get(key); + if (collection == null) { + collection = this.createCollection(key); + this.map.put(key, collection); + } + + return collection; + } + + public Collection replaceValues(@Nullable K key, Iterable values) { + Iterator iterator = values.iterator(); + if (!iterator.hasNext()) { + return this.removeAll(key); + } else { + Collection collection = this.getOrCreateCollection(key); + Collection oldValues = this.createCollection(); + oldValues.addAll(collection); + this.totalSize -= collection.size(); + collection.clear(); + + while(iterator.hasNext()) { + if (collection.add(iterator.next())) { + ++this.totalSize; + } + } + + return this.unmodifiableCollectionSubclass(oldValues); + } + } + + public Collection removeAll(@Nullable Object key) { + Collection collection = (Collection)this.map.remove(key); + if (collection == null) { + return this.createUnmodifiableEmptyCollection(); + } else { + Collection output = this.createCollection(); + output.addAll(collection); + this.totalSize -= collection.size(); + collection.clear(); + return this.unmodifiableCollectionSubclass(output); + } + } + + Collection unmodifiableCollectionSubclass(Collection collection) { + if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet)collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set)collection); + } else { + return (Collection)(collection instanceof List ? Collections.unmodifiableList((List)collection) : Collections.unmodifiableCollection(collection)); + } + } + + public void clear() { + Iterator i$ = this.map.values().iterator(); + + while(i$.hasNext()) { + Collection collection = (Collection)i$.next(); + collection.clear(); + } + + this.map.clear(); + this.totalSize = 0; + } + + public Collection get(@Nullable K key) { + Collection collection = (Collection)this.map.get(key); + if (collection == null) { + collection = this.createCollection(key); + } + + return this.wrapCollection(key, collection); + } + + Collection wrapCollection(@Nullable K key, Collection collection) { + if (collection instanceof SortedSet) { + return new AbstractMapBasedMultimap.WrappedSortedSet(key, (SortedSet)collection, (AbstractMapBasedMultimap.WrappedCollection)null); + } else if (collection instanceof Set) { + return new AbstractMapBasedMultimap.WrappedSet(key, (Set)collection); + } else { + return (Collection)(collection instanceof List ? this.wrapList(key, (List)collection, (AbstractMapBasedMultimap.WrappedCollection)null) : new AbstractMapBasedMultimap.WrappedCollection(key, collection, (AbstractMapBasedMultimap.WrappedCollection)null)); + } + } + + private List wrapList(@Nullable K key, List list, @Nullable AbstractMapBasedMultimap.WrappedCollection ancestor) { + return (List)(list instanceof RandomAccess ? new AbstractMapBasedMultimap.RandomAccessWrappedList(key, list, ancestor) : new AbstractMapBasedMultimap.WrappedList(key, list, ancestor)); + } + + private Iterator iteratorOrListIterator(Collection collection) { + return (Iterator)(collection instanceof List ? ((List)collection).listIterator() : collection.iterator()); + } + + Set createKeySet() { + return (Set)(this.map instanceof SortedMap ? new AbstractMapBasedMultimap.SortedKeySet((SortedMap)this.map) : new AbstractMapBasedMultimap.KeySet(this.map)); + } + + private int removeValuesForKey(Object key) { + Collection collection = (Collection)Maps.safeRemove(this.map, key); + int count = 0; + if (collection != null) { + count = collection.size(); + collection.clear(); + this.totalSize -= count; + } + + return count; + } + + public Collection values() { + return super.values(); + } + + Iterator valueIterator() { + return new AbstractMapBasedMultimap.Itr() { + V output(K key, V value) { + return value; + } + }; + } + + public Collection> entries() { + return super.entries(); + } + + Iterator> entryIterator() { + return new AbstractMapBasedMultimap.Itr>() { + Entry output(K key, V value) { + return Maps.immutableEntry(key, value); + } + }; + } + + Map> createAsMap() { + return (Map)(this.map instanceof SortedMap ? new AbstractMapBasedMultimap.SortedAsMap((SortedMap)this.map) : new AbstractMapBasedMultimap.AsMap(this.map)); + } + + @GwtIncompatible("NavigableAsMap") + class NavigableAsMap extends AbstractMapBasedMultimap.SortedAsMap implements NavigableMap> { + NavigableAsMap(NavigableMap> submap) { + super(submap); + } + + NavigableMap> sortedMap() { + return (NavigableMap)super.sortedMap(); + } + + public Entry> lowerEntry(K key) { + Entry> entry = this.sortedMap().lowerEntry(key); + return entry == null ? null : this.wrapEntry(entry); + } + + public K lowerKey(K key) { + return this.sortedMap().lowerKey(key); + } + + public Entry> floorEntry(K key) { + Entry> entry = this.sortedMap().floorEntry(key); + return entry == null ? null : this.wrapEntry(entry); + } + + public K floorKey(K key) { + return this.sortedMap().floorKey(key); + } + + public Entry> ceilingEntry(K key) { + Entry> entry = this.sortedMap().ceilingEntry(key); + return entry == null ? null : this.wrapEntry(entry); + } + + public K ceilingKey(K key) { + return this.sortedMap().ceilingKey(key); + } + + public Entry> higherEntry(K key) { + Entry> entry = this.sortedMap().higherEntry(key); + return entry == null ? null : this.wrapEntry(entry); + } + + public K higherKey(K key) { + return this.sortedMap().higherKey(key); + } + + public Entry> firstEntry() { + Entry> entry = this.sortedMap().firstEntry(); + return entry == null ? null : this.wrapEntry(entry); + } + + public Entry> lastEntry() { + Entry> entry = this.sortedMap().lastEntry(); + return entry == null ? null : this.wrapEntry(entry); + } + + public Entry> pollFirstEntry() { + return this.pollAsMapEntry(this.entrySet().iterator()); + } + + public Entry> pollLastEntry() { + return this.pollAsMapEntry(this.descendingMap().entrySet().iterator()); + } + + Entry> pollAsMapEntry(Iterator>> entryIterator) { + if (!entryIterator.hasNext()) { + return null; + } else { + Entry> entry = (Entry)entryIterator.next(); + Collection output = AbstractMapBasedMultimap.this.createCollection(); + output.addAll((Collection)entry.getValue()); + entryIterator.remove(); + return Maps.immutableEntry(entry.getKey(), AbstractMapBasedMultimap.this.unmodifiableCollectionSubclass(output)); + } + } + + public NavigableMap> descendingMap() { + return AbstractMapBasedMultimap.this.new NavigableAsMap(this.sortedMap().descendingMap()); + } + + public NavigableSet keySet() { + return (NavigableSet)super.keySet(); + } + + NavigableSet createKeySet() { + return AbstractMapBasedMultimap.this.new NavigableKeySet(this.sortedMap()); + } + + public NavigableSet navigableKeySet() { + return this.keySet(); + } + + public NavigableSet descendingKeySet() { + return this.descendingMap().navigableKeySet(); + } + + public NavigableMap> subMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public NavigableMap> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return AbstractMapBasedMultimap.this.new NavigableAsMap(this.sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + public NavigableMap> headMap(K toKey) { + return this.headMap(toKey, false); + } + + public NavigableMap> headMap(K toKey, boolean inclusive) { + return AbstractMapBasedMultimap.this.new NavigableAsMap(this.sortedMap().headMap(toKey, inclusive)); + } + + public NavigableMap> tailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + + public NavigableMap> tailMap(K fromKey, boolean inclusive) { + return AbstractMapBasedMultimap.this.new NavigableAsMap(this.sortedMap().tailMap(fromKey, inclusive)); + } + } + + private class SortedAsMap extends AbstractMapBasedMultimap.AsMap implements SortedMap> { + SortedSet sortedKeySet; + + SortedAsMap(SortedMap> submap) { + super(submap); + } + + SortedMap> sortedMap() { + return (SortedMap)this.submap; + } + + public Comparator comparator() { + return this.sortedMap().comparator(); + } + + public K firstKey() { + return this.sortedMap().firstKey(); + } + + public K lastKey() { + return this.sortedMap().lastKey(); + } + + public SortedMap> headMap(K toKey) { + return AbstractMapBasedMultimap.this.new SortedAsMap(this.sortedMap().headMap(toKey)); + } + + public SortedMap> subMap(K fromKey, K toKey) { + return AbstractMapBasedMultimap.this.new SortedAsMap(this.sortedMap().subMap(fromKey, toKey)); + } + + public SortedMap> tailMap(K fromKey) { + return AbstractMapBasedMultimap.this.new SortedAsMap(this.sortedMap().tailMap(fromKey)); + } + + public SortedSet keySet() { + SortedSet result = this.sortedKeySet; + return result == null ? (this.sortedKeySet = this.createKeySet()) : result; + } + + SortedSet createKeySet() { + return AbstractMapBasedMultimap.this.new SortedKeySet(this.sortedMap()); + } + } + + private class AsMap extends Maps.ImprovedAbstractMap> { + final transient Map> submap; + + AsMap(Map> submap) { + this.submap = submap; + } + + protected Set>> createEntrySet() { + return new AbstractMapBasedMultimap.AsMap.AsMapEntries(); + } + + public boolean containsKey(Object key) { + return Maps.safeContainsKey(this.submap, key); + } + + public Collection get(Object key) { + Collection collection = (Collection)Maps.safeGet(this.submap, key); + return collection == null ? null : AbstractMapBasedMultimap.this.wrapCollection(key, collection); + } + + public Set keySet() { + return AbstractMapBasedMultimap.this.keySet(); + } + + public int size() { + return this.submap.size(); + } + + public Collection remove(Object key) { + Collection collection = (Collection)this.submap.remove(key); + if (collection == null) { + return null; + } else { + Collection output = AbstractMapBasedMultimap.this.createCollection(); + output.addAll(collection); + AbstractMapBasedMultimap.this.totalSize = collection.size(); + collection.clear(); + return output; + } + } + + public boolean equals(@Nullable Object object) { + return this == object || this.submap.equals(object); + } + + public int hashCode() { + return this.submap.hashCode(); + } + + public String toString() { + return this.submap.toString(); + } + + public void clear() { + if (this.submap == AbstractMapBasedMultimap.this.map) { + AbstractMapBasedMultimap.this.clear(); + } else { + Iterators.clear(new AbstractMapBasedMultimap.AsMap.AsMapIterator()); + } + + } + + Entry> wrapEntry(Entry> entry) { + K key = entry.getKey(); + return Maps.immutableEntry(key, AbstractMapBasedMultimap.this.wrapCollection(key, (Collection)entry.getValue())); + } + + class AsMapIterator implements Iterator>> { + final Iterator>> delegateIterator; + Collection collection; + + AsMapIterator() { + this.delegateIterator = AsMap.this.submap.entrySet().iterator(); + } + + public boolean hasNext() { + return this.delegateIterator.hasNext(); + } + + public Entry> next() { + Entry> entry = (Entry)this.delegateIterator.next(); + this.collection = (Collection)entry.getValue(); + return AsMap.this.wrapEntry(entry); + } + + public void remove() { + this.delegateIterator.remove(); + AbstractMapBasedMultimap.this.totalSize = this.collection.size(); + this.collection.clear(); + } + } + + class AsMapEntries extends Maps.EntrySet> { + Map> map() { + return AsMap.this; + } + + public Iterator>> iterator() { + return AsMap.this.new AsMapIterator(); + } + + public boolean contains(Object o) { + return Collections2.safeContains(AsMap.this.submap.entrySet(), o); + } + + public boolean remove(Object o) { + if (!this.contains(o)) { + return false; + } else { + Entry entry = (Entry)o; + AbstractMapBasedMultimap.this.removeValuesForKey(entry.getKey()); + return true; + } + } + } + } + + private abstract class Itr implements Iterator { + final Iterator>> keyIterator; + K key; + Collection collection; + Iterator valueIterator; + + Itr() { + this.keyIterator = AbstractMapBasedMultimap.this.map.entrySet().iterator(); + this.key = null; + this.collection = null; + this.valueIterator = Iterators.emptyModifiableIterator(); + } + + abstract T output(K var1, V var2); + + public boolean hasNext() { + return this.keyIterator.hasNext() || this.valueIterator.hasNext(); + } + + public T next() { + if (!this.valueIterator.hasNext()) { + Entry> mapEntry = (Entry)this.keyIterator.next(); + this.key = mapEntry.getKey(); + this.collection = (Collection)mapEntry.getValue(); + this.valueIterator = this.collection.iterator(); + } + + return this.output(this.key, this.valueIterator.next()); + } + + public void remove() { + this.valueIterator.remove(); + if (this.collection.isEmpty()) { + this.keyIterator.remove(); + } + + AbstractMapBasedMultimap.this.totalSize--; + } + } + + @GwtIncompatible("NavigableSet") + class NavigableKeySet extends AbstractMapBasedMultimap.SortedKeySet implements NavigableSet { + NavigableKeySet(NavigableMap> subMap) { + super(subMap); + } + + NavigableMap> sortedMap() { + return (NavigableMap)super.sortedMap(); + } + + public K lower(K k) { + return this.sortedMap().lowerKey(k); + } + + public K floor(K k) { + return this.sortedMap().floorKey(k); + } + + public K ceiling(K k) { + return this.sortedMap().ceilingKey(k); + } + + public K higher(K k) { + return this.sortedMap().higherKey(k); + } + + public K pollFirst() { + return Iterators.pollNext(this.iterator()); + } + + public K pollLast() { + return Iterators.pollNext(this.descendingIterator()); + } + + public NavigableSet descendingSet() { + return AbstractMapBasedMultimap.this.new NavigableKeySet(this.sortedMap().descendingMap()); + } + + public Iterator descendingIterator() { + return this.descendingSet().iterator(); + } + + public NavigableSet headSet(K toElement) { + return this.headSet(toElement, false); + } + + public NavigableSet headSet(K toElement, boolean inclusive) { + return AbstractMapBasedMultimap.this.new NavigableKeySet(this.sortedMap().headMap(toElement, inclusive)); + } + + public NavigableSet subSet(K fromElement, K toElement) { + return this.subSet(fromElement, true, toElement, false); + } + + public NavigableSet subSet(K fromElement, boolean fromInclusive, K toElement, boolean toInclusive) { + return AbstractMapBasedMultimap.this.new NavigableKeySet(this.sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); + } + + public NavigableSet tailSet(K fromElement) { + return this.tailSet(fromElement, true); + } + + public NavigableSet tailSet(K fromElement, boolean inclusive) { + return AbstractMapBasedMultimap.this.new NavigableKeySet(this.sortedMap().tailMap(fromElement, inclusive)); + } + } + + private class SortedKeySet extends AbstractMapBasedMultimap.KeySet implements SortedSet { + SortedKeySet(SortedMap> subMap) { + super(subMap); + } + + SortedMap> sortedMap() { + return (SortedMap)super.map(); + } + + public Comparator comparator() { + return this.sortedMap().comparator(); + } + + public K first() { + return this.sortedMap().firstKey(); + } + + public SortedSet headSet(K toElement) { + return AbstractMapBasedMultimap.this.new SortedKeySet(this.sortedMap().headMap(toElement)); + } + + public K last() { + return this.sortedMap().lastKey(); + } + + public SortedSet subSet(K fromElement, K toElement) { + return AbstractMapBasedMultimap.this.new SortedKeySet(this.sortedMap().subMap(fromElement, toElement)); + } + + public SortedSet tailSet(K fromElement) { + return AbstractMapBasedMultimap.this.new SortedKeySet(this.sortedMap().tailMap(fromElement)); + } + } + + private class KeySet extends Maps.KeySet> { + KeySet(Map> subMap) { + super(subMap); + } + + public Iterator iterator() { + final Iterator>> entryIterator = this.map().entrySet().iterator(); + return new Iterator() { + Entry> entry; + + public boolean hasNext() { + return entryIterator.hasNext(); + } + + public K next() { + this.entry = (Entry)entryIterator.next(); + return this.entry.getKey(); + } + + public void remove() { + CollectPreconditions.checkRemove(this.entry != null); + Collection collection = (Collection)this.entry.getValue(); + entryIterator.remove(); + AbstractMapBasedMultimap.this.totalSize = collection.size(); + collection.clear(); + } + }; + } + + public boolean remove(Object key) { + int count = 0; + Collection collection = (Collection)this.map().remove(key); + if (collection != null) { + count = collection.size(); + collection.clear(); + AbstractMapBasedMultimap.this.totalSize = count; + } + + return count > 0; + } + + public void clear() { + Iterators.clear(this.iterator()); + } + + public boolean containsAll(Collection c) { + return this.map().keySet().containsAll(c); + } + + public boolean equals(@Nullable Object object) { + return this == object || this.map().keySet().equals(object); + } + + public int hashCode() { + return this.map().keySet().hashCode(); + } + } + + private class RandomAccessWrappedList extends AbstractMapBasedMultimap.WrappedList implements RandomAccess { + RandomAccessWrappedList(@Nullable K key, List delegate, @Nullable AbstractMapBasedMultimap.WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + } + + private class WrappedList extends AbstractMapBasedMultimap.WrappedCollection implements List { + WrappedList(@Nullable K key, List delegate, @Nullable AbstractMapBasedMultimap.WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + List getListDelegate() { + return (List)this.getDelegate(); + } + + public boolean addAll(int index, Collection c) { + if (c.isEmpty()) { + return false; + } else { + int oldSize = this.size(); + boolean changed = this.getListDelegate().addAll(index, c); + if (changed) { + int newSize = this.getDelegate().size(); + AbstractMapBasedMultimap.this.totalSize = newSize - oldSize; + if (oldSize == 0) { + this.addToMap(); + } + } + + return changed; + } + } + + public V get(int index) { + this.refreshIfEmpty(); + return this.getListDelegate().get(index); + } + + public V set(int index, V element) { + this.refreshIfEmpty(); + return this.getListDelegate().set(index, element); + } + + public void add(int index, V element) { + this.refreshIfEmpty(); + boolean wasEmpty = this.getDelegate().isEmpty(); + this.getListDelegate().add(index, element); + AbstractMapBasedMultimap.this.totalSize++; + if (wasEmpty) { + this.addToMap(); + } + + } + + public V remove(int index) { + this.refreshIfEmpty(); + V value = this.getListDelegate().remove(index); + AbstractMapBasedMultimap.this.totalSize--; + this.removeIfEmpty(); + return value; + } + + public int indexOf(Object o) { + this.refreshIfEmpty(); + return this.getListDelegate().indexOf(o); + } + + public int lastIndexOf(Object o) { + this.refreshIfEmpty(); + return this.getListDelegate().lastIndexOf(o); + } + + public ListIterator listIterator() { + this.refreshIfEmpty(); + return new AbstractMapBasedMultimap.WrappedList.WrappedListIterator(); + } + + public ListIterator listIterator(int index) { + this.refreshIfEmpty(); + return new AbstractMapBasedMultimap.WrappedList.WrappedListIterator(index); + } + + public List subList(int fromIndex, int toIndex) { + this.refreshIfEmpty(); + return AbstractMapBasedMultimap.this.wrapList(this.getKey(), this.getListDelegate().subList(fromIndex, toIndex), (AbstractMapBasedMultimap.WrappedCollection)(this.getAncestor() == null ? this : this.getAncestor())); + } + + private class WrappedListIterator extends AbstractMapBasedMultimap.WrappedCollection.WrappedIterator implements ListIterator { + WrappedListIterator() { + super(); + } + + public WrappedListIterator(int index) { + super(WrappedList.this.getListDelegate().listIterator(index)); + } + + private ListIterator getDelegateListIterator() { + return (ListIterator)this.getDelegateIterator(); + } + + public boolean hasPrevious() { + return this.getDelegateListIterator().hasPrevious(); + } + + public V previous() { + return this.getDelegateListIterator().previous(); + } + + public int nextIndex() { + return this.getDelegateListIterator().nextIndex(); + } + + public int previousIndex() { + return this.getDelegateListIterator().previousIndex(); + } + + public void set(V value) { + this.getDelegateListIterator().set(value); + } + + public void add(V value) { + boolean wasEmpty = WrappedList.this.isEmpty(); + this.getDelegateListIterator().add(value); + AbstractMapBasedMultimap.this.totalSize++; + if (wasEmpty) { + WrappedList.this.addToMap(); + } + + } + } + } + + @GwtIncompatible("NavigableSet") + class WrappedNavigableSet extends AbstractMapBasedMultimap.WrappedSortedSet implements NavigableSet { + WrappedNavigableSet(@Nullable K key, NavigableSet delegate, @Nullable AbstractMapBasedMultimap.WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + NavigableSet getSortedSetDelegate() { + return (NavigableSet)super.getSortedSetDelegate(); + } + + public V lower(V v) { + return this.getSortedSetDelegate().lower(v); + } + + public V floor(V v) { + return this.getSortedSetDelegate().floor(v); + } + + public V ceiling(V v) { + return this.getSortedSetDelegate().ceiling(v); + } + + public V higher(V v) { + return this.getSortedSetDelegate().higher(v); + } + + public V pollFirst() { + return Iterators.pollNext(this.iterator()); + } + + public V pollLast() { + return Iterators.pollNext(this.descendingIterator()); + } + + private NavigableSet wrap(NavigableSet wrapped) { + return AbstractMapBasedMultimap.this.new WrappedNavigableSet(this.key, wrapped, (AbstractMapBasedMultimap.WrappedCollection)(this.getAncestor() == null ? this : this.getAncestor())); + } + + public NavigableSet descendingSet() { + return this.wrap(this.getSortedSetDelegate().descendingSet()); + } + + public Iterator descendingIterator() { + return new AbstractMapBasedMultimap.WrappedCollection.WrappedIterator(this.getSortedSetDelegate().descendingIterator()); + } + + public NavigableSet subSet(V fromElement, boolean fromInclusive, V toElement, boolean toInclusive) { + return this.wrap(this.getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + public NavigableSet headSet(V toElement, boolean inclusive) { + return this.wrap(this.getSortedSetDelegate().headSet(toElement, inclusive)); + } + + public NavigableSet tailSet(V fromElement, boolean inclusive) { + return this.wrap(this.getSortedSetDelegate().tailSet(fromElement, inclusive)); + } + } + + private class WrappedSortedSet extends AbstractMapBasedMultimap.WrappedCollection implements SortedSet { + WrappedSortedSet(@Nullable K key, SortedSet delegate, @Nullable AbstractMapBasedMultimap.WrappedCollection ancestor) { + super(key, delegate, ancestor); + } + + SortedSet getSortedSetDelegate() { + return (SortedSet)this.getDelegate(); + } + + public Comparator comparator() { + return this.getSortedSetDelegate().comparator(); + } + + public V first() { + this.refreshIfEmpty(); + return this.getSortedSetDelegate().first(); + } + + public V last() { + this.refreshIfEmpty(); + return this.getSortedSetDelegate().last(); + } + + public SortedSet headSet(V toElement) { + this.refreshIfEmpty(); + return AbstractMapBasedMultimap.this.new WrappedSortedSet(this.getKey(), this.getSortedSetDelegate().headSet(toElement), (AbstractMapBasedMultimap.WrappedCollection)(this.getAncestor() == null ? this : this.getAncestor())); + } + + public SortedSet subSet(V fromElement, V toElement) { + this.refreshIfEmpty(); + return AbstractMapBasedMultimap.this.new WrappedSortedSet(this.getKey(), this.getSortedSetDelegate().subSet(fromElement, toElement), (AbstractMapBasedMultimap.WrappedCollection)(this.getAncestor() == null ? this : this.getAncestor())); + } + + public SortedSet tailSet(V fromElement) { + this.refreshIfEmpty(); + return AbstractMapBasedMultimap.this.new WrappedSortedSet(this.getKey(), this.getSortedSetDelegate().tailSet(fromElement), (AbstractMapBasedMultimap.WrappedCollection)(this.getAncestor() == null ? this : this.getAncestor())); + } + } + + private class WrappedSet extends AbstractMapBasedMultimap.WrappedCollection implements Set { + WrappedSet(@Nullable K key, Set delegate) { + super(key, delegate, (AbstractMapBasedMultimap.WrappedCollection)null); + } + + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } else { + int oldSize = this.size(); + boolean changed = Sets.removeAllImpl((Set)this.delegate, c); + if (changed) { + int newSize = this.delegate.size(); + AbstractMapBasedMultimap.this.totalSize = newSize - oldSize; + this.removeIfEmpty(); + } + + return changed; + } + } + } + + private class WrappedCollection extends AbstractCollection { + final K key; + Collection delegate; + final AbstractMapBasedMultimap.WrappedCollection ancestor; + final Collection ancestorDelegate; + + WrappedCollection(@Nullable K key, Collection delegate, @Nullable AbstractMapBasedMultimap.WrappedCollection ancestor) { + this.key = key; + this.delegate = delegate; + this.ancestor = ancestor; + this.ancestorDelegate = ancestor == null ? null : ancestor.getDelegate(); + } + + void refreshIfEmpty() { + if (this.ancestor != null) { + this.ancestor.refreshIfEmpty(); + if (this.ancestor.getDelegate() != this.ancestorDelegate) { + throw new ConcurrentModificationException(); + } + } else if (this.delegate.isEmpty()) { + Collection newDelegate = (Collection)AbstractMapBasedMultimap.this.map.get(this.key); + if (newDelegate != null) { + this.delegate = newDelegate; + } + } + + } + + void removeIfEmpty() { + if (this.ancestor != null) { + this.ancestor.removeIfEmpty(); + } else if (this.delegate.isEmpty()) { + AbstractMapBasedMultimap.this.map.remove(this.key); + } + + } + + K getKey() { + return this.key; + } + + void addToMap() { + if (this.ancestor != null) { + this.ancestor.addToMap(); + } else { + AbstractMapBasedMultimap.this.map.put(this.key, this.delegate); + } + + } + + public int size() { + this.refreshIfEmpty(); + return this.delegate.size(); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else { + this.refreshIfEmpty(); + return this.delegate.equals(object); + } + } + + public int hashCode() { + this.refreshIfEmpty(); + return this.delegate.hashCode(); + } + + public String toString() { + this.refreshIfEmpty(); + return this.delegate.toString(); + } + + Collection getDelegate() { + return this.delegate; + } + + public Iterator iterator() { + this.refreshIfEmpty(); + return new AbstractMapBasedMultimap.WrappedCollection.WrappedIterator(); + } + + public boolean add(V value) { + this.refreshIfEmpty(); + boolean wasEmpty = this.delegate.isEmpty(); + boolean changed = this.delegate.add(value); + if (changed) { + AbstractMapBasedMultimap.this.totalSize++; + if (wasEmpty) { + this.addToMap(); + } + } + + return changed; + } + + AbstractMapBasedMultimap.WrappedCollection getAncestor() { + return this.ancestor; + } + + public boolean addAll(Collection collection) { + if (collection.isEmpty()) { + return false; + } else { + int oldSize = this.size(); + boolean changed = this.delegate.addAll(collection); + if (changed) { + int newSize = this.delegate.size(); + AbstractMapBasedMultimap.this.totalSize = newSize - oldSize; + if (oldSize == 0) { + this.addToMap(); + } + } + + return changed; + } + } + + public boolean contains(Object o) { + this.refreshIfEmpty(); + return this.delegate.contains(o); + } + + public boolean containsAll(Collection c) { + this.refreshIfEmpty(); + return this.delegate.containsAll(c); + } + + public void clear() { + int oldSize = this.size(); + if (oldSize != 0) { + this.delegate.clear(); + AbstractMapBasedMultimap.this.totalSize = oldSize; + this.removeIfEmpty(); + } + } + + public boolean remove(Object o) { + this.refreshIfEmpty(); + boolean changed = this.delegate.remove(o); + if (changed) { + AbstractMapBasedMultimap.this.totalSize--; + this.removeIfEmpty(); + } + + return changed; + } + + public boolean removeAll(Collection c) { + if (c.isEmpty()) { + return false; + } else { + int oldSize = this.size(); + boolean changed = this.delegate.removeAll(c); + if (changed) { + int newSize = this.delegate.size(); + AbstractMapBasedMultimap.this.totalSize = newSize - oldSize; + this.removeIfEmpty(); + } + + return changed; + } + } + + public boolean retainAll(Collection c) { + Preconditions.checkNotNull(c); + int oldSize = this.size(); + boolean changed = this.delegate.retainAll(c); + if (changed) { + int newSize = this.delegate.size(); + AbstractMapBasedMultimap.this.totalSize = newSize - oldSize; + this.removeIfEmpty(); + } + + return changed; + } + + class WrappedIterator implements Iterator { + final Iterator delegateIterator; + final Collection originalDelegate; + + WrappedIterator() { + this.originalDelegate = WrappedCollection.this.delegate; + this.delegateIterator = AbstractMapBasedMultimap.this.iteratorOrListIterator(WrappedCollection.this.delegate); + } + + WrappedIterator(Iterator delegateIterator) { + this.originalDelegate = WrappedCollection.this.delegate; + this.delegateIterator = delegateIterator; + } + + void validateIterator() { + WrappedCollection.this.refreshIfEmpty(); + if (WrappedCollection.this.delegate != this.originalDelegate) { + throw new ConcurrentModificationException(); + } + } + + public boolean hasNext() { + this.validateIterator(); + return this.delegateIterator.hasNext(); + } + + public V next() { + this.validateIterator(); + return this.delegateIterator.next(); + } + + public void remove() { + this.delegateIterator.remove(); + AbstractMapBasedMultimap.this.totalSize--; + WrappedCollection.this.removeIfEmpty(); + } + + Iterator getDelegateIterator() { + this.validateIterator(); + return this.delegateIterator; + } + } + } +} diff --git a/src/main/com/google/common/collect/AbstractMapBasedMultiset.java b/src/main/com/google/common/collect/AbstractMapBasedMultiset.java new file mode 100644 index 0000000..9aecaa6 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractMapBasedMultiset.java @@ -0,0 +1,222 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +abstract class AbstractMapBasedMultiset extends AbstractMultiset implements Serializable { + private transient Map backingMap; + private transient long size; + @GwtIncompatible("not needed in emulated source.") + private static final long serialVersionUID = -2250766705698539974L; + + protected AbstractMapBasedMultiset(Map backingMap) { + this.backingMap = (Map)Preconditions.checkNotNull(backingMap); + this.size = (long)super.size(); + } + + void setBackingMap(Map backingMap) { + this.backingMap = backingMap; + } + + public Set> entrySet() { + return super.entrySet(); + } + + Iterator> entryIterator() { + final Iterator> backingEntries = this.backingMap.entrySet().iterator(); + return new Iterator>() { + java.util.Map.Entry toRemove; + + public boolean hasNext() { + return backingEntries.hasNext(); + } + + public Multiset.Entry next() { + final java.util.Map.Entry mapEntry = (java.util.Map.Entry)backingEntries.next(); + this.toRemove = mapEntry; + return new Multisets.AbstractEntry() { + public E getElement() { + return mapEntry.getKey(); + } + + public int getCount() { + Count count = (Count)mapEntry.getValue(); + if (count == null || count.get() == 0) { + Count frequency = (Count)AbstractMapBasedMultiset.this.backingMap.get(this.getElement()); + if (frequency != null) { + return frequency.get(); + } + } + + return count == null ? 0 : count.get(); + } + }; + } + + public void remove() { + CollectPreconditions.checkRemove(this.toRemove != null); + AbstractMapBasedMultiset.this.size = (long)((Count)this.toRemove.getValue()).getAndSet(0); + backingEntries.remove(); + this.toRemove = null; + } + }; + } + + public void clear() { + Iterator i$ = this.backingMap.values().iterator(); + + while(i$.hasNext()) { + Count frequency = (Count)i$.next(); + frequency.set(0); + } + + this.backingMap.clear(); + this.size = 0L; + } + + int distinctElements() { + return this.backingMap.size(); + } + + public int size() { + return Ints.saturatedCast(this.size); + } + + public Iterator iterator() { + return new AbstractMapBasedMultiset.MapBasedMultisetIterator(); + } + + public int count(@Nullable Object element) { + Count frequency = (Count)Maps.safeGet(this.backingMap, element); + return frequency == null ? 0 : frequency.get(); + } + + public int add(@Nullable E element, int occurrences) { + if (occurrences == 0) { + return this.count(element); + } else { + Preconditions.checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); + Count frequency = (Count)this.backingMap.get(element); + int oldCount; + if (frequency == null) { + oldCount = 0; + this.backingMap.put(element, new Count(occurrences)); + } else { + oldCount = frequency.get(); + long newCount = (long)oldCount + (long)occurrences; + Preconditions.checkArgument(newCount <= 2147483647L, "too many occurrences: %s", newCount); + frequency.getAndAdd(occurrences); + } + + this.size += (long)occurrences; + return oldCount; + } + } + + public int remove(@Nullable Object element, int occurrences) { + if (occurrences == 0) { + return this.count(element); + } else { + Preconditions.checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); + Count frequency = (Count)this.backingMap.get(element); + if (frequency == null) { + return 0; + } else { + int oldCount = frequency.get(); + int numberRemoved; + if (oldCount > occurrences) { + numberRemoved = occurrences; + } else { + numberRemoved = oldCount; + this.backingMap.remove(element); + } + + frequency.addAndGet(-numberRemoved); + this.size -= (long)numberRemoved; + return oldCount; + } + } + } + + public int setCount(@Nullable E element, int count) { + CollectPreconditions.checkNonnegative(count, "count"); + Count existingCounter; + int oldCount; + if (count == 0) { + existingCounter = (Count)this.backingMap.remove(element); + oldCount = getAndSet(existingCounter, count); + } else { + existingCounter = (Count)this.backingMap.get(element); + oldCount = getAndSet(existingCounter, count); + if (existingCounter == null) { + this.backingMap.put(element, new Count(count)); + } + } + + this.size += (long)(count - oldCount); + return oldCount; + } + + private static int getAndSet(Count i, int count) { + return i == null ? 0 : i.getAndSet(count); + } + + @GwtIncompatible("java.io.ObjectStreamException") + private void readObjectNoData() throws ObjectStreamException { + throw new InvalidObjectException("Stream data required"); + } + + private class MapBasedMultisetIterator implements Iterator { + final Iterator> entryIterator; + java.util.Map.Entry currentEntry; + int occurrencesLeft; + boolean canRemove; + + MapBasedMultisetIterator() { + this.entryIterator = AbstractMapBasedMultiset.this.backingMap.entrySet().iterator(); + } + + public boolean hasNext() { + return this.occurrencesLeft > 0 || this.entryIterator.hasNext(); + } + + public E next() { + if (this.occurrencesLeft == 0) { + this.currentEntry = (java.util.Map.Entry)this.entryIterator.next(); + this.occurrencesLeft = ((Count)this.currentEntry.getValue()).get(); + } + + --this.occurrencesLeft; + this.canRemove = true; + return this.currentEntry.getKey(); + } + + public void remove() { + CollectPreconditions.checkRemove(this.canRemove); + int frequency = ((Count)this.currentEntry.getValue()).get(); + if (frequency <= 0) { + throw new ConcurrentModificationException(); + } else { + if (((Count)this.currentEntry.getValue()).addAndGet(-1) == 0) { + this.entryIterator.remove(); + } + + AbstractMapBasedMultiset.this.size--; + this.canRemove = false; + } + } + } +} diff --git a/src/main/com/google/common/collect/AbstractMapEntry.java b/src/main/com/google/common/collect/AbstractMapEntry.java new file mode 100644 index 0000000..e97637e --- /dev/null +++ b/src/main/com/google/common/collect/AbstractMapEntry.java @@ -0,0 +1,38 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class AbstractMapEntry implements Entry { + public abstract K getKey(); + + public abstract V getValue(); + + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof Entry)) { + return false; + } else { + Entry that = (Entry)object; + return Objects.equal(this.getKey(), that.getKey()) && Objects.equal(this.getValue(), that.getValue()); + } + } + + public int hashCode() { + K k = this.getKey(); + V v = this.getValue(); + return (k == null ? 0 : k.hashCode()) ^ (v == null ? 0 : v.hashCode()); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.getKey())); + String var2 = String.valueOf(String.valueOf(this.getValue())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append("=").append(var2).toString(); + } +} diff --git a/src/main/com/google/common/collect/AbstractMultimap.java b/src/main/com/google/common/collect/AbstractMultimap.java new file mode 100644 index 0000000..3f0f1e4 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractMultimap.java @@ -0,0 +1,198 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class AbstractMultimap implements Multimap { + private transient Collection> entries; + private transient Set keySet; + private transient Multiset keys; + private transient Collection values; + private transient Map> asMap; + + public boolean isEmpty() { + return this.size() == 0; + } + + public boolean containsValue(@Nullable Object value) { + Iterator i$ = this.asMap().values().iterator(); + + Collection collection; + do { + if (!i$.hasNext()) { + return false; + } + + collection = (Collection)i$.next(); + } while(!collection.contains(value)); + + return true; + } + + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { + Collection collection = (Collection)this.asMap().get(key); + return collection != null && collection.contains(value); + } + + public boolean remove(@Nullable Object key, @Nullable Object value) { + Collection collection = (Collection)this.asMap().get(key); + return collection != null && collection.remove(value); + } + + public boolean put(@Nullable K key, @Nullable V value) { + return this.get(key).add(value); + } + + public boolean putAll(@Nullable K key, Iterable values) { + Preconditions.checkNotNull(values); + if (values instanceof Collection) { + Collection valueCollection = (Collection)values; + return !valueCollection.isEmpty() && this.get(key).addAll(valueCollection); + } else { + Iterator valueItr = values.iterator(); + return valueItr.hasNext() && Iterators.addAll(this.get(key), valueItr); + } + } + + public boolean putAll(Multimap multimap) { + boolean changed = false; + + Entry entry; + for(Iterator i$ = multimap.entries().iterator(); i$.hasNext(); changed |= this.put(entry.getKey(), entry.getValue())) { + entry = (Entry)i$.next(); + } + + return changed; + } + + public Collection replaceValues(@Nullable K key, Iterable values) { + Preconditions.checkNotNull(values); + Collection result = this.removeAll(key); + this.putAll(key, values); + return result; + } + + public Collection> entries() { + Collection> result = this.entries; + return result == null ? (this.entries = this.createEntries()) : result; + } + + Collection> createEntries() { + return (Collection)(this instanceof SetMultimap ? new AbstractMultimap.EntrySet() : new AbstractMultimap.Entries()); + } + + abstract Iterator> entryIterator(); + + public Set keySet() { + Set result = this.keySet; + return result == null ? (this.keySet = this.createKeySet()) : result; + } + + Set createKeySet() { + return new Maps.KeySet(this.asMap()); + } + + public Multiset keys() { + Multiset result = this.keys; + return result == null ? (this.keys = this.createKeys()) : result; + } + + Multiset createKeys() { + return new Multimaps.Keys(this); + } + + public Collection values() { + Collection result = this.values; + return result == null ? (this.values = this.createValues()) : result; + } + + Collection createValues() { + return new AbstractMultimap.Values(); + } + + Iterator valueIterator() { + return Maps.valueIterator(this.entries().iterator()); + } + + public Map> asMap() { + Map> result = this.asMap; + return result == null ? (this.asMap = this.createAsMap()) : result; + } + + abstract Map> createAsMap(); + + public boolean equals(@Nullable Object object) { + return Multimaps.equalsImpl(this, object); + } + + public int hashCode() { + return this.asMap().hashCode(); + } + + public String toString() { + return this.asMap().toString(); + } + + class Values extends AbstractCollection { + public Iterator iterator() { + return AbstractMultimap.this.valueIterator(); + } + + public int size() { + return AbstractMultimap.this.size(); + } + + public boolean contains(@Nullable Object o) { + return AbstractMultimap.this.containsValue(o); + } + + public void clear() { + AbstractMultimap.this.clear(); + } + } + + private class EntrySet extends AbstractMultimap.Entries implements Set> { + private EntrySet() { + super(null); + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + public boolean equals(@Nullable Object obj) { + return Sets.equalsImpl(this, obj); + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } + + private class Entries extends Multimaps.Entries { + private Entries() { + } + + Multimap multimap() { + return AbstractMultimap.this; + } + + public Iterator> iterator() { + return AbstractMultimap.this.entryIterator(); + } + + // $FF: synthetic method + Entries(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/AbstractMultiset.java b/src/main/com/google/common/collect/AbstractMultiset.java new file mode 100644 index 0000000..773e271 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractMultiset.java @@ -0,0 +1,149 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class AbstractMultiset extends AbstractCollection implements Multiset { + private transient Set elementSet; + private transient Set> entrySet; + + public int size() { + return Multisets.sizeImpl(this); + } + + public boolean isEmpty() { + return this.entrySet().isEmpty(); + } + + public boolean contains(@Nullable Object element) { + return this.count(element) > 0; + } + + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + public int count(@Nullable Object element) { + Iterator i$ = this.entrySet().iterator(); + + Multiset.Entry entry; + do { + if (!i$.hasNext()) { + return 0; + } + + entry = (Multiset.Entry)i$.next(); + } while(!Objects.equal(entry.getElement(), element)); + + return entry.getCount(); + } + + public boolean add(@Nullable E element) { + this.add(element, 1); + return true; + } + + public int add(@Nullable E element, int occurrences) { + throw new UnsupportedOperationException(); + } + + public boolean remove(@Nullable Object element) { + return this.remove(element, 1) > 0; + } + + public int remove(@Nullable Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + public int setCount(@Nullable E element, int count) { + return Multisets.setCountImpl(this, element, count); + } + + public boolean setCount(@Nullable E element, int oldCount, int newCount) { + return Multisets.setCountImpl(this, element, oldCount, newCount); + } + + public boolean addAll(Collection elementsToAdd) { + return Multisets.addAllImpl(this, elementsToAdd); + } + + public boolean removeAll(Collection elementsToRemove) { + return Multisets.removeAllImpl(this, elementsToRemove); + } + + public boolean retainAll(Collection elementsToRetain) { + return Multisets.retainAllImpl(this, elementsToRetain); + } + + public void clear() { + Iterators.clear(this.entryIterator()); + } + + public Set elementSet() { + Set result = this.elementSet; + if (result == null) { + this.elementSet = result = this.createElementSet(); + } + + return result; + } + + Set createElementSet() { + return new AbstractMultiset.ElementSet(); + } + + abstract Iterator> entryIterator(); + + abstract int distinctElements(); + + public Set> entrySet() { + Set> result = this.entrySet; + if (result == null) { + this.entrySet = result = this.createEntrySet(); + } + + return result; + } + + Set> createEntrySet() { + return new AbstractMultiset.EntrySet(); + } + + public boolean equals(@Nullable Object object) { + return Multisets.equalsImpl(this, object); + } + + public int hashCode() { + return this.entrySet().hashCode(); + } + + public String toString() { + return this.entrySet().toString(); + } + + class EntrySet extends Multisets.EntrySet { + Multiset multiset() { + return AbstractMultiset.this; + } + + public Iterator> iterator() { + return AbstractMultiset.this.entryIterator(); + } + + public int size() { + return AbstractMultiset.this.distinctElements(); + } + } + + class ElementSet extends Multisets.ElementSet { + Multiset multiset() { + return AbstractMultiset.this; + } + } +} diff --git a/src/main/com/google/common/collect/AbstractNavigableMap.java b/src/main/com/google/common/collect/AbstractNavigableMap.java new file mode 100644 index 0000000..65ae5d5 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractNavigableMap.java @@ -0,0 +1,155 @@ +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 java.util.Map.Entry; +import javax.annotation.Nullable; + +abstract class AbstractNavigableMap extends AbstractMap implements NavigableMap { + @Nullable + public abstract V get(@Nullable Object var1); + + @Nullable + public Entry firstEntry() { + return (Entry)Iterators.getNext(this.entryIterator(), (Object)null); + } + + @Nullable + public Entry lastEntry() { + return (Entry)Iterators.getNext(this.descendingEntryIterator(), (Object)null); + } + + @Nullable + public Entry pollFirstEntry() { + return (Entry)Iterators.pollNext(this.entryIterator()); + } + + @Nullable + public Entry pollLastEntry() { + return (Entry)Iterators.pollNext(this.descendingEntryIterator()); + } + + public K firstKey() { + Entry entry = this.firstEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + public K lastKey() { + Entry entry = this.lastEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + @Nullable + public Entry lowerEntry(K key) { + return this.headMap(key, false).lastEntry(); + } + + @Nullable + public Entry floorEntry(K key) { + return this.headMap(key, true).lastEntry(); + } + + @Nullable + public Entry ceilingEntry(K key) { + return this.tailMap(key, true).firstEntry(); + } + + @Nullable + public Entry higherEntry(K key) { + return this.tailMap(key, false).firstEntry(); + } + + public K lowerKey(K key) { + return Maps.keyOrNull(this.lowerEntry(key)); + } + + public K floorKey(K key) { + return Maps.keyOrNull(this.floorEntry(key)); + } + + public K ceilingKey(K key) { + return Maps.keyOrNull(this.ceilingEntry(key)); + } + + public K higherKey(K key) { + return Maps.keyOrNull(this.higherEntry(key)); + } + + abstract Iterator> entryIterator(); + + abstract Iterator> descendingEntryIterator(); + + public SortedMap subMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public SortedMap headMap(K toKey) { + return this.headMap(toKey, false); + } + + public SortedMap tailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + + public NavigableSet navigableKeySet() { + return new Maps.NavigableKeySet(this); + } + + public Set keySet() { + return this.navigableKeySet(); + } + + public abstract int size(); + + public Set> entrySet() { + return new Maps.EntrySet() { + Map map() { + return AbstractNavigableMap.this; + } + + public Iterator> iterator() { + return AbstractNavigableMap.this.entryIterator(); + } + }; + } + + public NavigableSet descendingKeySet() { + return this.descendingMap().navigableKeySet(); + } + + public NavigableMap descendingMap() { + return new AbstractNavigableMap.DescendingMap(); + } + + private final class DescendingMap extends Maps.DescendingMap { + private DescendingMap() { + } + + NavigableMap forward() { + return AbstractNavigableMap.this; + } + + Iterator> entryIterator() { + return AbstractNavigableMap.this.descendingEntryIterator(); + } + + // $FF: synthetic method + DescendingMap(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/AbstractRangeSet.java b/src/main/com/google/common/collect/AbstractRangeSet.java new file mode 100644 index 0000000..c851d82 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractRangeSet.java @@ -0,0 +1,84 @@ +package com.google.common.collect; + +import java.util.Iterator; +import javax.annotation.Nullable; + +abstract class AbstractRangeSet implements RangeSet { + public boolean contains(C value) { + return this.rangeContaining(value) != null; + } + + public abstract Range rangeContaining(C var1); + + public boolean isEmpty() { + return this.asRanges().isEmpty(); + } + + public void add(Range range) { + throw new UnsupportedOperationException(); + } + + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + public void clear() { + this.remove(Range.all()); + } + + public boolean enclosesAll(RangeSet other) { + Iterator i$ = other.asRanges().iterator(); + + Range range; + do { + if (!i$.hasNext()) { + return true; + } + + range = (Range)i$.next(); + } while(this.encloses(range)); + + return false; + } + + public void addAll(RangeSet other) { + Iterator i$ = other.asRanges().iterator(); + + while(i$.hasNext()) { + Range range = (Range)i$.next(); + this.add(range); + } + + } + + public void removeAll(RangeSet other) { + Iterator i$ = other.asRanges().iterator(); + + while(i$.hasNext()) { + Range range = (Range)i$.next(); + this.remove(range); + } + + } + + public abstract boolean encloses(Range var1); + + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof RangeSet) { + RangeSet other = (RangeSet)obj; + return this.asRanges().equals(other.asRanges()); + } else { + return false; + } + } + + public final int hashCode() { + return this.asRanges().hashCode(); + } + + public final String toString() { + return this.asRanges().toString(); + } +} diff --git a/src/main/com/google/common/collect/AbstractSequentialIterator.java b/src/main/com/google/common/collect/AbstractSequentialIterator.java new file mode 100644 index 0000000..452f3fa --- /dev/null +++ b/src/main/com/google/common/collect/AbstractSequentialIterator.java @@ -0,0 +1,35 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.NoSuchElementException; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class AbstractSequentialIterator extends UnmodifiableIterator { + private T nextOrNull; + + protected AbstractSequentialIterator(@Nullable T firstOrNull) { + this.nextOrNull = firstOrNull; + } + + protected abstract T computeNext(T var1); + + public final boolean hasNext() { + return this.nextOrNull != null; + } + + public final T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + Object var1; + try { + var1 = this.nextOrNull; + } finally { + this.nextOrNull = this.computeNext(this.nextOrNull); + } + + return var1; + } + } +} diff --git a/src/main/com/google/common/collect/AbstractSetMultimap.java b/src/main/com/google/common/collect/AbstractSetMultimap.java new file mode 100644 index 0000000..fdf6ba9 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractSetMultimap.java @@ -0,0 +1,51 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class AbstractSetMultimap extends AbstractMapBasedMultimap implements SetMultimap { + private static final long serialVersionUID = 7431625294878419160L; + + protected AbstractSetMultimap(Map> map) { + super(map); + } + + abstract Set createCollection(); + + Set createUnmodifiableEmptyCollection() { + return ImmutableSet.of(); + } + + public Set get(@Nullable K key) { + return (Set)super.get(key); + } + + public Set> entries() { + return (Set)super.entries(); + } + + public Set removeAll(@Nullable Object key) { + return (Set)super.removeAll(key); + } + + public Set replaceValues(@Nullable K key, Iterable values) { + return (Set)super.replaceValues(key, values); + } + + public Map> asMap() { + return super.asMap(); + } + + public boolean put(@Nullable K key, @Nullable V value) { + return super.put(key, value); + } + + public boolean equals(@Nullable Object object) { + return super.equals(object); + } +} diff --git a/src/main/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java b/src/main/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java new file mode 100644 index 0000000..1e36260 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractSortedKeySortedSetMultimap.java @@ -0,0 +1,25 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.SortedMap; +import java.util.SortedSet; + +@GwtCompatible +abstract class AbstractSortedKeySortedSetMultimap extends AbstractSortedSetMultimap { + AbstractSortedKeySortedSetMultimap(SortedMap> map) { + super(map); + } + + public SortedMap> asMap() { + return (SortedMap)super.asMap(); + } + + SortedMap> backingMap() { + return (SortedMap)super.backingMap(); + } + + public SortedSet keySet() { + return (SortedSet)super.keySet(); + } +} diff --git a/src/main/com/google/common/collect/AbstractSortedMultiset.java b/src/main/com/google/common/collect/AbstractSortedMultiset.java new file mode 100644 index 0000000..723d5a1 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractSortedMultiset.java @@ -0,0 +1,104 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +abstract class AbstractSortedMultiset extends AbstractMultiset implements SortedMultiset { + @GwtTransient + final Comparator comparator; + private transient SortedMultiset descendingMultiset; + + AbstractSortedMultiset() { + this(Ordering.natural()); + } + + AbstractSortedMultiset(Comparator comparator) { + this.comparator = (Comparator)Preconditions.checkNotNull(comparator); + } + + public NavigableSet elementSet() { + return (NavigableSet)super.elementSet(); + } + + NavigableSet createElementSet() { + return new SortedMultisets.NavigableElementSet(this); + } + + public Comparator comparator() { + return this.comparator; + } + + public Multiset.Entry firstEntry() { + Iterator> entryIterator = this.entryIterator(); + return entryIterator.hasNext() ? (Multiset.Entry)entryIterator.next() : null; + } + + public Multiset.Entry lastEntry() { + Iterator> entryIterator = this.descendingEntryIterator(); + return entryIterator.hasNext() ? (Multiset.Entry)entryIterator.next() : null; + } + + public Multiset.Entry pollFirstEntry() { + Iterator> entryIterator = this.entryIterator(); + if (entryIterator.hasNext()) { + Multiset.Entry result = (Multiset.Entry)entryIterator.next(); + result = Multisets.immutableEntry(result.getElement(), result.getCount()); + entryIterator.remove(); + return result; + } else { + return null; + } + } + + public Multiset.Entry pollLastEntry() { + Iterator> entryIterator = this.descendingEntryIterator(); + if (entryIterator.hasNext()) { + Multiset.Entry result = (Multiset.Entry)entryIterator.next(); + result = Multisets.immutableEntry(result.getElement(), result.getCount()); + entryIterator.remove(); + return result; + } else { + return null; + } + } + + public SortedMultiset subMultiset(@Nullable E fromElement, BoundType fromBoundType, @Nullable E toElement, BoundType toBoundType) { + Preconditions.checkNotNull(fromBoundType); + Preconditions.checkNotNull(toBoundType); + return this.tailMultiset(fromElement, fromBoundType).headMultiset(toElement, toBoundType); + } + + abstract Iterator> descendingEntryIterator(); + + Iterator descendingIterator() { + return Multisets.iteratorImpl(this.descendingMultiset()); + } + + public SortedMultiset descendingMultiset() { + SortedMultiset result = this.descendingMultiset; + return result == null ? (this.descendingMultiset = this.createDescendingMultiset()) : result; + } + + SortedMultiset createDescendingMultiset() { + return new DescendingMultiset() { + SortedMultiset forwardMultiset() { + return AbstractSortedMultiset.this; + } + + Iterator> entryIterator() { + return AbstractSortedMultiset.this.descendingEntryIterator(); + } + + public Iterator iterator() { + return AbstractSortedMultiset.this.descendingIterator(); + } + }; + } +} diff --git a/src/main/com/google/common/collect/AbstractSortedSetMultimap.java b/src/main/com/google/common/collect/AbstractSortedSetMultimap.java new file mode 100644 index 0000000..b8f2457 --- /dev/null +++ b/src/main/com/google/common/collect/AbstractSortedSetMultimap.java @@ -0,0 +1,45 @@ +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; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class AbstractSortedSetMultimap extends AbstractSetMultimap implements SortedSetMultimap { + private static final long serialVersionUID = 430848587173315748L; + + protected AbstractSortedSetMultimap(Map> map) { + super(map); + } + + abstract SortedSet createCollection(); + + SortedSet createUnmodifiableEmptyCollection() { + Comparator comparator = this.valueComparator(); + return (SortedSet)(comparator == null ? Collections.unmodifiableSortedSet(this.createCollection()) : ImmutableSortedSet.emptySet(this.valueComparator())); + } + + public SortedSet get(@Nullable K key) { + return (SortedSet)super.get(key); + } + + public SortedSet removeAll(@Nullable Object key) { + return (SortedSet)super.removeAll(key); + } + + public SortedSet replaceValues(@Nullable K key, Iterable values) { + return (SortedSet)super.replaceValues(key, values); + } + + public Map> asMap() { + return super.asMap(); + } + + public Collection values() { + return super.values(); + } +} diff --git a/src/main/com/google/common/collect/AbstractTable.java b/src/main/com/google/common/collect/AbstractTable.java new file mode 100644 index 0000000..cbdb00d --- /dev/null +++ b/src/main/com/google/common/collect/AbstractTable.java @@ -0,0 +1,176 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class AbstractTable implements Table { + private transient Set> cellSet; + private transient Collection values; + + public boolean containsRow(@Nullable Object rowKey) { + return Maps.safeContainsKey(this.rowMap(), rowKey); + } + + public boolean containsColumn(@Nullable Object columnKey) { + return Maps.safeContainsKey(this.columnMap(), columnKey); + } + + public Set rowKeySet() { + return this.rowMap().keySet(); + } + + public Set columnKeySet() { + return this.columnMap().keySet(); + } + + public boolean containsValue(@Nullable Object value) { + Iterator i$ = this.rowMap().values().iterator(); + + Map row; + do { + if (!i$.hasNext()) { + return false; + } + + row = (Map)i$.next(); + } while(!row.containsValue(value)); + + return true; + } + + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + Map row = (Map)Maps.safeGet(this.rowMap(), rowKey); + return row != null && Maps.safeContainsKey(row, columnKey); + } + + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + Map row = (Map)Maps.safeGet(this.rowMap(), rowKey); + return row == null ? null : Maps.safeGet(row, columnKey); + } + + public boolean isEmpty() { + return this.size() == 0; + } + + public void clear() { + Iterators.clear(this.cellSet().iterator()); + } + + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + Map row = (Map)Maps.safeGet(this.rowMap(), rowKey); + return row == null ? null : Maps.safeRemove(row, columnKey); + } + + public V put(R rowKey, C columnKey, V value) { + return this.row(rowKey).put(columnKey, value); + } + + public void putAll(Table table) { + Iterator i$ = table.cellSet().iterator(); + + while(i$.hasNext()) { + Table.Cell cell = (Table.Cell)i$.next(); + this.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + + } + + public Set> cellSet() { + Set> result = this.cellSet; + return result == null ? (this.cellSet = this.createCellSet()) : result; + } + + Set> createCellSet() { + return new AbstractTable.CellSet(); + } + + abstract Iterator> cellIterator(); + + public Collection values() { + Collection result = this.values; + return result == null ? (this.values = this.createValues()) : result; + } + + Collection createValues() { + return new AbstractTable.Values(); + } + + Iterator valuesIterator() { + return new TransformedIterator, V>(this.cellSet().iterator()) { + V transform(Table.Cell cell) { + return cell.getValue(); + } + }; + } + + public boolean equals(@Nullable Object obj) { + return Tables.equalsImpl(this, obj); + } + + public int hashCode() { + return this.cellSet().hashCode(); + } + + public String toString() { + return this.rowMap().toString(); + } + + class Values extends AbstractCollection { + public Iterator iterator() { + return AbstractTable.this.valuesIterator(); + } + + public boolean contains(Object o) { + return AbstractTable.this.containsValue(o); + } + + public void clear() { + AbstractTable.this.clear(); + } + + public int size() { + return AbstractTable.this.size(); + } + } + + class CellSet extends AbstractSet> { + public boolean contains(Object o) { + if (!(o instanceof Table.Cell)) { + return false; + } else { + Table.Cell cell = (Table.Cell)o; + Map row = (Map)Maps.safeGet(AbstractTable.this.rowMap(), cell.getRowKey()); + return row != null && Collections2.safeContains(row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + } + + public boolean remove(@Nullable Object o) { + if (!(o instanceof Table.Cell)) { + return false; + } else { + Table.Cell cell = (Table.Cell)o; + Map row = (Map)Maps.safeGet(AbstractTable.this.rowMap(), cell.getRowKey()); + return row != null && Collections2.safeRemove(row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + } + + public void clear() { + AbstractTable.this.clear(); + } + + public Iterator> iterator() { + return AbstractTable.this.cellIterator(); + } + + public int size() { + return AbstractTable.this.size(); + } + } +} diff --git a/src/main/com/google/common/collect/AllEqualOrdering.java b/src/main/com/google/common/collect/AllEqualOrdering.java new file mode 100644 index 0000000..5f87e79 --- /dev/null +++ b/src/main/com/google/common/collect/AllEqualOrdering.java @@ -0,0 +1,38 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.List; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class AllEqualOrdering extends Ordering implements Serializable { + static final AllEqualOrdering INSTANCE = new AllEqualOrdering(); + private static final long serialVersionUID = 0L; + + public int compare(@Nullable Object left, @Nullable Object right) { + return 0; + } + + public List sortedCopy(Iterable iterable) { + return Lists.newArrayList(iterable); + } + + public ImmutableList immutableSortedCopy(Iterable iterable) { + return ImmutableList.copyOf(iterable); + } + + public Ordering reverse() { + return this; + } + + private Object readResolve() { + return INSTANCE; + } + + public String toString() { + return "Ordering.allEqual()"; + } +} diff --git a/src/main/com/google/common/collect/ArrayListMultimap.java b/src/main/com/google/common/collect/ArrayListMultimap.java new file mode 100644 index 0000000..e318699 --- /dev/null +++ b/src/main/com/google/common/collect/ArrayListMultimap.java @@ -0,0 +1,86 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +@GwtCompatible( + serializable = true, + emulated = true +) +public final class ArrayListMultimap extends AbstractListMultimap { + private static final int DEFAULT_VALUES_PER_KEY = 3; + @VisibleForTesting + transient int expectedValuesPerKey; + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0L; + + public static ArrayListMultimap create() { + return new ArrayListMultimap(); + } + + public static ArrayListMultimap create(int expectedKeys, int expectedValuesPerKey) { + return new ArrayListMultimap(expectedKeys, expectedValuesPerKey); + } + + public static ArrayListMultimap create(Multimap multimap) { + return new ArrayListMultimap(multimap); + } + + private ArrayListMultimap() { + super(new HashMap()); + this.expectedValuesPerKey = 3; + } + + private ArrayListMultimap(int expectedKeys, int expectedValuesPerKey) { + super(Maps.newHashMapWithExpectedSize(expectedKeys)); + CollectPreconditions.checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + this.expectedValuesPerKey = expectedValuesPerKey; + } + + private ArrayListMultimap(Multimap multimap) { + this(multimap.keySet().size(), multimap instanceof ArrayListMultimap ? ((ArrayListMultimap)multimap).expectedValuesPerKey : 3); + this.putAll(multimap); + } + + List createCollection() { + return new ArrayList(this.expectedValuesPerKey); + } + + public void trimToSize() { + Iterator i$ = this.backingMap().values().iterator(); + + while(i$.hasNext()) { + Collection collection = (Collection)i$.next(); + ArrayList arrayList = (ArrayList)collection; + arrayList.trimToSize(); + } + + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(this.expectedValuesPerKey); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.expectedValuesPerKey = stream.readInt(); + int distinctKeys = Serialization.readCount(stream); + Map> map = Maps.newHashMapWithExpectedSize(distinctKeys); + this.setMap(map); + Serialization.populateMultimap(this, stream, distinctKeys); + } +} diff --git a/src/main/com/google/common/collect/ArrayTable.java b/src/main/com/google/common/collect/ArrayTable.java new file mode 100644 index 0000000..9c7ff46 --- /dev/null +++ b/src/main/com/google/common/collect/ArrayTable.java @@ -0,0 +1,460 @@ +package com.google.common.collect; + +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 com.google.common.base.Preconditions; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible( + emulated = true +) +public final class ArrayTable extends AbstractTable implements Serializable { + private final ImmutableList rowList; + private final ImmutableList columnList; + private final ImmutableMap rowKeyToIndex; + private final ImmutableMap columnKeyToIndex; + private final V[][] array; + private transient ArrayTable.ColumnMap columnMap; + private transient ArrayTable.RowMap rowMap; + private static final long serialVersionUID = 0L; + + public static ArrayTable create(Iterable rowKeys, Iterable columnKeys) { + return new ArrayTable(rowKeys, columnKeys); + } + + public static ArrayTable create(Table table) { + return table instanceof ArrayTable ? new ArrayTable((ArrayTable)table) : new ArrayTable(table); + } + + private ArrayTable(Iterable rowKeys, Iterable columnKeys) { + this.rowList = ImmutableList.copyOf(rowKeys); + this.columnList = ImmutableList.copyOf(columnKeys); + Preconditions.checkArgument(!this.rowList.isEmpty()); + Preconditions.checkArgument(!this.columnList.isEmpty()); + this.rowKeyToIndex = index(this.rowList); + this.columnKeyToIndex = index(this.columnList); + V[][] tmpArray = (Object[][])(new Object[this.rowList.size()][this.columnList.size()]); + this.array = tmpArray; + this.eraseAll(); + } + + private static ImmutableMap index(List list) { + ImmutableMap.Builder columnBuilder = ImmutableMap.builder(); + + for(int i = 0; i < list.size(); ++i) { + columnBuilder.put(list.get(i), i); + } + + return columnBuilder.build(); + } + + private ArrayTable(Table table) { + this(table.rowKeySet(), table.columnKeySet()); + this.putAll(table); + } + + private ArrayTable(ArrayTable table) { + this.rowList = table.rowList; + this.columnList = table.columnList; + this.rowKeyToIndex = table.rowKeyToIndex; + this.columnKeyToIndex = table.columnKeyToIndex; + V[][] copy = (Object[][])(new Object[this.rowList.size()][this.columnList.size()]); + this.array = copy; + this.eraseAll(); + + for(int i = 0; i < this.rowList.size(); ++i) { + System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length); + } + + } + + public ImmutableList rowKeyList() { + return this.rowList; + } + + public ImmutableList columnKeyList() { + return this.columnList; + } + + public V at(int rowIndex, int columnIndex) { + Preconditions.checkElementIndex(rowIndex, this.rowList.size()); + Preconditions.checkElementIndex(columnIndex, this.columnList.size()); + return this.array[rowIndex][columnIndex]; + } + + public V set(int rowIndex, int columnIndex, @Nullable V value) { + Preconditions.checkElementIndex(rowIndex, this.rowList.size()); + Preconditions.checkElementIndex(columnIndex, this.columnList.size()); + V oldValue = this.array[rowIndex][columnIndex]; + this.array[rowIndex][columnIndex] = value; + return oldValue; + } + + @GwtIncompatible("reflection") + public V[][] toArray(Class valueClass) { + V[][] copy = (Object[][])((Object[][])Array.newInstance(valueClass, new int[]{this.rowList.size(), this.columnList.size()})); + + for(int i = 0; i < this.rowList.size(); ++i) { + System.arraycopy(this.array[i], 0, copy[i], 0, this.array[i].length); + } + + return copy; + } + + /** @deprecated */ + @Deprecated + public void clear() { + throw new UnsupportedOperationException(); + } + + public void eraseAll() { + Object[][] arr$ = this.array; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + V[] row = arr$[i$]; + Arrays.fill(row, (Object)null); + } + + } + + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return this.containsRow(rowKey) && this.containsColumn(columnKey); + } + + public boolean containsColumn(@Nullable Object columnKey) { + return this.columnKeyToIndex.containsKey(columnKey); + } + + public boolean containsRow(@Nullable Object rowKey) { + return this.rowKeyToIndex.containsKey(rowKey); + } + + public boolean containsValue(@Nullable Object value) { + Object[][] arr$ = this.array; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + V[] row = arr$[i$]; + Object[] arr$ = row; + int len$ = row.length; + + for(int i$ = 0; i$ < len$; ++i$) { + V element = arr$[i$]; + if (Objects.equal(value, element)) { + return true; + } + } + } + + return false; + } + + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + Integer rowIndex = (Integer)this.rowKeyToIndex.get(rowKey); + Integer columnIndex = (Integer)this.columnKeyToIndex.get(columnKey); + return rowIndex != null && columnIndex != null ? this.at(rowIndex, columnIndex) : null; + } + + public boolean isEmpty() { + return false; + } + + public V put(R rowKey, C columnKey, @Nullable V value) { + Preconditions.checkNotNull(rowKey); + Preconditions.checkNotNull(columnKey); + Integer rowIndex = (Integer)this.rowKeyToIndex.get(rowKey); + Preconditions.checkArgument(rowIndex != null, "Row %s not in %s", rowKey, this.rowList); + Integer columnIndex = (Integer)this.columnKeyToIndex.get(columnKey); + Preconditions.checkArgument(columnIndex != null, "Column %s not in %s", columnKey, this.columnList); + return this.set(rowIndex, columnIndex, value); + } + + public void putAll(Table table) { + super.putAll(table); + } + + /** @deprecated */ + @Deprecated + public V remove(Object rowKey, Object columnKey) { + throw new UnsupportedOperationException(); + } + + public V erase(@Nullable Object rowKey, @Nullable Object columnKey) { + Integer rowIndex = (Integer)this.rowKeyToIndex.get(rowKey); + Integer columnIndex = (Integer)this.columnKeyToIndex.get(columnKey); + return rowIndex != null && columnIndex != null ? this.set(rowIndex, columnIndex, (Object)null) : null; + } + + public int size() { + return this.rowList.size() * this.columnList.size(); + } + + public Set> cellSet() { + return super.cellSet(); + } + + Iterator> cellIterator() { + return new AbstractIndexedListIterator>(this.size()) { + protected Table.Cell get(final int index) { + return new Tables.AbstractCell() { + final int rowIndex; + final int columnIndex; + + { + this.rowIndex = index / ArrayTable.this.columnList.size(); + this.columnIndex = index % ArrayTable.this.columnList.size(); + } + + public R getRowKey() { + return ArrayTable.this.rowList.get(this.rowIndex); + } + + public C getColumnKey() { + return ArrayTable.this.columnList.get(this.columnIndex); + } + + public V getValue() { + return ArrayTable.this.at(this.rowIndex, this.columnIndex); + } + }; + } + }; + } + + public Map column(C columnKey) { + Preconditions.checkNotNull(columnKey); + Integer columnIndex = (Integer)this.columnKeyToIndex.get(columnKey); + return (Map)(columnIndex == null ? ImmutableMap.of() : new ArrayTable.Column(columnIndex)); + } + + public ImmutableSet columnKeySet() { + return this.columnKeyToIndex.keySet(); + } + + public Map> columnMap() { + ArrayTable.ColumnMap map = this.columnMap; + return map == null ? (this.columnMap = new ArrayTable.ColumnMap()) : map; + } + + public Map row(R rowKey) { + Preconditions.checkNotNull(rowKey); + Integer rowIndex = (Integer)this.rowKeyToIndex.get(rowKey); + return (Map)(rowIndex == null ? ImmutableMap.of() : new ArrayTable.Row(rowIndex)); + } + + public ImmutableSet rowKeySet() { + return this.rowKeyToIndex.keySet(); + } + + public Map> rowMap() { + ArrayTable.RowMap map = this.rowMap; + return map == null ? (this.rowMap = new ArrayTable.RowMap()) : map; + } + + public Collection values() { + return super.values(); + } + + private class RowMap extends ArrayTable.ArrayMap> { + private RowMap() { + super(ArrayTable.this.rowKeyToIndex, null); + } + + String getKeyRole() { + return "Row"; + } + + Map getValue(int index) { + return ArrayTable.this.new Row(index); + } + + Map setValue(int index, Map newValue) { + throw new UnsupportedOperationException(); + } + + public Map put(R key, Map value) { + throw new UnsupportedOperationException(); + } + + // $FF: synthetic method + RowMap(Object x1) { + this(); + } + } + + private class Row extends ArrayTable.ArrayMap { + final int rowIndex; + + Row(int rowIndex) { + super(ArrayTable.this.columnKeyToIndex, null); + this.rowIndex = rowIndex; + } + + String getKeyRole() { + return "Column"; + } + + V getValue(int index) { + return ArrayTable.this.at(this.rowIndex, index); + } + + V setValue(int index, V newValue) { + return ArrayTable.this.set(this.rowIndex, index, newValue); + } + } + + private class ColumnMap extends ArrayTable.ArrayMap> { + private ColumnMap() { + super(ArrayTable.this.columnKeyToIndex, null); + } + + String getKeyRole() { + return "Column"; + } + + Map getValue(int index) { + return ArrayTable.this.new Column(index); + } + + Map setValue(int index, Map newValue) { + throw new UnsupportedOperationException(); + } + + public Map put(C key, Map value) { + throw new UnsupportedOperationException(); + } + + // $FF: synthetic method + ColumnMap(Object x1) { + this(); + } + } + + private class Column extends ArrayTable.ArrayMap { + final int columnIndex; + + Column(int columnIndex) { + super(ArrayTable.this.rowKeyToIndex, null); + this.columnIndex = columnIndex; + } + + String getKeyRole() { + return "Row"; + } + + V getValue(int index) { + return ArrayTable.this.at(index, this.columnIndex); + } + + V setValue(int index, V newValue) { + return ArrayTable.this.set(index, this.columnIndex, newValue); + } + } + + private abstract static class ArrayMap extends Maps.ImprovedAbstractMap { + private final ImmutableMap keyIndex; + + private ArrayMap(ImmutableMap keyIndex) { + this.keyIndex = keyIndex; + } + + public Set keySet() { + return this.keyIndex.keySet(); + } + + K getKey(int index) { + return this.keyIndex.keySet().asList().get(index); + } + + abstract String getKeyRole(); + + @Nullable + abstract V getValue(int var1); + + @Nullable + abstract V setValue(int var1, V var2); + + public int size() { + return this.keyIndex.size(); + } + + public boolean isEmpty() { + return this.keyIndex.isEmpty(); + } + + protected Set> createEntrySet() { + return new Maps.EntrySet() { + Map map() { + return ArrayMap.this; + } + + public Iterator> iterator() { + return new AbstractIndexedListIterator>(this.size()) { + protected Entry get(final int index) { + return new AbstractMapEntry() { + public K getKey() { + return ArrayMap.this.getKey(index); + } + + public V getValue() { + return ArrayMap.this.getValue(index); + } + + public V setValue(V value) { + return ArrayMap.this.setValue(index, value); + } + }; + } + }; + } + }; + } + + public boolean containsKey(@Nullable Object key) { + return this.keyIndex.containsKey(key); + } + + public V get(@Nullable Object key) { + Integer index = (Integer)this.keyIndex.get(key); + return index == null ? null : this.getValue(index); + } + + public V put(K key, V value) { + Integer index = (Integer)this.keyIndex.get(key); + if (index == null) { + String var4 = String.valueOf(String.valueOf(this.getKeyRole())); + String var5 = String.valueOf(String.valueOf(key)); + String var6 = String.valueOf(String.valueOf(this.keyIndex.keySet())); + throw new IllegalArgumentException((new StringBuilder(9 + var4.length() + var5.length() + var6.length())).append(var4).append(" ").append(var5).append(" not in ").append(var6).toString()); + } else { + return this.setValue(index, value); + } + } + + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + // $FF: synthetic method + ArrayMap(ImmutableMap x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/collect/BiMap.java b/src/main/com/google/common/collect/BiMap.java new file mode 100644 index 0000000..3da3f3b --- /dev/null +++ b/src/main/com/google/common/collect/BiMap.java @@ -0,0 +1,19 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +public interface BiMap extends Map { + V put(@Nullable K var1, @Nullable V var2); + + V forcePut(@Nullable K var1, @Nullable V var2); + + void putAll(Map var1); + + Set values(); + + BiMap inverse(); +} diff --git a/src/main/com/google/common/collect/BinaryTreeTraverser.java b/src/main/com/google/common/collect/BinaryTreeTraverser.java new file mode 100644 index 0000000..561c2f3 --- /dev/null +++ b/src/main/com/google/common/collect/BinaryTreeTraverser.java @@ -0,0 +1,156 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import java.util.ArrayDeque; +import java.util.BitSet; +import java.util.Deque; +import java.util.Iterator; + +@Beta +@GwtCompatible( + emulated = true +) +public abstract class BinaryTreeTraverser extends TreeTraverser { + public abstract Optional leftChild(T var1); + + public abstract Optional rightChild(T var1); + + public final Iterable children(final T root) { + Preconditions.checkNotNull(root); + return new FluentIterable() { + public Iterator iterator() { + return new AbstractIterator() { + boolean doneLeft; + boolean doneRight; + + protected T computeNext() { + Optional right; + if (!this.doneLeft) { + this.doneLeft = true; + right = BinaryTreeTraverser.this.leftChild(root); + if (right.isPresent()) { + return right.get(); + } + } + + if (!this.doneRight) { + this.doneRight = true; + right = BinaryTreeTraverser.this.rightChild(root); + if (right.isPresent()) { + return right.get(); + } + } + + return this.endOfData(); + } + }; + } + }; + } + + UnmodifiableIterator preOrderIterator(T root) { + return new BinaryTreeTraverser.PreOrderIterator(root); + } + + UnmodifiableIterator postOrderIterator(T root) { + return new BinaryTreeTraverser.PostOrderIterator(root); + } + + public final FluentIterable inOrderTraversal(final T root) { + Preconditions.checkNotNull(root); + return new FluentIterable() { + public UnmodifiableIterator iterator() { + return BinaryTreeTraverser.this.new InOrderIterator(root); + } + }; + } + + private static void pushIfPresent(Deque stack, Optional node) { + if (node.isPresent()) { + stack.addLast(node.get()); + } + + } + + private final class InOrderIterator extends AbstractIterator { + private final Deque stack = new ArrayDeque(); + private final BitSet hasExpandedLeft = new BitSet(); + + InOrderIterator(T root) { + this.stack.addLast(root); + } + + protected T computeNext() { + while(!this.stack.isEmpty()) { + T node = this.stack.getLast(); + if (this.hasExpandedLeft.get(this.stack.size() - 1)) { + this.stack.removeLast(); + this.hasExpandedLeft.clear(this.stack.size()); + BinaryTreeTraverser.pushIfPresent(this.stack, BinaryTreeTraverser.this.rightChild(node)); + return node; + } + + this.hasExpandedLeft.set(this.stack.size() - 1); + BinaryTreeTraverser.pushIfPresent(this.stack, BinaryTreeTraverser.this.leftChild(node)); + } + + return this.endOfData(); + } + } + + private final class PostOrderIterator extends UnmodifiableIterator { + private final Deque stack = new ArrayDeque(); + private final BitSet hasExpanded; + + PostOrderIterator(T root) { + this.stack.addLast(root); + this.hasExpanded = new BitSet(); + } + + public boolean hasNext() { + return !this.stack.isEmpty(); + } + + public T next() { + while(true) { + T node = this.stack.getLast(); + boolean expandedNode = this.hasExpanded.get(this.stack.size() - 1); + if (expandedNode) { + this.stack.removeLast(); + this.hasExpanded.clear(this.stack.size()); + return node; + } + + this.hasExpanded.set(this.stack.size() - 1); + BinaryTreeTraverser.pushIfPresent(this.stack, BinaryTreeTraverser.this.rightChild(node)); + BinaryTreeTraverser.pushIfPresent(this.stack, BinaryTreeTraverser.this.leftChild(node)); + } + } + } + + private final class PreOrderIterator extends UnmodifiableIterator implements PeekingIterator { + private final Deque stack = new ArrayDeque(); + + PreOrderIterator(T root) { + this.stack.addLast(root); + } + + public boolean hasNext() { + return !this.stack.isEmpty(); + } + + public T next() { + T result = this.stack.removeLast(); + BinaryTreeTraverser.pushIfPresent(this.stack, BinaryTreeTraverser.this.rightChild(result)); + BinaryTreeTraverser.pushIfPresent(this.stack, BinaryTreeTraverser.this.leftChild(result)); + return result; + } + + public T peek() { + return this.stack.getLast(); + } + } +} diff --git a/src/main/com/google/common/collect/BoundType.java b/src/main/com/google/common/collect/BoundType.java new file mode 100644 index 0000000..5f8e208 --- /dev/null +++ b/src/main/com/google/common/collect/BoundType.java @@ -0,0 +1,31 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible +public enum BoundType { + OPEN { + BoundType flip() { + return CLOSED; + } + }, + CLOSED { + BoundType flip() { + return OPEN; + } + }; + + private BoundType() { + } + + static BoundType forBoolean(boolean inclusive) { + return inclusive ? CLOSED : OPEN; + } + + abstract BoundType flip(); + + // $FF: synthetic method + BoundType(Object x2) { + this(); + } +} diff --git a/src/main/com/google/common/collect/ByFunctionOrdering.java b/src/main/com/google/common/collect/ByFunctionOrdering.java new file mode 100644 index 0000000..ac86cdd --- /dev/null +++ b/src/main/com/google/common/collect/ByFunctionOrdering.java @@ -0,0 +1,47 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class ByFunctionOrdering extends Ordering implements Serializable { + final Function function; + final Ordering ordering; + private static final long serialVersionUID = 0L; + + ByFunctionOrdering(Function function, Ordering ordering) { + this.function = (Function)Preconditions.checkNotNull(function); + this.ordering = (Ordering)Preconditions.checkNotNull(ordering); + } + + public int compare(F left, F right) { + return this.ordering.compare(this.function.apply(left), this.function.apply(right)); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (!(object instanceof ByFunctionOrdering)) { + return false; + } else { + ByFunctionOrdering that = (ByFunctionOrdering)object; + return this.function.equals(that.function) && this.ordering.equals(that.ordering); + } + } + + public int hashCode() { + return Objects.hashCode(this.function, this.ordering); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.ordering)); + String var2 = String.valueOf(String.valueOf(this.function)); + return (new StringBuilder(13 + var1.length() + var2.length())).append(var1).append(".onResultOf(").append(var2).append(")").toString(); + } +} diff --git a/src/main/com/google/common/collect/CartesianList.java b/src/main/com/google/common/collect/CartesianList.java new file mode 100644 index 0000000..0167700 --- /dev/null +++ b/src/main/com/google/common/collect/CartesianList.java @@ -0,0 +1,102 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import com.google.common.math.IntMath; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.RandomAccess; +import javax.annotation.Nullable; + +@GwtCompatible +final class CartesianList extends AbstractList> implements RandomAccess { + private final transient ImmutableList> axes; + private final transient int[] axesSizeProduct; + + static List> create(List> lists) { + ImmutableList.Builder> axesBuilder = new ImmutableList.Builder(lists.size()); + Iterator i$ = lists.iterator(); + + while(i$.hasNext()) { + List list = (List)i$.next(); + List copy = ImmutableList.copyOf((Collection)list); + if (copy.isEmpty()) { + return ImmutableList.of(); + } + + axesBuilder.add((Object)copy); + } + + return new CartesianList(axesBuilder.build()); + } + + CartesianList(ImmutableList> 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], ((List)axes.get(i)).size()); + } + } catch (ArithmeticException var4) { + 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 / this.axesSizeProduct[axis + 1] % ((List)this.axes.get(axis)).size(); + } + + public ImmutableList get(final int index) { + Preconditions.checkElementIndex(index, this.size()); + return new ImmutableList() { + public int size() { + return CartesianList.this.axes.size(); + } + + public E get(int axis) { + Preconditions.checkElementIndex(axis, this.size()); + int axisIndex = CartesianList.this.getAxisIndexForProductIndex(index, axis); + return ((List)CartesianList.this.axes.get(axis)).get(axisIndex); + } + + boolean isPartialView() { + return true; + } + }; + } + + public int size() { + return this.axesSizeProduct[0]; + } + + public boolean contains(@Nullable Object o) { + if (!(o instanceof List)) { + return false; + } else { + List list = (List)o; + if (list.size() != this.axes.size()) { + return false; + } else { + ListIterator itr = list.listIterator(); + + int index; + do { + if (!itr.hasNext()) { + return true; + } + + index = itr.nextIndex(); + } while(((List)this.axes.get(index)).contains(itr.next())); + + return false; + } + } + } +} diff --git a/src/main/com/google/common/collect/ClassToInstanceMap.java b/src/main/com/google/common/collect/ClassToInstanceMap.java new file mode 100644 index 0000000..e606e7f --- /dev/null +++ b/src/main/com/google/common/collect/ClassToInstanceMap.java @@ -0,0 +1,12 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible +public interface ClassToInstanceMap extends Map, B> { + T getInstance(Class var1); + + T putInstance(Class var1, @Nullable T var2); +} diff --git a/src/main/com/google/common/collect/CollectPreconditions.java b/src/main/com/google/common/collect/CollectPreconditions.java new file mode 100644 index 0000000..69d4d6c --- /dev/null +++ b/src/main/com/google/common/collect/CollectPreconditions.java @@ -0,0 +1,31 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; + +@GwtCompatible +final class CollectPreconditions { + static void checkEntryNotNull(Object key, Object value) { + String var2; + if (key == null) { + var2 = String.valueOf(String.valueOf(value)); + throw new NullPointerException((new StringBuilder(24 + var2.length())).append("null key in entry: null=").append(var2).toString()); + } else if (value == null) { + var2 = String.valueOf(String.valueOf(key)); + throw new NullPointerException((new StringBuilder(26 + var2.length())).append("null value in entry: ").append(var2).append("=null").toString()); + } + } + + static int checkNonnegative(int value, String name) { + if (value < 0) { + String var2 = String.valueOf(String.valueOf(name)); + throw new IllegalArgumentException((new StringBuilder(40 + var2.length())).append(var2).append(" cannot be negative but was: ").append(value).toString()); + } else { + return value; + } + } + + static void checkRemove(boolean canRemove) { + Preconditions.checkState(canRemove, "no calls to next() since the last call to remove()"); + } +} diff --git a/src/main/com/google/common/collect/Collections2.java b/src/main/com/google/common/collect/Collections2.java new file mode 100644 index 0000000..c315fd0 --- /dev/null +++ b/src/main/com/google/common/collect/Collections2.java @@ -0,0 +1,424 @@ +package com.google.common.collect; + +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.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.math.IntMath; +import com.google.common.math.LongMath; +import java.util.AbstractCollection; +import java.util.ArrayList; +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 javax.annotation.Nullable; + +@GwtCompatible +public final class Collections2 { + static final Joiner STANDARD_JOINER = Joiner.on(", ").useForNull("null"); + + private Collections2() { + } + + public static Collection filter(Collection unfiltered, Predicate predicate) { + return unfiltered instanceof Collections2.FilteredCollection ? ((Collections2.FilteredCollection)unfiltered).createCombined(predicate) : new Collections2.FilteredCollection((Collection)Preconditions.checkNotNull(unfiltered), (Predicate)Preconditions.checkNotNull(predicate)); + } + + static boolean safeContains(Collection collection, @Nullable Object object) { + Preconditions.checkNotNull(collection); + + try { + return collection.contains(object); + } catch (ClassCastException var3) { + return false; + } catch (NullPointerException var4) { + return false; + } + } + + static boolean safeRemove(Collection collection, @Nullable Object object) { + Preconditions.checkNotNull(collection); + + try { + return collection.remove(object); + } catch (ClassCastException var3) { + return false; + } catch (NullPointerException var4) { + return false; + } + } + + public static Collection transform(Collection fromCollection, Function function) { + return new Collections2.TransformedCollection(fromCollection, function); + } + + static boolean containsAllImpl(Collection self, Collection c) { + return Iterables.all(c, Predicates.in(self)); + } + + static String toStringImpl(final Collection collection) { + StringBuilder sb = newStringBuilderForCollection(collection.size()).append('['); + STANDARD_JOINER.appendTo(sb, Iterables.transform(collection, new Function() { + public Object apply(Object input) { + return input == collection ? "(this Collection)" : input; + } + })); + return sb.append(']').toString(); + } + + static StringBuilder newStringBuilderForCollection(int size) { + CollectPreconditions.checkNonnegative(size, "size"); + return new StringBuilder((int)Math.min((long)size * 8L, 1073741824L)); + } + + static Collection cast(Iterable iterable) { + return (Collection)iterable; + } + + @Beta + public static > Collection> orderedPermutations(Iterable elements) { + return orderedPermutations(elements, Ordering.natural()); + } + + @Beta + public static Collection> orderedPermutations(Iterable elements, Comparator comparator) { + return new Collections2.OrderedPermutationCollection(elements, comparator); + } + + @Beta + public static Collection> permutations(Collection elements) { + return new Collections2.PermutationCollection(ImmutableList.copyOf(elements)); + } + + private static boolean isPermutation(List first, List second) { + if (first.size() != second.size()) { + return false; + } else { + Multiset firstMultiset = HashMultiset.create(first); + Multiset secondMultiset = HashMultiset.create(second); + return firstMultiset.equals(secondMultiset); + } + } + + private static boolean isPositiveInt(long n) { + return n >= 0L && n <= 2147483647L; + } + + private static class PermutationIterator extends AbstractIterator> { + final List list; + final int[] c; + final int[] o; + int j; + + PermutationIterator(List list) { + this.list = new ArrayList(list); + int n = list.size(); + this.c = new int[n]; + this.o = new int[n]; + Arrays.fill(this.c, 0); + Arrays.fill(this.o, 1); + this.j = Integer.MAX_VALUE; + } + + protected List computeNext() { + if (this.j <= 0) { + return (List)this.endOfData(); + } else { + ImmutableList next = ImmutableList.copyOf((Collection)this.list); + this.calculateNextPermutation(); + return next; + } + } + + void calculateNextPermutation() { + this.j = this.list.size() - 1; + int s = 0; + if (this.j != -1) { + while(true) { + while(true) { + int q = this.c[this.j] + this.o[this.j]; + if (q >= 0) { + if (q != this.j + 1) { + Collections.swap(this.list, this.j - this.c[this.j] + s, this.j - q + s); + this.c[this.j] = q; + return; + } + + if (this.j == 0) { + return; + } + + ++s; + this.switchDirection(); + } else { + this.switchDirection(); + } + } + } + } + } + + void switchDirection() { + this.o[this.j] = -this.o[this.j]; + --this.j; + } + } + + private static final class PermutationCollection extends AbstractCollection> { + final ImmutableList inputList; + + PermutationCollection(ImmutableList input) { + this.inputList = input; + } + + public int size() { + return IntMath.factorial(this.inputList.size()); + } + + public boolean isEmpty() { + return false; + } + + public Iterator> iterator() { + return new Collections2.PermutationIterator(this.inputList); + } + + public boolean contains(@Nullable Object obj) { + if (obj instanceof List) { + List list = (List)obj; + return Collections2.isPermutation(this.inputList, list); + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.inputList)); + return (new StringBuilder(14 + var1.length())).append("permutations(").append(var1).append(")").toString(); + } + } + + private static final class OrderedPermutationIterator extends AbstractIterator> { + List nextPermutation; + final Comparator comparator; + + OrderedPermutationIterator(List list, Comparator comparator) { + this.nextPermutation = Lists.newArrayList((Iterable)list); + this.comparator = comparator; + } + + protected List computeNext() { + if (this.nextPermutation == null) { + return (List)this.endOfData(); + } else { + ImmutableList next = ImmutableList.copyOf((Collection)this.nextPermutation); + this.calculateNextPermutation(); + return next; + } + } + + void calculateNextPermutation() { + int j = this.findNextJ(); + if (j == -1) { + this.nextPermutation = null; + } else { + int l = this.findNextL(j); + Collections.swap(this.nextPermutation, j, l); + int n = this.nextPermutation.size(); + Collections.reverse(this.nextPermutation.subList(j + 1, n)); + } + } + + int findNextJ() { + for(int k = this.nextPermutation.size() - 2; k >= 0; --k) { + if (this.comparator.compare(this.nextPermutation.get(k), this.nextPermutation.get(k + 1)) < 0) { + return k; + } + } + + return -1; + } + + int findNextL(int j) { + E ak = this.nextPermutation.get(j); + + for(int l = this.nextPermutation.size() - 1; l > j; --l) { + if (this.comparator.compare(ak, this.nextPermutation.get(l)) < 0) { + return l; + } + } + + throw new AssertionError("this statement should be unreachable"); + } + } + + private static final class OrderedPermutationCollection extends AbstractCollection> { + final ImmutableList inputList; + final Comparator comparator; + final int size; + + OrderedPermutationCollection(Iterable input, Comparator comparator) { + this.inputList = Ordering.from(comparator).immutableSortedCopy(input); + this.comparator = comparator; + this.size = calculateSize(this.inputList, comparator); + } + + private static int calculateSize(List sortedInputList, Comparator comparator) { + long permutations = 1L; + int n = 1; + + int r; + for(r = 1; n < sortedInputList.size(); ++r) { + int comparison = comparator.compare(sortedInputList.get(n - 1), sortedInputList.get(n)); + if (comparison < 0) { + permutations *= LongMath.binomial(n, r); + r = 0; + if (!Collections2.isPositiveInt(permutations)) { + return Integer.MAX_VALUE; + } + } + + ++n; + } + + permutations *= LongMath.binomial(n, r); + if (!Collections2.isPositiveInt(permutations)) { + return Integer.MAX_VALUE; + } else { + return (int)permutations; + } + } + + public int size() { + return this.size; + } + + public boolean isEmpty() { + return false; + } + + public Iterator> iterator() { + return new Collections2.OrderedPermutationIterator(this.inputList, this.comparator); + } + + public boolean contains(@Nullable Object obj) { + if (obj instanceof List) { + List list = (List)obj; + return Collections2.isPermutation(this.inputList, list); + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.inputList)); + return (new StringBuilder(30 + var1.length())).append("orderedPermutationCollection(").append(var1).append(")").toString(); + } + } + + static class TransformedCollection extends AbstractCollection { + final Collection fromCollection; + final Function function; + + TransformedCollection(Collection fromCollection, Function function) { + this.fromCollection = (Collection)Preconditions.checkNotNull(fromCollection); + this.function = (Function)Preconditions.checkNotNull(function); + } + + public void clear() { + this.fromCollection.clear(); + } + + public boolean isEmpty() { + return this.fromCollection.isEmpty(); + } + + public Iterator iterator() { + return Iterators.transform(this.fromCollection.iterator(), this.function); + } + + public int size() { + return this.fromCollection.size(); + } + } + + static class FilteredCollection extends AbstractCollection { + final Collection unfiltered; + final Predicate predicate; + + FilteredCollection(Collection unfiltered, Predicate predicate) { + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + Collections2.FilteredCollection createCombined(Predicate newPredicate) { + return new Collections2.FilteredCollection(this.unfiltered, Predicates.and(this.predicate, newPredicate)); + } + + public boolean add(E element) { + Preconditions.checkArgument(this.predicate.apply(element)); + return this.unfiltered.add(element); + } + + public boolean addAll(Collection collection) { + Iterator i$ = collection.iterator(); + + while(i$.hasNext()) { + E element = i$.next(); + Preconditions.checkArgument(this.predicate.apply(element)); + } + + return this.unfiltered.addAll(collection); + } + + public void clear() { + Iterables.removeIf(this.unfiltered, this.predicate); + } + + public boolean contains(@Nullable Object element) { + return Collections2.safeContains(this.unfiltered, element) ? this.predicate.apply(element) : false; + } + + public boolean containsAll(Collection collection) { + return Collections2.containsAllImpl(this, collection); + } + + public boolean isEmpty() { + return !Iterables.any(this.unfiltered, this.predicate); + } + + public Iterator iterator() { + return Iterators.filter(this.unfiltered.iterator(), this.predicate); + } + + public boolean remove(Object element) { + return this.contains(element) && this.unfiltered.remove(element); + } + + public boolean removeAll(Collection collection) { + return Iterables.removeIf(this.unfiltered, Predicates.and(this.predicate, Predicates.in(collection))); + } + + public boolean retainAll(Collection collection) { + return Iterables.removeIf(this.unfiltered, Predicates.and(this.predicate, Predicates.not(Predicates.in(collection)))); + } + + public int size() { + return Iterators.size(this.iterator()); + } + + public Object[] toArray() { + return Lists.newArrayList(this.iterator()).toArray(); + } + + public T[] toArray(T[] array) { + return Lists.newArrayList(this.iterator()).toArray(array); + } + } +} diff --git a/src/main/com/google/common/collect/ComparatorOrdering.java b/src/main/com/google/common/collect/ComparatorOrdering.java new file mode 100644 index 0000000..227265f --- /dev/null +++ b/src/main/com/google/common/collect/ComparatorOrdering.java @@ -0,0 +1,42 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Comparator; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class ComparatorOrdering extends Ordering implements Serializable { + final Comparator comparator; + private static final long serialVersionUID = 0L; + + ComparatorOrdering(Comparator comparator) { + this.comparator = (Comparator)Preconditions.checkNotNull(comparator); + } + + public int compare(T a, T b) { + return this.comparator.compare(a, b); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (object instanceof ComparatorOrdering) { + ComparatorOrdering that = (ComparatorOrdering)object; + return this.comparator.equals(that.comparator); + } else { + return false; + } + } + + public int hashCode() { + return this.comparator.hashCode(); + } + + public String toString() { + return this.comparator.toString(); + } +} diff --git a/src/main/com/google/common/collect/ComparisonChain.java b/src/main/com/google/common/collect/ComparisonChain.java new file mode 100644 index 0000000..9ba8266 --- /dev/null +++ b/src/main/com/google/common/collect/ComparisonChain.java @@ -0,0 +1,130 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.primitives.Booleans; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import java.util.Comparator; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ComparisonChain { + private static final ComparisonChain ACTIVE = new ComparisonChain() { + public ComparisonChain compare(Comparable left, Comparable right) { + return this.classify(left.compareTo(right)); + } + + public ComparisonChain compare(@Nullable T left, @Nullable T right, Comparator comparator) { + return this.classify(comparator.compare(left, right)); + } + + public ComparisonChain compare(int left, int right) { + return this.classify(Ints.compare(left, right)); + } + + public ComparisonChain compare(long left, long right) { + return this.classify(Longs.compare(left, right)); + } + + public ComparisonChain compare(float left, float right) { + return this.classify(Float.compare(left, right)); + } + + public ComparisonChain compare(double left, double right) { + return this.classify(Double.compare(left, right)); + } + + public ComparisonChain compareTrueFirst(boolean left, boolean right) { + return this.classify(Booleans.compare(right, left)); + } + + public ComparisonChain compareFalseFirst(boolean left, boolean right) { + return this.classify(Booleans.compare(left, right)); + } + + ComparisonChain classify(int result) { + return result < 0 ? ComparisonChain.LESS : (result > 0 ? ComparisonChain.GREATER : ComparisonChain.ACTIVE); + } + + public int result() { + return 0; + } + }; + private static final ComparisonChain LESS = new ComparisonChain.InactiveComparisonChain(-1); + private static final ComparisonChain GREATER = new ComparisonChain.InactiveComparisonChain(1); + + private ComparisonChain() { + } + + public static ComparisonChain start() { + return ACTIVE; + } + + public abstract ComparisonChain compare(Comparable var1, Comparable var2); + + public abstract ComparisonChain compare(@Nullable T var1, @Nullable T var2, Comparator var3); + + public abstract ComparisonChain compare(int var1, int var2); + + public abstract ComparisonChain compare(long var1, long var3); + + public abstract ComparisonChain compare(float var1, float var2); + + public abstract ComparisonChain compare(double var1, double var3); + + public abstract ComparisonChain compareTrueFirst(boolean var1, boolean var2); + + public abstract ComparisonChain compareFalseFirst(boolean var1, boolean var2); + + public abstract int result(); + + // $FF: synthetic method + ComparisonChain(Object x0) { + this(); + } + + private static final class InactiveComparisonChain extends ComparisonChain { + final int result; + + InactiveComparisonChain(int result) { + super(null); + this.result = result; + } + + public ComparisonChain compare(@Nullable Comparable left, @Nullable Comparable right) { + return this; + } + + public ComparisonChain compare(@Nullable T left, @Nullable T right, @Nullable Comparator comparator) { + return this; + } + + public ComparisonChain compare(int left, int right) { + return this; + } + + public ComparisonChain compare(long left, long right) { + return this; + } + + public ComparisonChain compare(float left, float right) { + return this; + } + + public ComparisonChain compare(double left, double right) { + return this; + } + + public ComparisonChain compareTrueFirst(boolean left, boolean right) { + return this; + } + + public ComparisonChain compareFalseFirst(boolean left, boolean right) { + return this; + } + + public int result() { + return this.result; + } + } +} diff --git a/src/main/com/google/common/collect/CompoundOrdering.java b/src/main/com/google/common/collect/CompoundOrdering.java new file mode 100644 index 0000000..a0cbea9 --- /dev/null +++ b/src/main/com/google/common/collect/CompoundOrdering.java @@ -0,0 +1,54 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Comparator; + +@GwtCompatible( + serializable = true +) +final class CompoundOrdering extends Ordering implements Serializable { + final ImmutableList> comparators; + private static final long serialVersionUID = 0L; + + CompoundOrdering(Comparator primary, Comparator secondary) { + this.comparators = ImmutableList.of(primary, secondary); + } + + CompoundOrdering(Iterable> comparators) { + this.comparators = ImmutableList.copyOf(comparators); + } + + public int compare(T left, T right) { + int size = this.comparators.size(); + + for(int i = 0; i < size; ++i) { + int result = ((Comparator)this.comparators.get(i)).compare(left, right); + if (result != 0) { + return result; + } + } + + return 0; + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof CompoundOrdering) { + CompoundOrdering that = (CompoundOrdering)object; + return this.comparators.equals(that.comparators); + } else { + return false; + } + } + + public int hashCode() { + return this.comparators.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.comparators)); + return (new StringBuilder(19 + var1.length())).append("Ordering.compound(").append(var1).append(")").toString(); + } +} diff --git a/src/main/com/google/common/collect/ComputationException.java b/src/main/com/google/common/collect/ComputationException.java new file mode 100644 index 0000000..db57390 --- /dev/null +++ b/src/main/com/google/common/collect/ComputationException.java @@ -0,0 +1,13 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +public class ComputationException extends RuntimeException { + private static final long serialVersionUID = 0L; + + public ComputationException(@Nullable Throwable cause) { + super(cause); + } +} diff --git a/src/main/com/google/common/collect/ComputingConcurrentHashMap.java b/src/main/com/google/common/collect/ComputingConcurrentHashMap.java new file mode 100644 index 0000000..4dbebf0 --- /dev/null +++ b/src/main/com/google/common/collect/ComputingConcurrentHashMap.java @@ -0,0 +1,339 @@ +package com.google.common.collect; + +import com.google.common.base.Equivalence; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReferenceArray; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; + +class ComputingConcurrentHashMap extends MapMakerInternalMap { + final Function computingFunction; + private static final long serialVersionUID = 4L; + + ComputingConcurrentHashMap(MapMaker builder, Function computingFunction) { + super(builder); + this.computingFunction = (Function)Preconditions.checkNotNull(computingFunction); + } + + MapMakerInternalMap.Segment createSegment(int initialCapacity, int maxSegmentSize) { + return new ComputingConcurrentHashMap.ComputingSegment(this, initialCapacity, maxSegmentSize); + } + + ComputingConcurrentHashMap.ComputingSegment segmentFor(int hash) { + return (ComputingConcurrentHashMap.ComputingSegment)super.segmentFor(hash); + } + + V getOrCompute(K key) throws ExecutionException { + int hash = this.hash(Preconditions.checkNotNull(key)); + return this.segmentFor(hash).getOrCompute(key, hash, this.computingFunction); + } + + Object writeReplace() { + return new ComputingConcurrentHashMap.ComputingSerializationProxy(this.keyStrength, this.valueStrength, this.keyEquivalence, this.valueEquivalence, this.expireAfterWriteNanos, this.expireAfterAccessNanos, this.maximumSize, this.concurrencyLevel, this.removalListener, this, this.computingFunction); + } + + static final class ComputingSerializationProxy extends MapMakerInternalMap.AbstractSerializationProxy { + final Function computingFunction; + private static final long serialVersionUID = 4L; + + ComputingSerializationProxy(MapMakerInternalMap.Strength keyStrength, MapMakerInternalMap.Strength valueStrength, Equivalence keyEquivalence, Equivalence valueEquivalence, long expireAfterWriteNanos, long expireAfterAccessNanos, int maximumSize, int concurrencyLevel, MapMaker.RemovalListener removalListener, ConcurrentMap delegate, Function computingFunction) { + super(keyStrength, valueStrength, keyEquivalence, valueEquivalence, expireAfterWriteNanos, expireAfterAccessNanos, maximumSize, concurrencyLevel, removalListener, delegate); + this.computingFunction = computingFunction; + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + this.writeMapTo(out); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + MapMaker mapMaker = this.readMapMaker(in); + this.delegate = mapMaker.makeComputingMap(this.computingFunction); + this.readEntries(in); + } + + Object readResolve() { + return this.delegate; + } + } + + private static final class ComputingValueReference implements MapMakerInternalMap.ValueReference { + final Function computingFunction; + @GuardedBy("ComputingValueReference.this") + volatile MapMakerInternalMap.ValueReference computedReference = MapMakerInternalMap.unset(); + + public ComputingValueReference(Function computingFunction) { + this.computingFunction = computingFunction; + } + + public V get() { + return null; + } + + public MapMakerInternalMap.ReferenceEntry getEntry() { + return null; + } + + public MapMakerInternalMap.ValueReference copyFor(ReferenceQueue queue, @Nullable V value, MapMakerInternalMap.ReferenceEntry entry) { + return this; + } + + public boolean isComputingReference() { + return true; + } + + public V waitForValue() throws ExecutionException { + if (this.computedReference == MapMakerInternalMap.UNSET) { + boolean interrupted = false; + + try { + synchronized(this) { + while(this.computedReference == MapMakerInternalMap.UNSET) { + try { + this.wait(); + } catch (InterruptedException var9) { + interrupted = true; + } + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + return this.computedReference.waitForValue(); + } + + public void clear(MapMakerInternalMap.ValueReference newValue) { + this.setValueReference(newValue); + } + + V compute(K key, int hash) throws ExecutionException { + Object value; + try { + value = this.computingFunction.apply(key); + } catch (Throwable var5) { + this.setValueReference(new ComputingConcurrentHashMap.ComputationExceptionReference(var5)); + throw new ExecutionException(var5); + } + + this.setValueReference(new ComputingConcurrentHashMap.ComputedReference(value)); + return value; + } + + void setValueReference(MapMakerInternalMap.ValueReference valueReference) { + synchronized(this) { + if (this.computedReference == MapMakerInternalMap.UNSET) { + this.computedReference = valueReference; + this.notifyAll(); + } + + } + } + } + + private static final class ComputedReference implements MapMakerInternalMap.ValueReference { + final V value; + + ComputedReference(@Nullable V value) { + this.value = value; + } + + public V get() { + return this.value; + } + + public MapMakerInternalMap.ReferenceEntry getEntry() { + return null; + } + + public MapMakerInternalMap.ValueReference copyFor(ReferenceQueue queue, V value, MapMakerInternalMap.ReferenceEntry entry) { + return this; + } + + public boolean isComputingReference() { + return false; + } + + public V waitForValue() { + return this.get(); + } + + public void clear(MapMakerInternalMap.ValueReference newValue) { + } + } + + private static final class ComputationExceptionReference implements MapMakerInternalMap.ValueReference { + final Throwable t; + + ComputationExceptionReference(Throwable t) { + this.t = t; + } + + public V get() { + return null; + } + + public MapMakerInternalMap.ReferenceEntry getEntry() { + return null; + } + + public MapMakerInternalMap.ValueReference copyFor(ReferenceQueue queue, V value, MapMakerInternalMap.ReferenceEntry entry) { + return this; + } + + public boolean isComputingReference() { + return false; + } + + public V waitForValue() throws ExecutionException { + throw new ExecutionException(this.t); + } + + public void clear(MapMakerInternalMap.ValueReference newValue) { + } + } + + static final class ComputingSegment extends MapMakerInternalMap.Segment { + ComputingSegment(MapMakerInternalMap map, int initialCapacity, int maxSegmentSize) { + super(map, initialCapacity, maxSegmentSize); + } + + V getOrCompute(K key, int hash, Function computingFunction) throws ExecutionException { + while(true) { + Object var24; + try { + MapMakerInternalMap.ReferenceEntry e = this.getEntry(key, hash); + Object value; + if (e != null) { + value = this.getLiveValue(e); + if (value != null) { + this.recordRead(e); + var24 = value; + return var24; + } + } + + if (e == null || !e.getValueReference().isComputingReference()) { + boolean createNewEntry = true; + ComputingConcurrentHashMap.ComputingValueReference computingValueReference = null; + this.lock(); + + try { + this.preWriteCleanup(); + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference valueReference = e.getValueReference(); + if (valueReference.isComputingReference()) { + createNewEntry = false; + } else { + V value = e.getValueReference().get(); + if (value == null) { + this.enqueueNotification(entryKey, hash, value, MapMaker.RemovalCause.COLLECTED); + } else { + if (!this.map.expires() || !this.map.isExpired(e)) { + this.recordLockedRead(e); + Object var14 = value; + return var14; + } + + this.enqueueNotification(entryKey, hash, value, MapMaker.RemovalCause.EXPIRED); + } + + this.evictionQueue.remove(e); + this.expirationQueue.remove(e); + this.count = newCount; + } + break; + } + } + + if (createNewEntry) { + computingValueReference = new ComputingConcurrentHashMap.ComputingValueReference(computingFunction); + if (e == null) { + e = this.newEntry(key, hash, first); + e.setValueReference(computingValueReference); + table.set(index, e); + } else { + e.setValueReference(computingValueReference); + } + } + } finally { + this.unlock(); + this.postWriteCleanup(); + } + + if (createNewEntry) { + Object var25 = this.compute(key, hash, e, computingValueReference); + return var25; + } + } + + Preconditions.checkState(!Thread.holdsLock(e), "Recursive computation"); + value = e.getValueReference().waitForValue(); + if (value == null) { + continue; + } + + this.recordRead(e); + var24 = value; + } finally { + this.postReadCleanup(); + } + + return var24; + } + } + + V compute(K key, int hash, MapMakerInternalMap.ReferenceEntry e, ComputingConcurrentHashMap.ComputingValueReference computingValueReference) throws ExecutionException { + V value = null; + long start = System.nanoTime(); + long end = 0L; + + Object oldValue; + try { + synchronized(e) { + value = computingValueReference.compute(key, hash); + end = System.nanoTime(); + } + + if (value != null) { + oldValue = this.put(key, hash, value, true); + if (oldValue != null) { + this.enqueueNotification(key, hash, value, MapMaker.RemovalCause.REPLACED); + } + } + + oldValue = value; + } finally { + if (end == 0L) { + end = System.nanoTime(); + } + + if (value == null) { + this.clearValue(key, hash, computingValueReference); + } + + } + + return oldValue; + } + } +} diff --git a/src/main/com/google/common/collect/ConcurrentHashMultiset.java b/src/main/com/google/common/collect/ConcurrentHashMultiset.java new file mode 100644 index 0000000..9f01d32 --- /dev/null +++ b/src/main/com/google/common/collect/ConcurrentHashMultiset.java @@ -0,0 +1,393 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.math.IntMath; +import com.google.common.primitives.Ints; +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.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; + +public final class ConcurrentHashMultiset extends AbstractMultiset implements Serializable { + private final transient ConcurrentMap countMap; + private static final long serialVersionUID = 1L; + + public static ConcurrentHashMultiset create() { + return new ConcurrentHashMultiset(new ConcurrentHashMap()); + } + + public static ConcurrentHashMultiset create(Iterable elements) { + ConcurrentHashMultiset multiset = create(); + Iterables.addAll(multiset, elements); + return multiset; + } + + @Beta + public static ConcurrentHashMultiset create(MapMaker mapMaker) { + return new ConcurrentHashMultiset(mapMaker.makeMap()); + } + + @VisibleForTesting + ConcurrentHashMultiset(ConcurrentMap countMap) { + Preconditions.checkArgument(countMap.isEmpty()); + this.countMap = countMap; + } + + public int count(@Nullable Object element) { + AtomicInteger existingCounter = (AtomicInteger)Maps.safeGet(this.countMap, element); + return existingCounter == null ? 0 : existingCounter.get(); + } + + public int size() { + long sum = 0L; + + AtomicInteger value; + for(Iterator i$ = this.countMap.values().iterator(); i$.hasNext(); sum += (long)value.get()) { + value = (AtomicInteger)i$.next(); + } + + return Ints.saturatedCast(sum); + } + + public Object[] toArray() { + return this.snapshot().toArray(); + } + + public T[] toArray(T[] array) { + return this.snapshot().toArray(array); + } + + private List snapshot() { + List list = Lists.newArrayListWithExpectedSize(this.size()); + Iterator i$ = this.entrySet().iterator(); + + while(i$.hasNext()) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + E element = entry.getElement(); + + for(int i = entry.getCount(); i > 0; --i) { + list.add(element); + } + } + + return list; + } + + public int add(E element, int occurrences) { + Preconditions.checkNotNull(element); + if (occurrences == 0) { + return this.count(element); + } else { + Preconditions.checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); + + AtomicInteger existingCounter; + AtomicInteger newCounter; + do { + existingCounter = (AtomicInteger)Maps.safeGet(this.countMap, element); + if (existingCounter == null) { + existingCounter = (AtomicInteger)this.countMap.putIfAbsent(element, new AtomicInteger(occurrences)); + if (existingCounter == null) { + return 0; + } + } + + while(true) { + int oldValue = existingCounter.get(); + if (oldValue == 0) { + newCounter = new AtomicInteger(occurrences); + break; + } + + try { + int newValue = IntMath.checkedAdd(oldValue, occurrences); + if (existingCounter.compareAndSet(oldValue, newValue)) { + return oldValue; + } + } catch (ArithmeticException var8) { + throw new IllegalArgumentException((new StringBuilder(65)).append("Overflow adding ").append(occurrences).append(" occurrences to a count of ").append(oldValue).toString()); + } + } + } while(this.countMap.putIfAbsent(element, newCounter) != null && !this.countMap.replace(element, existingCounter, newCounter)); + + return 0; + } + } + + public int remove(@Nullable Object element, int occurrences) { + if (occurrences == 0) { + return this.count(element); + } else { + Preconditions.checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); + AtomicInteger existingCounter = (AtomicInteger)Maps.safeGet(this.countMap, element); + if (existingCounter == null) { + return 0; + } else { + int oldValue; + int newValue; + do { + oldValue = existingCounter.get(); + if (oldValue == 0) { + return 0; + } + + newValue = Math.max(0, oldValue - occurrences); + } while(!existingCounter.compareAndSet(oldValue, newValue)); + + if (newValue == 0) { + this.countMap.remove(element, existingCounter); + } + + return oldValue; + } + } + } + + public boolean removeExactly(@Nullable Object element, int occurrences) { + if (occurrences == 0) { + return true; + } else { + Preconditions.checkArgument(occurrences > 0, "Invalid occurrences: %s", occurrences); + AtomicInteger existingCounter = (AtomicInteger)Maps.safeGet(this.countMap, element); + if (existingCounter == null) { + return false; + } else { + int oldValue; + int newValue; + do { + oldValue = existingCounter.get(); + if (oldValue < occurrences) { + return false; + } + + newValue = oldValue - occurrences; + } while(!existingCounter.compareAndSet(oldValue, newValue)); + + if (newValue == 0) { + this.countMap.remove(element, existingCounter); + } + + return true; + } + } + } + + public int setCount(E element, int count) { + Preconditions.checkNotNull(element); + CollectPreconditions.checkNonnegative(count, "count"); + + AtomicInteger existingCounter; + AtomicInteger newCounter; + label40: + do { + existingCounter = (AtomicInteger)Maps.safeGet(this.countMap, element); + if (existingCounter == null) { + if (count == 0) { + return 0; + } + + existingCounter = (AtomicInteger)this.countMap.putIfAbsent(element, new AtomicInteger(count)); + if (existingCounter == null) { + return 0; + } + } + + int oldValue; + do { + oldValue = existingCounter.get(); + if (oldValue == 0) { + if (count == 0) { + return 0; + } + + newCounter = new AtomicInteger(count); + continue label40; + } + } while(!existingCounter.compareAndSet(oldValue, count)); + + if (count == 0) { + this.countMap.remove(element, existingCounter); + } + + return oldValue; + } while(this.countMap.putIfAbsent(element, newCounter) != null && !this.countMap.replace(element, existingCounter, newCounter)); + + return 0; + } + + public boolean setCount(E element, int expectedOldCount, int newCount) { + Preconditions.checkNotNull(element); + CollectPreconditions.checkNonnegative(expectedOldCount, "oldCount"); + CollectPreconditions.checkNonnegative(newCount, "newCount"); + AtomicInteger existingCounter = (AtomicInteger)Maps.safeGet(this.countMap, element); + if (existingCounter == null) { + if (expectedOldCount != 0) { + return false; + } else if (newCount == 0) { + return true; + } else { + return this.countMap.putIfAbsent(element, new AtomicInteger(newCount)) == null; + } + } else { + int oldValue = existingCounter.get(); + if (oldValue == expectedOldCount) { + if (oldValue == 0) { + if (newCount == 0) { + this.countMap.remove(element, existingCounter); + return true; + } + + AtomicInteger newCounter = new AtomicInteger(newCount); + return this.countMap.putIfAbsent(element, newCounter) == null || this.countMap.replace(element, existingCounter, newCounter); + } + + if (existingCounter.compareAndSet(oldValue, newCount)) { + if (newCount == 0) { + this.countMap.remove(element, existingCounter); + } + + return true; + } + } + + return false; + } + } + + Set createElementSet() { + final Set delegate = this.countMap.keySet(); + return new ForwardingSet() { + protected Set delegate() { + return delegate; + } + + public boolean contains(@Nullable Object object) { + return object != null && Collections2.safeContains(delegate, object); + } + + public boolean containsAll(Collection collection) { + return this.standardContainsAll(collection); + } + + public boolean remove(Object object) { + return object != null && Collections2.safeRemove(delegate, object); + } + + public boolean removeAll(Collection c) { + return this.standardRemoveAll(c); + } + }; + } + + public Set> createEntrySet() { + return new ConcurrentHashMultiset.EntrySet(); + } + + int distinctElements() { + return this.countMap.size(); + } + + public boolean isEmpty() { + return this.countMap.isEmpty(); + } + + Iterator> entryIterator() { + final Iterator> readOnlyIterator = new AbstractIterator>() { + private Iterator> mapEntries; + + { + this.mapEntries = ConcurrentHashMultiset.this.countMap.entrySet().iterator(); + } + + protected Multiset.Entry computeNext() { + java.util.Map.Entry mapEntry; + int count; + do { + if (!this.mapEntries.hasNext()) { + return (Multiset.Entry)this.endOfData(); + } + + mapEntry = (java.util.Map.Entry)this.mapEntries.next(); + count = ((AtomicInteger)mapEntry.getValue()).get(); + } while(count == 0); + + return Multisets.immutableEntry(mapEntry.getKey(), count); + } + }; + return new ForwardingIterator>() { + private Multiset.Entry last; + + protected Iterator> delegate() { + return readOnlyIterator; + } + + public Multiset.Entry next() { + this.last = (Multiset.Entry)super.next(); + return this.last; + } + + public void remove() { + CollectPreconditions.checkRemove(this.last != null); + ConcurrentHashMultiset.this.setCount(this.last.getElement(), 0); + this.last = null; + } + }; + } + + public void clear() { + this.countMap.clear(); + } + + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.countMap); + } + + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + ConcurrentMap deserializedCountMap = (ConcurrentMap)stream.readObject(); + ConcurrentHashMultiset.FieldSettersHolder.COUNT_MAP_FIELD_SETTER.set(this, deserializedCountMap); + } + + private class EntrySet extends AbstractMultiset.EntrySet { + private EntrySet() { + super(); + } + + ConcurrentHashMultiset multiset() { + return ConcurrentHashMultiset.this; + } + + public Object[] toArray() { + return this.snapshot().toArray(); + } + + public T[] toArray(T[] array) { + return this.snapshot().toArray(array); + } + + private List> snapshot() { + List> list = Lists.newArrayListWithExpectedSize(this.size()); + Iterators.addAll(list, this.iterator()); + return list; + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } + + private static class FieldSettersHolder { + static final Serialization.FieldSetter COUNT_MAP_FIELD_SETTER = Serialization.getFieldSetter(ConcurrentHashMultiset.class, "countMap"); + } +} diff --git a/src/main/com/google/common/collect/Constraint.java b/src/main/com/google/common/collect/Constraint.java new file mode 100644 index 0000000..c8acc47 --- /dev/null +++ b/src/main/com/google/common/collect/Constraint.java @@ -0,0 +1,10 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible +interface Constraint { + E checkElement(E var1); + + String toString(); +} diff --git a/src/main/com/google/common/collect/Constraints.java b/src/main/com/google/common/collect/Constraints.java new file mode 100644 index 0000000..ca068fb --- /dev/null +++ b/src/main/com/google/common/collect/Constraints.java @@ -0,0 +1,220 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedSet; + +@GwtCompatible +final class Constraints { + private Constraints() { + } + + public static Collection constrainedCollection(Collection collection, Constraint constraint) { + return new Constraints.ConstrainedCollection(collection, constraint); + } + + public static Set constrainedSet(Set set, Constraint constraint) { + return new Constraints.ConstrainedSet(set, constraint); + } + + public static SortedSet constrainedSortedSet(SortedSet sortedSet, Constraint constraint) { + return new Constraints.ConstrainedSortedSet(sortedSet, constraint); + } + + public static List constrainedList(List list, Constraint constraint) { + return (List)(list instanceof RandomAccess ? new Constraints.ConstrainedRandomAccessList(list, constraint) : new Constraints.ConstrainedList(list, constraint)); + } + + private static ListIterator constrainedListIterator(ListIterator listIterator, Constraint constraint) { + return new Constraints.ConstrainedListIterator(listIterator, constraint); + } + + static Collection constrainedTypePreservingCollection(Collection collection, Constraint constraint) { + if (collection instanceof SortedSet) { + return constrainedSortedSet((SortedSet)collection, constraint); + } else if (collection instanceof Set) { + return constrainedSet((Set)collection, constraint); + } else { + return (Collection)(collection instanceof List ? constrainedList((List)collection, constraint) : constrainedCollection(collection, constraint)); + } + } + + private static Collection checkElements(Collection elements, Constraint constraint) { + Collection copy = Lists.newArrayList((Iterable)elements); + Iterator i$ = copy.iterator(); + + while(i$.hasNext()) { + E element = i$.next(); + constraint.checkElement(element); + } + + return copy; + } + + static class ConstrainedListIterator extends ForwardingListIterator { + private final ListIterator delegate; + private final Constraint constraint; + + public ConstrainedListIterator(ListIterator delegate, Constraint constraint) { + this.delegate = delegate; + this.constraint = constraint; + } + + protected ListIterator delegate() { + return this.delegate; + } + + public void add(E element) { + this.constraint.checkElement(element); + this.delegate.add(element); + } + + public void set(E element) { + this.constraint.checkElement(element); + this.delegate.set(element); + } + } + + static class ConstrainedRandomAccessList extends Constraints.ConstrainedList implements RandomAccess { + ConstrainedRandomAccessList(List delegate, Constraint constraint) { + super(delegate, constraint); + } + } + + @GwtCompatible + private static class ConstrainedList extends ForwardingList { + final List delegate; + final Constraint constraint; + + ConstrainedList(List delegate, Constraint constraint) { + this.delegate = (List)Preconditions.checkNotNull(delegate); + this.constraint = (Constraint)Preconditions.checkNotNull(constraint); + } + + protected List delegate() { + return this.delegate; + } + + public boolean add(E element) { + this.constraint.checkElement(element); + return this.delegate.add(element); + } + + public void add(int index, E element) { + this.constraint.checkElement(element); + this.delegate.add(index, element); + } + + public boolean addAll(Collection elements) { + return this.delegate.addAll(Constraints.checkElements(elements, this.constraint)); + } + + public boolean addAll(int index, Collection elements) { + return this.delegate.addAll(index, Constraints.checkElements(elements, this.constraint)); + } + + public ListIterator listIterator() { + return Constraints.constrainedListIterator(this.delegate.listIterator(), this.constraint); + } + + public ListIterator listIterator(int index) { + return Constraints.constrainedListIterator(this.delegate.listIterator(index), this.constraint); + } + + public E set(int index, E element) { + this.constraint.checkElement(element); + return this.delegate.set(index, element); + } + + public List subList(int fromIndex, int toIndex) { + return Constraints.constrainedList(this.delegate.subList(fromIndex, toIndex), this.constraint); + } + } + + private static class ConstrainedSortedSet extends ForwardingSortedSet { + final SortedSet delegate; + final Constraint constraint; + + ConstrainedSortedSet(SortedSet delegate, Constraint constraint) { + this.delegate = (SortedSet)Preconditions.checkNotNull(delegate); + this.constraint = (Constraint)Preconditions.checkNotNull(constraint); + } + + protected SortedSet delegate() { + return this.delegate; + } + + public SortedSet headSet(E toElement) { + return Constraints.constrainedSortedSet(this.delegate.headSet(toElement), this.constraint); + } + + public SortedSet subSet(E fromElement, E toElement) { + return Constraints.constrainedSortedSet(this.delegate.subSet(fromElement, toElement), this.constraint); + } + + public SortedSet tailSet(E fromElement) { + return Constraints.constrainedSortedSet(this.delegate.tailSet(fromElement), this.constraint); + } + + public boolean add(E element) { + this.constraint.checkElement(element); + return this.delegate.add(element); + } + + public boolean addAll(Collection elements) { + return this.delegate.addAll(Constraints.checkElements(elements, this.constraint)); + } + } + + static class ConstrainedSet extends ForwardingSet { + private final Set delegate; + private final Constraint constraint; + + public ConstrainedSet(Set delegate, Constraint constraint) { + this.delegate = (Set)Preconditions.checkNotNull(delegate); + this.constraint = (Constraint)Preconditions.checkNotNull(constraint); + } + + protected Set delegate() { + return this.delegate; + } + + public boolean add(E element) { + this.constraint.checkElement(element); + return this.delegate.add(element); + } + + public boolean addAll(Collection elements) { + return this.delegate.addAll(Constraints.checkElements(elements, this.constraint)); + } + } + + static class ConstrainedCollection extends ForwardingCollection { + private final Collection delegate; + private final Constraint constraint; + + public ConstrainedCollection(Collection delegate, Constraint constraint) { + this.delegate = (Collection)Preconditions.checkNotNull(delegate); + this.constraint = (Constraint)Preconditions.checkNotNull(constraint); + } + + protected Collection delegate() { + return this.delegate; + } + + public boolean add(E element) { + this.constraint.checkElement(element); + return this.delegate.add(element); + } + + public boolean addAll(Collection elements) { + return this.delegate.addAll(Constraints.checkElements(elements, this.constraint)); + } + } +} diff --git a/src/main/com/google/common/collect/ContiguousSet.java b/src/main/com/google/common/collect/ContiguousSet.java new file mode 100644 index 0000000..31a5c8f --- /dev/null +++ b/src/main/com/google/common/collect/ContiguousSet.java @@ -0,0 +1,96 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import java.util.NoSuchElementException; + +@Beta +@GwtCompatible( + emulated = true +) +public abstract class ContiguousSet extends ImmutableSortedSet { + final DiscreteDomain domain; + + public static ContiguousSet create(Range range, DiscreteDomain domain) { + Preconditions.checkNotNull(range); + Preconditions.checkNotNull(domain); + Range 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 var4) { + throw new IllegalArgumentException(var4); + } + + boolean empty = effectiveRange.isEmpty() || Range.compareOrThrow(range.lowerBound.leastValueAbove(domain), range.upperBound.greatestValueBelow(domain)) > 0; + return (ContiguousSet)(empty ? new EmptyContiguousSet(domain) : new RegularContiguousSet(effectiveRange, domain)); + } + + ContiguousSet(DiscreteDomain domain) { + super(Ordering.natural()); + this.domain = domain; + } + + public ContiguousSet headSet(C toElement) { + return this.headSetImpl((Comparable)Preconditions.checkNotNull(toElement), false); + } + + @GwtIncompatible("NavigableSet") + public ContiguousSet headSet(C toElement, boolean inclusive) { + return this.headSetImpl((Comparable)Preconditions.checkNotNull(toElement), inclusive); + } + + public ContiguousSet subSet(C fromElement, C toElement) { + Preconditions.checkNotNull(fromElement); + Preconditions.checkNotNull(toElement); + Preconditions.checkArgument(this.comparator().compare(fromElement, toElement) <= 0); + return this.subSetImpl(fromElement, true, toElement, false); + } + + @GwtIncompatible("NavigableSet") + public ContiguousSet subSet(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + Preconditions.checkNotNull(fromElement); + Preconditions.checkNotNull(toElement); + Preconditions.checkArgument(this.comparator().compare(fromElement, toElement) <= 0); + return this.subSetImpl(fromElement, fromInclusive, toElement, toInclusive); + } + + public ContiguousSet tailSet(C fromElement) { + return this.tailSetImpl((Comparable)Preconditions.checkNotNull(fromElement), true); + } + + @GwtIncompatible("NavigableSet") + public ContiguousSet tailSet(C fromElement, boolean inclusive) { + return this.tailSetImpl((Comparable)Preconditions.checkNotNull(fromElement), inclusive); + } + + abstract ContiguousSet headSetImpl(C var1, boolean var2); + + abstract ContiguousSet subSetImpl(C var1, boolean var2, C var3, boolean var4); + + abstract ContiguousSet tailSetImpl(C var1, boolean var2); + + public abstract ContiguousSet intersection(ContiguousSet var1); + + public abstract Range range(); + + public abstract Range range(BoundType var1, BoundType var2); + + public String toString() { + return this.range().toString(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet.Builder builder() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/Count.java b/src/main/com/google/common/collect/Count.java new file mode 100644 index 0000000..68c7d36 --- /dev/null +++ b/src/main/com/google/common/collect/Count.java @@ -0,0 +1,50 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import javax.annotation.Nullable; + +@GwtCompatible +final class Count implements Serializable { + private int value; + + Count(int value) { + this.value = value; + } + + public int get() { + return this.value; + } + + public int getAndAdd(int delta) { + int result = this.value; + this.value = result + delta; + return result; + } + + public int addAndGet(int delta) { + return this.value += delta; + } + + public void set(int newValue) { + this.value = newValue; + } + + public int getAndSet(int newValue) { + int result = this.value; + this.value = newValue; + return result; + } + + public int hashCode() { + return this.value; + } + + public boolean equals(@Nullable Object obj) { + return obj instanceof Count && ((Count)obj).value == this.value; + } + + public String toString() { + return Integer.toString(this.value); + } +} diff --git a/src/main/com/google/common/collect/Cut.java b/src/main/com/google/common/collect/Cut.java new file mode 100644 index 0000000..b5edc75 --- /dev/null +++ b/src/main/com/google/common/collect/Cut.java @@ -0,0 +1,358 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Booleans; +import java.io.Serializable; +import java.util.NoSuchElementException; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class Cut implements Comparable>, Serializable { + final C endpoint; + private static final long serialVersionUID = 0L; + + Cut(@Nullable C endpoint) { + this.endpoint = endpoint; + } + + abstract boolean isLessThan(C var1); + + abstract BoundType typeAsLowerBound(); + + abstract BoundType typeAsUpperBound(); + + abstract Cut withLowerBoundType(BoundType var1, DiscreteDomain var2); + + abstract Cut withUpperBoundType(BoundType var1, DiscreteDomain var2); + + abstract void describeAsLowerBound(StringBuilder var1); + + abstract void describeAsUpperBound(StringBuilder var1); + + abstract C leastValueAbove(DiscreteDomain var1); + + abstract C greatestValueBelow(DiscreteDomain var1); + + Cut canonical(DiscreteDomain domain) { + return this; + } + + public int compareTo(Cut that) { + if (that == belowAll()) { + return 1; + } else if (that == aboveAll()) { + return -1; + } else { + int result = Range.compareOrThrow(this.endpoint, that.endpoint); + return result != 0 ? result : Booleans.compare(this instanceof Cut.AboveValue, that instanceof Cut.AboveValue); + } + } + + C endpoint() { + return this.endpoint; + } + + public boolean equals(Object obj) { + if (obj instanceof Cut) { + Cut that = (Cut)obj; + + try { + int compareResult = this.compareTo(that); + return compareResult == 0; + } catch (ClassCastException var4) { + } + } + + return false; + } + + static Cut belowAll() { + return Cut.BelowAll.INSTANCE; + } + + static Cut aboveAll() { + return Cut.AboveAll.INSTANCE; + } + + static Cut belowValue(C endpoint) { + return new Cut.BelowValue(endpoint); + } + + static Cut aboveValue(C endpoint) { + return new Cut.AboveValue(endpoint); + } + + private static final class AboveValue extends Cut { + private static final long serialVersionUID = 0L; + + AboveValue(C endpoint) { + super((Comparable)Preconditions.checkNotNull(endpoint)); + } + + boolean isLessThan(C value) { + return Range.compareOrThrow(this.endpoint, value) < 0; + } + + BoundType typeAsLowerBound() { + return BoundType.OPEN; + } + + BoundType typeAsUpperBound() { + return BoundType.CLOSED; + } + + Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { + switch(boundType) { + case CLOSED: + C next = domain.next(this.endpoint); + return next == null ? Cut.belowAll() : belowValue(next); + case OPEN: + return this; + default: + throw new AssertionError(); + } + } + + Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { + switch(boundType) { + case CLOSED: + return this; + case OPEN: + C next = domain.next(this.endpoint); + return next == null ? Cut.aboveAll() : belowValue(next); + default: + throw new AssertionError(); + } + } + + void describeAsLowerBound(StringBuilder sb) { + sb.append('(').append(this.endpoint); + } + + void describeAsUpperBound(StringBuilder sb) { + sb.append(this.endpoint).append(']'); + } + + C leastValueAbove(DiscreteDomain domain) { + return domain.next(this.endpoint); + } + + C greatestValueBelow(DiscreteDomain domain) { + return this.endpoint; + } + + Cut canonical(DiscreteDomain domain) { + C next = this.leastValueAbove(domain); + return next != null ? belowValue(next) : Cut.aboveAll(); + } + + public int hashCode() { + return ~this.endpoint.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.endpoint)); + return (new StringBuilder(2 + var1.length())).append("/").append(var1).append("\\").toString(); + } + } + + private static final class BelowValue extends Cut { + private static final long serialVersionUID = 0L; + + BelowValue(C endpoint) { + super((Comparable)Preconditions.checkNotNull(endpoint)); + } + + boolean isLessThan(C value) { + return Range.compareOrThrow(this.endpoint, value) <= 0; + } + + BoundType typeAsLowerBound() { + return BoundType.CLOSED; + } + + BoundType typeAsUpperBound() { + return BoundType.OPEN; + } + + Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain) { + switch(boundType) { + case CLOSED: + return this; + case OPEN: + C previous = domain.previous(this.endpoint); + return (Cut)(previous == null ? Cut.belowAll() : new Cut.AboveValue(previous)); + default: + throw new AssertionError(); + } + } + + Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { + switch(boundType) { + case CLOSED: + C previous = domain.previous(this.endpoint); + return (Cut)(previous == null ? Cut.aboveAll() : new Cut.AboveValue(previous)); + case OPEN: + return this; + default: + throw new AssertionError(); + } + } + + void describeAsLowerBound(StringBuilder sb) { + sb.append('[').append(this.endpoint); + } + + void describeAsUpperBound(StringBuilder sb) { + sb.append(this.endpoint).append(')'); + } + + C leastValueAbove(DiscreteDomain domain) { + return this.endpoint; + } + + C greatestValueBelow(DiscreteDomain domain) { + return domain.previous(this.endpoint); + } + + public int hashCode() { + return this.endpoint.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.endpoint)); + return (new StringBuilder(2 + var1.length())).append("\\").append(var1).append("/").toString(); + } + } + + private static final class AboveAll extends Cut> { + private static final Cut.AboveAll INSTANCE = new Cut.AboveAll(); + private static final long serialVersionUID = 0L; + + private AboveAll() { + super((Comparable)null); + } + + Comparable endpoint() { + throw new IllegalStateException("range unbounded on this side"); + } + + boolean isLessThan(Comparable value) { + return false; + } + + BoundType typeAsLowerBound() { + throw new AssertionError("this statement should be unreachable"); + } + + BoundType typeAsUpperBound() { + throw new IllegalStateException(); + } + + Cut> withLowerBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new AssertionError("this statement should be unreachable"); + } + + Cut> withUpperBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new IllegalStateException(); + } + + void describeAsLowerBound(StringBuilder sb) { + throw new AssertionError(); + } + + void describeAsUpperBound(StringBuilder sb) { + sb.append("+∞)"); + } + + Comparable leastValueAbove(DiscreteDomain> domain) { + throw new AssertionError(); + } + + Comparable greatestValueBelow(DiscreteDomain> domain) { + return domain.maxValue(); + } + + public int compareTo(Cut> o) { + return o == this ? 0 : 1; + } + + public String toString() { + return "+∞"; + } + + private Object readResolve() { + return INSTANCE; + } + } + + private static final class BelowAll extends Cut> { + private static final Cut.BelowAll INSTANCE = new Cut.BelowAll(); + private static final long serialVersionUID = 0L; + + private BelowAll() { + super((Comparable)null); + } + + Comparable endpoint() { + throw new IllegalStateException("range unbounded on this side"); + } + + boolean isLessThan(Comparable value) { + return true; + } + + BoundType typeAsLowerBound() { + throw new IllegalStateException(); + } + + BoundType typeAsUpperBound() { + throw new AssertionError("this statement should be unreachable"); + } + + Cut> withLowerBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new IllegalStateException(); + } + + Cut> withUpperBoundType(BoundType boundType, DiscreteDomain> domain) { + throw new AssertionError("this statement should be unreachable"); + } + + void describeAsLowerBound(StringBuilder sb) { + sb.append("(-∞"); + } + + void describeAsUpperBound(StringBuilder sb) { + throw new AssertionError(); + } + + Comparable leastValueAbove(DiscreteDomain> domain) { + return domain.minValue(); + } + + Comparable greatestValueBelow(DiscreteDomain> domain) { + throw new AssertionError(); + } + + Cut> canonical(DiscreteDomain> domain) { + try { + return Cut.belowValue(domain.minValue()); + } catch (NoSuchElementException var3) { + return this; + } + } + + public int compareTo(Cut> o) { + return o == this ? 0 : -1; + } + + public String toString() { + return "-∞"; + } + + private Object readResolve() { + return INSTANCE; + } + } +} diff --git a/src/main/com/google/common/collect/DenseImmutableTable.java b/src/main/com/google/common/collect/DenseImmutableTable.java new file mode 100644 index 0000000..3fc6a2d --- /dev/null +++ b/src/main/com/google/common/collect/DenseImmutableTable.java @@ -0,0 +1,249 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@GwtCompatible +@Immutable +final class DenseImmutableTable extends RegularImmutableTable { + private final ImmutableMap rowKeyToIndex; + private final ImmutableMap columnKeyToIndex; + private final ImmutableMap> rowMap; + private final ImmutableMap> columnMap; + private final int[] rowCounts; + private final int[] columnCounts; + private final V[][] values; + private final int[] iterationOrderRow; + private final int[] iterationOrderColumn; + + private static ImmutableMap makeIndex(ImmutableSet set) { + ImmutableMap.Builder indexBuilder = ImmutableMap.builder(); + int i = 0; + + for(Iterator i$ = set.iterator(); i$.hasNext(); ++i) { + E key = i$.next(); + indexBuilder.put(key, i); + } + + return indexBuilder.build(); + } + + DenseImmutableTable(ImmutableList> cellList, ImmutableSet rowSpace, ImmutableSet columnSpace) { + V[][] array = (Object[][])(new Object[rowSpace.size()][columnSpace.size()]); + this.values = array; + this.rowKeyToIndex = makeIndex(rowSpace); + this.columnKeyToIndex = makeIndex(columnSpace); + this.rowCounts = new int[this.rowKeyToIndex.size()]; + this.columnCounts = new int[this.columnKeyToIndex.size()]; + int[] iterationOrderRow = new int[cellList.size()]; + int[] iterationOrderColumn = new int[cellList.size()]; + + for(int i = 0; i < cellList.size(); ++i) { + Table.Cell cell = (Table.Cell)cellList.get(i); + R rowKey = cell.getRowKey(); + C columnKey = cell.getColumnKey(); + int rowIndex = (Integer)this.rowKeyToIndex.get(rowKey); + int columnIndex = (Integer)this.columnKeyToIndex.get(columnKey); + V existingValue = this.values[rowIndex][columnIndex]; + Preconditions.checkArgument(existingValue == null, "duplicate key: (%s, %s)", rowKey, columnKey); + this.values[rowIndex][columnIndex] = cell.getValue(); + int var10002 = this.rowCounts[rowIndex]++; + var10002 = this.columnCounts[columnIndex]++; + iterationOrderRow[i] = rowIndex; + iterationOrderColumn[i] = columnIndex; + } + + this.iterationOrderRow = iterationOrderRow; + this.iterationOrderColumn = iterationOrderColumn; + this.rowMap = new DenseImmutableTable.RowMap(); + this.columnMap = new DenseImmutableTable.ColumnMap(); + } + + public ImmutableMap> columnMap() { + return this.columnMap; + } + + public ImmutableMap> rowMap() { + return this.rowMap; + } + + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + Integer rowIndex = (Integer)this.rowKeyToIndex.get(rowKey); + Integer columnIndex = (Integer)this.columnKeyToIndex.get(columnKey); + return rowIndex != null && columnIndex != null ? this.values[rowIndex][columnIndex] : null; + } + + public int size() { + return this.iterationOrderRow.length; + } + + Table.Cell getCell(int index) { + int rowIndex = this.iterationOrderRow[index]; + int columnIndex = this.iterationOrderColumn[index]; + R rowKey = this.rowKeySet().asList().get(rowIndex); + C columnKey = this.columnKeySet().asList().get(columnIndex); + V value = this.values[rowIndex][columnIndex]; + return cellOf(rowKey, columnKey, value); + } + + V getValue(int index) { + return this.values[this.iterationOrderRow[index]][this.iterationOrderColumn[index]]; + } + + private final class ColumnMap extends DenseImmutableTable.ImmutableArrayMap> { + private ColumnMap() { + super(DenseImmutableTable.this.columnCounts.length); + } + + ImmutableMap keyToIndex() { + return DenseImmutableTable.this.columnKeyToIndex; + } + + Map getValue(int keyIndex) { + return DenseImmutableTable.this.new Column(keyIndex); + } + + boolean isPartialView() { + return false; + } + + // $FF: synthetic method + ColumnMap(Object x1) { + this(); + } + } + + private final class RowMap extends DenseImmutableTable.ImmutableArrayMap> { + private RowMap() { + super(DenseImmutableTable.this.rowCounts.length); + } + + ImmutableMap keyToIndex() { + return DenseImmutableTable.this.rowKeyToIndex; + } + + Map getValue(int keyIndex) { + return DenseImmutableTable.this.new Row(keyIndex); + } + + boolean isPartialView() { + return false; + } + + // $FF: synthetic method + RowMap(Object x1) { + this(); + } + } + + private final class Column extends DenseImmutableTable.ImmutableArrayMap { + private final int columnIndex; + + Column(int columnIndex) { + super(DenseImmutableTable.this.columnCounts[columnIndex]); + this.columnIndex = columnIndex; + } + + ImmutableMap keyToIndex() { + return DenseImmutableTable.this.rowKeyToIndex; + } + + V getValue(int keyIndex) { + return DenseImmutableTable.this.values[keyIndex][this.columnIndex]; + } + + boolean isPartialView() { + return true; + } + } + + private final class Row extends DenseImmutableTable.ImmutableArrayMap { + private final int rowIndex; + + Row(int rowIndex) { + super(DenseImmutableTable.this.rowCounts[rowIndex]); + this.rowIndex = rowIndex; + } + + ImmutableMap keyToIndex() { + return DenseImmutableTable.this.columnKeyToIndex; + } + + V getValue(int keyIndex) { + return DenseImmutableTable.this.values[this.rowIndex][keyIndex]; + } + + boolean isPartialView() { + return true; + } + } + + private abstract static class ImmutableArrayMap extends ImmutableMap { + private final int size; + + ImmutableArrayMap(int size) { + this.size = size; + } + + abstract ImmutableMap keyToIndex(); + + private boolean isFull() { + return this.size == this.keyToIndex().size(); + } + + K getKey(int index) { + return this.keyToIndex().keySet().asList().get(index); + } + + @Nullable + abstract V getValue(int var1); + + ImmutableSet createKeySet() { + return this.isFull() ? this.keyToIndex().keySet() : super.createKeySet(); + } + + public int size() { + return this.size; + } + + public V get(@Nullable Object key) { + Integer keyIndex = (Integer)this.keyToIndex().get(key); + return keyIndex == null ? null : this.getValue(keyIndex); + } + + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet() { + ImmutableMap map() { + return ImmutableArrayMap.this; + } + + public UnmodifiableIterator> iterator() { + return new AbstractIterator>() { + private int index = -1; + private final int maxIndex = ImmutableArrayMap.this.keyToIndex().size(); + + protected Entry computeNext() { + ++this.index; + + while(this.index < this.maxIndex) { + V value = ImmutableArrayMap.this.getValue(this.index); + if (value != null) { + return Maps.immutableEntry(ImmutableArrayMap.this.getKey(this.index), value); + } + + ++this.index; + } + + return (Entry)this.endOfData(); + } + }; + } + }; + } + } +} diff --git a/src/main/com/google/common/collect/DescendingImmutableSortedMultiset.java b/src/main/com/google/common/collect/DescendingImmutableSortedMultiset.java new file mode 100644 index 0000000..8076600 --- /dev/null +++ b/src/main/com/google/common/collect/DescendingImmutableSortedMultiset.java @@ -0,0 +1,51 @@ +package com.google.common.collect; + +import javax.annotation.Nullable; + +final class DescendingImmutableSortedMultiset extends ImmutableSortedMultiset { + private final transient ImmutableSortedMultiset forward; + + DescendingImmutableSortedMultiset(ImmutableSortedMultiset forward) { + this.forward = forward; + } + + public int count(@Nullable Object element) { + return this.forward.count(element); + } + + public Multiset.Entry firstEntry() { + return this.forward.lastEntry(); + } + + public Multiset.Entry lastEntry() { + return this.forward.firstEntry(); + } + + public int size() { + return this.forward.size(); + } + + public ImmutableSortedSet elementSet() { + return this.forward.elementSet().descendingSet(); + } + + Multiset.Entry getEntry(int index) { + return (Multiset.Entry)this.forward.entrySet().asList().reverse().get(index); + } + + public ImmutableSortedMultiset descendingMultiset() { + return this.forward; + } + + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + return this.forward.tailMultiset(upperBound, boundType).descendingMultiset(); + } + + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return this.forward.headMultiset(lowerBound, boundType).descendingMultiset(); + } + + boolean isPartialView() { + return this.forward.isPartialView(); + } +} diff --git a/src/main/com/google/common/collect/DescendingImmutableSortedSet.java b/src/main/com/google/common/collect/DescendingImmutableSortedSet.java new file mode 100644 index 0000000..4bd7e44 --- /dev/null +++ b/src/main/com/google/common/collect/DescendingImmutableSortedSet.java @@ -0,0 +1,73 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtIncompatible; +import javax.annotation.Nullable; + +class DescendingImmutableSortedSet extends ImmutableSortedSet { + private final ImmutableSortedSet forward; + + DescendingImmutableSortedSet(ImmutableSortedSet forward) { + super(Ordering.from(forward.comparator()).reverse()); + this.forward = forward; + } + + public int size() { + return this.forward.size(); + } + + public UnmodifiableIterator iterator() { + return this.forward.descendingIterator(); + } + + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return this.forward.tailSet(toElement, inclusive).descendingSet(); + } + + ImmutableSortedSet subSetImpl(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return this.forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); + } + + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return this.forward.headSet(fromElement, inclusive).descendingSet(); + } + + @GwtIncompatible("NavigableSet") + public ImmutableSortedSet descendingSet() { + return this.forward; + } + + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return this.forward.iterator(); + } + + @GwtIncompatible("NavigableSet") + ImmutableSortedSet createDescendingSet() { + throw new AssertionError("should never be called"); + } + + public E lower(E element) { + return this.forward.higher(element); + } + + public E floor(E element) { + return this.forward.ceiling(element); + } + + public E ceiling(E element) { + return this.forward.floor(element); + } + + public E higher(E element) { + return this.forward.lower(element); + } + + int indexOf(@Nullable Object target) { + int index = this.forward.indexOf(target); + return index == -1 ? index : this.size() - 1 - index; + } + + boolean isPartialView() { + return this.forward.isPartialView(); + } +} diff --git a/src/main/com/google/common/collect/DescendingMultiset.java b/src/main/com/google/common/collect/DescendingMultiset.java new file mode 100644 index 0000000..2a96108 --- /dev/null +++ b/src/main/com/google/common/collect/DescendingMultiset.java @@ -0,0 +1,103 @@ +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; + +@GwtCompatible( + emulated = true +) +abstract class DescendingMultiset extends ForwardingMultiset implements SortedMultiset { + private transient Comparator comparator; + private transient NavigableSet elementSet; + private transient Set> entrySet; + + abstract SortedMultiset forwardMultiset(); + + public Comparator comparator() { + Comparator result = this.comparator; + return result == null ? (this.comparator = Ordering.from(this.forwardMultiset().comparator()).reverse()) : result; + } + + public NavigableSet elementSet() { + NavigableSet result = this.elementSet; + return result == null ? (this.elementSet = new SortedMultisets.NavigableElementSet(this)) : result; + } + + public Multiset.Entry pollFirstEntry() { + return this.forwardMultiset().pollLastEntry(); + } + + public Multiset.Entry pollLastEntry() { + return this.forwardMultiset().pollFirstEntry(); + } + + public SortedMultiset headMultiset(E toElement, BoundType boundType) { + return this.forwardMultiset().tailMultiset(toElement, boundType).descendingMultiset(); + } + + public SortedMultiset subMultiset(E fromElement, BoundType fromBoundType, E toElement, BoundType toBoundType) { + return this.forwardMultiset().subMultiset(toElement, toBoundType, fromElement, fromBoundType).descendingMultiset(); + } + + public SortedMultiset tailMultiset(E fromElement, BoundType boundType) { + return this.forwardMultiset().headMultiset(fromElement, boundType).descendingMultiset(); + } + + protected Multiset delegate() { + return this.forwardMultiset(); + } + + public SortedMultiset descendingMultiset() { + return this.forwardMultiset(); + } + + public Multiset.Entry firstEntry() { + return this.forwardMultiset().lastEntry(); + } + + public Multiset.Entry lastEntry() { + return this.forwardMultiset().firstEntry(); + } + + abstract Iterator> entryIterator(); + + public Set> entrySet() { + Set> result = this.entrySet; + return result == null ? (this.entrySet = this.createEntrySet()) : result; + } + + Set> createEntrySet() { + return new Multisets.EntrySet() { + Multiset multiset() { + return DescendingMultiset.this; + } + + public Iterator> iterator() { + return DescendingMultiset.this.entryIterator(); + } + + public int size() { + return DescendingMultiset.this.forwardMultiset().entrySet().size(); + } + }; + } + + public Iterator iterator() { + return Multisets.iteratorImpl(this); + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + + public String toString() { + return this.entrySet().toString(); + } +} diff --git a/src/main/com/google/common/collect/DiscreteDomain.java b/src/main/com/google/common/collect/DiscreteDomain.java new file mode 100644 index 0000000..0488b9d --- /dev/null +++ b/src/main/com/google/common/collect/DiscreteDomain.java @@ -0,0 +1,142 @@ +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; + +@GwtCompatible +@Beta +public abstract class DiscreteDomain { + public static DiscreteDomain integers() { + return DiscreteDomain.IntegerDomain.INSTANCE; + } + + public static DiscreteDomain longs() { + return DiscreteDomain.LongDomain.INSTANCE; + } + + public static DiscreteDomain bigIntegers() { + return DiscreteDomain.BigIntegerDomain.INSTANCE; + } + + protected DiscreteDomain() { + } + + public abstract C next(C var1); + + public abstract C previous(C var1); + + public abstract long distance(C var1, C var2); + + public C minValue() { + throw new NoSuchElementException(); + } + + public C maxValue() { + throw new NoSuchElementException(); + } + + private static final class BigIntegerDomain extends DiscreteDomain implements Serializable { + private static final DiscreteDomain.BigIntegerDomain INSTANCE = new DiscreteDomain.BigIntegerDomain(); + private static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE); + private static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE); + private static final long serialVersionUID = 0L; + + public BigInteger next(BigInteger value) { + return value.add(BigInteger.ONE); + } + + public BigInteger previous(BigInteger value) { + return value.subtract(BigInteger.ONE); + } + + public long distance(BigInteger start, BigInteger end) { + return end.subtract(start).max(MIN_LONG).min(MAX_LONG).longValue(); + } + + private Object readResolve() { + return INSTANCE; + } + + public String toString() { + return "DiscreteDomain.bigIntegers()"; + } + } + + private static final class LongDomain extends DiscreteDomain implements Serializable { + private static final DiscreteDomain.LongDomain INSTANCE = new DiscreteDomain.LongDomain(); + private static final long serialVersionUID = 0L; + + public Long next(Long value) { + long l = value; + return l == Long.MAX_VALUE ? null : l + 1L; + } + + public Long previous(Long value) { + long l = value; + return l == Long.MIN_VALUE ? null : l - 1L; + } + + public long distance(Long start, Long end) { + long result = end - start; + if (end > start && result < 0L) { + return Long.MAX_VALUE; + } else { + return end < start && result > 0L ? Long.MIN_VALUE : result; + } + } + + public Long minValue() { + return Long.MIN_VALUE; + } + + public Long maxValue() { + return Long.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + public String toString() { + return "DiscreteDomain.longs()"; + } + } + + private static final class IntegerDomain extends DiscreteDomain implements Serializable { + private static final DiscreteDomain.IntegerDomain INSTANCE = new DiscreteDomain.IntegerDomain(); + private static final long serialVersionUID = 0L; + + public Integer next(Integer value) { + int i = value; + return i == Integer.MAX_VALUE ? null : i + 1; + } + + public Integer previous(Integer value) { + int i = value; + return i == Integer.MIN_VALUE ? null : i - 1; + } + + public long distance(Integer start, Integer end) { + return (long)end - (long)start; + } + + public Integer minValue() { + return Integer.MIN_VALUE; + } + + public Integer maxValue() { + return Integer.MAX_VALUE; + } + + private Object readResolve() { + return INSTANCE; + } + + public String toString() { + return "DiscreteDomain.integers()"; + } + } +} diff --git a/src/main/com/google/common/collect/EmptyContiguousSet.java b/src/main/com/google/common/collect/EmptyContiguousSet.java new file mode 100644 index 0000000..a2fe641 --- /dev/null +++ b/src/main/com/google/common/collect/EmptyContiguousSet.java @@ -0,0 +1,125 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.io.Serializable; +import java.util.NoSuchElementException; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class EmptyContiguousSet extends ContiguousSet { + EmptyContiguousSet(DiscreteDomain domain) { + super(domain); + } + + public C first() { + throw new NoSuchElementException(); + } + + public C last() { + throw new NoSuchElementException(); + } + + public int size() { + return 0; + } + + public ContiguousSet intersection(ContiguousSet other) { + return this; + } + + public Range range() { + throw new NoSuchElementException(); + } + + public Range range(BoundType lowerBoundType, BoundType upperBoundType) { + throw new NoSuchElementException(); + } + + ContiguousSet headSetImpl(C toElement, boolean inclusive) { + return this; + } + + ContiguousSet subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + return this; + } + + ContiguousSet tailSetImpl(C fromElement, boolean fromInclusive) { + return this; + } + + @GwtIncompatible("not used by GWT emulation") + int indexOf(Object target) { + return -1; + } + + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return Iterators.emptyIterator(); + } + + boolean isPartialView() { + return false; + } + + public boolean isEmpty() { + return true; + } + + public ImmutableList asList() { + return ImmutableList.of(); + } + + public String toString() { + return "[]"; + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Set) { + Set that = (Set)object; + return that.isEmpty(); + } else { + return false; + } + } + + public int hashCode() { + return 0; + } + + @GwtIncompatible("serialization") + Object writeReplace() { + return new EmptyContiguousSet.SerializedForm(this.domain); + } + + @GwtIncompatible("NavigableSet") + ImmutableSortedSet createDescendingSet() { + return new EmptyImmutableSortedSet(Ordering.natural().reverse()); + } + + @GwtIncompatible("serialization") + private static final class SerializedForm implements Serializable { + private final DiscreteDomain domain; + private static final long serialVersionUID = 0L; + + private SerializedForm(DiscreteDomain domain) { + this.domain = domain; + } + + private Object readResolve() { + return new EmptyContiguousSet(this.domain); + } + + // $FF: synthetic method + SerializedForm(DiscreteDomain x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/collect/EmptyImmutableBiMap.java b/src/main/com/google/common/collect/EmptyImmutableBiMap.java new file mode 100644 index 0000000..c8234a6 --- /dev/null +++ b/src/main/com/google/common/collect/EmptyImmutableBiMap.java @@ -0,0 +1,55 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class EmptyImmutableBiMap extends ImmutableBiMap { + static final EmptyImmutableBiMap INSTANCE = new EmptyImmutableBiMap(); + + private EmptyImmutableBiMap() { + } + + public ImmutableBiMap inverse() { + return this; + } + + public int size() { + return 0; + } + + public boolean isEmpty() { + return true; + } + + public Object get(@Nullable Object key) { + return null; + } + + public ImmutableSet> entrySet() { + return ImmutableSet.of(); + } + + ImmutableSet> createEntrySet() { + throw new AssertionError("should never be called"); + } + + public ImmutableSetMultimap asMultimap() { + return ImmutableSetMultimap.of(); + } + + public ImmutableSet keySet() { + return ImmutableSet.of(); + } + + boolean isPartialView() { + return false; + } + + Object readResolve() { + return INSTANCE; + } +} diff --git a/src/main/com/google/common/collect/EmptyImmutableListMultimap.java b/src/main/com/google/common/collect/EmptyImmutableListMultimap.java new file mode 100644 index 0000000..799e2ea --- /dev/null +++ b/src/main/com/google/common/collect/EmptyImmutableListMultimap.java @@ -0,0 +1,19 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible( + serializable = true +) +class EmptyImmutableListMultimap extends ImmutableListMultimap { + static final EmptyImmutableListMultimap INSTANCE = new EmptyImmutableListMultimap(); + private static final long serialVersionUID = 0L; + + private EmptyImmutableListMultimap() { + super(ImmutableMap.of(), 0); + } + + private Object readResolve() { + return INSTANCE; + } +} diff --git a/src/main/com/google/common/collect/EmptyImmutableSet.java b/src/main/com/google/common/collect/EmptyImmutableSet.java new file mode 100644 index 0000000..dbefa24 --- /dev/null +++ b/src/main/com/google/common/collect/EmptyImmutableSet.java @@ -0,0 +1,75 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class EmptyImmutableSet extends ImmutableSet { + static final EmptyImmutableSet INSTANCE = new EmptyImmutableSet(); + private static final long serialVersionUID = 0L; + + private EmptyImmutableSet() { + } + + public int size() { + return 0; + } + + public boolean isEmpty() { + return true; + } + + public boolean contains(@Nullable Object target) { + return false; + } + + public boolean containsAll(Collection targets) { + return targets.isEmpty(); + } + + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + boolean isPartialView() { + return false; + } + + int copyIntoArray(Object[] dst, int offset) { + return offset; + } + + public ImmutableList asList() { + return ImmutableList.of(); + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Set) { + Set that = (Set)object; + return that.isEmpty(); + } else { + return false; + } + } + + public final int hashCode() { + return 0; + } + + boolean isHashCodeFast() { + return true; + } + + public String toString() { + return "[]"; + } + + Object readResolve() { + return INSTANCE; + } +} diff --git a/src/main/com/google/common/collect/EmptyImmutableSetMultimap.java b/src/main/com/google/common/collect/EmptyImmutableSetMultimap.java new file mode 100644 index 0000000..401a316 --- /dev/null +++ b/src/main/com/google/common/collect/EmptyImmutableSetMultimap.java @@ -0,0 +1,20 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Comparator; + +@GwtCompatible( + serializable = true +) +class EmptyImmutableSetMultimap extends ImmutableSetMultimap { + static final EmptyImmutableSetMultimap INSTANCE = new EmptyImmutableSetMultimap(); + private static final long serialVersionUID = 0L; + + private EmptyImmutableSetMultimap() { + super(ImmutableMap.of(), 0, (Comparator)null); + } + + private Object readResolve() { + return INSTANCE; + } +} diff --git a/src/main/com/google/common/collect/EmptyImmutableSortedMap.java b/src/main/com/google/common/collect/EmptyImmutableSortedMap.java new file mode 100644 index 0000000..1a1be74 --- /dev/null +++ b/src/main/com/google/common/collect/EmptyImmutableSortedMap.java @@ -0,0 +1,77 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Comparator; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class EmptyImmutableSortedMap extends ImmutableSortedMap { + private final transient ImmutableSortedSet keySet; + + EmptyImmutableSortedMap(Comparator comparator) { + this.keySet = ImmutableSortedSet.emptySet(comparator); + } + + EmptyImmutableSortedMap(Comparator comparator, ImmutableSortedMap descendingMap) { + super(descendingMap); + this.keySet = ImmutableSortedSet.emptySet(comparator); + } + + public V get(@Nullable Object key) { + return null; + } + + public ImmutableSortedSet keySet() { + return this.keySet; + } + + public int size() { + return 0; + } + + public boolean isEmpty() { + return true; + } + + public ImmutableCollection values() { + return ImmutableList.of(); + } + + public String toString() { + return "{}"; + } + + boolean isPartialView() { + return false; + } + + public ImmutableSet> entrySet() { + return ImmutableSet.of(); + } + + ImmutableSet> createEntrySet() { + throw new AssertionError("should never be called"); + } + + public ImmutableSetMultimap asMultimap() { + return ImmutableSetMultimap.of(); + } + + public ImmutableSortedMap headMap(K toKey, boolean inclusive) { + Preconditions.checkNotNull(toKey); + return this; + } + + public ImmutableSortedMap tailMap(K fromKey, boolean inclusive) { + Preconditions.checkNotNull(fromKey); + return this; + } + + ImmutableSortedMap createDescendingMap() { + return new EmptyImmutableSortedMap(Ordering.from(this.comparator()).reverse(), this); + } +} diff --git a/src/main/com/google/common/collect/EmptyImmutableSortedMultiset.java b/src/main/com/google/common/collect/EmptyImmutableSortedMultiset.java new file mode 100644 index 0000000..8b1930c --- /dev/null +++ b/src/main/com/google/common/collect/EmptyImmutableSortedMultiset.java @@ -0,0 +1,79 @@ +package com.google.common.collect; + +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Comparator; +import javax.annotation.Nullable; + +final class EmptyImmutableSortedMultiset extends ImmutableSortedMultiset { + private final ImmutableSortedSet elementSet; + + EmptyImmutableSortedMultiset(Comparator comparator) { + this.elementSet = ImmutableSortedSet.emptySet(comparator); + } + + public Multiset.Entry firstEntry() { + return null; + } + + public Multiset.Entry lastEntry() { + return null; + } + + public int count(@Nullable Object element) { + return 0; + } + + public boolean containsAll(Collection targets) { + return targets.isEmpty(); + } + + public int size() { + return 0; + } + + public ImmutableSortedSet elementSet() { + return this.elementSet; + } + + Multiset.Entry getEntry(int index) { + throw new AssertionError("should never be called"); + } + + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + Preconditions.checkNotNull(upperBound); + Preconditions.checkNotNull(boundType); + return this; + } + + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + Preconditions.checkNotNull(lowerBound); + Preconditions.checkNotNull(boundType); + return this; + } + + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Multiset) { + Multiset other = (Multiset)object; + return other.isEmpty(); + } else { + return false; + } + } + + boolean isPartialView() { + return false; + } + + int copyIntoArray(Object[] dst, int offset) { + return offset; + } + + public ImmutableList asList() { + return ImmutableList.of(); + } +} diff --git a/src/main/com/google/common/collect/EmptyImmutableSortedSet.java b/src/main/com/google/common/collect/EmptyImmutableSortedSet.java new file mode 100644 index 0000000..cb99d3e --- /dev/null +++ b/src/main/com/google/common/collect/EmptyImmutableSortedSet.java @@ -0,0 +1,101 @@ +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; +import java.util.NoSuchElementException; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +class EmptyImmutableSortedSet extends ImmutableSortedSet { + EmptyImmutableSortedSet(Comparator comparator) { + super(comparator); + } + + public int size() { + return 0; + } + + public boolean isEmpty() { + return true; + } + + public boolean contains(@Nullable Object target) { + return false; + } + + public boolean containsAll(Collection targets) { + return targets.isEmpty(); + } + + public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return Iterators.emptyIterator(); + } + + boolean isPartialView() { + return false; + } + + public ImmutableList asList() { + return ImmutableList.of(); + } + + int copyIntoArray(Object[] dst, int offset) { + return offset; + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Set) { + Set that = (Set)object; + return that.isEmpty(); + } else { + return false; + } + } + + public int hashCode() { + return 0; + } + + public String toString() { + return "[]"; + } + + public E first() { + throw new NoSuchElementException(); + } + + public E last() { + throw new NoSuchElementException(); + } + + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return this; + } + + ImmutableSortedSet subSetImpl(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return this; + } + + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return this; + } + + int indexOf(@Nullable Object target) { + return -1; + } + + ImmutableSortedSet createDescendingSet() { + return new EmptyImmutableSortedSet(Ordering.from(this.comparator).reverse()); + } +} diff --git a/src/main/com/google/common/collect/EnumBiMap.java b/src/main/com/google/common/collect/EnumBiMap.java new file mode 100644 index 0000000..70d6fe9 --- /dev/null +++ b/src/main/com/google/common/collect/EnumBiMap.java @@ -0,0 +1,89 @@ +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.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.Map; + +@GwtCompatible( + emulated = true +) +public final class EnumBiMap, V extends Enum> extends AbstractBiMap { + private transient Class keyType; + private transient Class valueType; + @GwtIncompatible("not needed in emulated source.") + private static final long serialVersionUID = 0L; + + public static , V extends Enum> EnumBiMap create(Class keyType, Class valueType) { + return new EnumBiMap(keyType, valueType); + } + + public static , V extends Enum> EnumBiMap create(Map map) { + EnumBiMap bimap = create(inferKeyType(map), inferValueType(map)); + bimap.putAll(map); + return bimap; + } + + private EnumBiMap(Class keyType, Class valueType) { + super(WellBehavedMap.wrap(new EnumMap(keyType)), (Map)WellBehavedMap.wrap(new EnumMap(valueType))); + this.keyType = keyType; + this.valueType = valueType; + } + + static > Class inferKeyType(Map map) { + if (map instanceof EnumBiMap) { + return ((EnumBiMap)map).keyType(); + } else if (map instanceof EnumHashBiMap) { + return ((EnumHashBiMap)map).keyType(); + } else { + Preconditions.checkArgument(!map.isEmpty()); + return ((Enum)map.keySet().iterator().next()).getDeclaringClass(); + } + } + + private static > Class inferValueType(Map map) { + if (map instanceof EnumBiMap) { + return ((EnumBiMap)map).valueType; + } else { + Preconditions.checkArgument(!map.isEmpty()); + return ((Enum)map.values().iterator().next()).getDeclaringClass(); + } + } + + public Class keyType() { + return this.keyType; + } + + public Class valueType() { + return this.valueType; + } + + K checkKey(K key) { + return (Enum)Preconditions.checkNotNull(key); + } + + V checkValue(V value) { + return (Enum)Preconditions.checkNotNull(value); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.keyType); + stream.writeObject(this.valueType); + Serialization.writeMap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.keyType = (Class)stream.readObject(); + this.valueType = (Class)stream.readObject(); + this.setDelegates(WellBehavedMap.wrap(new EnumMap(this.keyType)), WellBehavedMap.wrap(new EnumMap(this.valueType))); + Serialization.populateMap(this, stream); + } +} diff --git a/src/main/com/google/common/collect/EnumHashBiMap.java b/src/main/com/google/common/collect/EnumHashBiMap.java new file mode 100644 index 0000000..1bb91ca --- /dev/null +++ b/src/main/com/google/common/collect/EnumHashBiMap.java @@ -0,0 +1,67 @@ +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.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class EnumHashBiMap, V> extends AbstractBiMap { + private transient Class keyType; + @GwtIncompatible("only needed in emulated source.") + private static final long serialVersionUID = 0L; + + public static , V> EnumHashBiMap create(Class keyType) { + return new EnumHashBiMap(keyType); + } + + public static , V> EnumHashBiMap create(Map map) { + EnumHashBiMap bimap = create(EnumBiMap.inferKeyType(map)); + bimap.putAll(map); + return bimap; + } + + private EnumHashBiMap(Class keyType) { + super(WellBehavedMap.wrap(new EnumMap(keyType)), (Map)Maps.newHashMapWithExpectedSize(((Enum[])keyType.getEnumConstants()).length)); + this.keyType = keyType; + } + + K checkKey(K key) { + return (Enum)Preconditions.checkNotNull(key); + } + + public V put(K key, @Nullable V value) { + return super.put(key, value); + } + + public V forcePut(K key, @Nullable V value) { + return super.forcePut(key, value); + } + + public Class keyType() { + return this.keyType; + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.keyType); + Serialization.writeMap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.keyType = (Class)stream.readObject(); + this.setDelegates(WellBehavedMap.wrap(new EnumMap(this.keyType)), new HashMap(((Enum[])this.keyType.getEnumConstants()).length * 3 / 2)); + Serialization.populateMap(this, stream); + } +} diff --git a/src/main/com/google/common/collect/EnumMultiset.java b/src/main/com/google/common/collect/EnumMultiset.java new file mode 100644 index 0000000..86aa8c9 --- /dev/null +++ b/src/main/com/google/common/collect/EnumMultiset.java @@ -0,0 +1,58 @@ +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.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.EnumMap; +import java.util.Iterator; + +@GwtCompatible( + emulated = true +) +public final class EnumMultiset> extends AbstractMapBasedMultiset { + private transient Class type; + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0L; + + public static > EnumMultiset create(Class type) { + return new EnumMultiset(type); + } + + public static > EnumMultiset create(Iterable elements) { + Iterator iterator = elements.iterator(); + Preconditions.checkArgument(iterator.hasNext(), "EnumMultiset constructor passed empty Iterable"); + EnumMultiset multiset = new EnumMultiset(((Enum)iterator.next()).getDeclaringClass()); + Iterables.addAll(multiset, elements); + return multiset; + } + + public static > EnumMultiset create(Iterable elements, Class type) { + EnumMultiset result = create(type); + Iterables.addAll(result, elements); + return result; + } + + private EnumMultiset(Class type) { + super(WellBehavedMap.wrap(new EnumMap(type))); + this.type = type; + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.type); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + Class localType = (Class)stream.readObject(); + this.type = localType; + this.setBackingMap(WellBehavedMap.wrap(new EnumMap(this.type))); + Serialization.populateMultiset(this, stream); + } +} diff --git a/src/main/com/google/common/collect/EvictingQueue.java b/src/main/com/google/common/collect/EvictingQueue.java new file mode 100644 index 0000000..2945841 --- /dev/null +++ b/src/main/com/google/common/collect/EvictingQueue.java @@ -0,0 +1,67 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Queue; + +@Beta +@GwtIncompatible("java.util.ArrayDeque") +public final class EvictingQueue extends ForwardingQueue implements Serializable { + private final Queue delegate; + @VisibleForTesting + final int maxSize; + private static final long serialVersionUID = 0L; + + private EvictingQueue(int maxSize) { + Preconditions.checkArgument(maxSize >= 0, "maxSize (%s) must >= 0", maxSize); + this.delegate = new ArrayDeque(maxSize); + this.maxSize = maxSize; + } + + public static EvictingQueue create(int maxSize) { + return new EvictingQueue(maxSize); + } + + public int remainingCapacity() { + return this.maxSize - this.size(); + } + + protected Queue delegate() { + return this.delegate; + } + + public boolean offer(E e) { + return this.add(e); + } + + public boolean add(E e) { + Preconditions.checkNotNull(e); + if (this.maxSize == 0) { + return true; + } else { + if (this.size() == this.maxSize) { + this.delegate.remove(); + } + + this.delegate.add(e); + return true; + } + } + + public boolean addAll(Collection collection) { + return this.standardAddAll(collection); + } + + public boolean contains(Object object) { + return this.delegate().contains(Preconditions.checkNotNull(object)); + } + + public boolean remove(Object object) { + return this.delegate().remove(Preconditions.checkNotNull(object)); + } +} diff --git a/src/main/com/google/common/collect/ExplicitOrdering.java b/src/main/com/google/common/collect/ExplicitOrdering.java new file mode 100644 index 0000000..febad03 --- /dev/null +++ b/src/main/com/google/common/collect/ExplicitOrdering.java @@ -0,0 +1,67 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class ExplicitOrdering extends Ordering implements Serializable { + final ImmutableMap rankMap; + private static final long serialVersionUID = 0L; + + ExplicitOrdering(List valuesInOrder) { + this(buildRankMap(valuesInOrder)); + } + + ExplicitOrdering(ImmutableMap rankMap) { + this.rankMap = rankMap; + } + + public int compare(T left, T right) { + return this.rank(left) - this.rank(right); + } + + private int rank(T value) { + Integer rank = (Integer)this.rankMap.get(value); + if (rank == null) { + throw new Ordering.IncomparableValueException(value); + } else { + return rank; + } + } + + private static ImmutableMap buildRankMap(List valuesInOrder) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + int rank = 0; + Iterator i$ = valuesInOrder.iterator(); + + while(i$.hasNext()) { + T value = i$.next(); + builder.put(value, rank++); + } + + return builder.build(); + } + + public boolean equals(@Nullable Object object) { + if (object instanceof ExplicitOrdering) { + ExplicitOrdering that = (ExplicitOrdering)object; + return this.rankMap.equals(that.rankMap); + } else { + return false; + } + } + + public int hashCode() { + return this.rankMap.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.rankMap.keySet())); + return (new StringBuilder(19 + var1.length())).append("Ordering.explicit(").append(var1).append(")").toString(); + } +} diff --git a/src/main/com/google/common/collect/FilteredEntryMultimap.java b/src/main/com/google/common/collect/FilteredEntryMultimap.java new file mode 100644 index 0000000..de0c1ef --- /dev/null +++ b/src/main/com/google/common/collect/FilteredEntryMultimap.java @@ -0,0 +1,336 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +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.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +class FilteredEntryMultimap extends AbstractMultimap implements FilteredMultimap { + final Multimap unfiltered; + final Predicate> predicate; + + FilteredEntryMultimap(Multimap unfiltered, Predicate> predicate) { + this.unfiltered = (Multimap)Preconditions.checkNotNull(unfiltered); + this.predicate = (Predicate)Preconditions.checkNotNull(predicate); + } + + public Multimap unfiltered() { + return this.unfiltered; + } + + public Predicate> entryPredicate() { + return this.predicate; + } + + public int size() { + return this.entries().size(); + } + + private boolean satisfies(K key, V value) { + return this.predicate.apply(Maps.immutableEntry(key, value)); + } + + static Collection filterCollection(Collection collection, Predicate predicate) { + return (Collection)(collection instanceof Set ? Sets.filter((Set)collection, predicate) : Collections2.filter(collection, predicate)); + } + + public boolean containsKey(@Nullable Object key) { + return this.asMap().get(key) != null; + } + + public Collection removeAll(@Nullable Object key) { + return (Collection)MoreObjects.firstNonNull(this.asMap().remove(key), this.unmodifiableEmptyCollection()); + } + + Collection unmodifiableEmptyCollection() { + return (Collection)(this.unfiltered instanceof SetMultimap ? Collections.emptySet() : Collections.emptyList()); + } + + public void clear() { + this.entries().clear(); + } + + public Collection get(K key) { + return filterCollection(this.unfiltered.get(key), new FilteredEntryMultimap.ValuePredicate(key)); + } + + Collection> createEntries() { + return filterCollection(this.unfiltered.entries(), this.predicate); + } + + Collection createValues() { + return new FilteredMultimapValues(this); + } + + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + Map> createAsMap() { + return new FilteredEntryMultimap.AsMap(); + } + + public Set keySet() { + return this.asMap().keySet(); + } + + boolean removeEntriesIf(Predicate>> predicate) { + Iterator>> entryIterator = this.unfiltered.asMap().entrySet().iterator(); + boolean changed = false; + + while(entryIterator.hasNext()) { + Entry> entry = (Entry)entryIterator.next(); + K key = entry.getKey(); + Collection collection = filterCollection((Collection)entry.getValue(), new FilteredEntryMultimap.ValuePredicate(key)); + if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) { + if (collection.size() == ((Collection)entry.getValue()).size()) { + entryIterator.remove(); + } else { + collection.clear(); + } + + changed = true; + } + } + + return changed; + } + + Multiset createKeys() { + return new FilteredEntryMultimap.Keys(); + } + + class Keys extends Multimaps.Keys { + Keys() { + super(FilteredEntryMultimap.this); + } + + public int remove(@Nullable Object key, int occurrences) { + CollectPreconditions.checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return this.count(key); + } else { + Collection collection = (Collection)FilteredEntryMultimap.this.unfiltered.asMap().get(key); + if (collection == null) { + return 0; + } else { + K k = key; + int oldCount = 0; + Iterator itr = collection.iterator(); + + while(itr.hasNext()) { + V v = itr.next(); + if (FilteredEntryMultimap.this.satisfies(k, v)) { + ++oldCount; + if (oldCount <= occurrences) { + itr.remove(); + } + } + } + + return oldCount; + } + } + } + + public Set> entrySet() { + return new Multisets.EntrySet() { + Multiset multiset() { + return Keys.this; + } + + public Iterator> iterator() { + return Keys.this.entryIterator(); + } + + public int size() { + return FilteredEntryMultimap.this.keySet().size(); + } + + private boolean removeEntriesIf(final Predicate> predicate) { + return FilteredEntryMultimap.this.removeEntriesIf(new Predicate>>() { + public boolean apply(Entry> entry) { + return predicate.apply(Multisets.immutableEntry(entry.getKey(), ((Collection)entry.getValue()).size())); + } + }); + } + + public boolean removeAll(Collection c) { + return this.removeEntriesIf(Predicates.in(c)); + } + + public boolean retainAll(Collection c) { + return this.removeEntriesIf(Predicates.not(Predicates.in(c))); + } + }; + } + } + + class AsMap extends Maps.ImprovedAbstractMap> { + public boolean containsKey(@Nullable Object key) { + return this.get(key) != null; + } + + public void clear() { + FilteredEntryMultimap.this.clear(); + } + + public Collection get(@Nullable Object key) { + Collection result = (Collection)FilteredEntryMultimap.this.unfiltered.asMap().get(key); + if (result == null) { + return null; + } else { + result = FilteredEntryMultimap.filterCollection(result, FilteredEntryMultimap.this.new ValuePredicate(key)); + return result.isEmpty() ? null : result; + } + } + + public Collection remove(@Nullable Object key) { + Collection collection = (Collection)FilteredEntryMultimap.this.unfiltered.asMap().get(key); + if (collection == null) { + return null; + } else { + K k = key; + List result = Lists.newArrayList(); + Iterator itr = collection.iterator(); + + while(itr.hasNext()) { + V v = itr.next(); + if (FilteredEntryMultimap.this.satisfies(k, v)) { + itr.remove(); + result.add(v); + } + } + + if (result.isEmpty()) { + return null; + } else if (FilteredEntryMultimap.this.unfiltered instanceof SetMultimap) { + return Collections.unmodifiableSet(Sets.newLinkedHashSet(result)); + } else { + return Collections.unmodifiableList(result); + } + } + } + + Set createKeySet() { + return new Maps.KeySet>(this) { + public boolean removeAll(Collection c) { + return FilteredEntryMultimap.this.removeEntriesIf(Maps.keyPredicateOnEntries(Predicates.in(c))); + } + + public boolean retainAll(Collection c) { + return FilteredEntryMultimap.this.removeEntriesIf(Maps.keyPredicateOnEntries(Predicates.not(Predicates.in(c)))); + } + + public boolean remove(@Nullable Object o) { + return AsMap.this.remove(o) != null; + } + }; + } + + Set>> createEntrySet() { + return new Maps.EntrySet>() { + Map> map() { + return AsMap.this; + } + + public Iterator>> iterator() { + return new AbstractIterator>>() { + final Iterator>> backingIterator; + + { + this.backingIterator = FilteredEntryMultimap.this.unfiltered.asMap().entrySet().iterator(); + } + + protected Entry> computeNext() { + while(true) { + if (this.backingIterator.hasNext()) { + Entry> entry = (Entry)this.backingIterator.next(); + K key = entry.getKey(); + Collection collection = FilteredEntryMultimap.filterCollection((Collection)entry.getValue(), FilteredEntryMultimap.this.new ValuePredicate(key)); + if (collection.isEmpty()) { + continue; + } + + return Maps.immutableEntry(key, collection); + } + + return (Entry)this.endOfData(); + } + } + }; + } + + public boolean removeAll(Collection c) { + return FilteredEntryMultimap.this.removeEntriesIf(Predicates.in(c)); + } + + public boolean retainAll(Collection c) { + return FilteredEntryMultimap.this.removeEntriesIf(Predicates.not(Predicates.in(c))); + } + + public int size() { + return Iterators.size(this.iterator()); + } + }; + } + + Collection> createValues() { + return new Maps.Values>(this) { + public boolean remove(@Nullable Object o) { + if (o instanceof Collection) { + Collection c = (Collection)o; + Iterator entryIterator = FilteredEntryMultimap.this.unfiltered.asMap().entrySet().iterator(); + + while(entryIterator.hasNext()) { + Entry> entry = (Entry)entryIterator.next(); + K key = entry.getKey(); + Collection collection = FilteredEntryMultimap.filterCollection((Collection)entry.getValue(), FilteredEntryMultimap.this.new ValuePredicate(key)); + if (!collection.isEmpty() && c.equals(collection)) { + if (collection.size() == ((Collection)entry.getValue()).size()) { + entryIterator.remove(); + } else { + collection.clear(); + } + + return true; + } + } + } + + return false; + } + + public boolean removeAll(Collection c) { + return FilteredEntryMultimap.this.removeEntriesIf(Maps.valuePredicateOnEntries(Predicates.in(c))); + } + + public boolean retainAll(Collection c) { + return FilteredEntryMultimap.this.removeEntriesIf(Maps.valuePredicateOnEntries(Predicates.not(Predicates.in(c)))); + } + }; + } + } + + final class ValuePredicate implements Predicate { + private final K key; + + ValuePredicate(K key) { + this.key = key; + } + + public boolean apply(@Nullable V value) { + return FilteredEntryMultimap.this.satisfies(this.key, value); + } + } +} diff --git a/src/main/com/google/common/collect/FilteredEntrySetMultimap.java b/src/main/com/google/common/collect/FilteredEntrySetMultimap.java new file mode 100644 index 0000000..6744954 --- /dev/null +++ b/src/main/com/google/common/collect/FilteredEntrySetMultimap.java @@ -0,0 +1,37 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; +import java.util.Set; +import java.util.Map.Entry; + +@GwtCompatible +final class FilteredEntrySetMultimap extends FilteredEntryMultimap implements FilteredSetMultimap { + FilteredEntrySetMultimap(SetMultimap unfiltered, Predicate> predicate) { + super(unfiltered, predicate); + } + + public SetMultimap unfiltered() { + return (SetMultimap)this.unfiltered; + } + + public Set get(K key) { + return (Set)super.get(key); + } + + public Set removeAll(Object key) { + return (Set)super.removeAll(key); + } + + public Set replaceValues(K key, Iterable values) { + return (Set)super.replaceValues(key, values); + } + + Set> createEntries() { + return Sets.filter(this.unfiltered().entries(), this.entryPredicate()); + } + + public Set> entries() { + return (Set)super.entries(); + } +} diff --git a/src/main/com/google/common/collect/FilteredKeyListMultimap.java b/src/main/com/google/common/collect/FilteredKeyListMultimap.java new file mode 100644 index 0000000..06932d8 --- /dev/null +++ b/src/main/com/google/common/collect/FilteredKeyListMultimap.java @@ -0,0 +1,29 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; +import java.util.List; +import javax.annotation.Nullable; + +@GwtCompatible +final class FilteredKeyListMultimap extends FilteredKeyMultimap implements ListMultimap { + FilteredKeyListMultimap(ListMultimap unfiltered, Predicate keyPredicate) { + super(unfiltered, keyPredicate); + } + + public ListMultimap unfiltered() { + return (ListMultimap)super.unfiltered(); + } + + public List get(K key) { + return (List)super.get(key); + } + + public List removeAll(@Nullable Object key) { + return (List)super.removeAll(key); + } + + public List replaceValues(K key, Iterable values) { + return (List)super.replaceValues(key, values); + } +} diff --git a/src/main/com/google/common/collect/FilteredKeyMultimap.java b/src/main/com/google/common/collect/FilteredKeyMultimap.java new file mode 100644 index 0000000..b734197 --- /dev/null +++ b/src/main/com/google/common/collect/FilteredKeyMultimap.java @@ -0,0 +1,166 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +class FilteredKeyMultimap extends AbstractMultimap implements FilteredMultimap { + final Multimap unfiltered; + final Predicate keyPredicate; + + FilteredKeyMultimap(Multimap unfiltered, Predicate keyPredicate) { + this.unfiltered = (Multimap)Preconditions.checkNotNull(unfiltered); + this.keyPredicate = (Predicate)Preconditions.checkNotNull(keyPredicate); + } + + public Multimap unfiltered() { + return this.unfiltered; + } + + public Predicate> entryPredicate() { + return Maps.keyPredicateOnEntries(this.keyPredicate); + } + + public int size() { + int size = 0; + + Collection collection; + for(Iterator i$ = this.asMap().values().iterator(); i$.hasNext(); size += collection.size()) { + collection = (Collection)i$.next(); + } + + return size; + } + + public boolean containsKey(@Nullable Object key) { + return this.unfiltered.containsKey(key) ? this.keyPredicate.apply(key) : false; + } + + public Collection removeAll(Object key) { + return this.containsKey(key) ? this.unfiltered.removeAll(key) : this.unmodifiableEmptyCollection(); + } + + Collection unmodifiableEmptyCollection() { + return (Collection)(this.unfiltered instanceof SetMultimap ? ImmutableSet.of() : ImmutableList.of()); + } + + public void clear() { + this.keySet().clear(); + } + + Set createKeySet() { + return Sets.filter(this.unfiltered.keySet(), this.keyPredicate); + } + + public Collection get(K key) { + if (this.keyPredicate.apply(key)) { + return this.unfiltered.get(key); + } else { + return (Collection)(this.unfiltered instanceof SetMultimap ? new FilteredKeyMultimap.AddRejectingSet(key) : new FilteredKeyMultimap.AddRejectingList(key)); + } + } + + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + Collection> createEntries() { + return new FilteredKeyMultimap.Entries(); + } + + Collection createValues() { + return new FilteredMultimapValues(this); + } + + Map> createAsMap() { + return Maps.filterKeys(this.unfiltered.asMap(), this.keyPredicate); + } + + Multiset createKeys() { + return Multisets.filter(this.unfiltered.keys(), this.keyPredicate); + } + + class Entries extends ForwardingCollection> { + protected Collection> delegate() { + return Collections2.filter(FilteredKeyMultimap.this.unfiltered.entries(), FilteredKeyMultimap.this.entryPredicate()); + } + + public boolean remove(@Nullable Object o) { + if (o instanceof Entry) { + Entry entry = (Entry)o; + if (FilteredKeyMultimap.this.unfiltered.containsKey(entry.getKey()) && FilteredKeyMultimap.this.keyPredicate.apply(entry.getKey())) { + return FilteredKeyMultimap.this.unfiltered.remove(entry.getKey(), entry.getValue()); + } + } + + return false; + } + } + + static class AddRejectingList extends ForwardingList { + final K key; + + AddRejectingList(K key) { + this.key = key; + } + + public boolean add(V v) { + this.add(0, v); + return true; + } + + public boolean addAll(Collection collection) { + this.addAll(0, collection); + return true; + } + + public void add(int index, V element) { + Preconditions.checkPositionIndex(index, 0); + String var3 = String.valueOf(String.valueOf(this.key)); + throw new IllegalArgumentException((new StringBuilder(32 + var3.length())).append("Key does not satisfy predicate: ").append(var3).toString()); + } + + public boolean addAll(int index, Collection elements) { + Preconditions.checkNotNull(elements); + Preconditions.checkPositionIndex(index, 0); + String var3 = String.valueOf(String.valueOf(this.key)); + throw new IllegalArgumentException((new StringBuilder(32 + var3.length())).append("Key does not satisfy predicate: ").append(var3).toString()); + } + + protected List delegate() { + return Collections.emptyList(); + } + } + + static class AddRejectingSet extends ForwardingSet { + final K key; + + AddRejectingSet(K key) { + this.key = key; + } + + public boolean add(V element) { + String var2 = String.valueOf(String.valueOf(this.key)); + throw new IllegalArgumentException((new StringBuilder(32 + var2.length())).append("Key does not satisfy predicate: ").append(var2).toString()); + } + + public boolean addAll(Collection collection) { + Preconditions.checkNotNull(collection); + String var2 = String.valueOf(String.valueOf(this.key)); + throw new IllegalArgumentException((new StringBuilder(32 + var2.length())).append("Key does not satisfy predicate: ").append(var2).toString()); + } + + protected Set delegate() { + return Collections.emptySet(); + } + } +} diff --git a/src/main/com/google/common/collect/FilteredKeySetMultimap.java b/src/main/com/google/common/collect/FilteredKeySetMultimap.java new file mode 100644 index 0000000..11d18c0 --- /dev/null +++ b/src/main/com/google/common/collect/FilteredKeySetMultimap.java @@ -0,0 +1,52 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +final class FilteredKeySetMultimap extends FilteredKeyMultimap implements FilteredSetMultimap { + FilteredKeySetMultimap(SetMultimap unfiltered, Predicate keyPredicate) { + super(unfiltered, keyPredicate); + } + + public SetMultimap unfiltered() { + return (SetMultimap)this.unfiltered; + } + + public Set get(K key) { + return (Set)super.get(key); + } + + public Set removeAll(Object key) { + return (Set)super.removeAll(key); + } + + public Set replaceValues(K key, Iterable values) { + return (Set)super.replaceValues(key, values); + } + + public Set> entries() { + return (Set)super.entries(); + } + + Set> createEntries() { + return new FilteredKeySetMultimap.EntrySet(); + } + + class EntrySet extends FilteredKeyMultimap.Entries implements Set> { + EntrySet() { + super(); + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + public boolean equals(@Nullable Object o) { + return Sets.equalsImpl(this, o); + } + } +} diff --git a/src/main/com/google/common/collect/FilteredMultimap.java b/src/main/com/google/common/collect/FilteredMultimap.java new file mode 100644 index 0000000..a9ef44a --- /dev/null +++ b/src/main/com/google/common/collect/FilteredMultimap.java @@ -0,0 +1,12 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Predicate; +import java.util.Map.Entry; + +@GwtCompatible +interface FilteredMultimap extends Multimap { + Multimap unfiltered(); + + Predicate> entryPredicate(); +} diff --git a/src/main/com/google/common/collect/FilteredMultimapValues.java b/src/main/com/google/common/collect/FilteredMultimapValues.java new file mode 100644 index 0000000..08b76bf --- /dev/null +++ b/src/main/com/google/common/collect/FilteredMultimapValues.java @@ -0,0 +1,62 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +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 java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +final class FilteredMultimapValues extends AbstractCollection { + private final FilteredMultimap multimap; + + FilteredMultimapValues(FilteredMultimap multimap) { + this.multimap = (FilteredMultimap)Preconditions.checkNotNull(multimap); + } + + public Iterator iterator() { + return Maps.valueIterator(this.multimap.entries().iterator()); + } + + public boolean contains(@Nullable Object o) { + return this.multimap.containsValue(o); + } + + public int size() { + return this.multimap.size(); + } + + public boolean remove(@Nullable Object o) { + Predicate> entryPredicate = this.multimap.entryPredicate(); + Iterator unfilteredItr = this.multimap.unfiltered().entries().iterator(); + + Entry entry; + do { + if (!unfilteredItr.hasNext()) { + return false; + } + + entry = (Entry)unfilteredItr.next(); + } while(!entryPredicate.apply(entry) || !Objects.equal(entry.getValue(), o)); + + unfilteredItr.remove(); + return true; + } + + public boolean removeAll(Collection c) { + return Iterables.removeIf(this.multimap.unfiltered().entries(), Predicates.and(this.multimap.entryPredicate(), Maps.valuePredicateOnEntries(Predicates.in(c)))); + } + + public boolean retainAll(Collection c) { + return Iterables.removeIf(this.multimap.unfiltered().entries(), Predicates.and(this.multimap.entryPredicate(), Maps.valuePredicateOnEntries(Predicates.not(Predicates.in(c))))); + } + + public void clear() { + this.multimap.clear(); + } +} diff --git a/src/main/com/google/common/collect/FilteredSetMultimap.java b/src/main/com/google/common/collect/FilteredSetMultimap.java new file mode 100644 index 0000000..c484c3d --- /dev/null +++ b/src/main/com/google/common/collect/FilteredSetMultimap.java @@ -0,0 +1,8 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible +interface FilteredSetMultimap extends FilteredMultimap, SetMultimap { + SetMultimap unfiltered(); +} diff --git a/src/main/com/google/common/collect/FluentIterable.java b/src/main/com/google/common/collect/FluentIterable.java new file mode 100644 index 0000000..bd50167 --- /dev/null +++ b/src/main/com/google/common/collect/FluentIterable.java @@ -0,0 +1,217 @@ +package com.google.common.collect; + +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.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public abstract class FluentIterable implements Iterable { + private final Iterable iterable; + + protected FluentIterable() { + this.iterable = this; + } + + FluentIterable(Iterable iterable) { + this.iterable = (Iterable)Preconditions.checkNotNull(iterable); + } + + public static FluentIterable from(final Iterable iterable) { + return iterable instanceof FluentIterable ? (FluentIterable)iterable : new FluentIterable(iterable) { + public Iterator iterator() { + return iterable.iterator(); + } + }; + } + + /** @deprecated */ + @Deprecated + public static FluentIterable from(FluentIterable iterable) { + return (FluentIterable)Preconditions.checkNotNull(iterable); + } + + @Beta + public static FluentIterable of(E[] elements) { + return from((Iterable)Lists.newArrayList(elements)); + } + + public String toString() { + return Iterables.toString(this.iterable); + } + + public final int size() { + return Iterables.size(this.iterable); + } + + public final boolean contains(@Nullable Object element) { + return Iterables.contains(this.iterable, element); + } + + @CheckReturnValue + public final FluentIterable cycle() { + return from(Iterables.cycle(this.iterable)); + } + + @CheckReturnValue + @Beta + public final FluentIterable append(Iterable other) { + return from(Iterables.concat(this.iterable, other)); + } + + @CheckReturnValue + @Beta + public final FluentIterable append(E... elements) { + return from(Iterables.concat(this.iterable, Arrays.asList(elements))); + } + + @CheckReturnValue + public final FluentIterable filter(Predicate predicate) { + return from(Iterables.filter(this.iterable, predicate)); + } + + @CheckReturnValue + @GwtIncompatible("Class.isInstance") + public final FluentIterable filter(Class type) { + return from(Iterables.filter(this.iterable, type)); + } + + public final boolean anyMatch(Predicate predicate) { + return Iterables.any(this.iterable, predicate); + } + + public final boolean allMatch(Predicate predicate) { + return Iterables.all(this.iterable, predicate); + } + + public final Optional firstMatch(Predicate predicate) { + return Iterables.tryFind(this.iterable, predicate); + } + + public final FluentIterable transform(Function function) { + return from(Iterables.transform(this.iterable, function)); + } + + public FluentIterable transformAndConcat(Function> function) { + return from(Iterables.concat((Iterable)this.transform(function))); + } + + public final Optional first() { + Iterator iterator = this.iterable.iterator(); + return iterator.hasNext() ? Optional.of(iterator.next()) : Optional.absent(); + } + + public final Optional last() { + if (this.iterable instanceof List) { + List list = (List)this.iterable; + return list.isEmpty() ? Optional.absent() : Optional.of(list.get(list.size() - 1)); + } else { + Iterator iterator = this.iterable.iterator(); + if (!iterator.hasNext()) { + return Optional.absent(); + } else if (this.iterable instanceof SortedSet) { + SortedSet sortedSet = (SortedSet)this.iterable; + return Optional.of(sortedSet.last()); + } else { + Object current; + do { + current = iterator.next(); + } while(iterator.hasNext()); + + return Optional.of(current); + } + } + } + + @CheckReturnValue + public final FluentIterable skip(int numberToSkip) { + return from(Iterables.skip(this.iterable, numberToSkip)); + } + + @CheckReturnValue + public final FluentIterable limit(int size) { + return from(Iterables.limit(this.iterable, size)); + } + + public final boolean isEmpty() { + return !this.iterable.iterator().hasNext(); + } + + public final ImmutableList toList() { + return ImmutableList.copyOf(this.iterable); + } + + public final ImmutableList toSortedList(Comparator comparator) { + return Ordering.from(comparator).immutableSortedCopy(this.iterable); + } + + public final ImmutableSet toSet() { + return ImmutableSet.copyOf(this.iterable); + } + + public final ImmutableSortedSet toSortedSet(Comparator comparator) { + return ImmutableSortedSet.copyOf(comparator, this.iterable); + } + + public final ImmutableMap toMap(Function valueFunction) { + return Maps.toMap(this.iterable, valueFunction); + } + + public final ImmutableListMultimap index(Function keyFunction) { + return Multimaps.index(this.iterable, keyFunction); + } + + public final ImmutableMap uniqueIndex(Function keyFunction) { + return Maps.uniqueIndex(this.iterable, keyFunction); + } + + @GwtIncompatible("Array.newArray(Class, int)") + public final E[] toArray(Class type) { + return Iterables.toArray(this.iterable, type); + } + + public final > C copyInto(C collection) { + Preconditions.checkNotNull(collection); + if (this.iterable instanceof Collection) { + collection.addAll(Collections2.cast(this.iterable)); + } else { + Iterator i$ = this.iterable.iterator(); + + while(i$.hasNext()) { + E item = i$.next(); + collection.add(item); + } + } + + return collection; + } + + @Beta + public final String join(Joiner joiner) { + return joiner.join((Iterable)this); + } + + public final E get(int position) { + return Iterables.get(this.iterable, position); + } + + private static class FromIterableFunction implements Function, FluentIterable> { + public FluentIterable apply(Iterable fromObject) { + return FluentIterable.from(fromObject); + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingBlockingDeque.java b/src/main/com/google/common/collect/ForwardingBlockingDeque.java new file mode 100644 index 0000000..5294f1e --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingBlockingDeque.java @@ -0,0 +1,72 @@ +package com.google.common.collect; + +import java.util.Collection; +import java.util.concurrent.BlockingDeque; +import java.util.concurrent.TimeUnit; + +public abstract class ForwardingBlockingDeque extends ForwardingDeque implements BlockingDeque { + protected ForwardingBlockingDeque() { + } + + protected abstract BlockingDeque delegate(); + + public int remainingCapacity() { + return this.delegate().remainingCapacity(); + } + + public void putFirst(E e) throws InterruptedException { + this.delegate().putFirst(e); + } + + public void putLast(E e) throws InterruptedException { + this.delegate().putLast(e); + } + + public boolean offerFirst(E e, long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().offerFirst(e, timeout, unit); + } + + public boolean offerLast(E e, long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().offerLast(e, timeout, unit); + } + + public E takeFirst() throws InterruptedException { + return this.delegate().takeFirst(); + } + + public E takeLast() throws InterruptedException { + return this.delegate().takeLast(); + } + + public E pollFirst(long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().pollFirst(timeout, unit); + } + + public E pollLast(long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().pollLast(timeout, unit); + } + + public void put(E e) throws InterruptedException { + this.delegate().put(e); + } + + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().offer(e, timeout, unit); + } + + public E take() throws InterruptedException { + return this.delegate().take(); + } + + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().poll(timeout, unit); + } + + public int drainTo(Collection c) { + return this.delegate().drainTo(c); + } + + public int drainTo(Collection c, int maxElements) { + return this.delegate().drainTo(c, maxElements); + } +} diff --git a/src/main/com/google/common/collect/ForwardingCollection.java b/src/main/com/google/common/collect/ForwardingCollection.java new file mode 100644 index 0000000..0f4e45f --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingCollection.java @@ -0,0 +1,121 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingCollection extends ForwardingObject implements Collection { + protected ForwardingCollection() { + } + + protected abstract Collection delegate(); + + public Iterator iterator() { + return this.delegate().iterator(); + } + + public int size() { + return this.delegate().size(); + } + + public boolean removeAll(Collection collection) { + return this.delegate().removeAll(collection); + } + + public boolean isEmpty() { + return this.delegate().isEmpty(); + } + + public boolean contains(Object object) { + return this.delegate().contains(object); + } + + public boolean add(E element) { + return this.delegate().add(element); + } + + public boolean remove(Object object) { + return this.delegate().remove(object); + } + + public boolean containsAll(Collection collection) { + return this.delegate().containsAll(collection); + } + + public boolean addAll(Collection collection) { + return this.delegate().addAll(collection); + } + + public boolean retainAll(Collection collection) { + return this.delegate().retainAll(collection); + } + + public void clear() { + this.delegate().clear(); + } + + public Object[] toArray() { + return this.delegate().toArray(); + } + + public T[] toArray(T[] array) { + return this.delegate().toArray(array); + } + + protected boolean standardContains(@Nullable Object object) { + return Iterators.contains(this.iterator(), object); + } + + protected boolean standardContainsAll(Collection collection) { + return Collections2.containsAllImpl(this, collection); + } + + protected boolean standardAddAll(Collection collection) { + return Iterators.addAll(this, collection.iterator()); + } + + protected boolean standardRemove(@Nullable Object object) { + Iterator iterator = this.iterator(); + + do { + if (!iterator.hasNext()) { + return false; + } + } while(!Objects.equal(iterator.next(), object)); + + iterator.remove(); + return true; + } + + protected boolean standardRemoveAll(Collection collection) { + return Iterators.removeAll(this.iterator(), collection); + } + + protected boolean standardRetainAll(Collection collection) { + return Iterators.retainAll(this.iterator(), collection); + } + + protected void standardClear() { + Iterators.clear(this.iterator()); + } + + protected boolean standardIsEmpty() { + return !this.iterator().hasNext(); + } + + protected String standardToString() { + return Collections2.toStringImpl(this); + } + + protected Object[] standardToArray() { + Object[] newArray = new Object[this.size()]; + return this.toArray(newArray); + } + + protected T[] standardToArray(T[] array) { + return ObjectArrays.toArrayImpl(this, array); + } +} diff --git a/src/main/com/google/common/collect/ForwardingConcurrentMap.java b/src/main/com/google/common/collect/ForwardingConcurrentMap.java new file mode 100644 index 0000000..ce550e8 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingConcurrentMap.java @@ -0,0 +1,28 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.concurrent.ConcurrentMap; + +@GwtCompatible +public abstract class ForwardingConcurrentMap extends ForwardingMap implements ConcurrentMap { + protected ForwardingConcurrentMap() { + } + + protected abstract ConcurrentMap delegate(); + + public V putIfAbsent(K key, V value) { + return this.delegate().putIfAbsent(key, value); + } + + public boolean remove(Object key, Object value) { + return this.delegate().remove(key, value); + } + + public V replace(K key, V value) { + return this.delegate().replace(key, value); + } + + public boolean replace(K key, V oldValue, V newValue) { + return this.delegate().replace(key, oldValue, newValue); + } +} diff --git a/src/main/com/google/common/collect/ForwardingDeque.java b/src/main/com/google/common/collect/ForwardingDeque.java new file mode 100644 index 0000000..ecb7368 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingDeque.java @@ -0,0 +1,79 @@ +package com.google.common.collect; + +import java.util.Deque; +import java.util.Iterator; + +public abstract class ForwardingDeque extends ForwardingQueue implements Deque { + protected ForwardingDeque() { + } + + protected abstract Deque delegate(); + + public void addFirst(E e) { + this.delegate().addFirst(e); + } + + public void addLast(E e) { + this.delegate().addLast(e); + } + + public Iterator descendingIterator() { + return this.delegate().descendingIterator(); + } + + public E getFirst() { + return this.delegate().getFirst(); + } + + public E getLast() { + return this.delegate().getLast(); + } + + public boolean offerFirst(E e) { + return this.delegate().offerFirst(e); + } + + public boolean offerLast(E e) { + return this.delegate().offerLast(e); + } + + public E peekFirst() { + return this.delegate().peekFirst(); + } + + public E peekLast() { + return this.delegate().peekLast(); + } + + public E pollFirst() { + return this.delegate().pollFirst(); + } + + public E pollLast() { + return this.delegate().pollLast(); + } + + public E pop() { + return this.delegate().pop(); + } + + public void push(E e) { + this.delegate().push(e); + } + + public E removeFirst() { + return this.delegate().removeFirst(); + } + + public E removeLast() { + return this.delegate().removeLast(); + } + + public boolean removeFirstOccurrence(Object o) { + return this.delegate().removeFirstOccurrence(o); + } + + public boolean removeLastOccurrence(Object o) { + return this.delegate().removeLastOccurrence(o); + } +} diff --git a/src/main/com/google/common/collect/ForwardingImmutableCollection.java b/src/main/com/google/common/collect/ForwardingImmutableCollection.java new file mode 100644 index 0000000..82552eb --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingImmutableCollection.java @@ -0,0 +1,11 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible( + emulated = true +) +class ForwardingImmutableCollection { + private ForwardingImmutableCollection() { + } +} diff --git a/src/main/com/google/common/collect/ForwardingImmutableList.java b/src/main/com/google/common/collect/ForwardingImmutableList.java new file mode 100644 index 0000000..bf613e1 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingImmutableList.java @@ -0,0 +1,11 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible( + emulated = true +) +abstract class ForwardingImmutableList { + private ForwardingImmutableList() { + } +} diff --git a/src/main/com/google/common/collect/ForwardingImmutableMap.java b/src/main/com/google/common/collect/ForwardingImmutableMap.java new file mode 100644 index 0000000..b146b58 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingImmutableMap.java @@ -0,0 +1,11 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible( + emulated = true +) +abstract class ForwardingImmutableMap { + private ForwardingImmutableMap() { + } +} diff --git a/src/main/com/google/common/collect/ForwardingImmutableSet.java b/src/main/com/google/common/collect/ForwardingImmutableSet.java new file mode 100644 index 0000000..9e9e911 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingImmutableSet.java @@ -0,0 +1,11 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible( + emulated = true +) +abstract class ForwardingImmutableSet { + private ForwardingImmutableSet() { + } +} diff --git a/src/main/com/google/common/collect/ForwardingIterator.java b/src/main/com/google/common/collect/ForwardingIterator.java new file mode 100644 index 0000000..ea8264b --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingIterator.java @@ -0,0 +1,24 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; + +@GwtCompatible +public abstract class ForwardingIterator extends ForwardingObject implements Iterator { + protected ForwardingIterator() { + } + + protected abstract Iterator delegate(); + + public boolean hasNext() { + return this.delegate().hasNext(); + } + + public T next() { + return this.delegate().next(); + } + + public void remove() { + this.delegate().remove(); + } +} diff --git a/src/main/com/google/common/collect/ForwardingList.java b/src/main/com/google/common/collect/ForwardingList.java new file mode 100644 index 0000000..8a49d5c --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingList.java @@ -0,0 +1,110 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingList extends ForwardingCollection implements List { + protected ForwardingList() { + } + + protected abstract List delegate(); + + public void add(int index, E element) { + this.delegate().add(index, element); + } + + public boolean addAll(int index, Collection elements) { + return this.delegate().addAll(index, elements); + } + + public E get(int index) { + return this.delegate().get(index); + } + + public int indexOf(Object element) { + return this.delegate().indexOf(element); + } + + public int lastIndexOf(Object element) { + return this.delegate().lastIndexOf(element); + } + + public ListIterator listIterator() { + return this.delegate().listIterator(); + } + + public ListIterator listIterator(int index) { + return this.delegate().listIterator(index); + } + + public E remove(int index) { + return this.delegate().remove(index); + } + + public E set(int index, E element) { + return this.delegate().set(index, element); + } + + public List subList(int fromIndex, int toIndex) { + return this.delegate().subList(fromIndex, toIndex); + } + + public boolean equals(@Nullable Object object) { + return object == this || this.delegate().equals(object); + } + + public int hashCode() { + return this.delegate().hashCode(); + } + + protected boolean standardAdd(E element) { + this.add(this.size(), element); + return true; + } + + protected boolean standardAddAll(int index, Iterable elements) { + return Lists.addAllImpl(this, index, elements); + } + + protected int standardIndexOf(@Nullable Object element) { + return Lists.indexOfImpl(this, element); + } + + protected int standardLastIndexOf(@Nullable Object element) { + return Lists.lastIndexOfImpl(this, element); + } + + protected Iterator standardIterator() { + return this.listIterator(); + } + + protected ListIterator standardListIterator() { + return this.listIterator(0); + } + + @Beta + protected ListIterator standardListIterator(int start) { + return Lists.listIteratorImpl(this, start); + } + + @Beta + protected List standardSubList(int fromIndex, int toIndex) { + return Lists.subListImpl(this, fromIndex, toIndex); + } + + @Beta + protected boolean standardEquals(@Nullable Object object) { + return Lists.equalsImpl(this, object); + } + + @Beta + protected int standardHashCode() { + return Lists.hashCodeImpl(this); + } +} diff --git a/src/main/com/google/common/collect/ForwardingListIterator.java b/src/main/com/google/common/collect/ForwardingListIterator.java new file mode 100644 index 0000000..439f02c --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingListIterator.java @@ -0,0 +1,36 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.ListIterator; + +@GwtCompatible +public abstract class ForwardingListIterator extends ForwardingIterator implements ListIterator { + protected ForwardingListIterator() { + } + + protected abstract ListIterator delegate(); + + public void add(E element) { + this.delegate().add(element); + } + + public boolean hasPrevious() { + return this.delegate().hasPrevious(); + } + + public int nextIndex() { + return this.delegate().nextIndex(); + } + + public E previous() { + return this.delegate().previous(); + } + + public int previousIndex() { + return this.delegate().previousIndex(); + } + + public void set(E element) { + this.delegate().set(element); + } +} diff --git a/src/main/com/google/common/collect/ForwardingListMultimap.java b/src/main/com/google/common/collect/ForwardingListMultimap.java new file mode 100644 index 0000000..8f2fb93 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingListMultimap.java @@ -0,0 +1,25 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.List; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingListMultimap extends ForwardingMultimap implements ListMultimap { + protected ForwardingListMultimap() { + } + + protected abstract ListMultimap delegate(); + + public List get(@Nullable K key) { + return this.delegate().get(key); + } + + public List removeAll(@Nullable Object key) { + return this.delegate().removeAll(key); + } + + public List replaceValues(K key, Iterable values) { + return this.delegate().replaceValues(key, values); + } +} diff --git a/src/main/com/google/common/collect/ForwardingMap.java b/src/main/com/google/common/collect/ForwardingMap.java new file mode 100644 index 0000000..6edd90c --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingMap.java @@ -0,0 +1,150 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingMap extends ForwardingObject implements Map { + protected ForwardingMap() { + } + + protected abstract Map delegate(); + + public int size() { + return this.delegate().size(); + } + + public boolean isEmpty() { + return this.delegate().isEmpty(); + } + + public V remove(Object object) { + return this.delegate().remove(object); + } + + public void clear() { + this.delegate().clear(); + } + + public boolean containsKey(@Nullable Object key) { + return this.delegate().containsKey(key); + } + + public boolean containsValue(@Nullable Object value) { + return this.delegate().containsValue(value); + } + + public V get(@Nullable Object key) { + return this.delegate().get(key); + } + + public V put(K key, V value) { + return this.delegate().put(key, value); + } + + public void putAll(Map map) { + this.delegate().putAll(map); + } + + public Set keySet() { + return this.delegate().keySet(); + } + + public Collection values() { + return this.delegate().values(); + } + + public Set> entrySet() { + return this.delegate().entrySet(); + } + + public boolean equals(@Nullable Object object) { + return object == this || this.delegate().equals(object); + } + + public int hashCode() { + return this.delegate().hashCode(); + } + + protected void standardPutAll(Map map) { + Maps.putAllImpl(this, map); + } + + @Beta + protected V standardRemove(@Nullable Object key) { + Iterator entryIterator = this.entrySet().iterator(); + + Entry entry; + do { + if (!entryIterator.hasNext()) { + return null; + } + + entry = (Entry)entryIterator.next(); + } while(!Objects.equal(entry.getKey(), key)); + + V value = entry.getValue(); + entryIterator.remove(); + return value; + } + + protected void standardClear() { + Iterators.clear(this.entrySet().iterator()); + } + + @Beta + protected boolean standardContainsKey(@Nullable Object key) { + return Maps.containsKeyImpl(this, key); + } + + protected boolean standardContainsValue(@Nullable Object value) { + return Maps.containsValueImpl(this, value); + } + + protected boolean standardIsEmpty() { + return !this.entrySet().iterator().hasNext(); + } + + protected boolean standardEquals(@Nullable Object object) { + return Maps.equalsImpl(this, object); + } + + protected int standardHashCode() { + return Sets.hashCodeImpl(this.entrySet()); + } + + protected String standardToString() { + return Maps.toStringImpl(this); + } + + @Beta + protected abstract class StandardEntrySet extends Maps.EntrySet { + public StandardEntrySet() { + } + + Map map() { + return ForwardingMap.this; + } + } + + @Beta + protected class StandardValues extends Maps.Values { + public StandardValues() { + super(ForwardingMap.this); + } + } + + @Beta + protected class StandardKeySet extends Maps.KeySet { + public StandardKeySet() { + super(ForwardingMap.this); + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingMapEntry.java b/src/main/com/google/common/collect/ForwardingMapEntry.java new file mode 100644 index 0000000..8dfc520 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingMapEntry.java @@ -0,0 +1,57 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingMapEntry extends ForwardingObject implements Entry { + protected ForwardingMapEntry() { + } + + protected abstract Entry delegate(); + + public K getKey() { + return this.delegate().getKey(); + } + + public V getValue() { + return this.delegate().getValue(); + } + + public V setValue(V value) { + return this.delegate().setValue(value); + } + + public boolean equals(@Nullable Object object) { + return this.delegate().equals(object); + } + + public int hashCode() { + return this.delegate().hashCode(); + } + + protected boolean standardEquals(@Nullable Object object) { + if (!(object instanceof Entry)) { + return false; + } else { + Entry that = (Entry)object; + return Objects.equal(this.getKey(), that.getKey()) && Objects.equal(this.getValue(), that.getValue()); + } + } + + protected int standardHashCode() { + K k = this.getKey(); + V v = this.getValue(); + return (k == null ? 0 : k.hashCode()) ^ (v == null ? 0 : v.hashCode()); + } + + @Beta + protected String standardToString() { + String var1 = String.valueOf(String.valueOf(this.getKey())); + String var2 = String.valueOf(String.valueOf(this.getValue())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append("=").append(var2).toString(); + } +} diff --git a/src/main/com/google/common/collect/ForwardingMultimap.java b/src/main/com/google/common/collect/ForwardingMultimap.java new file mode 100644 index 0000000..882a877 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingMultimap.java @@ -0,0 +1,96 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingMultimap extends ForwardingObject implements Multimap { + protected ForwardingMultimap() { + } + + protected abstract Multimap delegate(); + + public Map> asMap() { + return this.delegate().asMap(); + } + + public void clear() { + this.delegate().clear(); + } + + public boolean containsEntry(@Nullable Object key, @Nullable Object value) { + return this.delegate().containsEntry(key, value); + } + + public boolean containsKey(@Nullable Object key) { + return this.delegate().containsKey(key); + } + + public boolean containsValue(@Nullable Object value) { + return this.delegate().containsValue(value); + } + + public Collection> entries() { + return this.delegate().entries(); + } + + public Collection get(@Nullable K key) { + return this.delegate().get(key); + } + + public boolean isEmpty() { + return this.delegate().isEmpty(); + } + + public Multiset keys() { + return this.delegate().keys(); + } + + public Set keySet() { + return this.delegate().keySet(); + } + + public boolean put(K key, V value) { + return this.delegate().put(key, value); + } + + public boolean putAll(K key, Iterable values) { + return this.delegate().putAll(key, values); + } + + public boolean putAll(Multimap multimap) { + return this.delegate().putAll(multimap); + } + + public boolean remove(@Nullable Object key, @Nullable Object value) { + return this.delegate().remove(key, value); + } + + public Collection removeAll(@Nullable Object key) { + return this.delegate().removeAll(key); + } + + public Collection replaceValues(K key, Iterable values) { + return this.delegate().replaceValues(key, values); + } + + public int size() { + return this.delegate().size(); + } + + public Collection values() { + return this.delegate().values(); + } + + public boolean equals(@Nullable Object object) { + return object == this || this.delegate().equals(object); + } + + public int hashCode() { + return this.delegate().hashCode(); + } +} diff --git a/src/main/com/google/common/collect/ForwardingMultiset.java b/src/main/com/google/common/collect/ForwardingMultiset.java new file mode 100644 index 0000000..573a1b0 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingMultiset.java @@ -0,0 +1,137 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingMultiset extends ForwardingCollection implements Multiset { + protected ForwardingMultiset() { + } + + protected abstract Multiset delegate(); + + public int count(Object element) { + return this.delegate().count(element); + } + + public int add(E element, int occurrences) { + return this.delegate().add(element, occurrences); + } + + public int remove(Object element, int occurrences) { + return this.delegate().remove(element, occurrences); + } + + public Set elementSet() { + return this.delegate().elementSet(); + } + + public Set> entrySet() { + return this.delegate().entrySet(); + } + + public boolean equals(@Nullable Object object) { + return object == this || this.delegate().equals(object); + } + + public int hashCode() { + return this.delegate().hashCode(); + } + + public int setCount(E element, int count) { + return this.delegate().setCount(element, count); + } + + public boolean setCount(E element, int oldCount, int newCount) { + return this.delegate().setCount(element, oldCount, newCount); + } + + protected boolean standardContains(@Nullable Object object) { + return this.count(object) > 0; + } + + protected void standardClear() { + Iterators.clear(this.entrySet().iterator()); + } + + @Beta + protected int standardCount(@Nullable Object object) { + Iterator i$ = this.entrySet().iterator(); + + Multiset.Entry entry; + do { + if (!i$.hasNext()) { + return 0; + } + + entry = (Multiset.Entry)i$.next(); + } while(!Objects.equal(entry.getElement(), object)); + + return entry.getCount(); + } + + protected boolean standardAdd(E element) { + this.add(element, 1); + return true; + } + + @Beta + protected boolean standardAddAll(Collection elementsToAdd) { + return Multisets.addAllImpl(this, elementsToAdd); + } + + protected boolean standardRemove(Object element) { + return this.remove(element, 1) > 0; + } + + protected boolean standardRemoveAll(Collection elementsToRemove) { + return Multisets.removeAllImpl(this, elementsToRemove); + } + + protected boolean standardRetainAll(Collection elementsToRetain) { + return Multisets.retainAllImpl(this, elementsToRetain); + } + + protected int standardSetCount(E element, int count) { + return Multisets.setCountImpl(this, element, count); + } + + protected boolean standardSetCount(E element, int oldCount, int newCount) { + return Multisets.setCountImpl(this, element, oldCount, newCount); + } + + protected Iterator standardIterator() { + return Multisets.iteratorImpl(this); + } + + protected int standardSize() { + return Multisets.sizeImpl(this); + } + + protected boolean standardEquals(@Nullable Object object) { + return Multisets.equalsImpl(this, object); + } + + protected int standardHashCode() { + return this.entrySet().hashCode(); + } + + protected String standardToString() { + return this.entrySet().toString(); + } + + @Beta + protected class StandardElementSet extends Multisets.ElementSet { + public StandardElementSet() { + } + + Multiset multiset() { + return ForwardingMultiset.this; + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingNavigableMap.java b/src/main/com/google/common/collect/ForwardingNavigableMap.java new file mode 100644 index 0000000..f2eb64f --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingNavigableMap.java @@ -0,0 +1,221 @@ +package com.google.common.collect; + +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; +import java.util.Map.Entry; + +public abstract class ForwardingNavigableMap extends ForwardingSortedMap implements NavigableMap { + protected ForwardingNavigableMap() { + } + + protected abstract NavigableMap delegate(); + + public Entry lowerEntry(K key) { + return this.delegate().lowerEntry(key); + } + + protected Entry standardLowerEntry(K key) { + return this.headMap(key, false).lastEntry(); + } + + public K lowerKey(K key) { + return this.delegate().lowerKey(key); + } + + protected K standardLowerKey(K key) { + return Maps.keyOrNull(this.lowerEntry(key)); + } + + public Entry floorEntry(K key) { + return this.delegate().floorEntry(key); + } + + protected Entry standardFloorEntry(K key) { + return this.headMap(key, true).lastEntry(); + } + + public K floorKey(K key) { + return this.delegate().floorKey(key); + } + + protected K standardFloorKey(K key) { + return Maps.keyOrNull(this.floorEntry(key)); + } + + public Entry ceilingEntry(K key) { + return this.delegate().ceilingEntry(key); + } + + protected Entry standardCeilingEntry(K key) { + return this.tailMap(key, true).firstEntry(); + } + + public K ceilingKey(K key) { + return this.delegate().ceilingKey(key); + } + + protected K standardCeilingKey(K key) { + return Maps.keyOrNull(this.ceilingEntry(key)); + } + + public Entry higherEntry(K key) { + return this.delegate().higherEntry(key); + } + + protected Entry standardHigherEntry(K key) { + return this.tailMap(key, false).firstEntry(); + } + + public K higherKey(K key) { + return this.delegate().higherKey(key); + } + + protected K standardHigherKey(K key) { + return Maps.keyOrNull(this.higherEntry(key)); + } + + public Entry firstEntry() { + return this.delegate().firstEntry(); + } + + protected Entry standardFirstEntry() { + return (Entry)Iterables.getFirst(this.entrySet(), (Object)null); + } + + protected K standardFirstKey() { + Entry entry = this.firstEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + public Entry lastEntry() { + return this.delegate().lastEntry(); + } + + protected Entry standardLastEntry() { + return (Entry)Iterables.getFirst(this.descendingMap().entrySet(), (Object)null); + } + + protected K standardLastKey() { + Entry entry = this.lastEntry(); + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getKey(); + } + } + + public Entry pollFirstEntry() { + return this.delegate().pollFirstEntry(); + } + + protected Entry standardPollFirstEntry() { + return (Entry)Iterators.pollNext(this.entrySet().iterator()); + } + + public Entry pollLastEntry() { + return this.delegate().pollLastEntry(); + } + + protected Entry standardPollLastEntry() { + return (Entry)Iterators.pollNext(this.descendingMap().entrySet().iterator()); + } + + public NavigableMap descendingMap() { + return this.delegate().descendingMap(); + } + + public NavigableSet navigableKeySet() { + return this.delegate().navigableKeySet(); + } + + public NavigableSet descendingKeySet() { + return this.delegate().descendingKeySet(); + } + + @Beta + protected NavigableSet standardDescendingKeySet() { + return this.descendingMap().navigableKeySet(); + } + + protected SortedMap standardSubMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return this.delegate().subMap(fromKey, fromInclusive, toKey, toInclusive); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return this.delegate().headMap(toKey, inclusive); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return this.delegate().tailMap(fromKey, inclusive); + } + + protected SortedMap standardHeadMap(K toKey) { + return this.headMap(toKey, false); + } + + protected SortedMap standardTailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + + @Beta + protected class StandardNavigableKeySet extends Maps.NavigableKeySet { + public StandardNavigableKeySet() { + super(ForwardingNavigableMap.this); + } + } + + @Beta + protected class StandardDescendingMap extends Maps.DescendingMap { + public StandardDescendingMap() { + } + + NavigableMap forward() { + return ForwardingNavigableMap.this; + } + + protected Iterator> entryIterator() { + return new Iterator>() { + private Entry toRemove = null; + private Entry nextOrNull = StandardDescendingMap.this.forward().lastEntry(); + + public boolean hasNext() { + return this.nextOrNull != null; + } + + public Entry next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + Entry var1; + try { + var1 = this.nextOrNull; + } finally { + this.toRemove = this.nextOrNull; + this.nextOrNull = StandardDescendingMap.this.forward().lowerEntry(this.nextOrNull.getKey()); + } + + return var1; + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.toRemove != null); + StandardDescendingMap.this.forward().remove(this.toRemove.getKey()); + this.toRemove = null; + } + }; + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingNavigableSet.java b/src/main/com/google/common/collect/ForwardingNavigableSet.java new file mode 100644 index 0000000..aa20276 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingNavigableSet.java @@ -0,0 +1,113 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.SortedSet; + +public abstract class ForwardingNavigableSet extends ForwardingSortedSet implements NavigableSet { + protected ForwardingNavigableSet() { + } + + protected abstract NavigableSet delegate(); + + public E lower(E e) { + return this.delegate().lower(e); + } + + protected E standardLower(E e) { + return Iterators.getNext(this.headSet(e, false).descendingIterator(), (Object)null); + } + + public E floor(E e) { + return this.delegate().floor(e); + } + + protected E standardFloor(E e) { + return Iterators.getNext(this.headSet(e, true).descendingIterator(), (Object)null); + } + + public E ceiling(E e) { + return this.delegate().ceiling(e); + } + + protected E standardCeiling(E e) { + return Iterators.getNext(this.tailSet(e, true).iterator(), (Object)null); + } + + public E higher(E e) { + return this.delegate().higher(e); + } + + protected E standardHigher(E e) { + return Iterators.getNext(this.tailSet(e, false).iterator(), (Object)null); + } + + public E pollFirst() { + return this.delegate().pollFirst(); + } + + protected E standardPollFirst() { + return Iterators.pollNext(this.iterator()); + } + + public E pollLast() { + return this.delegate().pollLast(); + } + + protected E standardPollLast() { + return Iterators.pollNext(this.descendingIterator()); + } + + protected E standardFirst() { + return this.iterator().next(); + } + + protected E standardLast() { + return this.descendingIterator().next(); + } + + public NavigableSet descendingSet() { + return this.delegate().descendingSet(); + } + + public Iterator descendingIterator() { + return this.delegate().descendingIterator(); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return this.delegate().subSet(fromElement, fromInclusive, toElement, toInclusive); + } + + @Beta + protected NavigableSet standardSubSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return this.tailSet(fromElement, fromInclusive).headSet(toElement, toInclusive); + } + + protected SortedSet standardSubSet(E fromElement, E toElement) { + return this.subSet(fromElement, true, toElement, false); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return this.delegate().headSet(toElement, inclusive); + } + + protected SortedSet standardHeadSet(E toElement) { + return this.headSet(toElement, false); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return this.delegate().tailSet(fromElement, inclusive); + } + + protected SortedSet standardTailSet(E fromElement) { + return this.tailSet(fromElement, true); + } + + @Beta + protected class StandardDescendingSet extends Sets.DescendingSet { + public StandardDescendingSet() { + super(ForwardingNavigableSet.this); + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingObject.java b/src/main/com/google/common/collect/ForwardingObject.java new file mode 100644 index 0000000..8c08549 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingObject.java @@ -0,0 +1,15 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible +public abstract class ForwardingObject { + protected ForwardingObject() { + } + + protected abstract Object delegate(); + + public String toString() { + return this.delegate().toString(); + } +} diff --git a/src/main/com/google/common/collect/ForwardingQueue.java b/src/main/com/google/common/collect/ForwardingQueue.java new file mode 100644 index 0000000..4bbd735 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingQueue.java @@ -0,0 +1,57 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.NoSuchElementException; +import java.util.Queue; + +@GwtCompatible +public abstract class ForwardingQueue extends ForwardingCollection implements Queue { + protected ForwardingQueue() { + } + + protected abstract Queue delegate(); + + public boolean offer(E o) { + return this.delegate().offer(o); + } + + public E poll() { + return this.delegate().poll(); + } + + public E remove() { + return this.delegate().remove(); + } + + public E peek() { + return this.delegate().peek(); + } + + public E element() { + return this.delegate().element(); + } + + protected boolean standardOffer(E e) { + try { + return this.add(e); + } catch (IllegalStateException var3) { + return false; + } + } + + protected E standardPeek() { + try { + return this.element(); + } catch (NoSuchElementException var2) { + return null; + } + } + + protected E standardPoll() { + try { + return this.remove(); + } catch (NoSuchElementException var2) { + return null; + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingSet.java b/src/main/com/google/common/collect/ForwardingSet.java new file mode 100644 index 0000000..70f9a68 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingSet.java @@ -0,0 +1,35 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingSet extends ForwardingCollection implements Set { + protected ForwardingSet() { + } + + protected abstract Set delegate(); + + public boolean equals(@Nullable Object object) { + return object == this || this.delegate().equals(object); + } + + public int hashCode() { + return this.delegate().hashCode(); + } + + protected boolean standardRemoveAll(Collection collection) { + return Sets.removeAllImpl(this, (Collection)((Collection)Preconditions.checkNotNull(collection))); + } + + protected boolean standardEquals(@Nullable Object object) { + return Sets.equalsImpl(this, object); + } + + protected int standardHashCode() { + return Sets.hashCodeImpl(this); + } +} diff --git a/src/main/com/google/common/collect/ForwardingSetMultimap.java b/src/main/com/google/common/collect/ForwardingSetMultimap.java new file mode 100644 index 0000000..c3535d9 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingSetMultimap.java @@ -0,0 +1,27 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingSetMultimap extends ForwardingMultimap implements SetMultimap { + protected abstract SetMultimap delegate(); + + public Set> entries() { + return this.delegate().entries(); + } + + public Set get(@Nullable K key) { + return this.delegate().get(key); + } + + public Set removeAll(@Nullable Object key) { + return this.delegate().removeAll(key); + } + + public Set replaceValues(K key, Iterable values) { + return this.delegate().replaceValues(key, values); + } +} diff --git a/src/main/com/google/common/collect/ForwardingSortedMap.java b/src/main/com/google/common/collect/ForwardingSortedMap.java new file mode 100644 index 0000000..b02feb5 --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingSortedMap.java @@ -0,0 +1,73 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Comparator; +import java.util.NoSuchElementException; +import java.util.SortedMap; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingSortedMap extends ForwardingMap implements SortedMap { + protected ForwardingSortedMap() { + } + + protected abstract SortedMap delegate(); + + public Comparator comparator() { + return this.delegate().comparator(); + } + + public K firstKey() { + return this.delegate().firstKey(); + } + + public SortedMap headMap(K toKey) { + return this.delegate().headMap(toKey); + } + + public K lastKey() { + return this.delegate().lastKey(); + } + + public SortedMap subMap(K fromKey, K toKey) { + return this.delegate().subMap(fromKey, toKey); + } + + public SortedMap tailMap(K fromKey) { + return this.delegate().tailMap(fromKey); + } + + private int unsafeCompare(Object k1, Object k2) { + Comparator comparator = this.comparator(); + return comparator == null ? ((Comparable)k1).compareTo(k2) : comparator.compare(k1, k2); + } + + @Beta + protected boolean standardContainsKey(@Nullable Object key) { + try { + Object ceilingKey = this.tailMap(key).firstKey(); + return this.unsafeCompare(ceilingKey, key) == 0; + } catch (ClassCastException var4) { + return false; + } catch (NoSuchElementException var5) { + return false; + } catch (NullPointerException var6) { + return false; + } + } + + @Beta + protected SortedMap standardSubMap(K fromKey, K toKey) { + Preconditions.checkArgument(this.unsafeCompare(fromKey, toKey) <= 0, "fromKey must be <= toKey"); + return this.tailMap(fromKey).headMap(toKey); + } + + @Beta + protected class StandardKeySet extends Maps.SortedKeySet { + public StandardKeySet() { + super(ForwardingSortedMap.this); + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingSortedMultiset.java b/src/main/com/google/common/collect/ForwardingSortedMultiset.java new file mode 100644 index 0000000..5238d5d --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingSortedMultiset.java @@ -0,0 +1,121 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; + +@Beta +@GwtCompatible( + emulated = true +) +public abstract class ForwardingSortedMultiset extends ForwardingMultiset implements SortedMultiset { + protected ForwardingSortedMultiset() { + } + + protected abstract SortedMultiset delegate(); + + public NavigableSet elementSet() { + return (NavigableSet)super.elementSet(); + } + + public Comparator comparator() { + return this.delegate().comparator(); + } + + public SortedMultiset descendingMultiset() { + return this.delegate().descendingMultiset(); + } + + public Multiset.Entry firstEntry() { + return this.delegate().firstEntry(); + } + + protected Multiset.Entry standardFirstEntry() { + Iterator> entryIterator = this.entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } else { + Multiset.Entry entry = (Multiset.Entry)entryIterator.next(); + return Multisets.immutableEntry(entry.getElement(), entry.getCount()); + } + } + + public Multiset.Entry lastEntry() { + return this.delegate().lastEntry(); + } + + protected Multiset.Entry standardLastEntry() { + Iterator> entryIterator = this.descendingMultiset().entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } else { + Multiset.Entry entry = (Multiset.Entry)entryIterator.next(); + return Multisets.immutableEntry(entry.getElement(), entry.getCount()); + } + } + + public Multiset.Entry pollFirstEntry() { + return this.delegate().pollFirstEntry(); + } + + protected Multiset.Entry standardPollFirstEntry() { + Iterator> entryIterator = this.entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } else { + Multiset.Entry entry = (Multiset.Entry)entryIterator.next(); + entry = Multisets.immutableEntry(entry.getElement(), entry.getCount()); + entryIterator.remove(); + return entry; + } + } + + public Multiset.Entry pollLastEntry() { + return this.delegate().pollLastEntry(); + } + + protected Multiset.Entry standardPollLastEntry() { + Iterator> entryIterator = this.descendingMultiset().entrySet().iterator(); + if (!entryIterator.hasNext()) { + return null; + } else { + Multiset.Entry entry = (Multiset.Entry)entryIterator.next(); + entry = Multisets.immutableEntry(entry.getElement(), entry.getCount()); + entryIterator.remove(); + return entry; + } + } + + public SortedMultiset headMultiset(E upperBound, BoundType boundType) { + return this.delegate().headMultiset(upperBound, boundType); + } + + public SortedMultiset subMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { + return this.delegate().subMultiset(lowerBound, lowerBoundType, upperBound, upperBoundType); + } + + protected SortedMultiset standardSubMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { + return this.tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); + } + + public SortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return this.delegate().tailMultiset(lowerBound, boundType); + } + + protected abstract class StandardDescendingMultiset extends DescendingMultiset { + public StandardDescendingMultiset() { + } + + SortedMultiset forwardMultiset() { + return ForwardingSortedMultiset.this; + } + } + + protected class StandardElementSet extends SortedMultisets.NavigableElementSet { + public StandardElementSet() { + super(ForwardingSortedMultiset.this); + } + } +} diff --git a/src/main/com/google/common/collect/ForwardingSortedSet.java b/src/main/com/google/common/collect/ForwardingSortedSet.java new file mode 100644 index 0000000..d983fda --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingSortedSet.java @@ -0,0 +1,85 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.SortedSet; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingSortedSet extends ForwardingSet implements SortedSet { + protected ForwardingSortedSet() { + } + + protected abstract SortedSet delegate(); + + public Comparator comparator() { + return this.delegate().comparator(); + } + + public E first() { + return this.delegate().first(); + } + + public SortedSet headSet(E toElement) { + return this.delegate().headSet(toElement); + } + + public E last() { + return this.delegate().last(); + } + + public SortedSet subSet(E fromElement, E toElement) { + return this.delegate().subSet(fromElement, toElement); + } + + public SortedSet tailSet(E fromElement) { + return this.delegate().tailSet(fromElement); + } + + private int unsafeCompare(Object o1, Object o2) { + Comparator comparator = this.comparator(); + return comparator == null ? ((Comparable)o1).compareTo(o2) : comparator.compare(o1, o2); + } + + @Beta + protected boolean standardContains(@Nullable Object object) { + try { + Object ceiling = this.tailSet(object).first(); + return this.unsafeCompare(ceiling, object) == 0; + } catch (ClassCastException var4) { + return false; + } catch (NoSuchElementException var5) { + return false; + } catch (NullPointerException var6) { + return false; + } + } + + @Beta + protected boolean standardRemove(@Nullable Object object) { + try { + Iterator iterator = this.tailSet(object).iterator(); + if (iterator.hasNext()) { + Object ceiling = iterator.next(); + if (this.unsafeCompare(ceiling, object) == 0) { + iterator.remove(); + return true; + } + } + + return false; + } catch (ClassCastException var5) { + return false; + } catch (NullPointerException var6) { + return false; + } + } + + @Beta + protected SortedSet standardSubSet(E fromElement, E toElement) { + return this.tailSet(fromElement).headSet(toElement); + } +} diff --git a/src/main/com/google/common/collect/ForwardingSortedSetMultimap.java b/src/main/com/google/common/collect/ForwardingSortedSetMultimap.java new file mode 100644 index 0000000..1c7ccac --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingSortedSetMultimap.java @@ -0,0 +1,30 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Comparator; +import java.util.SortedSet; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ForwardingSortedSetMultimap extends ForwardingSetMultimap implements SortedSetMultimap { + protected ForwardingSortedSetMultimap() { + } + + protected abstract SortedSetMultimap delegate(); + + public SortedSet get(@Nullable K key) { + return this.delegate().get(key); + } + + public SortedSet removeAll(@Nullable Object key) { + return this.delegate().removeAll(key); + } + + public SortedSet replaceValues(K key, Iterable values) { + return this.delegate().replaceValues(key, values); + } + + public Comparator valueComparator() { + return this.delegate().valueComparator(); + } +} diff --git a/src/main/com/google/common/collect/ForwardingTable.java b/src/main/com/google/common/collect/ForwardingTable.java new file mode 100644 index 0000000..617b18a --- /dev/null +++ b/src/main/com/google/common/collect/ForwardingTable.java @@ -0,0 +1,98 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +@GwtCompatible +public abstract class ForwardingTable extends ForwardingObject implements Table { + protected ForwardingTable() { + } + + protected abstract Table delegate(); + + public Set> cellSet() { + return this.delegate().cellSet(); + } + + public void clear() { + this.delegate().clear(); + } + + public Map column(C columnKey) { + return this.delegate().column(columnKey); + } + + public Set columnKeySet() { + return this.delegate().columnKeySet(); + } + + public Map> columnMap() { + return this.delegate().columnMap(); + } + + public boolean contains(Object rowKey, Object columnKey) { + return this.delegate().contains(rowKey, columnKey); + } + + public boolean containsColumn(Object columnKey) { + return this.delegate().containsColumn(columnKey); + } + + public boolean containsRow(Object rowKey) { + return this.delegate().containsRow(rowKey); + } + + public boolean containsValue(Object value) { + return this.delegate().containsValue(value); + } + + public V get(Object rowKey, Object columnKey) { + return this.delegate().get(rowKey, columnKey); + } + + public boolean isEmpty() { + return this.delegate().isEmpty(); + } + + public V put(R rowKey, C columnKey, V value) { + return this.delegate().put(rowKey, columnKey, value); + } + + public void putAll(Table table) { + this.delegate().putAll(table); + } + + public V remove(Object rowKey, Object columnKey) { + return this.delegate().remove(rowKey, columnKey); + } + + public Map row(R rowKey) { + return this.delegate().row(rowKey); + } + + public Set rowKeySet() { + return this.delegate().rowKeySet(); + } + + public Map> rowMap() { + return this.delegate().rowMap(); + } + + public int size() { + return this.delegate().size(); + } + + public Collection values() { + return this.delegate().values(); + } + + public boolean equals(Object obj) { + return obj == this || this.delegate().equals(obj); + } + + public int hashCode() { + return this.delegate().hashCode(); + } +} diff --git a/src/main/com/google/common/collect/GeneralRange.java b/src/main/com/google/common/collect/GeneralRange.java new file mode 100644 index 0000000..3cbb8f6 --- /dev/null +++ b/src/main/com/google/common/collect/GeneralRange.java @@ -0,0 +1,204 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Comparator; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class GeneralRange implements Serializable { + private final Comparator comparator; + private final boolean hasLowerBound; + @Nullable + private final T lowerEndpoint; + private final BoundType lowerBoundType; + private final boolean hasUpperBound; + @Nullable + private final T upperEndpoint; + private final BoundType upperBoundType; + private transient GeneralRange reverse; + + static GeneralRange from(Range range) { + T lowerEndpoint = range.hasLowerBound() ? range.lowerEndpoint() : null; + BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : BoundType.OPEN; + T upperEndpoint = range.hasUpperBound() ? range.upperEndpoint() : null; + BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : BoundType.OPEN; + return new GeneralRange(Ordering.natural(), range.hasLowerBound(), lowerEndpoint, lowerBoundType, range.hasUpperBound(), upperEndpoint, upperBoundType); + } + + static GeneralRange all(Comparator comparator) { + return new GeneralRange(comparator, false, (Object)null, BoundType.OPEN, false, (Object)null, BoundType.OPEN); + } + + static GeneralRange downTo(Comparator comparator, @Nullable T endpoint, BoundType boundType) { + return new GeneralRange(comparator, true, endpoint, boundType, false, (Object)null, BoundType.OPEN); + } + + static GeneralRange upTo(Comparator comparator, @Nullable T endpoint, BoundType boundType) { + return new GeneralRange(comparator, false, (Object)null, BoundType.OPEN, true, endpoint, boundType); + } + + static GeneralRange range(Comparator comparator, @Nullable T lower, BoundType lowerType, @Nullable T upper, BoundType upperType) { + return new GeneralRange(comparator, true, lower, lowerType, true, upper, upperType); + } + + private GeneralRange(Comparator comparator, boolean hasLowerBound, @Nullable T lowerEndpoint, BoundType lowerBoundType, boolean hasUpperBound, @Nullable T upperEndpoint, BoundType upperBoundType) { + this.comparator = (Comparator)Preconditions.checkNotNull(comparator); + this.hasLowerBound = hasLowerBound; + this.hasUpperBound = hasUpperBound; + this.lowerEndpoint = lowerEndpoint; + this.lowerBoundType = (BoundType)Preconditions.checkNotNull(lowerBoundType); + this.upperEndpoint = upperEndpoint; + this.upperBoundType = (BoundType)Preconditions.checkNotNull(upperBoundType); + if (hasLowerBound) { + comparator.compare(lowerEndpoint, lowerEndpoint); + } + + if (hasUpperBound) { + comparator.compare(upperEndpoint, upperEndpoint); + } + + if (hasLowerBound && hasUpperBound) { + int cmp = comparator.compare(lowerEndpoint, upperEndpoint); + Preconditions.checkArgument(cmp <= 0, "lowerEndpoint (%s) > upperEndpoint (%s)", lowerEndpoint, upperEndpoint); + if (cmp == 0) { + Preconditions.checkArgument(lowerBoundType != BoundType.OPEN | upperBoundType != BoundType.OPEN); + } + } + + } + + Comparator comparator() { + return this.comparator; + } + + boolean hasLowerBound() { + return this.hasLowerBound; + } + + boolean hasUpperBound() { + return this.hasUpperBound; + } + + boolean isEmpty() { + return this.hasUpperBound() && this.tooLow(this.getUpperEndpoint()) || this.hasLowerBound() && this.tooHigh(this.getLowerEndpoint()); + } + + boolean tooLow(@Nullable T t) { + if (!this.hasLowerBound()) { + return false; + } else { + T lbound = this.getLowerEndpoint(); + int cmp = this.comparator.compare(t, lbound); + return cmp < 0 | cmp == 0 & this.getLowerBoundType() == BoundType.OPEN; + } + } + + boolean tooHigh(@Nullable T t) { + if (!this.hasUpperBound()) { + return false; + } else { + T ubound = this.getUpperEndpoint(); + int cmp = this.comparator.compare(t, ubound); + return cmp > 0 | cmp == 0 & this.getUpperBoundType() == BoundType.OPEN; + } + } + + boolean contains(@Nullable T t) { + return !this.tooLow(t) && !this.tooHigh(t); + } + + GeneralRange intersect(GeneralRange other) { + Preconditions.checkNotNull(other); + Preconditions.checkArgument(this.comparator.equals(other.comparator)); + boolean hasLowBound = this.hasLowerBound; + T lowEnd = this.getLowerEndpoint(); + BoundType lowType = this.getLowerBoundType(); + if (!this.hasLowerBound()) { + hasLowBound = other.hasLowerBound; + lowEnd = other.getLowerEndpoint(); + lowType = other.getLowerBoundType(); + } else if (other.hasLowerBound()) { + int cmp = this.comparator.compare(this.getLowerEndpoint(), other.getLowerEndpoint()); + if (cmp < 0 || cmp == 0 && other.getLowerBoundType() == BoundType.OPEN) { + lowEnd = other.getLowerEndpoint(); + lowType = other.getLowerBoundType(); + } + } + + boolean hasUpBound = this.hasUpperBound; + T upEnd = this.getUpperEndpoint(); + BoundType upType = this.getUpperBoundType(); + int cmp; + if (!this.hasUpperBound()) { + hasUpBound = other.hasUpperBound; + upEnd = other.getUpperEndpoint(); + upType = other.getUpperBoundType(); + } else if (other.hasUpperBound()) { + cmp = this.comparator.compare(this.getUpperEndpoint(), other.getUpperEndpoint()); + if (cmp > 0 || cmp == 0 && other.getUpperBoundType() == BoundType.OPEN) { + upEnd = other.getUpperEndpoint(); + upType = other.getUpperBoundType(); + } + } + + if (hasLowBound && hasUpBound) { + cmp = this.comparator.compare(lowEnd, upEnd); + if (cmp > 0 || cmp == 0 && lowType == BoundType.OPEN && upType == BoundType.OPEN) { + lowEnd = upEnd; + lowType = BoundType.OPEN; + upType = BoundType.CLOSED; + } + } + + return new GeneralRange(this.comparator, hasLowBound, lowEnd, lowType, hasUpBound, upEnd, upType); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof GeneralRange)) { + return false; + } else { + GeneralRange r = (GeneralRange)obj; + return this.comparator.equals(r.comparator) && this.hasLowerBound == r.hasLowerBound && this.hasUpperBound == r.hasUpperBound && this.getLowerBoundType().equals(r.getLowerBoundType()) && this.getUpperBoundType().equals(r.getUpperBoundType()) && Objects.equal(this.getLowerEndpoint(), r.getLowerEndpoint()) && Objects.equal(this.getUpperEndpoint(), r.getUpperEndpoint()); + } + } + + public int hashCode() { + return Objects.hashCode(this.comparator, this.getLowerEndpoint(), this.getLowerBoundType(), this.getUpperEndpoint(), this.getUpperBoundType()); + } + + GeneralRange reverse() { + GeneralRange result = this.reverse; + if (result == null) { + result = new GeneralRange(Ordering.from(this.comparator).reverse(), this.hasUpperBound, this.getUpperEndpoint(), this.getUpperBoundType(), this.hasLowerBound, this.getLowerEndpoint(), this.getLowerBoundType()); + result.reverse = this; + return this.reverse = result; + } else { + return result; + } + } + + public String toString() { + return this.comparator + ":" + (this.lowerBoundType == BoundType.CLOSED ? '[' : '(') + (this.hasLowerBound ? this.lowerEndpoint : "-∞") + ',' + (this.hasUpperBound ? this.upperEndpoint : "∞") + (this.upperBoundType == BoundType.CLOSED ? ']' : ')'); + } + + T getLowerEndpoint() { + return this.lowerEndpoint; + } + + BoundType getLowerBoundType() { + return this.lowerBoundType; + } + + T getUpperEndpoint() { + return this.upperEndpoint; + } + + BoundType getUpperBoundType() { + return this.upperBoundType; + } +} diff --git a/src/main/com/google/common/collect/GenericMapMaker.java b/src/main/com/google/common/collect/GenericMapMaker.java new file mode 100644 index 0000000..b7e908f --- /dev/null +++ b/src/main/com/google/common/collect/GenericMapMaker.java @@ -0,0 +1,68 @@ +package com.google.common.collect; + +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.Function; +import com.google.common.base.MoreObjects; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +/** @deprecated */ +@Deprecated +@Beta +@GwtCompatible( + emulated = true +) +abstract class GenericMapMaker { + @GwtIncompatible("To be supported") + MapMaker.RemovalListener removalListener; + + @GwtIncompatible("To be supported") + abstract GenericMapMaker keyEquivalence(Equivalence var1); + + public abstract GenericMapMaker initialCapacity(int var1); + + abstract GenericMapMaker maximumSize(int var1); + + public abstract GenericMapMaker concurrencyLevel(int var1); + + @GwtIncompatible("java.lang.ref.WeakReference") + public abstract GenericMapMaker weakKeys(); + + @GwtIncompatible("java.lang.ref.WeakReference") + public abstract GenericMapMaker weakValues(); + + /** @deprecated */ + @Deprecated + @GwtIncompatible("java.lang.ref.SoftReference") + public abstract GenericMapMaker softValues(); + + abstract GenericMapMaker expireAfterWrite(long var1, TimeUnit var3); + + @GwtIncompatible("To be supported") + abstract GenericMapMaker expireAfterAccess(long var1, TimeUnit var3); + + @GwtIncompatible("To be supported") + MapMaker.RemovalListener getRemovalListener() { + return (MapMaker.RemovalListener)MoreObjects.firstNonNull(this.removalListener, GenericMapMaker.NullListener.INSTANCE); + } + + public abstract ConcurrentMap makeMap(); + + @GwtIncompatible("MapMakerInternalMap") + abstract MapMakerInternalMap makeCustomMap(); + + /** @deprecated */ + @Deprecated + abstract ConcurrentMap makeComputingMap(Function var1); + + @GwtIncompatible("To be supported") + static enum NullListener implements MapMaker.RemovalListener { + INSTANCE; + + public void onRemoval(MapMaker.RemovalNotification notification) { + } + } +} diff --git a/src/main/com/google/common/collect/GwtTransient.java b/src/main/com/google/common/collect/GwtTransient.java new file mode 100644 index 0000000..f5a4a30 --- /dev/null +++ b/src/main/com/google/common/collect/GwtTransient.java @@ -0,0 +1,15 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +@GwtCompatible +@interface GwtTransient { +} diff --git a/src/main/com/google/common/collect/HashBasedTable.java b/src/main/com/google/common/collect/HashBasedTable.java new file mode 100644 index 0000000..3105264 --- /dev/null +++ b/src/main/com/google/common/collect/HashBasedTable.java @@ -0,0 +1,76 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Supplier; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +public class HashBasedTable extends StandardTable { + private static final long serialVersionUID = 0L; + + public static HashBasedTable create() { + return new HashBasedTable(new HashMap(), new HashBasedTable.Factory(0)); + } + + public static HashBasedTable create(int expectedRows, int expectedCellsPerRow) { + CollectPreconditions.checkNonnegative(expectedCellsPerRow, "expectedCellsPerRow"); + Map> backingMap = Maps.newHashMapWithExpectedSize(expectedRows); + return new HashBasedTable(backingMap, new HashBasedTable.Factory(expectedCellsPerRow)); + } + + public static HashBasedTable create(Table table) { + HashBasedTable result = create(); + result.putAll(table); + return result; + } + + HashBasedTable(Map> backingMap, HashBasedTable.Factory factory) { + super(backingMap, factory); + } + + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return super.contains(rowKey, columnKey); + } + + public boolean containsColumn(@Nullable Object columnKey) { + return super.containsColumn(columnKey); + } + + public boolean containsRow(@Nullable Object rowKey) { + return super.containsRow(rowKey); + } + + public boolean containsValue(@Nullable Object value) { + return super.containsValue(value); + } + + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + return super.get(rowKey, columnKey); + } + + public boolean equals(@Nullable Object obj) { + return super.equals(obj); + } + + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + return super.remove(rowKey, columnKey); + } + + private static class Factory implements Supplier>, Serializable { + final int expectedSize; + private static final long serialVersionUID = 0L; + + Factory(int expectedSize) { + this.expectedSize = expectedSize; + } + + public Map get() { + return Maps.newHashMapWithExpectedSize(this.expectedSize); + } + } +} diff --git a/src/main/com/google/common/collect/HashBiMap.java b/src/main/com/google/common/collect/HashBiMap.java new file mode 100644 index 0000000..dc0a31c --- /dev/null +++ b/src/main/com/google/common/collect/HashBiMap.java @@ -0,0 +1,586 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +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.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class HashBiMap extends AbstractMap implements BiMap, Serializable { + private static final double LOAD_FACTOR = 1.0D; + private transient HashBiMap.BiEntry[] hashTableKToV; + private transient HashBiMap.BiEntry[] hashTableVToK; + private transient int size; + private transient int mask; + private transient int modCount; + private transient BiMap inverse; + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0L; + + public static HashBiMap create() { + return create(16); + } + + public static HashBiMap create(int expectedSize) { + return new HashBiMap(expectedSize); + } + + public static HashBiMap create(Map map) { + HashBiMap bimap = create(map.size()); + bimap.putAll(map); + return bimap; + } + + private HashBiMap(int expectedSize) { + this.init(expectedSize); + } + + private void init(int expectedSize) { + CollectPreconditions.checkNonnegative(expectedSize, "expectedSize"); + int tableSize = Hashing.closedTableSize(expectedSize, 1.0D); + this.hashTableKToV = this.createTable(tableSize); + this.hashTableVToK = this.createTable(tableSize); + this.mask = tableSize - 1; + this.modCount = 0; + this.size = 0; + } + + private void delete(HashBiMap.BiEntry entry) { + int keyBucket = entry.keyHash & this.mask; + HashBiMap.BiEntry prevBucketEntry = null; + + for(HashBiMap.BiEntry bucketEntry = this.hashTableKToV[keyBucket]; bucketEntry != entry; bucketEntry = bucketEntry.nextInKToVBucket) { + prevBucketEntry = bucketEntry; + } + + if (prevBucketEntry == null) { + this.hashTableKToV[keyBucket] = entry.nextInKToVBucket; + } else { + prevBucketEntry.nextInKToVBucket = entry.nextInKToVBucket; + } + + int valueBucket = entry.valueHash & this.mask; + prevBucketEntry = null; + + for(HashBiMap.BiEntry bucketEntry = this.hashTableVToK[valueBucket]; bucketEntry != entry; bucketEntry = bucketEntry.nextInVToKBucket) { + prevBucketEntry = bucketEntry; + } + + if (prevBucketEntry == null) { + this.hashTableVToK[valueBucket] = entry.nextInVToKBucket; + } else { + prevBucketEntry.nextInVToKBucket = entry.nextInVToKBucket; + } + + --this.size; + ++this.modCount; + } + + private void insert(HashBiMap.BiEntry entry) { + int keyBucket = entry.keyHash & this.mask; + entry.nextInKToVBucket = this.hashTableKToV[keyBucket]; + this.hashTableKToV[keyBucket] = entry; + int valueBucket = entry.valueHash & this.mask; + entry.nextInVToKBucket = this.hashTableVToK[valueBucket]; + this.hashTableVToK[valueBucket] = entry; + ++this.size; + ++this.modCount; + } + + private static int hash(@Nullable Object o) { + return Hashing.smear(o == null ? 0 : o.hashCode()); + } + + private HashBiMap.BiEntry seekByKey(@Nullable Object key, int keyHash) { + for(HashBiMap.BiEntry entry = this.hashTableKToV[keyHash & this.mask]; entry != null; entry = entry.nextInKToVBucket) { + if (keyHash == entry.keyHash && Objects.equal(key, entry.key)) { + return entry; + } + } + + return null; + } + + private HashBiMap.BiEntry seekByValue(@Nullable Object value, int valueHash) { + for(HashBiMap.BiEntry entry = this.hashTableVToK[valueHash & this.mask]; entry != null; entry = entry.nextInVToKBucket) { + if (valueHash == entry.valueHash && Objects.equal(value, entry.value)) { + return entry; + } + } + + return null; + } + + public boolean containsKey(@Nullable Object key) { + return this.seekByKey(key, hash(key)) != null; + } + + public boolean containsValue(@Nullable Object value) { + return this.seekByValue(value, hash(value)) != null; + } + + @Nullable + public V get(@Nullable Object key) { + HashBiMap.BiEntry entry = this.seekByKey(key, hash(key)); + return entry == null ? null : entry.value; + } + + public V put(@Nullable K key, @Nullable V value) { + return this.put(key, value, false); + } + + public V forcePut(@Nullable K key, @Nullable V value) { + return this.put(key, value, true); + } + + private V put(@Nullable K key, @Nullable V value, boolean force) { + int keyHash = hash(key); + int valueHash = hash(value); + HashBiMap.BiEntry oldEntryForKey = this.seekByKey(key, keyHash); + if (oldEntryForKey != null && valueHash == oldEntryForKey.valueHash && Objects.equal(value, oldEntryForKey.value)) { + return value; + } else { + HashBiMap.BiEntry oldEntryForValue = this.seekByValue(value, valueHash); + if (oldEntryForValue != null) { + if (!force) { + String var9 = String.valueOf(String.valueOf(value)); + throw new IllegalArgumentException((new StringBuilder(23 + var9.length())).append("value already present: ").append(var9).toString()); + } + + this.delete(oldEntryForValue); + } + + if (oldEntryForKey != null) { + this.delete(oldEntryForKey); + } + + HashBiMap.BiEntry newEntry = new HashBiMap.BiEntry(key, keyHash, value, valueHash); + this.insert(newEntry); + this.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); + HashBiMap.BiEntry oldEntryForValue = this.seekByValue(value, valueHash); + if (oldEntryForValue != null && keyHash == oldEntryForValue.keyHash && Objects.equal(key, oldEntryForValue.key)) { + return key; + } else { + HashBiMap.BiEntry oldEntryForKey = this.seekByKey(key, keyHash); + if (oldEntryForKey != null) { + if (!force) { + String var9 = String.valueOf(String.valueOf(key)); + throw new IllegalArgumentException((new StringBuilder(23 + var9.length())).append("value already present: ").append(var9).toString()); + } + + this.delete(oldEntryForKey); + } + + if (oldEntryForValue != null) { + this.delete(oldEntryForValue); + } + + HashBiMap.BiEntry newEntry = new HashBiMap.BiEntry(key, keyHash, value, valueHash); + this.insert(newEntry); + this.rehashIfNecessary(); + return oldEntryForValue == null ? null : oldEntryForValue.key; + } + } + + private void rehashIfNecessary() { + HashBiMap.BiEntry[] oldKToV = this.hashTableKToV; + if (Hashing.needsResizing(this.size, oldKToV.length, 1.0D)) { + int newTableSize = oldKToV.length * 2; + this.hashTableKToV = this.createTable(newTableSize); + this.hashTableVToK = this.createTable(newTableSize); + this.mask = newTableSize - 1; + this.size = 0; + + HashBiMap.BiEntry nextEntry; + for(int bucket = 0; bucket < oldKToV.length; ++bucket) { + for(HashBiMap.BiEntry entry = oldKToV[bucket]; entry != null; entry = nextEntry) { + nextEntry = entry.nextInKToVBucket; + this.insert(entry); + } + } + + ++this.modCount; + } + + } + + private HashBiMap.BiEntry[] createTable(int length) { + return new HashBiMap.BiEntry[length]; + } + + public V remove(@Nullable Object key) { + HashBiMap.BiEntry entry = this.seekByKey(key, hash(key)); + if (entry == null) { + return null; + } else { + this.delete(entry); + return entry.value; + } + } + + public void clear() { + this.size = 0; + Arrays.fill(this.hashTableKToV, (Object)null); + Arrays.fill(this.hashTableVToK, (Object)null); + ++this.modCount; + } + + public int size() { + return this.size; + } + + public Set keySet() { + return new HashBiMap.KeySet(); + } + + public Set values() { + return this.inverse().keySet(); + } + + public Set> entrySet() { + return new HashBiMap.EntrySet(); + } + + public BiMap inverse() { + return this.inverse == null ? (this.inverse = new HashBiMap.Inverse()) : this.inverse; + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int size = Serialization.readCount(stream); + this.init(size); + Serialization.populateMap(this, stream, size); + } + + private static final class InverseSerializedForm implements Serializable { + private final HashBiMap bimap; + + InverseSerializedForm(HashBiMap bimap) { + this.bimap = bimap; + } + + Object readResolve() { + return this.bimap.inverse(); + } + } + + private final class Inverse extends AbstractMap implements BiMap, Serializable { + private Inverse() { + } + + BiMap forward() { + return HashBiMap.this; + } + + public int size() { + return HashBiMap.this.size; + } + + public void clear() { + this.forward().clear(); + } + + public boolean containsKey(@Nullable Object value) { + return this.forward().containsValue(value); + } + + public K get(@Nullable Object value) { + HashBiMap.BiEntry entry = HashBiMap.this.seekByValue(value, HashBiMap.hash(value)); + return entry == null ? null : entry.key; + } + + public K put(@Nullable V value, @Nullable K key) { + return HashBiMap.this.putInverse(value, key, false); + } + + public K forcePut(@Nullable V value, @Nullable K key) { + return HashBiMap.this.putInverse(value, key, true); + } + + public K remove(@Nullable Object value) { + HashBiMap.BiEntry entry = HashBiMap.this.seekByValue(value, HashBiMap.hash(value)); + if (entry == null) { + return null; + } else { + HashBiMap.this.delete(entry); + return entry.key; + } + } + + public BiMap inverse() { + return this.forward(); + } + + public Set keySet() { + return new HashBiMap.Inverse.InverseKeySet(); + } + + public Set values() { + return this.forward().keySet(); + } + + public Set> entrySet() { + return new Maps.EntrySet() { + Map map() { + return Inverse.this; + } + + public Iterator> iterator() { + return new HashBiMap.Itr>() { + Entry output(HashBiMap.BiEntry entry) { + return new null.InverseEntry(entry); + } + + class InverseEntry extends AbstractMapEntry { + HashBiMap.BiEntry delegate; + + InverseEntry(HashBiMap.BiEntry entry) { + this.delegate = entry; + } + + public V getKey() { + return this.delegate.value; + } + + public K getValue() { + return this.delegate.key; + } + + public K setValue(K key) { + K oldKey = this.delegate.key; + int keyHash = HashBiMap.hash(key); + if (keyHash == this.delegate.keyHash && Objects.equal(key, oldKey)) { + return key; + } else { + Preconditions.checkArgument(HashBiMap.this.seekByKey(key, keyHash) == null, "value already present: %s", key); + HashBiMap.this.delete(this.delegate); + HashBiMap.BiEntry newEntry = new HashBiMap.BiEntry(key, keyHash, this.delegate.value, this.delegate.valueHash); + HashBiMap.this.insert(newEntry); + expectedModCount = HashBiMap.this.modCount; + return oldKey; + } + } + } + }; + } + }; + } + + Object writeReplace() { + return new HashBiMap.InverseSerializedForm(HashBiMap.this); + } + + // $FF: synthetic method + Inverse(Object x1) { + this(); + } + + private final class InverseKeySet extends Maps.KeySet { + InverseKeySet() { + super(Inverse.this); + } + + public boolean remove(@Nullable Object o) { + HashBiMap.BiEntry entry = HashBiMap.this.seekByValue(o, HashBiMap.hash(o)); + if (entry == null) { + return false; + } else { + HashBiMap.this.delete(entry); + return true; + } + } + + public Iterator iterator() { + return new HashBiMap.Itr() { + V output(HashBiMap.BiEntry entry) { + return entry.value; + } + }; + } + } + } + + private final class EntrySet extends Maps.EntrySet { + private EntrySet() { + } + + Map map() { + return HashBiMap.this; + } + + public Iterator> iterator() { + return new HashBiMap.Itr>() { + Entry output(HashBiMap.BiEntry entry) { + return new null.MapEntry(entry); + } + + class MapEntry extends AbstractMapEntry { + HashBiMap.BiEntry delegate; + + MapEntry(HashBiMap.BiEntry entry) { + this.delegate = entry; + } + + public K getKey() { + return this.delegate.key; + } + + public V getValue() { + return this.delegate.value; + } + + public V setValue(V value) { + V oldValue = this.delegate.value; + int valueHash = HashBiMap.hash(value); + if (valueHash == this.delegate.valueHash && Objects.equal(value, oldValue)) { + return value; + } else { + Preconditions.checkArgument(HashBiMap.this.seekByValue(value, valueHash) == null, "value already present: %s", value); + HashBiMap.this.delete(this.delegate); + HashBiMap.BiEntry newEntry = new HashBiMap.BiEntry(this.delegate.key, this.delegate.keyHash, value, valueHash); + HashBiMap.this.insert(newEntry); + expectedModCount = HashBiMap.this.modCount; + if (toRemove == this.delegate) { + toRemove = newEntry; + } + + this.delegate = newEntry; + return oldValue; + } + } + } + }; + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } + + private final class KeySet extends Maps.KeySet { + KeySet() { + super(HashBiMap.this); + } + + public Iterator iterator() { + return new HashBiMap.Itr() { + K output(HashBiMap.BiEntry entry) { + return entry.key; + } + }; + } + + public boolean remove(@Nullable Object o) { + HashBiMap.BiEntry entry = HashBiMap.this.seekByKey(o, HashBiMap.hash(o)); + if (entry == null) { + return false; + } else { + HashBiMap.this.delete(entry); + return true; + } + } + } + + abstract class Itr implements Iterator { + int nextBucket = 0; + HashBiMap.BiEntry next = null; + HashBiMap.BiEntry toRemove = null; + int expectedModCount; + + Itr() { + this.expectedModCount = HashBiMap.this.modCount; + } + + private void checkForConcurrentModification() { + if (HashBiMap.this.modCount != this.expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + public boolean hasNext() { + this.checkForConcurrentModification(); + if (this.next != null) { + return true; + } else { + while(this.nextBucket < HashBiMap.this.hashTableKToV.length) { + if (HashBiMap.this.hashTableKToV[this.nextBucket] != null) { + this.next = HashBiMap.this.hashTableKToV[this.nextBucket++]; + return true; + } + + ++this.nextBucket; + } + + return false; + } + } + + public T next() { + this.checkForConcurrentModification(); + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + HashBiMap.BiEntry entry = this.next; + this.next = entry.nextInKToVBucket; + this.toRemove = entry; + return this.output(entry); + } + } + + public void remove() { + this.checkForConcurrentModification(); + CollectPreconditions.checkRemove(this.toRemove != null); + HashBiMap.this.delete(this.toRemove); + this.expectedModCount = HashBiMap.this.modCount; + this.toRemove = null; + } + + abstract T output(HashBiMap.BiEntry var1); + } + + private static final class BiEntry extends ImmutableEntry { + final int keyHash; + final int valueHash; + @Nullable + HashBiMap.BiEntry nextInKToVBucket; + @Nullable + HashBiMap.BiEntry nextInVToKBucket; + + BiEntry(K key, int keyHash, V value, int valueHash) { + super(key, value); + this.keyHash = keyHash; + this.valueHash = valueHash; + } + } +} diff --git a/src/main/com/google/common/collect/HashMultimap.java b/src/main/com/google/common/collect/HashMultimap.java new file mode 100644 index 0000000..199783e --- /dev/null +++ b/src/main/com/google/common/collect/HashMultimap.java @@ -0,0 +1,73 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@GwtCompatible( + serializable = true, + emulated = true +) +public final class HashMultimap extends AbstractSetMultimap { + private static final int DEFAULT_VALUES_PER_KEY = 2; + @VisibleForTesting + transient int expectedValuesPerKey = 2; + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0L; + + public static HashMultimap create() { + return new HashMultimap(); + } + + public static HashMultimap create(int expectedKeys, int expectedValuesPerKey) { + return new HashMultimap(expectedKeys, expectedValuesPerKey); + } + + public static HashMultimap create(Multimap multimap) { + return new HashMultimap(multimap); + } + + private HashMultimap() { + super(new HashMap()); + } + + private HashMultimap(int expectedKeys, int expectedValuesPerKey) { + super(Maps.newHashMapWithExpectedSize(expectedKeys)); + Preconditions.checkArgument(expectedValuesPerKey >= 0); + this.expectedValuesPerKey = expectedValuesPerKey; + } + + private HashMultimap(Multimap multimap) { + super(Maps.newHashMapWithExpectedSize(multimap.keySet().size())); + this.putAll(multimap); + } + + Set createCollection() { + return Sets.newHashSetWithExpectedSize(this.expectedValuesPerKey); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(this.expectedValuesPerKey); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.expectedValuesPerKey = stream.readInt(); + int distinctKeys = Serialization.readCount(stream); + Map> map = Maps.newHashMapWithExpectedSize(distinctKeys); + this.setMap(map); + Serialization.populateMultimap(this, stream, distinctKeys); + } +} diff --git a/src/main/com/google/common/collect/HashMultiset.java b/src/main/com/google/common/collect/HashMultiset.java new file mode 100644 index 0000000..ab80fdf --- /dev/null +++ b/src/main/com/google/common/collect/HashMultiset.java @@ -0,0 +1,53 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; + +@GwtCompatible( + serializable = true, + emulated = true +) +public final class HashMultiset extends AbstractMapBasedMultiset { + @GwtIncompatible("Not needed in emulated source.") + private static final long serialVersionUID = 0L; + + public static HashMultiset create() { + return new HashMultiset(); + } + + public static HashMultiset create(int distinctElements) { + return new HashMultiset(distinctElements); + } + + public static HashMultiset create(Iterable elements) { + HashMultiset multiset = create(Multisets.inferDistinctElements(elements)); + Iterables.addAll(multiset, elements); + return multiset; + } + + private HashMultiset() { + super(new HashMap()); + } + + private HashMultiset(int distinctElements) { + super(Maps.newHashMapWithExpectedSize(distinctElements)); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int distinctElements = Serialization.readCount(stream); + this.setBackingMap(Maps.newHashMapWithExpectedSize(distinctElements)); + Serialization.populateMultiset(this, stream, distinctElements); + } +} diff --git a/src/main/com/google/common/collect/Hashing.java b/src/main/com/google/common/collect/Hashing.java new file mode 100644 index 0000000..8e1da5f --- /dev/null +++ b/src/main/com/google/common/collect/Hashing.java @@ -0,0 +1,37 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +final class Hashing { + private static final int C1 = -862048943; + private static final int C2 = 461845907; + private static int MAX_TABLE_SIZE = 1073741824; + + private Hashing() { + } + + static int smear(int hashCode) { + return 461845907 * Integer.rotateLeft(hashCode * -862048943, 15); + } + + static int smearedHash(@Nullable Object o) { + return smear(o == null ? 0 : o.hashCode()); + } + + static int closedTableSize(int expectedEntries, double loadFactor) { + expectedEntries = Math.max(expectedEntries, 2); + int tableSize = Integer.highestOneBit(expectedEntries); + if (expectedEntries > (int)(loadFactor * (double)tableSize)) { + tableSize <<= 1; + return tableSize > 0 ? tableSize : MAX_TABLE_SIZE; + } else { + return tableSize; + } + } + + static boolean needsResizing(int size, int tableSize, double loadFactor) { + return (double)size > loadFactor * (double)tableSize && tableSize < MAX_TABLE_SIZE; + } +} diff --git a/src/main/com/google/common/collect/ImmutableAsList.java b/src/main/com/google/common/collect/ImmutableAsList.java new file mode 100644 index 0000000..f37cfc1 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableAsList.java @@ -0,0 +1,55 @@ +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; + +@GwtCompatible( + serializable = true, + emulated = true +) +abstract class ImmutableAsList extends ImmutableList { + abstract ImmutableCollection delegateCollection(); + + public boolean contains(Object target) { + return this.delegateCollection().contains(target); + } + + public int size() { + return this.delegateCollection().size(); + } + + public boolean isEmpty() { + return this.delegateCollection().isEmpty(); + } + + boolean isPartialView() { + return this.delegateCollection().isPartialView(); + } + + @GwtIncompatible("serialization") + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @GwtIncompatible("serialization") + Object writeReplace() { + return new ImmutableAsList.SerializedForm(this.delegateCollection()); + } + + @GwtIncompatible("serialization") + static class SerializedForm implements Serializable { + final ImmutableCollection collection; + private static final long serialVersionUID = 0L; + + SerializedForm(ImmutableCollection collection) { + this.collection = collection; + } + + Object readResolve() { + return this.collection.asList(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableBiMap.java b/src/main/com/google/common/collect/ImmutableBiMap.java new file mode 100644 index 0000000..29fed4e --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableBiMap.java @@ -0,0 +1,116 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import java.util.Map.Entry; + +@GwtCompatible( + serializable = true, + emulated = true +) +public abstract class ImmutableBiMap extends ImmutableMap implements BiMap { + private static final Entry[] EMPTY_ENTRY_ARRAY = new Entry[0]; + + public static ImmutableBiMap of() { + return EmptyImmutableBiMap.INSTANCE; + } + + public static ImmutableBiMap of(K k1, V v1) { + return new SingletonImmutableBiMap(k1, v1); + } + + public static ImmutableBiMap of(K k1, V v1, K k2, V v2) { + return new RegularImmutableBiMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2)}); + } + + public static ImmutableBiMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return new RegularImmutableBiMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)}); + } + + public static ImmutableBiMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return new RegularImmutableBiMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)}); + } + + public static ImmutableBiMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return new RegularImmutableBiMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5)}); + } + + public static ImmutableBiMap.Builder builder() { + return new ImmutableBiMap.Builder(); + } + + public static ImmutableBiMap copyOf(Map map) { + if (map instanceof ImmutableBiMap) { + ImmutableBiMap bimap = (ImmutableBiMap)map; + if (!bimap.isPartialView()) { + return bimap; + } + } + + Entry[] entries = (Entry[])map.entrySet().toArray(EMPTY_ENTRY_ARRAY); + switch(entries.length) { + case 0: + return of(); + case 1: + Entry entry = entries[0]; + return of(entry.getKey(), entry.getValue()); + default: + return new RegularImmutableBiMap(entries); + } + } + + ImmutableBiMap() { + } + + public abstract ImmutableBiMap inverse(); + + public ImmutableSet values() { + return this.inverse().keySet(); + } + + /** @deprecated */ + @Deprecated + public V forcePut(K key, V value) { + throw new UnsupportedOperationException(); + } + + Object writeReplace() { + return new ImmutableBiMap.SerializedForm(this); + } + + private static class SerializedForm extends ImmutableMap.SerializedForm { + private static final long serialVersionUID = 0L; + + SerializedForm(ImmutableBiMap bimap) { + super(bimap); + } + + Object readResolve() { + ImmutableBiMap.Builder builder = new ImmutableBiMap.Builder(); + return this.createMap(builder); + } + } + + public static final class Builder extends ImmutableMap.Builder { + public ImmutableBiMap.Builder put(K key, V value) { + super.put(key, value); + return this; + } + + public ImmutableBiMap.Builder putAll(Map map) { + super.putAll(map); + return this; + } + + public ImmutableBiMap build() { + switch(this.size) { + case 0: + return ImmutableBiMap.of(); + case 1: + return ImmutableBiMap.of(this.entries[0].getKey(), this.entries[0].getValue()); + default: + return new RegularImmutableBiMap(this.size, this.entries); + } + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableClassToInstanceMap.java b/src/main/com/google/common/collect/ImmutableClassToInstanceMap.java new file mode 100644 index 0000000..0cfff05 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableClassToInstanceMap.java @@ -0,0 +1,80 @@ +package com.google.common.collect; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Primitives; +import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +public final class ImmutableClassToInstanceMap extends ForwardingMap, B> implements ClassToInstanceMap, Serializable { + private final ImmutableMap, B> delegate; + + public static ImmutableClassToInstanceMap.Builder builder() { + return new ImmutableClassToInstanceMap.Builder(); + } + + public static ImmutableClassToInstanceMap copyOf(Map, ? extends S> map) { + if (map instanceof ImmutableClassToInstanceMap) { + ImmutableClassToInstanceMap cast = (ImmutableClassToInstanceMap)map; + return cast; + } else { + return (new ImmutableClassToInstanceMap.Builder()).putAll(map).build(); + } + } + + private ImmutableClassToInstanceMap(ImmutableMap, B> delegate) { + this.delegate = delegate; + } + + protected Map, B> delegate() { + return this.delegate; + } + + @Nullable + public T getInstance(Class type) { + return this.delegate.get(Preconditions.checkNotNull(type)); + } + + /** @deprecated */ + @Deprecated + public T putInstance(Class type, T value) { + throw new UnsupportedOperationException(); + } + + // $FF: synthetic method + ImmutableClassToInstanceMap(ImmutableMap x0, Object x1) { + this(x0); + } + + public static final class Builder { + private final ImmutableMap.Builder, B> mapBuilder = ImmutableMap.builder(); + + public ImmutableClassToInstanceMap.Builder put(Class key, T value) { + this.mapBuilder.put(key, value); + return this; + } + + public ImmutableClassToInstanceMap.Builder putAll(Map, ? extends T> map) { + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry, ? extends T> entry = (Entry)i$.next(); + Class type = (Class)entry.getKey(); + T value = entry.getValue(); + this.mapBuilder.put(type, cast(type, value)); + } + + return this; + } + + private static T cast(Class type, B value) { + return Primitives.wrap(type).cast(value); + } + + public ImmutableClassToInstanceMap build() { + return new ImmutableClassToInstanceMap(this.mapBuilder.build()); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableCollection.java b/src/main/com/google/common/collect/ImmutableCollection.java new file mode 100644 index 0000000..ee02663 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableCollection.java @@ -0,0 +1,218 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public abstract class ImmutableCollection extends AbstractCollection implements Serializable { + private transient ImmutableList asList; + + ImmutableCollection() { + } + + public abstract UnmodifiableIterator iterator(); + + public final Object[] toArray() { + int size = this.size(); + if (size == 0) { + return ObjectArrays.EMPTY_ARRAY; + } else { + Object[] result = new Object[size]; + this.copyIntoArray(result, 0); + return result; + } + } + + public final T[] toArray(T[] other) { + Preconditions.checkNotNull(other); + int size = this.size(); + if (other.length < size) { + other = ObjectArrays.newArray(other, size); + } else if (other.length > size) { + other[size] = null; + } + + this.copyIntoArray(other, 0); + return other; + } + + public boolean contains(@Nullable Object object) { + return object != null && super.contains(object); + } + + /** @deprecated */ + @Deprecated + public final boolean add(E e) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final boolean remove(Object object) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final boolean addAll(Collection newElements) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final boolean removeAll(Collection oldElements) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final boolean retainAll(Collection elementsToKeep) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final void clear() { + throw new UnsupportedOperationException(); + } + + public ImmutableList asList() { + ImmutableList list = this.asList; + return list == null ? (this.asList = this.createAsList()) : list; + } + + ImmutableList createAsList() { + switch(this.size()) { + case 0: + return ImmutableList.of(); + case 1: + return ImmutableList.of(this.iterator().next()); + default: + return new RegularImmutableAsList(this, this.toArray()); + } + } + + abstract boolean isPartialView(); + + int copyIntoArray(Object[] dst, int offset) { + Object e; + for(Iterator i$ = this.iterator(); i$.hasNext(); dst[offset++] = e) { + e = i$.next(); + } + + return offset; + } + + Object writeReplace() { + return new ImmutableList.SerializedForm(this.toArray()); + } + + abstract static class ArrayBasedBuilder extends ImmutableCollection.Builder { + Object[] contents; + int size; + + ArrayBasedBuilder(int initialCapacity) { + CollectPreconditions.checkNonnegative(initialCapacity, "initialCapacity"); + this.contents = new Object[initialCapacity]; + this.size = 0; + } + + private void ensureCapacity(int minCapacity) { + if (this.contents.length < minCapacity) { + this.contents = ObjectArrays.arraysCopyOf(this.contents, expandedCapacity(this.contents.length, minCapacity)); + } + + } + + public ImmutableCollection.ArrayBasedBuilder add(E element) { + Preconditions.checkNotNull(element); + this.ensureCapacity(this.size + 1); + this.contents[this.size++] = element; + return this; + } + + public ImmutableCollection.Builder add(E... elements) { + ObjectArrays.checkElementsNotNull(elements); + this.ensureCapacity(this.size + elements.length); + System.arraycopy(elements, 0, this.contents, this.size, elements.length); + this.size += elements.length; + return this; + } + + public ImmutableCollection.Builder addAll(Iterable elements) { + if (elements instanceof Collection) { + Collection collection = (Collection)elements; + this.ensureCapacity(this.size + collection.size()); + } + + super.addAll(elements); + return this; + } + } + + public abstract static class Builder { + static final int DEFAULT_INITIAL_CAPACITY = 4; + + static int expandedCapacity(int oldCapacity, int minCapacity) { + if (minCapacity < 0) { + throw new AssertionError("cannot store more than MAX_VALUE elements"); + } else { + int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; + if (newCapacity < minCapacity) { + newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; + } + + if (newCapacity < 0) { + newCapacity = Integer.MAX_VALUE; + } + + return newCapacity; + } + } + + Builder() { + } + + public abstract ImmutableCollection.Builder add(E var1); + + public ImmutableCollection.Builder add(E... elements) { + Object[] arr$ = elements; + int len$ = elements.length; + + for(int i$ = 0; i$ < len$; ++i$) { + E element = arr$[i$]; + this.add(element); + } + + return this; + } + + public ImmutableCollection.Builder addAll(Iterable elements) { + Iterator i$ = elements.iterator(); + + while(i$.hasNext()) { + E element = i$.next(); + this.add(element); + } + + return this; + } + + public ImmutableCollection.Builder addAll(Iterator elements) { + while(elements.hasNext()) { + this.add(elements.next()); + } + + return this; + } + + public abstract ImmutableCollection build(); + } +} diff --git a/src/main/com/google/common/collect/ImmutableEntry.java b/src/main/com/google/common/collect/ImmutableEntry.java new file mode 100644 index 0000000..37606d5 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableEntry.java @@ -0,0 +1,33 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +class ImmutableEntry extends AbstractMapEntry implements Serializable { + final K key; + final V value; + private static final long serialVersionUID = 0L; + + ImmutableEntry(@Nullable K key, @Nullable V value) { + this.key = key; + this.value = value; + } + + @Nullable + public final K getKey() { + return this.key; + } + + @Nullable + public final V getValue() { + return this.value; + } + + public final V setValue(V value) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/ImmutableEnumMap.java b/src/main/com/google/common/collect/ImmutableEnumMap.java new file mode 100644 index 0000000..944b7e6 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableEnumMap.java @@ -0,0 +1,119 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class ImmutableEnumMap, V> extends ImmutableMap { + private final transient EnumMap delegate; + + static , V> ImmutableMap asImmutable(EnumMap map) { + switch(map.size()) { + case 0: + return ImmutableMap.of(); + case 1: + Entry entry = (Entry)Iterables.getOnlyElement(map.entrySet()); + return ImmutableMap.of(entry.getKey(), entry.getValue()); + default: + return new ImmutableEnumMap(map); + } + } + + private ImmutableEnumMap(EnumMap delegate) { + this.delegate = delegate; + Preconditions.checkArgument(!delegate.isEmpty()); + } + + ImmutableSet createKeySet() { + return new ImmutableSet() { + public boolean contains(Object object) { + return ImmutableEnumMap.this.delegate.containsKey(object); + } + + public int size() { + return ImmutableEnumMap.this.size(); + } + + public UnmodifiableIterator iterator() { + return Iterators.unmodifiableIterator(ImmutableEnumMap.this.delegate.keySet().iterator()); + } + + boolean isPartialView() { + return true; + } + }; + } + + public int size() { + return this.delegate.size(); + } + + public boolean containsKey(@Nullable Object key) { + return this.delegate.containsKey(key); + } + + public V get(Object key) { + return this.delegate.get(key); + } + + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet() { + ImmutableMap map() { + return ImmutableEnumMap.this; + } + + public UnmodifiableIterator> iterator() { + return new UnmodifiableIterator>() { + private final Iterator> backingIterator; + + { + this.backingIterator = ImmutableEnumMap.this.delegate.entrySet().iterator(); + } + + public boolean hasNext() { + return this.backingIterator.hasNext(); + } + + public Entry next() { + Entry entry = (Entry)this.backingIterator.next(); + return Maps.immutableEntry(entry.getKey(), entry.getValue()); + } + }; + } + }; + } + + boolean isPartialView() { + return false; + } + + Object writeReplace() { + return new ImmutableEnumMap.EnumSerializedForm(this.delegate); + } + + // $FF: synthetic method + ImmutableEnumMap(EnumMap x0, Object x1) { + this(x0); + } + + private static class EnumSerializedForm, V> implements Serializable { + final EnumMap delegate; + private static final long serialVersionUID = 0L; + + EnumSerializedForm(EnumMap delegate) { + this.delegate = delegate; + } + + Object readResolve() { + return new ImmutableEnumMap(this.delegate); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableEnumSet.java b/src/main/com/google/common/collect/ImmutableEnumSet.java new file mode 100644 index 0000000..248cc86 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableEnumSet.java @@ -0,0 +1,89 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Collection; +import java.util.EnumSet; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class ImmutableEnumSet> extends ImmutableSet { + private final transient EnumSet delegate; + private transient int hashCode; + + static > ImmutableSet asImmutable(EnumSet set) { + switch(set.size()) { + case 0: + return ImmutableSet.of(); + case 1: + return ImmutableSet.of(Iterables.getOnlyElement(set)); + default: + return new ImmutableEnumSet(set); + } + } + + private ImmutableEnumSet(EnumSet delegate) { + this.delegate = delegate; + } + + boolean isPartialView() { + return false; + } + + public UnmodifiableIterator iterator() { + return Iterators.unmodifiableIterator(this.delegate.iterator()); + } + + public int size() { + return this.delegate.size(); + } + + public boolean contains(Object object) { + return this.delegate.contains(object); + } + + public boolean containsAll(Collection collection) { + return this.delegate.containsAll(collection); + } + + public boolean isEmpty() { + return this.delegate.isEmpty(); + } + + public boolean equals(Object object) { + return object == this || this.delegate.equals(object); + } + + public int hashCode() { + int result = this.hashCode; + return result == 0 ? (this.hashCode = this.delegate.hashCode()) : result; + } + + public String toString() { + return this.delegate.toString(); + } + + Object writeReplace() { + return new ImmutableEnumSet.EnumSerializedForm(this.delegate); + } + + // $FF: synthetic method + ImmutableEnumSet(EnumSet x0, Object x1) { + this(x0); + } + + private static class EnumSerializedForm> implements Serializable { + final EnumSet delegate; + private static final long serialVersionUID = 0L; + + EnumSerializedForm(EnumSet delegate) { + this.delegate = delegate; + } + + Object readResolve() { + return new ImmutableEnumSet(this.delegate.clone()); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableList.java b/src/main/com/google/common/collect/ImmutableList.java new file mode 100644 index 0000000..e75bef0 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableList.java @@ -0,0 +1,393 @@ +package com.google.common.collect; + +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.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.RandomAccess; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public abstract class ImmutableList extends ImmutableCollection implements List, RandomAccess { + private static final ImmutableList EMPTY; + + public static ImmutableList of() { + return EMPTY; + } + + public static ImmutableList of(E element) { + return new SingletonImmutableList(element); + } + + public static ImmutableList of(E e1, E e2) { + return construct(e1, e2); + } + + public static ImmutableList of(E e1, E e2, E e3) { + return construct(e1, e2, e3); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4) { + return construct(e1, e2, e3, e4); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5) { + return construct(e1, e2, e3, e4, e5); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6) { + return construct(e1, e2, e3, e4, e5, e6); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) { + return construct(e1, e2, e3, e4, e5, e6, e7); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8, e9); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11) { + return construct(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11); + } + + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E e11, E e12, E... others) { + Object[] array = new Object[12 + others.length]; + array[0] = e1; + array[1] = e2; + array[2] = e3; + array[3] = e4; + array[4] = e5; + array[5] = e6; + array[6] = e7; + array[7] = e8; + array[8] = e9; + array[9] = e10; + array[10] = e11; + array[11] = e12; + System.arraycopy(others, 0, array, 12, others.length); + return construct(array); + } + + public static ImmutableList copyOf(Iterable elements) { + Preconditions.checkNotNull(elements); + return elements instanceof Collection ? copyOf((Collection)elements) : copyOf(elements.iterator()); + } + + public static ImmutableList copyOf(Collection elements) { + if (elements instanceof ImmutableCollection) { + ImmutableList list = ((ImmutableCollection)elements).asList(); + return list.isPartialView() ? asImmutableList(list.toArray()) : list; + } else { + return construct(elements.toArray()); + } + } + + public static ImmutableList copyOf(Iterator elements) { + if (!elements.hasNext()) { + return of(); + } else { + E first = elements.next(); + return !elements.hasNext() ? of(first) : (new ImmutableList.Builder()).add(first).addAll(elements).build(); + } + } + + public static ImmutableList copyOf(E[] elements) { + switch(elements.length) { + case 0: + return of(); + case 1: + return new SingletonImmutableList(elements[0]); + default: + return new RegularImmutableList(ObjectArrays.checkElementsNotNull((Object[])elements.clone())); + } + } + + private static ImmutableList construct(Object... elements) { + return asImmutableList(ObjectArrays.checkElementsNotNull(elements)); + } + + static ImmutableList asImmutableList(Object[] elements) { + return asImmutableList(elements, elements.length); + } + + static ImmutableList asImmutableList(Object[] elements, int length) { + switch(length) { + case 0: + return of(); + case 1: + ImmutableList list = new SingletonImmutableList(elements[0]); + return list; + default: + if (length < elements.length) { + elements = ObjectArrays.arraysCopyOf(elements, length); + } + + return new RegularImmutableList(elements); + } + } + + ImmutableList() { + } + + public UnmodifiableIterator iterator() { + return this.listIterator(); + } + + public UnmodifiableListIterator listIterator() { + return this.listIterator(0); + } + + public UnmodifiableListIterator listIterator(int index) { + return new AbstractIndexedListIterator(this.size(), index) { + protected E get(int index) { + return ImmutableList.this.get(index); + } + }; + } + + public int indexOf(@Nullable Object object) { + return object == null ? -1 : Lists.indexOfImpl(this, object); + } + + public int lastIndexOf(@Nullable Object object) { + return object == null ? -1 : Lists.lastIndexOfImpl(this, object); + } + + public boolean contains(@Nullable Object object) { + return this.indexOf(object) >= 0; + } + + public ImmutableList subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, this.size()); + int length = toIndex - fromIndex; + switch(length) { + case 0: + return of(); + case 1: + return of(this.get(fromIndex)); + default: + return this.subListUnchecked(fromIndex, toIndex); + } + } + + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + return new ImmutableList.SubList(fromIndex, toIndex - fromIndex); + } + + /** @deprecated */ + @Deprecated + public final boolean addAll(int index, Collection newElements) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final E set(int index, E element) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final void add(int index, E element) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final E remove(int index) { + throw new UnsupportedOperationException(); + } + + public final ImmutableList asList() { + return this; + } + + int copyIntoArray(Object[] dst, int offset) { + int size = this.size(); + + for(int i = 0; i < size; ++i) { + dst[offset + i] = this.get(i); + } + + return offset + size; + } + + public ImmutableList reverse() { + return new ImmutableList.ReverseImmutableList(this); + } + + public boolean equals(@Nullable Object obj) { + return Lists.equalsImpl(this, obj); + } + + public int hashCode() { + int hashCode = 1; + int n = this.size(); + + for(int i = 0; i < n; ++i) { + hashCode = 31 * hashCode + this.get(i).hashCode(); + hashCode = ~(~hashCode); + } + + return hashCode; + } + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + Object writeReplace() { + return new ImmutableList.SerializedForm(this.toArray()); + } + + public static ImmutableList.Builder builder() { + return new ImmutableList.Builder(); + } + + static { + EMPTY = new RegularImmutableList(ObjectArrays.EMPTY_ARRAY); + } + + public static final class Builder extends ImmutableCollection.ArrayBasedBuilder { + public Builder() { + this(4); + } + + Builder(int capacity) { + super(capacity); + } + + public ImmutableList.Builder add(E element) { + super.add(element); + return this; + } + + public ImmutableList.Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + public ImmutableList.Builder add(E... elements) { + super.add(elements); + return this; + } + + public ImmutableList.Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + public ImmutableList build() { + return ImmutableList.asImmutableList(this.contents, this.size); + } + } + + static class SerializedForm implements Serializable { + final Object[] elements; + private static final long serialVersionUID = 0L; + + SerializedForm(Object[] elements) { + this.elements = elements; + } + + Object readResolve() { + return ImmutableList.copyOf(this.elements); + } + } + + private static class ReverseImmutableList extends ImmutableList { + private final transient ImmutableList forwardList; + + ReverseImmutableList(ImmutableList backingList) { + this.forwardList = backingList; + } + + private int reverseIndex(int index) { + return this.size() - 1 - index; + } + + private int reversePosition(int index) { + return this.size() - index; + } + + public ImmutableList reverse() { + return this.forwardList; + } + + public boolean contains(@Nullable Object object) { + return this.forwardList.contains(object); + } + + public int indexOf(@Nullable Object object) { + int index = this.forwardList.lastIndexOf(object); + return index >= 0 ? this.reverseIndex(index) : -1; + } + + public int lastIndexOf(@Nullable Object object) { + int index = this.forwardList.indexOf(object); + return index >= 0 ? this.reverseIndex(index) : -1; + } + + public ImmutableList subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, this.size()); + return this.forwardList.subList(this.reversePosition(toIndex), this.reversePosition(fromIndex)).reverse(); + } + + public E get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.forwardList.get(this.reverseIndex(index)); + } + + public int size() { + return this.forwardList.size(); + } + + boolean isPartialView() { + return this.forwardList.isPartialView(); + } + } + + class SubList extends ImmutableList { + final transient int offset; + final transient int length; + + SubList(int offset, int length) { + this.offset = offset; + this.length = length; + } + + public int size() { + return this.length; + } + + public E get(int index) { + Preconditions.checkElementIndex(index, this.length); + return ImmutableList.this.get(index + this.offset); + } + + public ImmutableList subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, this.length); + return ImmutableList.this.subList(fromIndex + this.offset, toIndex + this.offset); + } + + boolean isPartialView() { + return true; + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableListMultimap.java b/src/main/com/google/common/collect/ImmutableListMultimap.java new file mode 100644 index 0000000..ee316b5 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableListMultimap.java @@ -0,0 +1,225 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public class ImmutableListMultimap extends ImmutableMultimap implements ListMultimap { + private transient ImmutableListMultimap inverse; + @GwtIncompatible("Not needed in emulated source") + private static final long serialVersionUID = 0L; + + public static ImmutableListMultimap of() { + return EmptyImmutableListMultimap.INSTANCE; + } + + public static ImmutableListMultimap of(K k1, V v1) { + ImmutableListMultimap.Builder builder = builder(); + builder.put(k1, v1); + return builder.build(); + } + + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2) { + ImmutableListMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + return builder.build(); + } + + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + ImmutableListMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + return builder.build(); + } + + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + ImmutableListMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + return builder.build(); + } + + public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + ImmutableListMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + builder.put(k5, v5); + return builder.build(); + } + + public static ImmutableListMultimap.Builder builder() { + return new ImmutableListMultimap.Builder(); + } + + public static ImmutableListMultimap copyOf(Multimap multimap) { + if (multimap.isEmpty()) { + return of(); + } else { + if (multimap instanceof ImmutableListMultimap) { + ImmutableListMultimap kvMultimap = (ImmutableListMultimap)multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int size = 0; + Iterator i$ = multimap.asMap().entrySet().iterator(); + + while(i$.hasNext()) { + Entry> entry = (Entry)i$.next(); + ImmutableList list = ImmutableList.copyOf((Collection)entry.getValue()); + if (!list.isEmpty()) { + builder.put(entry.getKey(), list); + size += list.size(); + } + } + + return new ImmutableListMultimap(builder.build(), size); + } + } + + ImmutableListMultimap(ImmutableMap> map, int size) { + super(map, size); + } + + public ImmutableList get(@Nullable K key) { + ImmutableList list = (ImmutableList)this.map.get(key); + return list == null ? ImmutableList.of() : list; + } + + public ImmutableListMultimap inverse() { + ImmutableListMultimap result = this.inverse; + return result == null ? (this.inverse = this.invert()) : result; + } + + private ImmutableListMultimap invert() { + ImmutableListMultimap.Builder builder = builder(); + Iterator i$ = this.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + builder.put(entry.getValue(), entry.getKey()); + } + + ImmutableListMultimap invertedMultimap = builder.build(); + invertedMultimap.inverse = this; + return invertedMultimap; + } + + /** @deprecated */ + @Deprecated + public ImmutableList removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public ImmutableList replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int keyCount = stream.readInt(); + if (keyCount < 0) { + throw new InvalidObjectException((new StringBuilder(29)).append("Invalid key count ").append(keyCount).toString()); + } else { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int tmpSize = 0; + + for(int i = 0; i < keyCount; ++i) { + Object key = stream.readObject(); + int valueCount = stream.readInt(); + if (valueCount <= 0) { + throw new InvalidObjectException((new StringBuilder(31)).append("Invalid value count ").append(valueCount).toString()); + } + + Object[] array = new Object[valueCount]; + + for(int j = 0; j < valueCount; ++j) { + array[j] = stream.readObject(); + } + + builder.put(key, ImmutableList.copyOf(array)); + tmpSize += valueCount; + } + + ImmutableMap tmpMap; + try { + tmpMap = builder.build(); + } catch (IllegalArgumentException var10) { + throw (InvalidObjectException)(new InvalidObjectException(var10.getMessage())).initCause(var10); + } + + ImmutableMultimap.FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); + ImmutableMultimap.FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); + } + } + + public static final class Builder extends ImmutableMultimap.Builder { + public ImmutableListMultimap.Builder put(K key, V value) { + super.put(key, value); + return this; + } + + public ImmutableListMultimap.Builder put(Entry entry) { + super.put(entry); + return this; + } + + public ImmutableListMultimap.Builder putAll(K key, Iterable values) { + super.putAll(key, values); + return this; + } + + public ImmutableListMultimap.Builder putAll(K key, V... values) { + super.putAll(key, values); + return this; + } + + public ImmutableListMultimap.Builder putAll(Multimap multimap) { + super.putAll(multimap); + return this; + } + + public ImmutableListMultimap.Builder orderKeysBy(Comparator keyComparator) { + super.orderKeysBy(keyComparator); + return this; + } + + public ImmutableListMultimap.Builder orderValuesBy(Comparator valueComparator) { + super.orderValuesBy(valueComparator); + return this; + } + + public ImmutableListMultimap build() { + return (ImmutableListMultimap)super.build(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableMap.java b/src/main/com/google/common/collect/ImmutableMap.java new file mode 100644 index 0000000..5579253 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableMap.java @@ -0,0 +1,341 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public abstract class ImmutableMap implements Map, Serializable { + private static final Entry[] EMPTY_ENTRY_ARRAY = new Entry[0]; + private transient ImmutableSet> entrySet; + private transient ImmutableSet keySet; + private transient ImmutableCollection values; + private transient ImmutableSetMultimap multimapView; + + public static ImmutableMap of() { + return ImmutableBiMap.of(); + } + + public static ImmutableMap of(K k1, V v1) { + return ImmutableBiMap.of(k1, v1); + } + + public static ImmutableMap of(K k1, V v1, K k2, V v2) { + return new RegularImmutableMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2)}); + } + + public static ImmutableMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return new RegularImmutableMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)}); + } + + public static ImmutableMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return new RegularImmutableMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)}); + } + + public static ImmutableMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return new RegularImmutableMap(new ImmutableMapEntry.TerminalEntry[]{entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5)}); + } + + static ImmutableMapEntry.TerminalEntry entryOf(K key, V value) { + CollectPreconditions.checkEntryNotNull(key, value); + return new ImmutableMapEntry.TerminalEntry(key, value); + } + + public static ImmutableMap.Builder builder() { + return new ImmutableMap.Builder(); + } + + static void checkNoConflict(boolean safe, String conflictDescription, Entry entry1, Entry entry2) { + if (!safe) { + String var4 = String.valueOf(String.valueOf(conflictDescription)); + String var5 = String.valueOf(String.valueOf(entry1)); + String var6 = String.valueOf(String.valueOf(entry2)); + throw new IllegalArgumentException((new StringBuilder(34 + var4.length() + var5.length() + var6.length())).append("Multiple entries with same ").append(var4).append(": ").append(var5).append(" and ").append(var6).toString()); + } + } + + public static ImmutableMap copyOf(Map map) { + if (map instanceof ImmutableMap && !(map instanceof ImmutableSortedMap)) { + ImmutableMap kvMap = (ImmutableMap)map; + if (!kvMap.isPartialView()) { + return kvMap; + } + } else if (map instanceof EnumMap) { + return copyOfEnumMapUnsafe(map); + } + + Entry[] entries = (Entry[])map.entrySet().toArray(EMPTY_ENTRY_ARRAY); + switch(entries.length) { + case 0: + return of(); + case 1: + Entry onlyEntry = entries[0]; + return of(onlyEntry.getKey(), onlyEntry.getValue()); + default: + return new RegularImmutableMap(entries); + } + } + + private static ImmutableMap copyOfEnumMapUnsafe(Map map) { + return copyOfEnumMap((EnumMap)map); + } + + private static , V> ImmutableMap copyOfEnumMap(Map original) { + EnumMap copy = new EnumMap(original); + Iterator i$ = copy.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + CollectPreconditions.checkEntryNotNull(entry.getKey(), entry.getValue()); + } + + return ImmutableEnumMap.asImmutable(copy); + } + + ImmutableMap() { + } + + /** @deprecated */ + @Deprecated + public final V put(K k, V v) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final V remove(Object o) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final void putAll(Map map) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final void clear() { + throw new UnsupportedOperationException(); + } + + public boolean isEmpty() { + return this.size() == 0; + } + + public boolean containsKey(@Nullable Object key) { + return this.get(key) != null; + } + + public boolean containsValue(@Nullable Object value) { + return this.values().contains(value); + } + + public abstract V get(@Nullable Object var1); + + public ImmutableSet> entrySet() { + ImmutableSet> result = this.entrySet; + return result == null ? (this.entrySet = this.createEntrySet()) : result; + } + + abstract ImmutableSet> createEntrySet(); + + public ImmutableSet keySet() { + ImmutableSet result = this.keySet; + return result == null ? (this.keySet = this.createKeySet()) : result; + } + + ImmutableSet createKeySet() { + return new ImmutableMapKeySet(this); + } + + public ImmutableCollection values() { + ImmutableCollection result = this.values; + return result == null ? (this.values = new ImmutableMapValues(this)) : result; + } + + @Beta + public ImmutableSetMultimap asMultimap() { + ImmutableSetMultimap result = this.multimapView; + return result == null ? (this.multimapView = this.createMultimapView()) : result; + } + + private ImmutableSetMultimap createMultimapView() { + ImmutableMap> map = this.viewMapValuesAsSingletonSets(); + return new ImmutableSetMultimap(map, map.size(), (Comparator)null); + } + + private ImmutableMap> viewMapValuesAsSingletonSets() { + return new ImmutableMap.MapViewOfValuesAsSingletonSets(this); + } + + public boolean equals(@Nullable Object object) { + return Maps.equalsImpl(this, object); + } + + abstract boolean isPartialView(); + + public int hashCode() { + return this.entrySet().hashCode(); + } + + public String toString() { + return Maps.toStringImpl(this); + } + + Object writeReplace() { + return new ImmutableMap.SerializedForm(this); + } + + static class SerializedForm implements Serializable { + private final Object[] keys; + private final Object[] values; + private static final long serialVersionUID = 0L; + + SerializedForm(ImmutableMap map) { + this.keys = new Object[map.size()]; + this.values = new Object[map.size()]; + int i = 0; + + for(Iterator i$ = map.entrySet().iterator(); i$.hasNext(); ++i) { + Entry entry = (Entry)i$.next(); + this.keys[i] = entry.getKey(); + this.values[i] = entry.getValue(); + } + + } + + Object readResolve() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return this.createMap(builder); + } + + Object createMap(ImmutableMap.Builder builder) { + for(int i = 0; i < this.keys.length; ++i) { + builder.put(this.keys[i], this.values[i]); + } + + return builder.build(); + } + } + + private static final class MapViewOfValuesAsSingletonSets extends ImmutableMap> { + private final ImmutableMap delegate; + + MapViewOfValuesAsSingletonSets(ImmutableMap delegate) { + this.delegate = (ImmutableMap)Preconditions.checkNotNull(delegate); + } + + public int size() { + return this.delegate.size(); + } + + public boolean containsKey(@Nullable Object key) { + return this.delegate.containsKey(key); + } + + public ImmutableSet get(@Nullable Object key) { + V outerValue = this.delegate.get(key); + return outerValue == null ? null : ImmutableSet.of(outerValue); + } + + boolean isPartialView() { + return false; + } + + ImmutableSet>> createEntrySet() { + return new ImmutableMapEntrySet>() { + ImmutableMap> map() { + return MapViewOfValuesAsSingletonSets.this; + } + + public UnmodifiableIterator>> iterator() { + final Iterator> backingIterator = MapViewOfValuesAsSingletonSets.this.delegate.entrySet().iterator(); + return new UnmodifiableIterator>>() { + public boolean hasNext() { + return backingIterator.hasNext(); + } + + public Entry> next() { + final Entry backingEntry = (Entry)backingIterator.next(); + return new AbstractMapEntry>() { + public K getKey() { + return backingEntry.getKey(); + } + + public ImmutableSet getValue() { + return ImmutableSet.of(backingEntry.getValue()); + } + }; + } + }; + } + }; + } + } + + public static class Builder { + ImmutableMapEntry.TerminalEntry[] entries; + int size; + + public Builder() { + this(4); + } + + Builder(int initialCapacity) { + this.entries = new ImmutableMapEntry.TerminalEntry[initialCapacity]; + this.size = 0; + } + + private void ensureCapacity(int minCapacity) { + if (minCapacity > this.entries.length) { + this.entries = (ImmutableMapEntry.TerminalEntry[])ObjectArrays.arraysCopyOf(this.entries, ImmutableCollection.Builder.expandedCapacity(this.entries.length, minCapacity)); + } + + } + + public ImmutableMap.Builder put(K key, V value) { + this.ensureCapacity(this.size + 1); + ImmutableMapEntry.TerminalEntry entry = ImmutableMap.entryOf(key, value); + this.entries[this.size++] = entry; + return this; + } + + public ImmutableMap.Builder put(Entry entry) { + return this.put(entry.getKey(), entry.getValue()); + } + + public ImmutableMap.Builder putAll(Map map) { + this.ensureCapacity(this.size + map.size()); + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + this.put(entry); + } + + return this; + } + + public ImmutableMap build() { + switch(this.size) { + case 0: + return ImmutableMap.of(); + case 1: + return ImmutableMap.of(this.entries[0].getKey(), this.entries[0].getValue()); + default: + return new RegularImmutableMap(this.size, this.entries); + } + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableMapEntry.java b/src/main/com/google/common/collect/ImmutableMapEntry.java new file mode 100644 index 0000000..7d56b21 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableMapEntry.java @@ -0,0 +1,42 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtIncompatible; +import javax.annotation.Nullable; + +@GwtIncompatible("unnecessary") +abstract class ImmutableMapEntry extends ImmutableEntry { + ImmutableMapEntry(K key, V value) { + super(key, value); + CollectPreconditions.checkEntryNotNull(key, value); + } + + ImmutableMapEntry(ImmutableMapEntry contents) { + super(contents.getKey(), contents.getValue()); + } + + @Nullable + abstract ImmutableMapEntry getNextInKeyBucket(); + + @Nullable + abstract ImmutableMapEntry getNextInValueBucket(); + + static final class TerminalEntry extends ImmutableMapEntry { + TerminalEntry(ImmutableMapEntry contents) { + super(contents); + } + + TerminalEntry(K key, V value) { + super(key, value); + } + + @Nullable + ImmutableMapEntry getNextInKeyBucket() { + return null; + } + + @Nullable + ImmutableMapEntry getNextInValueBucket() { + return null; + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableMapEntrySet.java b/src/main/com/google/common/collect/ImmutableMapEntrySet.java new file mode 100644 index 0000000..dd6a2b2 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableMapEntrySet.java @@ -0,0 +1,51 @@ +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; + +@GwtCompatible( + emulated = true +) +abstract class ImmutableMapEntrySet extends ImmutableSet> { + abstract ImmutableMap map(); + + public int size() { + return this.map().size(); + } + + public boolean contains(@Nullable Object object) { + if (!(object instanceof Entry)) { + return false; + } else { + Entry entry = (Entry)object; + V value = this.map().get(entry.getKey()); + return value != null && value.equals(entry.getValue()); + } + } + + boolean isPartialView() { + return this.map().isPartialView(); + } + + @GwtIncompatible("serialization") + Object writeReplace() { + return new ImmutableMapEntrySet.EntrySetSerializedForm(this.map()); + } + + @GwtIncompatible("serialization") + private static class EntrySetSerializedForm implements Serializable { + final ImmutableMap map; + private static final long serialVersionUID = 0L; + + EntrySetSerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return this.map.entrySet(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableMapKeySet.java b/src/main/com/google/common/collect/ImmutableMapKeySet.java new file mode 100644 index 0000000..ab438f8 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableMapKeySet.java @@ -0,0 +1,66 @@ +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; + +@GwtCompatible( + emulated = true +) +final class ImmutableMapKeySet extends ImmutableSet { + private final ImmutableMap map; + + ImmutableMapKeySet(ImmutableMap map) { + this.map = map; + } + + public int size() { + return this.map.size(); + } + + public UnmodifiableIterator iterator() { + return this.asList().iterator(); + } + + public boolean contains(@Nullable Object object) { + return this.map.containsKey(object); + } + + ImmutableList createAsList() { + final ImmutableList> entryList = this.map.entrySet().asList(); + return new ImmutableAsList() { + public K get(int index) { + return ((Entry)entryList.get(index)).getKey(); + } + + ImmutableCollection delegateCollection() { + return ImmutableMapKeySet.this; + } + }; + } + + boolean isPartialView() { + return true; + } + + @GwtIncompatible("serialization") + Object writeReplace() { + return new ImmutableMapKeySet.KeySetSerializedForm(this.map); + } + + @GwtIncompatible("serialization") + private static class KeySetSerializedForm implements Serializable { + final ImmutableMap map; + private static final long serialVersionUID = 0L; + + KeySetSerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return this.map.keySet(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableMapValues.java b/src/main/com/google/common/collect/ImmutableMapValues.java new file mode 100644 index 0000000..cc4d92e --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableMapValues.java @@ -0,0 +1,66 @@ +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; + +@GwtCompatible( + emulated = true +) +final class ImmutableMapValues extends ImmutableCollection { + private final ImmutableMap map; + + ImmutableMapValues(ImmutableMap map) { + this.map = map; + } + + public int size() { + return this.map.size(); + } + + public UnmodifiableIterator iterator() { + return Maps.valueIterator(this.map.entrySet().iterator()); + } + + public boolean contains(@Nullable Object object) { + return object != null && Iterators.contains(this.iterator(), object); + } + + boolean isPartialView() { + return true; + } + + ImmutableList createAsList() { + final ImmutableList> entryList = this.map.entrySet().asList(); + return new ImmutableAsList() { + public V get(int index) { + return ((Entry)entryList.get(index)).getValue(); + } + + ImmutableCollection delegateCollection() { + return ImmutableMapValues.this; + } + }; + } + + @GwtIncompatible("serialization") + Object writeReplace() { + return new ImmutableMapValues.SerializedForm(this.map); + } + + @GwtIncompatible("serialization") + private static class SerializedForm implements Serializable { + final ImmutableMap map; + private static final long serialVersionUID = 0L; + + SerializedForm(ImmutableMap map) { + this.map = map; + } + + Object readResolve() { + return this.map.values(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableMultimap.java b/src/main/com/google/common/collect/ImmutableMultimap.java new file mode 100644 index 0000000..5f8c590 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableMultimap.java @@ -0,0 +1,427 @@ +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.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public abstract class ImmutableMultimap extends AbstractMultimap implements Serializable { + final transient ImmutableMap> map; + final transient int size; + private static final long serialVersionUID = 0L; + + public static ImmutableMultimap of() { + return ImmutableListMultimap.of(); + } + + public static ImmutableMultimap of(K k1, V v1) { + return ImmutableListMultimap.of(k1, v1); + } + + public static ImmutableMultimap of(K k1, V v1, K k2, V v2) { + return ImmutableListMultimap.of(k1, v1, k2, v2); + } + + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); + } + + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); + } + + public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); + } + + public static ImmutableMultimap.Builder builder() { + return new ImmutableMultimap.Builder(); + } + + public static ImmutableMultimap copyOf(Multimap multimap) { + if (multimap instanceof ImmutableMultimap) { + ImmutableMultimap kvMultimap = (ImmutableMultimap)multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + + return ImmutableListMultimap.copyOf(multimap); + } + + ImmutableMultimap(ImmutableMap> map, int size) { + this.map = map; + this.size = size; + } + + /** @deprecated */ + @Deprecated + public ImmutableCollection removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public ImmutableCollection replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public void clear() { + throw new UnsupportedOperationException(); + } + + public abstract ImmutableCollection get(K var1); + + public abstract ImmutableMultimap inverse(); + + /** @deprecated */ + @Deprecated + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + boolean isPartialView() { + return this.map.isPartialView(); + } + + public boolean containsKey(@Nullable Object key) { + return this.map.containsKey(key); + } + + public boolean containsValue(@Nullable Object value) { + return value != null && super.containsValue(value); + } + + public int size() { + return this.size; + } + + public ImmutableSet keySet() { + return this.map.keySet(); + } + + public ImmutableMap> asMap() { + return this.map; + } + + Map> createAsMap() { + throw new AssertionError("should never be called"); + } + + public ImmutableCollection> entries() { + return (ImmutableCollection)super.entries(); + } + + ImmutableCollection> createEntries() { + return new ImmutableMultimap.EntryCollection(this); + } + + UnmodifiableIterator> entryIterator() { + return new ImmutableMultimap.Itr>() { + Entry output(K key, V value) { + return Maps.immutableEntry(key, value); + } + }; + } + + public ImmutableMultiset keys() { + return (ImmutableMultiset)super.keys(); + } + + ImmutableMultiset createKeys() { + return new ImmutableMultimap.Keys(); + } + + public ImmutableCollection values() { + return (ImmutableCollection)super.values(); + } + + ImmutableCollection createValues() { + return new ImmutableMultimap.Values(this); + } + + UnmodifiableIterator valueIterator() { + return new ImmutableMultimap.Itr() { + V output(K key, V value) { + return value; + } + }; + } + + private static final class Values extends ImmutableCollection { + private final transient ImmutableMultimap multimap; + private static final long serialVersionUID = 0L; + + Values(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + public boolean contains(@Nullable Object object) { + return this.multimap.containsValue(object); + } + + public UnmodifiableIterator iterator() { + return this.multimap.valueIterator(); + } + + @GwtIncompatible("not present in emulated superclass") + int copyIntoArray(Object[] dst, int offset) { + ImmutableCollection valueCollection; + for(Iterator i$ = this.multimap.map.values().iterator(); i$.hasNext(); offset = valueCollection.copyIntoArray(dst, offset)) { + valueCollection = (ImmutableCollection)i$.next(); + } + + return offset; + } + + public int size() { + return this.multimap.size(); + } + + boolean isPartialView() { + return true; + } + } + + class Keys extends ImmutableMultiset { + public boolean contains(@Nullable Object object) { + return ImmutableMultimap.this.containsKey(object); + } + + public int count(@Nullable Object element) { + Collection values = (Collection)ImmutableMultimap.this.map.get(element); + return values == null ? 0 : values.size(); + } + + public Set elementSet() { + return ImmutableMultimap.this.keySet(); + } + + public int size() { + return ImmutableMultimap.this.size(); + } + + Multiset.Entry getEntry(int index) { + Entry> entry = (Entry)ImmutableMultimap.this.map.entrySet().asList().get(index); + return Multisets.immutableEntry(entry.getKey(), ((Collection)entry.getValue()).size()); + } + + boolean isPartialView() { + return true; + } + } + + private abstract class Itr extends UnmodifiableIterator { + final Iterator>> mapIterator; + K key; + Iterator valueIterator; + + private Itr() { + this.mapIterator = ImmutableMultimap.this.asMap().entrySet().iterator(); + this.key = null; + this.valueIterator = Iterators.emptyIterator(); + } + + abstract T output(K var1, V var2); + + public boolean hasNext() { + return this.mapIterator.hasNext() || this.valueIterator.hasNext(); + } + + public T next() { + if (!this.valueIterator.hasNext()) { + Entry> mapEntry = (Entry)this.mapIterator.next(); + this.key = mapEntry.getKey(); + this.valueIterator = ((Collection)mapEntry.getValue()).iterator(); + } + + return this.output(this.key, this.valueIterator.next()); + } + + // $FF: synthetic method + Itr(Object x1) { + this(); + } + } + + private static class EntryCollection extends ImmutableCollection> { + final ImmutableMultimap multimap; + private static final long serialVersionUID = 0L; + + EntryCollection(ImmutableMultimap multimap) { + this.multimap = multimap; + } + + public UnmodifiableIterator> iterator() { + return this.multimap.entryIterator(); + } + + boolean isPartialView() { + return this.multimap.isPartialView(); + } + + public int size() { + return this.multimap.size(); + } + + public boolean contains(Object object) { + if (object instanceof Entry) { + Entry entry = (Entry)object; + return this.multimap.containsEntry(entry.getKey(), entry.getValue()); + } else { + return false; + } + } + } + + @GwtIncompatible("java serialization is not supported") + static class FieldSettersHolder { + static final Serialization.FieldSetter MAP_FIELD_SETTER = Serialization.getFieldSetter(ImmutableMultimap.class, "map"); + static final Serialization.FieldSetter SIZE_FIELD_SETTER = Serialization.getFieldSetter(ImmutableMultimap.class, "size"); + static final Serialization.FieldSetter EMPTY_SET_FIELD_SETTER = Serialization.getFieldSetter(ImmutableSetMultimap.class, "emptySet"); + } + + public static class Builder { + Multimap builderMultimap = new ImmutableMultimap.BuilderMultimap(); + Comparator keyComparator; + Comparator valueComparator; + + public ImmutableMultimap.Builder put(K key, V value) { + CollectPreconditions.checkEntryNotNull(key, value); + this.builderMultimap.put(key, value); + return this; + } + + public ImmutableMultimap.Builder put(Entry entry) { + return this.put(entry.getKey(), entry.getValue()); + } + + public ImmutableMultimap.Builder putAll(K key, Iterable values) { + if (key == null) { + NullPointerException var10000 = new NullPointerException; + String var10003 = String.valueOf(Iterables.toString(values)); + String var10002; + if (var10003.length() != 0) { + var10002 = "null key in entry: null=".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("null key in entry: null="); + } + + var10000.(var10002); + throw var10000; + } else { + Collection valueList = this.builderMultimap.get(key); + Iterator i$ = values.iterator(); + + while(i$.hasNext()) { + V value = i$.next(); + CollectPreconditions.checkEntryNotNull(key, value); + valueList.add(value); + } + + return this; + } + } + + public ImmutableMultimap.Builder putAll(K key, V... values) { + return this.putAll(key, (Iterable)Arrays.asList(values)); + } + + public ImmutableMultimap.Builder putAll(Multimap multimap) { + Iterator i$ = multimap.asMap().entrySet().iterator(); + + while(i$.hasNext()) { + Entry> entry = (Entry)i$.next(); + this.putAll(entry.getKey(), (Iterable)entry.getValue()); + } + + return this; + } + + public ImmutableMultimap.Builder orderKeysBy(Comparator keyComparator) { + this.keyComparator = (Comparator)Preconditions.checkNotNull(keyComparator); + return this; + } + + public ImmutableMultimap.Builder orderValuesBy(Comparator valueComparator) { + this.valueComparator = (Comparator)Preconditions.checkNotNull(valueComparator); + return this; + } + + public ImmutableMultimap build() { + if (this.valueComparator != null) { + Iterator i$ = this.builderMultimap.asMap().values().iterator(); + + while(i$.hasNext()) { + Collection values = (Collection)i$.next(); + List list = (List)values; + Collections.sort(list, this.valueComparator); + } + } + + if (this.keyComparator != null) { + Multimap sortedCopy = new ImmutableMultimap.BuilderMultimap(); + List>> entries = Lists.newArrayList((Iterable)this.builderMultimap.asMap().entrySet()); + Collections.sort(entries, Ordering.from(this.keyComparator).onKeys()); + Iterator i$ = entries.iterator(); + + while(i$.hasNext()) { + Entry> entry = (Entry)i$.next(); + sortedCopy.putAll(entry.getKey(), (Iterable)entry.getValue()); + } + + this.builderMultimap = sortedCopy; + } + + return ImmutableMultimap.copyOf(this.builderMultimap); + } + } + + private static class BuilderMultimap extends AbstractMapBasedMultimap { + private static final long serialVersionUID = 0L; + + BuilderMultimap() { + super(new LinkedHashMap()); + } + + Collection createCollection() { + return Lists.newArrayList(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableMultiset.java b/src/main/com/google/common/collect/ImmutableMultiset.java new file mode 100644 index 0000000..d097e23 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableMultiset.java @@ -0,0 +1,356 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public abstract class ImmutableMultiset extends ImmutableCollection implements Multiset { + private static final ImmutableMultiset EMPTY = new RegularImmutableMultiset(ImmutableMap.of(), 0); + private transient ImmutableSet> entrySet; + + public static ImmutableMultiset of() { + return EMPTY; + } + + public static ImmutableMultiset of(E element) { + return copyOfInternal(element); + } + + public static ImmutableMultiset of(E e1, E e2) { + return copyOfInternal(e1, e2); + } + + public static ImmutableMultiset of(E e1, E e2, E e3) { + return copyOfInternal(e1, e2, e3); + } + + public static ImmutableMultiset of(E e1, E e2, E e3, E e4) { + return copyOfInternal(e1, e2, e3, e4); + } + + public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5) { + return copyOfInternal(e1, e2, e3, e4, e5); + } + + public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { + return (new ImmutableMultiset.Builder()).add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build(); + } + + public static ImmutableMultiset copyOf(E[] elements) { + return copyOf((Iterable)Arrays.asList(elements)); + } + + public static ImmutableMultiset copyOf(Iterable elements) { + if (elements instanceof ImmutableMultiset) { + ImmutableMultiset result = (ImmutableMultiset)elements; + if (!result.isPartialView()) { + return result; + } + } + + Multiset multiset = elements instanceof Multiset ? Multisets.cast(elements) : LinkedHashMultiset.create(elements); + return copyOfInternal((Multiset)multiset); + } + + private static ImmutableMultiset copyOfInternal(E... elements) { + return copyOf((Iterable)Arrays.asList(elements)); + } + + private static ImmutableMultiset copyOfInternal(Multiset multiset) { + return copyFromEntries(multiset.entrySet()); + } + + static ImmutableMultiset copyFromEntries(Collection> entries) { + long size = 0L; + ImmutableMap.Builder builder = ImmutableMap.builder(); + Iterator i$ = entries.iterator(); + + while(i$.hasNext()) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + int count = entry.getCount(); + if (count > 0) { + builder.put(entry.getElement(), count); + size += (long)count; + } + } + + if (size == 0L) { + return of(); + } else { + return new RegularImmutableMultiset(builder.build(), Ints.saturatedCast(size)); + } + } + + public static ImmutableMultiset copyOf(Iterator elements) { + Multiset multiset = LinkedHashMultiset.create(); + Iterators.addAll(multiset, elements); + return copyOfInternal((Multiset)multiset); + } + + ImmutableMultiset() { + } + + public UnmodifiableIterator iterator() { + final Iterator> entryIterator = this.entrySet().iterator(); + return new UnmodifiableIterator() { + int remaining; + E element; + + public boolean hasNext() { + return this.remaining > 0 || entryIterator.hasNext(); + } + + public E next() { + if (this.remaining <= 0) { + Multiset.Entry entry = (Multiset.Entry)entryIterator.next(); + this.element = entry.getElement(); + this.remaining = entry.getCount(); + } + + --this.remaining; + return this.element; + } + }; + } + + public boolean contains(@Nullable Object object) { + return this.count(object) > 0; + } + + public boolean containsAll(Collection targets) { + return this.elementSet().containsAll(targets); + } + + /** @deprecated */ + @Deprecated + public final int add(E element, int occurrences) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final int remove(Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final int setCount(E element, int count) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final boolean setCount(E element, int oldCount, int newCount) { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible("not present in emulated superclass") + int copyIntoArray(Object[] dst, int offset) { + Multiset.Entry entry; + for(Iterator i$ = this.entrySet().iterator(); i$.hasNext(); offset += entry.getCount()) { + entry = (Multiset.Entry)i$.next(); + Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement()); + } + + return offset; + } + + public boolean equals(@Nullable Object object) { + return Multisets.equalsImpl(this, object); + } + + public int hashCode() { + return Sets.hashCodeImpl(this.entrySet()); + } + + public String toString() { + return this.entrySet().toString(); + } + + public ImmutableSet> entrySet() { + ImmutableSet> es = this.entrySet; + return es == null ? (this.entrySet = this.createEntrySet()) : es; + } + + private final ImmutableSet> createEntrySet() { + return (ImmutableSet)(this.isEmpty() ? ImmutableSet.of() : new ImmutableMultiset.EntrySet()); + } + + abstract Multiset.Entry getEntry(int var1); + + Object writeReplace() { + return new ImmutableMultiset.SerializedForm(this); + } + + public static ImmutableMultiset.Builder builder() { + return new ImmutableMultiset.Builder(); + } + + public static class Builder extends ImmutableCollection.Builder { + final Multiset contents; + + public Builder() { + this(LinkedHashMultiset.create()); + } + + Builder(Multiset contents) { + this.contents = contents; + } + + public ImmutableMultiset.Builder add(E element) { + this.contents.add(Preconditions.checkNotNull(element)); + return this; + } + + public ImmutableMultiset.Builder addCopies(E element, int occurrences) { + this.contents.add(Preconditions.checkNotNull(element), occurrences); + return this; + } + + public ImmutableMultiset.Builder setCount(E element, int count) { + this.contents.setCount(Preconditions.checkNotNull(element), count); + return this; + } + + public ImmutableMultiset.Builder add(E... elements) { + super.add(elements); + return this; + } + + public ImmutableMultiset.Builder addAll(Iterable elements) { + if (elements instanceof Multiset) { + Multiset multiset = Multisets.cast(elements); + Iterator i$ = multiset.entrySet().iterator(); + + while(i$.hasNext()) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + this.addCopies(entry.getElement(), entry.getCount()); + } + } else { + super.addAll(elements); + } + + return this; + } + + public ImmutableMultiset.Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + public ImmutableMultiset build() { + return ImmutableMultiset.copyOf((Iterable)this.contents); + } + } + + private static class SerializedForm implements Serializable { + final Object[] elements; + final int[] counts; + private static final long serialVersionUID = 0L; + + SerializedForm(Multiset multiset) { + int distinct = multiset.entrySet().size(); + this.elements = new Object[distinct]; + this.counts = new int[distinct]; + int i = 0; + + for(Iterator i$ = multiset.entrySet().iterator(); i$.hasNext(); ++i) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + this.elements[i] = entry.getElement(); + this.counts[i] = entry.getCount(); + } + + } + + Object readResolve() { + LinkedHashMultiset multiset = LinkedHashMultiset.create(this.elements.length); + + for(int i = 0; i < this.elements.length; ++i) { + multiset.add(this.elements[i], this.counts[i]); + } + + return ImmutableMultiset.copyOf((Iterable)multiset); + } + } + + static class EntrySetSerializedForm implements Serializable { + final ImmutableMultiset multiset; + + EntrySetSerializedForm(ImmutableMultiset multiset) { + this.multiset = multiset; + } + + Object readResolve() { + return this.multiset.entrySet(); + } + } + + private final class EntrySet extends ImmutableSet> { + private static final long serialVersionUID = 0L; + + private EntrySet() { + } + + boolean isPartialView() { + return ImmutableMultiset.this.isPartialView(); + } + + public UnmodifiableIterator> iterator() { + return this.asList().iterator(); + } + + ImmutableList> createAsList() { + return new ImmutableAsList>() { + public Multiset.Entry get(int index) { + return ImmutableMultiset.this.getEntry(index); + } + + ImmutableCollection> delegateCollection() { + return EntrySet.this; + } + }; + } + + public int size() { + return ImmutableMultiset.this.elementSet().size(); + } + + public boolean contains(Object o) { + if (o instanceof Multiset.Entry) { + Multiset.Entry entry = (Multiset.Entry)o; + if (entry.getCount() <= 0) { + return false; + } else { + int count = ImmutableMultiset.this.count(entry.getElement()); + return count == entry.getCount(); + } + } else { + return false; + } + } + + public int hashCode() { + return ImmutableMultiset.this.hashCode(); + } + + Object writeReplace() { + return new ImmutableMultiset.EntrySetSerializedForm(ImmutableMultiset.this); + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableRangeMap.java b/src/main/com/google/common/collect/ImmutableRangeMap.java new file mode 100644 index 0000000..63166dc --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableRangeMap.java @@ -0,0 +1,218 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +@GwtIncompatible("NavigableMap") +public class ImmutableRangeMap, V> implements RangeMap { + private static final ImmutableRangeMap, Object> EMPTY = new ImmutableRangeMap(ImmutableList.of(), ImmutableList.of()); + private final ImmutableList> ranges; + private final ImmutableList values; + + public static , V> ImmutableRangeMap of() { + return EMPTY; + } + + public static , V> ImmutableRangeMap of(Range range, V value) { + return new ImmutableRangeMap(ImmutableList.of(range), ImmutableList.of(value)); + } + + public static , V> ImmutableRangeMap copyOf(RangeMap rangeMap) { + if (rangeMap instanceof ImmutableRangeMap) { + return (ImmutableRangeMap)rangeMap; + } else { + Map, ? extends V> map = rangeMap.asMapOfRanges(); + ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder(map.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(map.size()); + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry, ? extends V> entry = (Entry)i$.next(); + rangesBuilder.add(entry.getKey()); + valuesBuilder.add(entry.getValue()); + } + + return new ImmutableRangeMap(rangesBuilder.build(), valuesBuilder.build()); + } + } + + public static , V> ImmutableRangeMap.Builder builder() { + return new ImmutableRangeMap.Builder(); + } + + ImmutableRangeMap(ImmutableList> ranges, ImmutableList values) { + this.ranges = ranges; + this.values = values; + } + + @Nullable + public V get(K key) { + int index = SortedLists.binarySearch(this.ranges, (Function)Range.lowerBoundFn(), (Comparable)Cut.belowValue(key), SortedLists.KeyPresentBehavior.ANY_PRESENT, SortedLists.KeyAbsentBehavior.NEXT_LOWER); + if (index == -1) { + return null; + } else { + Range range = (Range)this.ranges.get(index); + return range.contains(key) ? this.values.get(index) : null; + } + } + + @Nullable + public Entry, V> getEntry(K key) { + int index = SortedLists.binarySearch(this.ranges, (Function)Range.lowerBoundFn(), (Comparable)Cut.belowValue(key), SortedLists.KeyPresentBehavior.ANY_PRESENT, SortedLists.KeyAbsentBehavior.NEXT_LOWER); + if (index == -1) { + return null; + } else { + Range range = (Range)this.ranges.get(index); + return range.contains(key) ? Maps.immutableEntry(range, this.values.get(index)) : null; + } + } + + public Range span() { + if (this.ranges.isEmpty()) { + throw new NoSuchElementException(); + } else { + Range firstRange = (Range)this.ranges.get(0); + Range lastRange = (Range)this.ranges.get(this.ranges.size() - 1); + return Range.create(firstRange.lowerBound, lastRange.upperBound); + } + } + + public void put(Range range, V value) { + throw new UnsupportedOperationException(); + } + + public void putAll(RangeMap rangeMap) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + public ImmutableMap, V> asMapOfRanges() { + if (this.ranges.isEmpty()) { + return ImmutableMap.of(); + } else { + RegularImmutableSortedSet> rangeSet = new RegularImmutableSortedSet(this.ranges, Range.RANGE_LEX_ORDERING); + return new RegularImmutableSortedMap(rangeSet, this.values); + } + } + + public ImmutableRangeMap subRangeMap(final Range range) { + if (((Range)Preconditions.checkNotNull(range)).isEmpty()) { + return of(); + } else if (!this.ranges.isEmpty() && !range.encloses(this.span())) { + final int lowerIndex = SortedLists.binarySearch(this.ranges, (Function)Range.upperBoundFn(), (Comparable)range.lowerBound, SortedLists.KeyPresentBehavior.FIRST_AFTER, SortedLists.KeyAbsentBehavior.NEXT_HIGHER); + int upperIndex = SortedLists.binarySearch(this.ranges, (Function)Range.lowerBoundFn(), (Comparable)range.upperBound, SortedLists.KeyPresentBehavior.ANY_PRESENT, SortedLists.KeyAbsentBehavior.NEXT_HIGHER); + if (lowerIndex >= upperIndex) { + return of(); + } else { + final int len = upperIndex - lowerIndex; + ImmutableList> subRanges = new ImmutableList>() { + public int size() { + return len; + } + + public Range get(int index) { + Preconditions.checkElementIndex(index, len); + return index != 0 && index != len - 1 ? (Range)ImmutableRangeMap.this.ranges.get(index + lowerIndex) : ((Range)ImmutableRangeMap.this.ranges.get(index + lowerIndex)).intersection(range); + } + + boolean isPartialView() { + return true; + } + }; + return new ImmutableRangeMap(subRanges, this.values.subList(lowerIndex, upperIndex)) { + public ImmutableRangeMap subRangeMap(Range subRange) { + return range.isConnected(subRange) ? ImmutableRangeMap.this.subRangeMap(subRange.intersection(range)) : ImmutableRangeMap.of(); + } + }; + } + } else { + return this; + } + } + + public int hashCode() { + return this.asMapOfRanges().hashCode(); + } + + public boolean equals(@Nullable Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap)o; + return this.asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } else { + return false; + } + } + + public String toString() { + return this.asMapOfRanges().toString(); + } + + public static final class Builder, V> { + private final RangeSet keyRanges = TreeRangeSet.create(); + private final RangeMap rangeMap = TreeRangeMap.create(); + + public ImmutableRangeMap.Builder put(Range range, V value) { + Preconditions.checkNotNull(range); + Preconditions.checkNotNull(value); + Preconditions.checkArgument(!range.isEmpty(), "Range must not be empty, but was %s", range); + if (!this.keyRanges.complement().encloses(range)) { + Iterator i$ = this.rangeMap.asMapOfRanges().entrySet().iterator(); + + while(i$.hasNext()) { + Entry, V> entry = (Entry)i$.next(); + Range key = (Range)entry.getKey(); + if (key.isConnected(range) && !key.intersection(range).isEmpty()) { + String var6 = String.valueOf(String.valueOf(range)); + String var7 = String.valueOf(String.valueOf(entry)); + throw new IllegalArgumentException((new StringBuilder(47 + var6.length() + var7.length())).append("Overlapping ranges: range ").append(var6).append(" overlaps with entry ").append(var7).toString()); + } + } + } + + this.keyRanges.add(range); + this.rangeMap.put(range, value); + return this; + } + + public ImmutableRangeMap.Builder putAll(RangeMap rangeMap) { + Iterator i$ = rangeMap.asMapOfRanges().entrySet().iterator(); + + while(i$.hasNext()) { + Entry, ? extends V> entry = (Entry)i$.next(); + this.put((Range)entry.getKey(), entry.getValue()); + } + + return this; + } + + public ImmutableRangeMap build() { + Map, V> map = this.rangeMap.asMapOfRanges(); + ImmutableList.Builder> rangesBuilder = new ImmutableList.Builder(map.size()); + ImmutableList.Builder valuesBuilder = new ImmutableList.Builder(map.size()); + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry, V> entry = (Entry)i$.next(); + rangesBuilder.add(entry.getKey()); + valuesBuilder.add(entry.getValue()); + } + + return new ImmutableRangeMap(rangesBuilder.build(), valuesBuilder.build()); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableRangeSet.java b/src/main/com/google/common/collect/ImmutableRangeSet.java new file mode 100644 index 0000000..987cc3c --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableRangeSet.java @@ -0,0 +1,476 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import javax.annotation.Nullable; + +@Beta +public final class ImmutableRangeSet extends AbstractRangeSet implements Serializable { + private static final ImmutableRangeSet> EMPTY = new ImmutableRangeSet(ImmutableList.of()); + private static final ImmutableRangeSet> ALL = new ImmutableRangeSet(ImmutableList.of(Range.all())); + private final transient ImmutableList> ranges; + private transient ImmutableRangeSet complement; + + public static ImmutableRangeSet of() { + return EMPTY; + } + + static ImmutableRangeSet all() { + return ALL; + } + + public static ImmutableRangeSet of(Range range) { + Preconditions.checkNotNull(range); + if (range.isEmpty()) { + return of(); + } else { + return range.equals(Range.all()) ? all() : new ImmutableRangeSet(ImmutableList.of(range)); + } + } + + public static ImmutableRangeSet copyOf(RangeSet rangeSet) { + Preconditions.checkNotNull(rangeSet); + if (rangeSet.isEmpty()) { + return of(); + } else if (rangeSet.encloses(Range.all())) { + return all(); + } else { + if (rangeSet instanceof ImmutableRangeSet) { + ImmutableRangeSet immutableRangeSet = (ImmutableRangeSet)rangeSet; + if (!immutableRangeSet.isPartialView()) { + return immutableRangeSet; + } + } + + return new ImmutableRangeSet(ImmutableList.copyOf((Collection)rangeSet.asRanges())); + } + } + + ImmutableRangeSet(ImmutableList> ranges) { + this.ranges = ranges; + } + + private ImmutableRangeSet(ImmutableList> ranges, ImmutableRangeSet complement) { + this.ranges = ranges; + this.complement = complement; + } + + public boolean encloses(Range otherRange) { + int index = SortedLists.binarySearch(this.ranges, Range.lowerBoundFn(), otherRange.lowerBound, Ordering.natural(), SortedLists.KeyPresentBehavior.ANY_PRESENT, SortedLists.KeyAbsentBehavior.NEXT_LOWER); + return index != -1 && ((Range)this.ranges.get(index)).encloses(otherRange); + } + + public Range rangeContaining(C value) { + int index = SortedLists.binarySearch(this.ranges, Range.lowerBoundFn(), Cut.belowValue(value), Ordering.natural(), SortedLists.KeyPresentBehavior.ANY_PRESENT, SortedLists.KeyAbsentBehavior.NEXT_LOWER); + if (index != -1) { + Range range = (Range)this.ranges.get(index); + return range.contains(value) ? range : null; + } else { + return null; + } + } + + public Range span() { + if (this.ranges.isEmpty()) { + throw new NoSuchElementException(); + } else { + return Range.create(((Range)this.ranges.get(0)).lowerBound, ((Range)this.ranges.get(this.ranges.size() - 1)).upperBound); + } + } + + public boolean isEmpty() { + return this.ranges.isEmpty(); + } + + public void add(Range range) { + throw new UnsupportedOperationException(); + } + + public void addAll(RangeSet other) { + throw new UnsupportedOperationException(); + } + + public void remove(Range range) { + throw new UnsupportedOperationException(); + } + + public void removeAll(RangeSet other) { + throw new UnsupportedOperationException(); + } + + public ImmutableSet> asRanges() { + return (ImmutableSet)(this.ranges.isEmpty() ? ImmutableSet.of() : new RegularImmutableSortedSet(this.ranges, Range.RANGE_LEX_ORDERING)); + } + + public ImmutableRangeSet complement() { + ImmutableRangeSet result = this.complement; + if (result != null) { + return result; + } else if (this.ranges.isEmpty()) { + return this.complement = all(); + } else if (this.ranges.size() == 1 && ((Range)this.ranges.get(0)).equals(Range.all())) { + return this.complement = of(); + } else { + ImmutableList> complementRanges = new ImmutableRangeSet.ComplementRanges(); + result = this.complement = new ImmutableRangeSet(complementRanges, this); + return result; + } + } + + private ImmutableList> intersectRanges(final Range range) { + if (!this.ranges.isEmpty() && !range.isEmpty()) { + if (range.encloses(this.span())) { + return this.ranges; + } else { + final int fromIndex; + if (range.hasLowerBound()) { + fromIndex = SortedLists.binarySearch(this.ranges, (Function)Range.upperBoundFn(), (Comparable)range.lowerBound, SortedLists.KeyPresentBehavior.FIRST_AFTER, SortedLists.KeyAbsentBehavior.NEXT_HIGHER); + } else { + fromIndex = 0; + } + + int toIndex; + if (range.hasUpperBound()) { + toIndex = SortedLists.binarySearch(this.ranges, (Function)Range.lowerBoundFn(), (Comparable)range.upperBound, SortedLists.KeyPresentBehavior.FIRST_PRESENT, SortedLists.KeyAbsentBehavior.NEXT_HIGHER); + } else { + toIndex = this.ranges.size(); + } + + final int length = toIndex - fromIndex; + return length == 0 ? ImmutableList.of() : new ImmutableList>() { + public int size() { + return length; + } + + public Range get(int index) { + Preconditions.checkElementIndex(index, length); + return index != 0 && index != length - 1 ? (Range)ImmutableRangeSet.this.ranges.get(index + fromIndex) : ((Range)ImmutableRangeSet.this.ranges.get(index + fromIndex)).intersection(range); + } + + boolean isPartialView() { + return true; + } + }; + } + } else { + return ImmutableList.of(); + } + } + + public ImmutableRangeSet subRangeSet(Range range) { + if (!this.isEmpty()) { + Range span = this.span(); + if (range.encloses(span)) { + return this; + } + + if (range.isConnected(span)) { + return new ImmutableRangeSet(this.intersectRanges(range)); + } + } + + return of(); + } + + public ImmutableSortedSet asSet(DiscreteDomain domain) { + Preconditions.checkNotNull(domain); + if (this.isEmpty()) { + return ImmutableSortedSet.of(); + } else { + Range span = this.span().canonical(domain); + if (!span.hasLowerBound()) { + throw new IllegalArgumentException("Neither the DiscreteDomain nor this range set are bounded below"); + } else { + if (!span.hasUpperBound()) { + try { + domain.maxValue(); + } catch (NoSuchElementException var4) { + throw new IllegalArgumentException("Neither the DiscreteDomain nor this range set are bounded above"); + } + } + + return new ImmutableRangeSet.AsSet(domain); + } + } + } + + boolean isPartialView() { + return this.ranges.isPartialView(); + } + + public static > ImmutableRangeSet.Builder builder() { + return new ImmutableRangeSet.Builder(); + } + + Object writeReplace() { + return new ImmutableRangeSet.SerializedForm(this.ranges); + } + + private static final class SerializedForm implements Serializable { + private final ImmutableList> ranges; + + SerializedForm(ImmutableList> ranges) { + this.ranges = ranges; + } + + Object readResolve() { + if (this.ranges.isEmpty()) { + return ImmutableRangeSet.of(); + } else { + return this.ranges.equals(ImmutableList.of(Range.all())) ? ImmutableRangeSet.all() : new ImmutableRangeSet(this.ranges); + } + } + } + + public static class Builder> { + private final RangeSet rangeSet = TreeRangeSet.create(); + + public ImmutableRangeSet.Builder add(Range range) { + if (range.isEmpty()) { + String var4 = String.valueOf(String.valueOf(range)); + throw new IllegalArgumentException((new StringBuilder(33 + var4.length())).append("range must not be empty, but was ").append(var4).toString()); + } else if (this.rangeSet.complement().encloses(range)) { + this.rangeSet.add(range); + return this; + } else { + Iterator i$ = this.rangeSet.asRanges().iterator(); + + while(i$.hasNext()) { + Range currentRange = (Range)i$.next(); + Preconditions.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"); + } + } + + public ImmutableRangeSet.Builder addAll(RangeSet ranges) { + Iterator i$ = ranges.asRanges().iterator(); + + while(i$.hasNext()) { + Range range = (Range)i$.next(); + this.add(range); + } + + return this; + } + + public ImmutableRangeSet build() { + return ImmutableRangeSet.copyOf(this.rangeSet); + } + } + + private static class AsSetSerializedForm implements Serializable { + private final ImmutableList> ranges; + private final DiscreteDomain domain; + + AsSetSerializedForm(ImmutableList> ranges, DiscreteDomain domain) { + this.ranges = ranges; + this.domain = domain; + } + + Object readResolve() { + return (new ImmutableRangeSet(this.ranges)).asSet(this.domain); + } + } + + private final class AsSet extends ImmutableSortedSet { + private final DiscreteDomain domain; + private transient Integer size; + + AsSet(DiscreteDomain domain) { + super(Ordering.natural()); + this.domain = domain; + } + + public int size() { + Integer result = this.size; + if (result == null) { + long total = 0L; + Iterator i$ = ImmutableRangeSet.this.ranges.iterator(); + + while(i$.hasNext()) { + Range range = (Range)i$.next(); + total += (long)ContiguousSet.create(range, this.domain).size(); + if (total >= 2147483647L) { + break; + } + } + + result = this.size = Ints.saturatedCast(total); + } + + return result; + } + + public UnmodifiableIterator iterator() { + return new AbstractIterator() { + final Iterator> rangeItr; + Iterator elemItr; + + { + this.rangeItr = ImmutableRangeSet.this.ranges.iterator(); + this.elemItr = Iterators.emptyIterator(); + } + + protected C computeNext() { + while(true) { + if (!this.elemItr.hasNext()) { + if (this.rangeItr.hasNext()) { + this.elemItr = ContiguousSet.create((Range)this.rangeItr.next(), AsSet.this.domain).iterator(); + continue; + } + + return (Comparable)this.endOfData(); + } + + return (Comparable)this.elemItr.next(); + } + } + }; + } + + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return new AbstractIterator() { + final Iterator> rangeItr; + Iterator elemItr; + + { + this.rangeItr = ImmutableRangeSet.this.ranges.reverse().iterator(); + this.elemItr = Iterators.emptyIterator(); + } + + protected C computeNext() { + while(true) { + if (!this.elemItr.hasNext()) { + if (this.rangeItr.hasNext()) { + this.elemItr = ContiguousSet.create((Range)this.rangeItr.next(), AsSet.this.domain).descendingIterator(); + continue; + } + + return (Comparable)this.endOfData(); + } + + return (Comparable)this.elemItr.next(); + } + } + }; + } + + ImmutableSortedSet subSet(Range range) { + return ImmutableRangeSet.this.subRangeSet(range).asSet(this.domain); + } + + ImmutableSortedSet headSetImpl(C toElement, boolean inclusive) { + return this.subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive))); + } + + ImmutableSortedSet subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + return !fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0 ? ImmutableSortedSet.of() : this.subSet(Range.range(fromElement, BoundType.forBoolean(fromInclusive), toElement, BoundType.forBoolean(toInclusive))); + } + + ImmutableSortedSet tailSetImpl(C fromElement, boolean inclusive) { + return this.subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); + } + + public boolean contains(@Nullable Object o) { + if (o == null) { + return false; + } else { + try { + C c = (Comparable)o; + return ImmutableRangeSet.this.contains(c); + } catch (ClassCastException var3) { + return false; + } + } + } + + int indexOf(Object target) { + if (this.contains(target)) { + C c = (Comparable)target; + long total = 0L; + + Range range; + for(Iterator i$ = ImmutableRangeSet.this.ranges.iterator(); i$.hasNext(); total += (long)ContiguousSet.create(range, this.domain).size()) { + range = (Range)i$.next(); + if (range.contains(c)) { + return Ints.saturatedCast(total + (long)ContiguousSet.create(range, this.domain).indexOf(c)); + } + } + + throw new AssertionError("impossible"); + } else { + return -1; + } + } + + boolean isPartialView() { + return ImmutableRangeSet.this.ranges.isPartialView(); + } + + public String toString() { + return ImmutableRangeSet.this.ranges.toString(); + } + + Object writeReplace() { + return new ImmutableRangeSet.AsSetSerializedForm(ImmutableRangeSet.this.ranges, this.domain); + } + } + + private final class ComplementRanges extends ImmutableList> { + private final boolean positiveBoundedBelow; + private final boolean positiveBoundedAbove; + private final int size; + + ComplementRanges() { + this.positiveBoundedBelow = ((Range)ImmutableRangeSet.this.ranges.get(0)).hasLowerBound(); + this.positiveBoundedAbove = ((Range)Iterables.getLast(ImmutableRangeSet.this.ranges)).hasUpperBound(); + int size = ImmutableRangeSet.this.ranges.size() - 1; + if (this.positiveBoundedBelow) { + ++size; + } + + if (this.positiveBoundedAbove) { + ++size; + } + + this.size = size; + } + + public int size() { + return this.size; + } + + public Range get(int index) { + Preconditions.checkElementIndex(index, this.size); + Cut lowerBound; + if (this.positiveBoundedBelow) { + lowerBound = index == 0 ? Cut.belowAll() : ((Range)ImmutableRangeSet.this.ranges.get(index - 1)).upperBound; + } else { + lowerBound = ((Range)ImmutableRangeSet.this.ranges.get(index)).upperBound; + } + + Cut upperBound; + if (this.positiveBoundedAbove && index == this.size - 1) { + upperBound = Cut.aboveAll(); + } else { + upperBound = ((Range)ImmutableRangeSet.this.ranges.get(index + (this.positiveBoundedBelow ? 0 : 1))).lowerBound; + } + + return Range.create(lowerBound, upperBound); + } + + boolean isPartialView() { + return true; + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableSet.java b/src/main/com/google/common/collect/ImmutableSet.java new file mode 100644 index 0000000..f743d49 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSet.java @@ -0,0 +1,244 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public abstract class ImmutableSet extends ImmutableCollection implements Set { + static final int MAX_TABLE_SIZE = 1073741824; + private static final double DESIRED_LOAD_FACTOR = 0.7D; + private static final int CUTOFF = 751619276; + + public static ImmutableSet of() { + return EmptyImmutableSet.INSTANCE; + } + + public static ImmutableSet of(E element) { + return new SingletonImmutableSet(element); + } + + public static ImmutableSet of(E e1, E e2) { + return construct(2, e1, e2); + } + + public static ImmutableSet of(E e1, E e2, E e3) { + return construct(3, e1, e2, e3); + } + + public static ImmutableSet of(E e1, E e2, E e3, E e4) { + return construct(4, e1, e2, e3, e4); + } + + public static ImmutableSet of(E e1, E e2, E e3, E e4, E e5) { + return construct(5, e1, e2, e3, e4, e5); + } + + public static ImmutableSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { + int paramCount = true; + Object[] elements = new Object[6 + others.length]; + elements[0] = e1; + elements[1] = e2; + elements[2] = e3; + elements[3] = e4; + elements[4] = e5; + elements[5] = e6; + System.arraycopy(others, 0, elements, 6, others.length); + return construct(elements.length, elements); + } + + private static ImmutableSet construct(int n, Object... elements) { + switch(n) { + case 0: + return of(); + case 1: + E elem = elements[0]; + return of(elem); + default: + int tableSize = chooseTableSize(n); + Object[] table = new Object[tableSize]; + int mask = tableSize - 1; + int hashCode = 0; + int uniques = 0; + int i = 0; + + for(; i < n; ++i) { + Object element = ObjectArrays.checkElementNotNull(elements[i], i); + int hash = element.hashCode(); + int j = Hashing.smear(hash); + + while(true) { + int index = j & mask; + Object value = table[index]; + if (value == null) { + elements[uniques++] = element; + table[index] = element; + hashCode += hash; + break; + } + + if (value.equals(element)) { + break; + } + + ++j; + } + } + + Arrays.fill(elements, uniques, n, (Object)null); + if (uniques == 1) { + E element = elements[0]; + return new SingletonImmutableSet(element, hashCode); + } else if (tableSize != chooseTableSize(uniques)) { + return construct(uniques, elements); + } else { + Object[] uniqueElements = uniques < elements.length ? ObjectArrays.arraysCopyOf(elements, uniques) : elements; + return new RegularImmutableSet(uniqueElements, hashCode, table, mask); + } + } + } + + @VisibleForTesting + static int chooseTableSize(int setSize) { + if (setSize >= 751619276) { + Preconditions.checkArgument(setSize < 1073741824, "collection too large"); + return 1073741824; + } else { + int tableSize; + for(tableSize = Integer.highestOneBit(setSize - 1) << 1; (double)tableSize * 0.7D < (double)setSize; tableSize <<= 1) { + } + + return tableSize; + } + } + + public static ImmutableSet copyOf(E[] elements) { + switch(elements.length) { + case 0: + return of(); + case 1: + return of(elements[0]); + default: + return construct(elements.length, (Object[])elements.clone()); + } + } + + public static ImmutableSet copyOf(Iterable elements) { + return elements instanceof Collection ? copyOf((Collection)elements) : copyOf(elements.iterator()); + } + + public static ImmutableSet copyOf(Iterator elements) { + if (!elements.hasNext()) { + return of(); + } else { + E first = elements.next(); + return !elements.hasNext() ? of(first) : (new ImmutableSet.Builder()).add(first).addAll(elements).build(); + } + } + + public static ImmutableSet copyOf(Collection elements) { + if (elements instanceof ImmutableSet && !(elements instanceof ImmutableSortedSet)) { + ImmutableSet set = (ImmutableSet)elements; + if (!set.isPartialView()) { + return set; + } + } else if (elements instanceof EnumSet) { + return copyOfEnumSet((EnumSet)elements); + } + + Object[] array = elements.toArray(); + return construct(array.length, array); + } + + private static > ImmutableSet copyOfEnumSet(EnumSet enumSet) { + return ImmutableEnumSet.asImmutable(EnumSet.copyOf(enumSet)); + } + + ImmutableSet() { + } + + boolean isHashCodeFast() { + return false; + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else { + return object instanceof ImmutableSet && this.isHashCodeFast() && ((ImmutableSet)object).isHashCodeFast() && this.hashCode() != object.hashCode() ? false : Sets.equalsImpl(this, object); + } + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + public abstract UnmodifiableIterator iterator(); + + Object writeReplace() { + return new ImmutableSet.SerializedForm(this.toArray()); + } + + public static ImmutableSet.Builder builder() { + return new ImmutableSet.Builder(); + } + + public static class Builder extends ImmutableCollection.ArrayBasedBuilder { + public Builder() { + this(4); + } + + Builder(int capacity) { + super(capacity); + } + + public ImmutableSet.Builder add(E element) { + super.add(element); + return this; + } + + public ImmutableSet.Builder add(E... elements) { + super.add(elements); + return this; + } + + public ImmutableSet.Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + public ImmutableSet.Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + public ImmutableSet build() { + ImmutableSet result = ImmutableSet.construct(this.size, this.contents); + this.size = result.size(); + return result; + } + } + + private static class SerializedForm implements Serializable { + final Object[] elements; + private static final long serialVersionUID = 0L; + + SerializedForm(Object[] elements) { + this.elements = elements; + } + + Object readResolve() { + return ImmutableSet.copyOf(this.elements); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableSetMultimap.java b/src/main/com/google/common/collect/ImmutableSetMultimap.java new file mode 100644 index 0000000..0e7574b --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSetMultimap.java @@ -0,0 +1,339 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.InvalidObjectException; +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.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public class ImmutableSetMultimap extends ImmutableMultimap implements SetMultimap { + private final transient ImmutableSet emptySet; + private transient ImmutableSetMultimap inverse; + private transient ImmutableSet> entries; + @GwtIncompatible("not needed in emulated source.") + private static final long serialVersionUID = 0L; + + public static ImmutableSetMultimap of() { + return EmptyImmutableSetMultimap.INSTANCE; + } + + public static ImmutableSetMultimap of(K k1, V v1) { + ImmutableSetMultimap.Builder builder = builder(); + builder.put(k1, v1); + return builder.build(); + } + + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2) { + ImmutableSetMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + return builder.build(); + } + + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { + ImmutableSetMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + return builder.build(); + } + + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + ImmutableSetMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + return builder.build(); + } + + public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + ImmutableSetMultimap.Builder builder = builder(); + builder.put(k1, v1); + builder.put(k2, v2); + builder.put(k3, v3); + builder.put(k4, v4); + builder.put(k5, v5); + return builder.build(); + } + + public static ImmutableSetMultimap.Builder builder() { + return new ImmutableSetMultimap.Builder(); + } + + public static ImmutableSetMultimap copyOf(Multimap multimap) { + return copyOf(multimap, (Comparator)null); + } + + private static ImmutableSetMultimap copyOf(Multimap multimap, Comparator valueComparator) { + Preconditions.checkNotNull(multimap); + if (multimap.isEmpty() && valueComparator == null) { + return of(); + } else { + if (multimap instanceof ImmutableSetMultimap) { + ImmutableSetMultimap kvMultimap = (ImmutableSetMultimap)multimap; + if (!kvMultimap.isPartialView()) { + return kvMultimap; + } + } + + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int size = 0; + Iterator i$ = multimap.asMap().entrySet().iterator(); + + while(i$.hasNext()) { + Entry> entry = (Entry)i$.next(); + K key = entry.getKey(); + Collection values = (Collection)entry.getValue(); + ImmutableSet set = valueSet(valueComparator, values); + if (!set.isEmpty()) { + builder.put(key, set); + size += set.size(); + } + } + + return new ImmutableSetMultimap(builder.build(), size, valueComparator); + } + } + + ImmutableSetMultimap(ImmutableMap> map, int size, @Nullable Comparator valueComparator) { + super(map, size); + this.emptySet = emptySet(valueComparator); + } + + public ImmutableSet get(@Nullable K key) { + ImmutableSet set = (ImmutableSet)this.map.get(key); + return (ImmutableSet)MoreObjects.firstNonNull(set, this.emptySet); + } + + public ImmutableSetMultimap inverse() { + ImmutableSetMultimap result = this.inverse; + return result == null ? (this.inverse = this.invert()) : result; + } + + private ImmutableSetMultimap invert() { + ImmutableSetMultimap.Builder builder = builder(); + Iterator i$ = this.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + builder.put(entry.getValue(), entry.getKey()); + } + + ImmutableSetMultimap invertedMultimap = builder.build(); + invertedMultimap.inverse = this; + return invertedMultimap; + } + + /** @deprecated */ + @Deprecated + public ImmutableSet removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public ImmutableSet replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public ImmutableSet> entries() { + ImmutableSet> result = this.entries; + return result == null ? (this.entries = new ImmutableSetMultimap.EntrySet(this)) : result; + } + + private static ImmutableSet valueSet(@Nullable Comparator valueComparator, Collection values) { + return (ImmutableSet)(valueComparator == null ? ImmutableSet.copyOf(values) : ImmutableSortedSet.copyOf(valueComparator, values)); + } + + private static ImmutableSet emptySet(@Nullable Comparator valueComparator) { + return (ImmutableSet)(valueComparator == null ? ImmutableSet.of() : ImmutableSortedSet.emptySet(valueComparator)); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.valueComparator()); + Serialization.writeMultimap(this, stream); + } + + @Nullable + Comparator valueComparator() { + return this.emptySet instanceof ImmutableSortedSet ? ((ImmutableSortedSet)this.emptySet).comparator() : null; + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + Comparator valueComparator = (Comparator)stream.readObject(); + int keyCount = stream.readInt(); + if (keyCount < 0) { + throw new InvalidObjectException((new StringBuilder(29)).append("Invalid key count ").append(keyCount).toString()); + } else { + ImmutableMap.Builder> builder = ImmutableMap.builder(); + int tmpSize = 0; + + for(int i = 0; i < keyCount; ++i) { + Object key = stream.readObject(); + int valueCount = stream.readInt(); + if (valueCount <= 0) { + throw new InvalidObjectException((new StringBuilder(31)).append("Invalid value count ").append(valueCount).toString()); + } + + Object[] array = new Object[valueCount]; + + for(int j = 0; j < valueCount; ++j) { + array[j] = stream.readObject(); + } + + ImmutableSet valueSet = valueSet(valueComparator, Arrays.asList(array)); + if (valueSet.size() != array.length) { + String var11 = String.valueOf(String.valueOf(key)); + throw new InvalidObjectException((new StringBuilder(40 + var11.length())).append("Duplicate key-value pairs exist for key ").append(var11).toString()); + } + + builder.put(key, valueSet); + tmpSize += valueCount; + } + + ImmutableMap tmpMap; + try { + tmpMap = builder.build(); + } catch (IllegalArgumentException var12) { + throw (InvalidObjectException)(new InvalidObjectException(var12.getMessage())).initCause(var12); + } + + ImmutableMultimap.FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); + ImmutableMultimap.FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); + ImmutableMultimap.FieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(this, emptySet(valueComparator)); + } + } + + private static final class EntrySet extends ImmutableSet> { + private final transient ImmutableSetMultimap multimap; + + EntrySet(ImmutableSetMultimap multimap) { + this.multimap = multimap; + } + + public boolean contains(@Nullable Object object) { + if (object instanceof Entry) { + Entry entry = (Entry)object; + return this.multimap.containsEntry(entry.getKey(), entry.getValue()); + } else { + return false; + } + } + + public int size() { + return this.multimap.size(); + } + + public UnmodifiableIterator> iterator() { + return this.multimap.entryIterator(); + } + + boolean isPartialView() { + return false; + } + } + + public static final class Builder extends ImmutableMultimap.Builder { + public Builder() { + this.builderMultimap = new ImmutableSetMultimap.BuilderMultimap(); + } + + public ImmutableSetMultimap.Builder put(K key, V value) { + this.builderMultimap.put(Preconditions.checkNotNull(key), Preconditions.checkNotNull(value)); + return this; + } + + public ImmutableSetMultimap.Builder put(Entry entry) { + this.builderMultimap.put(Preconditions.checkNotNull(entry.getKey()), Preconditions.checkNotNull(entry.getValue())); + return this; + } + + public ImmutableSetMultimap.Builder putAll(K key, Iterable values) { + Collection collection = this.builderMultimap.get(Preconditions.checkNotNull(key)); + Iterator i$ = values.iterator(); + + while(i$.hasNext()) { + V value = i$.next(); + collection.add(Preconditions.checkNotNull(value)); + } + + return this; + } + + public ImmutableSetMultimap.Builder putAll(K key, V... values) { + return this.putAll(key, (Iterable)Arrays.asList(values)); + } + + public ImmutableSetMultimap.Builder putAll(Multimap multimap) { + Iterator i$ = multimap.asMap().entrySet().iterator(); + + while(i$.hasNext()) { + Entry> entry = (Entry)i$.next(); + this.putAll(entry.getKey(), (Iterable)entry.getValue()); + } + + return this; + } + + public ImmutableSetMultimap.Builder orderKeysBy(Comparator keyComparator) { + this.keyComparator = (Comparator)Preconditions.checkNotNull(keyComparator); + return this; + } + + public ImmutableSetMultimap.Builder orderValuesBy(Comparator valueComparator) { + super.orderValuesBy(valueComparator); + return this; + } + + public ImmutableSetMultimap build() { + if (this.keyComparator != null) { + Multimap sortedCopy = new ImmutableSetMultimap.BuilderMultimap(); + List>> entries = Lists.newArrayList((Iterable)this.builderMultimap.asMap().entrySet()); + Collections.sort(entries, Ordering.from(this.keyComparator).onKeys()); + Iterator i$ = entries.iterator(); + + while(i$.hasNext()) { + Entry> entry = (Entry)i$.next(); + sortedCopy.putAll(entry.getKey(), (Iterable)entry.getValue()); + } + + this.builderMultimap = sortedCopy; + } + + return ImmutableSetMultimap.copyOf(this.builderMultimap, this.valueComparator); + } + } + + private static class BuilderMultimap extends AbstractMapBasedMultimap { + private static final long serialVersionUID = 0L; + + BuilderMultimap() { + super(new LinkedHashMap()); + } + + Collection createCollection() { + return Sets.newLinkedHashSet(); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableSortedAsList.java b/src/main/com/google/common/collect/ImmutableSortedAsList.java new file mode 100644 index 0000000..10acda9 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSortedAsList.java @@ -0,0 +1,43 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.util.Comparator; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class ImmutableSortedAsList extends RegularImmutableAsList implements SortedIterable { + ImmutableSortedAsList(ImmutableSortedSet backingSet, ImmutableList backingList) { + super(backingSet, (ImmutableList)backingList); + } + + ImmutableSortedSet delegateCollection() { + return (ImmutableSortedSet)super.delegateCollection(); + } + + public Comparator comparator() { + return this.delegateCollection().comparator(); + } + + @GwtIncompatible("ImmutableSortedSet.indexOf") + public int indexOf(@Nullable Object target) { + int index = this.delegateCollection().indexOf(target); + return index >= 0 && this.get(index).equals(target) ? index : -1; + } + + @GwtIncompatible("ImmutableSortedSet.indexOf") + public int lastIndexOf(@Nullable Object target) { + return this.indexOf(target); + } + + public boolean contains(Object target) { + return this.indexOf(target) >= 0; + } + + @GwtIncompatible("super.subListUnchecked does not exist; inherited subList is valid if slow") + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + return (new RegularImmutableSortedSet(super.subListUnchecked(fromIndex, toIndex), this.comparator())).asList(); + } +} diff --git a/src/main/com/google/common/collect/ImmutableSortedMap.java b/src/main/com/google/common/collect/ImmutableSortedMap.java new file mode 100644 index 0000000..a4ac8d9 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSortedMap.java @@ -0,0 +1,328 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public abstract class ImmutableSortedMap extends ImmutableSortedMapFauxverideShim implements NavigableMap { + private static final Comparator NATURAL_ORDER = Ordering.natural(); + private static final ImmutableSortedMap NATURAL_EMPTY_MAP; + private transient ImmutableSortedMap descendingMap; + private static final long serialVersionUID = 0L; + + static ImmutableSortedMap emptyMap(Comparator comparator) { + return (ImmutableSortedMap)(Ordering.natural().equals(comparator) ? of() : new EmptyImmutableSortedMap(comparator)); + } + + static ImmutableSortedMap fromSortedEntries(Comparator comparator, int size, Entry[] entries) { + if (size == 0) { + return emptyMap(comparator); + } else { + ImmutableList.Builder keyBuilder = ImmutableList.builder(); + ImmutableList.Builder valueBuilder = ImmutableList.builder(); + + for(int i = 0; i < size; ++i) { + Entry entry = entries[i]; + keyBuilder.add(entry.getKey()); + valueBuilder.add(entry.getValue()); + } + + return new RegularImmutableSortedMap(new RegularImmutableSortedSet(keyBuilder.build(), comparator), valueBuilder.build()); + } + } + + static ImmutableSortedMap from(ImmutableSortedSet keySet, ImmutableList valueList) { + return (ImmutableSortedMap)(keySet.isEmpty() ? emptyMap(keySet.comparator()) : new RegularImmutableSortedMap((RegularImmutableSortedSet)keySet, valueList)); + } + + public static ImmutableSortedMap of() { + return NATURAL_EMPTY_MAP; + } + + public static , V> ImmutableSortedMap of(K k1, V v1) { + return from(ImmutableSortedSet.of(k1), ImmutableList.of(v1)); + } + + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2) { + return fromEntries(Ordering.natural(), false, 2, entryOf(k1, v1), entryOf(k2, v2)); + } + + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + return fromEntries(Ordering.natural(), false, 3, entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); + } + + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return fromEntries(Ordering.natural(), false, 4, entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)); + } + + public static , V> ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return fromEntries(Ordering.natural(), false, 5, entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5)); + } + + public static ImmutableSortedMap copyOf(Map map) { + Ordering naturalOrder = Ordering.natural(); + return copyOfInternal(map, naturalOrder); + } + + public static ImmutableSortedMap copyOf(Map map, Comparator comparator) { + return copyOfInternal(map, (Comparator)Preconditions.checkNotNull(comparator)); + } + + public static ImmutableSortedMap copyOfSorted(SortedMap map) { + Comparator comparator = map.comparator(); + if (comparator == null) { + comparator = NATURAL_ORDER; + } + + return copyOfInternal(map, comparator); + } + + private static ImmutableSortedMap copyOfInternal(Map map, Comparator comparator) { + boolean sameComparator = false; + if (map instanceof SortedMap) { + SortedMap sortedMap = (SortedMap)map; + Comparator comparator2 = sortedMap.comparator(); + sameComparator = comparator2 == null ? comparator == NATURAL_ORDER : comparator.equals(comparator2); + } + + if (sameComparator && map instanceof ImmutableSortedMap) { + ImmutableSortedMap kvMap = (ImmutableSortedMap)map; + if (!kvMap.isPartialView()) { + return kvMap; + } + } + + Entry[] entries = (Entry[])map.entrySet().toArray(new Entry[0]); + return fromEntries(comparator, sameComparator, entries.length, entries); + } + + static ImmutableSortedMap fromEntries(Comparator comparator, boolean sameComparator, int size, Entry... entries) { + for(int i = 0; i < size; ++i) { + Entry entry = entries[i]; + entries[i] = entryOf(entry.getKey(), entry.getValue()); + } + + if (!sameComparator) { + sortEntries(comparator, size, entries); + validateEntries(size, entries, comparator); + } + + return fromSortedEntries(comparator, size, entries); + } + + private static void sortEntries(Comparator comparator, int size, Entry[] entries) { + Arrays.sort(entries, 0, size, Ordering.from(comparator).onKeys()); + } + + private static void validateEntries(int size, Entry[] entries, Comparator comparator) { + for(int i = 1; i < size; ++i) { + checkNoConflict(comparator.compare(entries[i - 1].getKey(), entries[i].getKey()) != 0, "key", entries[i - 1], entries[i]); + } + + } + + public static , V> ImmutableSortedMap.Builder naturalOrder() { + return new ImmutableSortedMap.Builder(Ordering.natural()); + } + + public static ImmutableSortedMap.Builder orderedBy(Comparator comparator) { + return new ImmutableSortedMap.Builder(comparator); + } + + public static , V> ImmutableSortedMap.Builder reverseOrder() { + return new ImmutableSortedMap.Builder(Ordering.natural().reverse()); + } + + ImmutableSortedMap() { + } + + ImmutableSortedMap(ImmutableSortedMap descendingMap) { + this.descendingMap = descendingMap; + } + + public int size() { + return this.values().size(); + } + + public boolean containsValue(@Nullable Object value) { + return this.values().contains(value); + } + + boolean isPartialView() { + return this.keySet().isPartialView() || this.values().isPartialView(); + } + + public ImmutableSet> entrySet() { + return super.entrySet(); + } + + public abstract ImmutableSortedSet keySet(); + + public abstract ImmutableCollection values(); + + public Comparator comparator() { + return this.keySet().comparator(); + } + + public K firstKey() { + return this.keySet().first(); + } + + public K lastKey() { + return this.keySet().last(); + } + + public ImmutableSortedMap headMap(K toKey) { + return this.headMap(toKey, false); + } + + public abstract ImmutableSortedMap headMap(K var1, boolean var2); + + public ImmutableSortedMap subMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public ImmutableSortedMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + Preconditions.checkNotNull(fromKey); + Preconditions.checkNotNull(toKey); + Preconditions.checkArgument(this.comparator().compare(fromKey, toKey) <= 0, "expected fromKey <= toKey but %s > %s", fromKey, toKey); + return this.headMap(toKey, toInclusive).tailMap(fromKey, fromInclusive); + } + + public ImmutableSortedMap tailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + + public abstract ImmutableSortedMap tailMap(K var1, boolean var2); + + public Entry lowerEntry(K key) { + return this.headMap(key, false).lastEntry(); + } + + public K lowerKey(K key) { + return Maps.keyOrNull(this.lowerEntry(key)); + } + + public Entry floorEntry(K key) { + return this.headMap(key, true).lastEntry(); + } + + public K floorKey(K key) { + return Maps.keyOrNull(this.floorEntry(key)); + } + + public Entry ceilingEntry(K key) { + return this.tailMap(key, true).firstEntry(); + } + + public K ceilingKey(K key) { + return Maps.keyOrNull(this.ceilingEntry(key)); + } + + public Entry higherEntry(K key) { + return this.tailMap(key, false).firstEntry(); + } + + public K higherKey(K key) { + return Maps.keyOrNull(this.higherEntry(key)); + } + + public Entry firstEntry() { + return this.isEmpty() ? null : (Entry)this.entrySet().asList().get(0); + } + + public Entry lastEntry() { + return this.isEmpty() ? null : (Entry)this.entrySet().asList().get(this.size() - 1); + } + + /** @deprecated */ + @Deprecated + public final Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + public ImmutableSortedMap descendingMap() { + ImmutableSortedMap result = this.descendingMap; + if (result == null) { + result = this.descendingMap = this.createDescendingMap(); + } + + return result; + } + + abstract ImmutableSortedMap createDescendingMap(); + + public ImmutableSortedSet navigableKeySet() { + return this.keySet(); + } + + public ImmutableSortedSet descendingKeySet() { + return this.keySet().descendingSet(); + } + + Object writeReplace() { + return new ImmutableSortedMap.SerializedForm(this); + } + + static { + NATURAL_EMPTY_MAP = new EmptyImmutableSortedMap(NATURAL_ORDER); + } + + private static class SerializedForm extends ImmutableMap.SerializedForm { + private final Comparator comparator; + private static final long serialVersionUID = 0L; + + SerializedForm(ImmutableSortedMap sortedMap) { + super(sortedMap); + this.comparator = sortedMap.comparator(); + } + + Object readResolve() { + ImmutableSortedMap.Builder builder = new ImmutableSortedMap.Builder(this.comparator); + return this.createMap(builder); + } + } + + public static class Builder extends ImmutableMap.Builder { + private final Comparator comparator; + + public Builder(Comparator comparator) { + this.comparator = (Comparator)Preconditions.checkNotNull(comparator); + } + + public ImmutableSortedMap.Builder put(K key, V value) { + super.put(key, value); + return this; + } + + public ImmutableSortedMap.Builder put(Entry entry) { + super.put(entry); + return this; + } + + public ImmutableSortedMap.Builder putAll(Map map) { + super.putAll(map); + return this; + } + + public ImmutableSortedMap build() { + return ImmutableSortedMap.fromEntries(this.comparator, false, this.size, this.entries); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableSortedMapFauxverideShim.java b/src/main/com/google/common/collect/ImmutableSortedMapFauxverideShim.java new file mode 100644 index 0000000..0b323f7 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSortedMapFauxverideShim.java @@ -0,0 +1,39 @@ +package com.google.common.collect; + +abstract class ImmutableSortedMapFauxverideShim extends ImmutableMap { + /** @deprecated */ + @Deprecated + public static ImmutableSortedMap.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/ImmutableSortedMultiset.java b/src/main/com/google/common/collect/ImmutableSortedMultiset.java new file mode 100644 index 0000000..9f0ab21 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSortedMultiset.java @@ -0,0 +1,247 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +@Beta +@GwtIncompatible("hasn't been tested yet") +public abstract class ImmutableSortedMultiset extends ImmutableSortedMultisetFauxverideShim implements SortedMultiset { + private static final Comparator NATURAL_ORDER = Ordering.natural(); + private static final ImmutableSortedMultiset NATURAL_EMPTY_MULTISET; + transient ImmutableSortedMultiset descendingMultiset; + + public static ImmutableSortedMultiset of() { + return NATURAL_EMPTY_MULTISET; + } + + public static > ImmutableSortedMultiset of(E element) { + RegularImmutableSortedSet elementSet = (RegularImmutableSortedSet)ImmutableSortedSet.of(element); + int[] counts = new int[]{1}; + long[] cumulativeCounts = new long[]{0L, 1L}; + return new RegularImmutableSortedMultiset(elementSet, counts, cumulativeCounts, 0, 1); + } + + public static > ImmutableSortedMultiset of(E e1, E e2) { + return copyOf(Ordering.natural(), (Iterable)Arrays.asList(e1, e2)); + } + + public static > ImmutableSortedMultiset of(E e1, E e2, E e3) { + return copyOf(Ordering.natural(), (Iterable)Arrays.asList(e1, e2, e3)); + } + + public static > ImmutableSortedMultiset of(E e1, E e2, E e3, E e4) { + return copyOf(Ordering.natural(), (Iterable)Arrays.asList(e1, e2, e3, e4)); + } + + public static > ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5) { + return copyOf(Ordering.natural(), (Iterable)Arrays.asList(e1, e2, e3, e4, e5)); + } + + public static > ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + int size = remaining.length + 6; + List all = Lists.newArrayListWithCapacity(size); + Collections.addAll(all, new Comparable[]{e1, e2, e3, e4, e5, e6}); + Collections.addAll(all, remaining); + return copyOf(Ordering.natural(), (Iterable)all); + } + + public static > ImmutableSortedMultiset copyOf(E[] elements) { + return copyOf(Ordering.natural(), (Iterable)Arrays.asList(elements)); + } + + public static ImmutableSortedMultiset copyOf(Iterable elements) { + Ordering naturalOrder = Ordering.natural(); + return copyOf(naturalOrder, (Iterable)elements); + } + + public static ImmutableSortedMultiset copyOf(Iterator elements) { + Ordering naturalOrder = Ordering.natural(); + return copyOf(naturalOrder, (Iterator)elements); + } + + public static ImmutableSortedMultiset copyOf(Comparator comparator, Iterator elements) { + Preconditions.checkNotNull(comparator); + return (new ImmutableSortedMultiset.Builder(comparator)).addAll(elements).build(); + } + + public static ImmutableSortedMultiset copyOf(Comparator comparator, Iterable elements) { + if (elements instanceof ImmutableSortedMultiset) { + ImmutableSortedMultiset multiset = (ImmutableSortedMultiset)elements; + if (comparator.equals(multiset.comparator())) { + if (multiset.isPartialView()) { + return copyOfSortedEntries(comparator, multiset.entrySet().asList()); + } + + return multiset; + } + } + + Iterable elements = Lists.newArrayList(elements); + TreeMultiset sortedCopy = TreeMultiset.create((Comparator)Preconditions.checkNotNull(comparator)); + Iterables.addAll(sortedCopy, elements); + return copyOfSortedEntries(comparator, sortedCopy.entrySet()); + } + + public static ImmutableSortedMultiset copyOfSorted(SortedMultiset sortedMultiset) { + return copyOfSortedEntries(sortedMultiset.comparator(), Lists.newArrayList((Iterable)sortedMultiset.entrySet())); + } + + private static ImmutableSortedMultiset copyOfSortedEntries(Comparator comparator, Collection> entries) { + if (entries.isEmpty()) { + return emptyMultiset(comparator); + } else { + ImmutableList.Builder elementsBuilder = new ImmutableList.Builder(entries.size()); + int[] counts = new int[entries.size()]; + long[] cumulativeCounts = new long[entries.size() + 1]; + int i = 0; + + for(Iterator i$ = entries.iterator(); i$.hasNext(); ++i) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + elementsBuilder.add(entry.getElement()); + counts[i] = entry.getCount(); + cumulativeCounts[i + 1] = cumulativeCounts[i] + (long)counts[i]; + } + + return new RegularImmutableSortedMultiset(new RegularImmutableSortedSet(elementsBuilder.build(), comparator), counts, cumulativeCounts, 0, entries.size()); + } + } + + static ImmutableSortedMultiset emptyMultiset(Comparator comparator) { + return (ImmutableSortedMultiset)(NATURAL_ORDER.equals(comparator) ? NATURAL_EMPTY_MULTISET : new EmptyImmutableSortedMultiset(comparator)); + } + + ImmutableSortedMultiset() { + } + + public final Comparator comparator() { + return this.elementSet().comparator(); + } + + public abstract ImmutableSortedSet elementSet(); + + public ImmutableSortedMultiset descendingMultiset() { + ImmutableSortedMultiset result = this.descendingMultiset; + return result == null ? (this.descendingMultiset = new DescendingImmutableSortedMultiset(this)) : result; + } + + /** @deprecated */ + @Deprecated + public final Multiset.Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final Multiset.Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + public abstract ImmutableSortedMultiset headMultiset(E var1, BoundType var2); + + public ImmutableSortedMultiset subMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { + Preconditions.checkArgument(this.comparator().compare(lowerBound, upperBound) <= 0, "Expected lowerBound <= upperBound but %s > %s", lowerBound, upperBound); + return this.tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); + } + + public abstract ImmutableSortedMultiset tailMultiset(E var1, BoundType var2); + + public static ImmutableSortedMultiset.Builder orderedBy(Comparator comparator) { + return new ImmutableSortedMultiset.Builder(comparator); + } + + public static > ImmutableSortedMultiset.Builder reverseOrder() { + return new ImmutableSortedMultiset.Builder(Ordering.natural().reverse()); + } + + public static > ImmutableSortedMultiset.Builder naturalOrder() { + return new ImmutableSortedMultiset.Builder(Ordering.natural()); + } + + Object writeReplace() { + return new ImmutableSortedMultiset.SerializedForm(this); + } + + static { + NATURAL_EMPTY_MULTISET = new EmptyImmutableSortedMultiset(NATURAL_ORDER); + } + + private static final class SerializedForm implements Serializable { + Comparator comparator; + E[] elements; + int[] counts; + + SerializedForm(SortedMultiset multiset) { + this.comparator = multiset.comparator(); + int n = multiset.entrySet().size(); + this.elements = (Object[])(new Object[n]); + this.counts = new int[n]; + int i = 0; + + for(Iterator i$ = multiset.entrySet().iterator(); i$.hasNext(); ++i) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + this.elements[i] = entry.getElement(); + this.counts[i] = entry.getCount(); + } + + } + + Object readResolve() { + int n = this.elements.length; + ImmutableSortedMultiset.Builder builder = new ImmutableSortedMultiset.Builder(this.comparator); + + for(int i = 0; i < n; ++i) { + builder.addCopies(this.elements[i], this.counts[i]); + } + + return builder.build(); + } + } + + public static class Builder extends ImmutableMultiset.Builder { + public Builder(Comparator comparator) { + super(TreeMultiset.create((Comparator)Preconditions.checkNotNull(comparator))); + } + + public ImmutableSortedMultiset.Builder add(E element) { + super.add(element); + return this; + } + + public ImmutableSortedMultiset.Builder addCopies(E element, int occurrences) { + super.addCopies(element, occurrences); + return this; + } + + public ImmutableSortedMultiset.Builder setCount(E element, int count) { + super.setCount(element, count); + return this; + } + + public ImmutableSortedMultiset.Builder add(E... elements) { + super.add(elements); + return this; + } + + public ImmutableSortedMultiset.Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + public ImmutableSortedMultiset.Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + public ImmutableSortedMultiset build() { + return ImmutableSortedMultiset.copyOfSorted((SortedMultiset)this.contents); + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java b/src/main/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java new file mode 100644 index 0000000..28af87e --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSortedMultisetFauxverideShim.java @@ -0,0 +1,51 @@ +package com.google.common.collect; + +abstract class ImmutableSortedMultisetFauxverideShim extends ImmutableMultiset { + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset of(E element) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedMultiset copyOf(E[] elements) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/ImmutableSortedSet.java b/src/main/com/google/common/collect/ImmutableSortedSet.java new file mode 100644 index 0000000..dc77a55 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSortedSet.java @@ -0,0 +1,330 @@ +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.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.SortedSet; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public abstract class ImmutableSortedSet extends ImmutableSortedSetFauxverideShim implements NavigableSet, SortedIterable { + private static final Comparator NATURAL_ORDER = Ordering.natural(); + private static final ImmutableSortedSet NATURAL_EMPTY_SET; + final transient Comparator comparator; + @GwtIncompatible("NavigableSet") + transient ImmutableSortedSet descendingSet; + + private static ImmutableSortedSet emptySet() { + return NATURAL_EMPTY_SET; + } + + static ImmutableSortedSet emptySet(Comparator comparator) { + return (ImmutableSortedSet)(NATURAL_ORDER.equals(comparator) ? emptySet() : new EmptyImmutableSortedSet(comparator)); + } + + public static ImmutableSortedSet of() { + return emptySet(); + } + + public static > ImmutableSortedSet of(E element) { + return new RegularImmutableSortedSet(ImmutableList.of(element), Ordering.natural()); + } + + public static > ImmutableSortedSet of(E e1, E e2) { + return construct(Ordering.natural(), 2, e1, e2); + } + + public static > ImmutableSortedSet of(E e1, E e2, E e3) { + return construct(Ordering.natural(), 3, e1, e2, e3); + } + + public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4) { + return construct(Ordering.natural(), 4, e1, e2, e3, e4); + } + + public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5) { + return construct(Ordering.natural(), 5, e1, e2, e3, e4, e5); + } + + public static > ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + Comparable[] contents = new Comparable[6 + remaining.length]; + contents[0] = e1; + contents[1] = e2; + contents[2] = e3; + contents[3] = e4; + contents[4] = e5; + contents[5] = e6; + System.arraycopy(remaining, 0, contents, 6, remaining.length); + return construct(Ordering.natural(), contents.length, (Comparable[])contents); + } + + public static > ImmutableSortedSet copyOf(E[] elements) { + return construct(Ordering.natural(), elements.length, (Object[])elements.clone()); + } + + public static ImmutableSortedSet copyOf(Iterable elements) { + Ordering naturalOrder = Ordering.natural(); + return copyOf(naturalOrder, (Iterable)elements); + } + + public static ImmutableSortedSet copyOf(Collection elements) { + Ordering naturalOrder = Ordering.natural(); + return copyOf(naturalOrder, (Collection)elements); + } + + public static ImmutableSortedSet copyOf(Iterator elements) { + Ordering naturalOrder = Ordering.natural(); + return copyOf(naturalOrder, (Iterator)elements); + } + + public static ImmutableSortedSet copyOf(Comparator comparator, Iterator elements) { + return (new ImmutableSortedSet.Builder(comparator)).addAll(elements).build(); + } + + public static ImmutableSortedSet copyOf(Comparator comparator, Iterable elements) { + Preconditions.checkNotNull(comparator); + boolean hasSameComparator = SortedIterables.hasSameComparator(comparator, elements); + if (hasSameComparator && elements instanceof ImmutableSortedSet) { + ImmutableSortedSet original = (ImmutableSortedSet)elements; + if (!original.isPartialView()) { + return original; + } + } + + E[] array = (Object[])Iterables.toArray(elements); + return construct(comparator, array.length, array); + } + + public static ImmutableSortedSet copyOf(Comparator comparator, Collection elements) { + return copyOf(comparator, (Iterable)elements); + } + + public static ImmutableSortedSet copyOfSorted(SortedSet sortedSet) { + Comparator comparator = SortedIterables.comparator(sortedSet); + ImmutableList list = ImmutableList.copyOf((Collection)sortedSet); + return (ImmutableSortedSet)(list.isEmpty() ? emptySet(comparator) : new RegularImmutableSortedSet(list, comparator)); + } + + static ImmutableSortedSet construct(Comparator comparator, int n, E... contents) { + if (n == 0) { + return emptySet(comparator); + } else { + ObjectArrays.checkElementsNotNull(contents, n); + 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; + } + } + + Arrays.fill(contents, uniques, n, (Object)null); + return new RegularImmutableSortedSet(ImmutableList.asImmutableList(contents, uniques), comparator); + } + } + + public static ImmutableSortedSet.Builder orderedBy(Comparator comparator) { + return new ImmutableSortedSet.Builder(comparator); + } + + public static > ImmutableSortedSet.Builder reverseOrder() { + return new ImmutableSortedSet.Builder(Ordering.natural().reverse()); + } + + public static > ImmutableSortedSet.Builder naturalOrder() { + return new ImmutableSortedSet.Builder(Ordering.natural()); + } + + int unsafeCompare(Object a, Object b) { + return unsafeCompare(this.comparator, a, b); + } + + static int unsafeCompare(Comparator comparator, Object a, Object b) { + return comparator.compare(a, b); + } + + ImmutableSortedSet(Comparator comparator) { + this.comparator = comparator; + } + + public Comparator comparator() { + return this.comparator; + } + + public abstract UnmodifiableIterator iterator(); + + public ImmutableSortedSet headSet(E toElement) { + return this.headSet(toElement, false); + } + + @GwtIncompatible("NavigableSet") + public ImmutableSortedSet headSet(E toElement, boolean inclusive) { + return this.headSetImpl(Preconditions.checkNotNull(toElement), inclusive); + } + + public ImmutableSortedSet subSet(E fromElement, E toElement) { + return this.subSet(fromElement, true, toElement, false); + } + + @GwtIncompatible("NavigableSet") + public ImmutableSortedSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + Preconditions.checkNotNull(fromElement); + Preconditions.checkNotNull(toElement); + Preconditions.checkArgument(this.comparator.compare(fromElement, toElement) <= 0); + return this.subSetImpl(fromElement, fromInclusive, toElement, toInclusive); + } + + public ImmutableSortedSet tailSet(E fromElement) { + return this.tailSet(fromElement, true); + } + + @GwtIncompatible("NavigableSet") + public ImmutableSortedSet tailSet(E fromElement, boolean inclusive) { + return this.tailSetImpl(Preconditions.checkNotNull(fromElement), inclusive); + } + + abstract ImmutableSortedSet headSetImpl(E var1, boolean var2); + + abstract ImmutableSortedSet subSetImpl(E var1, boolean var2, E var3, boolean var4); + + abstract ImmutableSortedSet tailSetImpl(E var1, boolean var2); + + @GwtIncompatible("NavigableSet") + public E lower(E e) { + return Iterators.getNext(this.headSet(e, false).descendingIterator(), (Object)null); + } + + @GwtIncompatible("NavigableSet") + public E floor(E e) { + return Iterators.getNext(this.headSet(e, true).descendingIterator(), (Object)null); + } + + @GwtIncompatible("NavigableSet") + public E ceiling(E e) { + return Iterables.getFirst(this.tailSet(e, true), (Object)null); + } + + @GwtIncompatible("NavigableSet") + public E higher(E e) { + return Iterables.getFirst(this.tailSet(e, false), (Object)null); + } + + public E first() { + return this.iterator().next(); + } + + public E last() { + return this.descendingIterator().next(); + } + + /** @deprecated */ + @Deprecated + @GwtIncompatible("NavigableSet") + public final E pollFirst() { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + @GwtIncompatible("NavigableSet") + public final E pollLast() { + throw new UnsupportedOperationException(); + } + + @GwtIncompatible("NavigableSet") + public ImmutableSortedSet descendingSet() { + ImmutableSortedSet result = this.descendingSet; + if (result == null) { + result = this.descendingSet = this.createDescendingSet(); + result.descendingSet = this; + } + + return result; + } + + @GwtIncompatible("NavigableSet") + ImmutableSortedSet createDescendingSet() { + return new DescendingImmutableSortedSet(this); + } + + @GwtIncompatible("NavigableSet") + public abstract UnmodifiableIterator descendingIterator(); + + abstract int indexOf(@Nullable Object var1); + + private void readObject(ObjectInputStream stream) throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + Object writeReplace() { + return new ImmutableSortedSet.SerializedForm(this.comparator, this.toArray()); + } + + static { + NATURAL_EMPTY_SET = new EmptyImmutableSortedSet(NATURAL_ORDER); + } + + private static class SerializedForm implements Serializable { + final Comparator comparator; + final Object[] elements; + private static final long serialVersionUID = 0L; + + public SerializedForm(Comparator comparator, Object[] elements) { + this.comparator = comparator; + this.elements = elements; + } + + Object readResolve() { + return (new ImmutableSortedSet.Builder(this.comparator)).add((Object[])this.elements).build(); + } + } + + public static final class Builder extends ImmutableSet.Builder { + private final Comparator comparator; + + public Builder(Comparator comparator) { + this.comparator = (Comparator)Preconditions.checkNotNull(comparator); + } + + public ImmutableSortedSet.Builder add(E element) { + super.add(element); + return this; + } + + public ImmutableSortedSet.Builder add(E... elements) { + super.add(elements); + return this; + } + + public ImmutableSortedSet.Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + public ImmutableSortedSet.Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + public ImmutableSortedSet build() { + E[] contentsArray = (Object[])this.contents; + ImmutableSortedSet result = ImmutableSortedSet.construct(this.comparator, this.size, contentsArray); + this.size = result.size(); + return result; + } + } +} diff --git a/src/main/com/google/common/collect/ImmutableSortedSetFauxverideShim.java b/src/main/com/google/common/collect/ImmutableSortedSetFauxverideShim.java new file mode 100644 index 0000000..c18567c --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableSortedSetFauxverideShim.java @@ -0,0 +1,51 @@ +package com.google.common.collect; + +abstract class ImmutableSortedSetFauxverideShim extends ImmutableSet { + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet.Builder builder() { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet of(E element) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet of(E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public static ImmutableSortedSet copyOf(E[] elements) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/ImmutableTable.java b/src/main/com/google/common/collect/ImmutableTable.java new file mode 100644 index 0000000..06bde90 --- /dev/null +++ b/src/main/com/google/common/collect/ImmutableTable.java @@ -0,0 +1,191 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class ImmutableTable extends AbstractTable { + private static final ImmutableTable EMPTY = new SparseImmutableTable(ImmutableList.of(), ImmutableSet.of(), ImmutableSet.of()); + + public static ImmutableTable of() { + return EMPTY; + } + + public static ImmutableTable of(R rowKey, C columnKey, V value) { + return new SingletonImmutableTable(rowKey, columnKey, value); + } + + public static ImmutableTable copyOf(Table table) { + if (table instanceof ImmutableTable) { + ImmutableTable parameterizedTable = (ImmutableTable)table; + return parameterizedTable; + } else { + int size = table.size(); + switch(size) { + case 0: + return of(); + case 1: + Table.Cell onlyCell = (Table.Cell)Iterables.getOnlyElement(table.cellSet()); + return of(onlyCell.getRowKey(), onlyCell.getColumnKey(), onlyCell.getValue()); + default: + ImmutableSet.Builder> cellSetBuilder = ImmutableSet.builder(); + Iterator i$ = table.cellSet().iterator(); + + while(i$.hasNext()) { + Table.Cell cell = (Table.Cell)i$.next(); + cellSetBuilder.add((Object)cellOf(cell.getRowKey(), cell.getColumnKey(), cell.getValue())); + } + + return RegularImmutableTable.forCells(cellSetBuilder.build()); + } + } + } + + public static ImmutableTable.Builder builder() { + return new ImmutableTable.Builder(); + } + + static Table.Cell cellOf(R rowKey, C columnKey, V value) { + return Tables.immutableCell(Preconditions.checkNotNull(rowKey), Preconditions.checkNotNull(columnKey), Preconditions.checkNotNull(value)); + } + + ImmutableTable() { + } + + public ImmutableSet> cellSet() { + return (ImmutableSet)super.cellSet(); + } + + abstract ImmutableSet> createCellSet(); + + final UnmodifiableIterator> cellIterator() { + throw new AssertionError("should never be called"); + } + + public ImmutableCollection values() { + return (ImmutableCollection)super.values(); + } + + abstract ImmutableCollection createValues(); + + final Iterator valuesIterator() { + throw new AssertionError("should never be called"); + } + + public ImmutableMap column(C columnKey) { + Preconditions.checkNotNull(columnKey); + return (ImmutableMap)MoreObjects.firstNonNull((ImmutableMap)this.columnMap().get(columnKey), ImmutableMap.of()); + } + + public ImmutableSet columnKeySet() { + return this.columnMap().keySet(); + } + + public abstract ImmutableMap> columnMap(); + + public ImmutableMap row(R rowKey) { + Preconditions.checkNotNull(rowKey); + return (ImmutableMap)MoreObjects.firstNonNull((ImmutableMap)this.rowMap().get(rowKey), ImmutableMap.of()); + } + + public ImmutableSet rowKeySet() { + return this.rowMap().keySet(); + } + + public abstract ImmutableMap> rowMap(); + + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return this.get(rowKey, columnKey) != null; + } + + public boolean containsValue(@Nullable Object value) { + return this.values().contains(value); + } + + /** @deprecated */ + @Deprecated + public final void clear() { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final V put(R rowKey, C columnKey, V value) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final V remove(Object rowKey, Object columnKey) { + throw new UnsupportedOperationException(); + } + + public static final class Builder { + private final List> cells = Lists.newArrayList(); + private Comparator rowComparator; + private Comparator columnComparator; + + public ImmutableTable.Builder orderRowsBy(Comparator rowComparator) { + this.rowComparator = (Comparator)Preconditions.checkNotNull(rowComparator); + return this; + } + + public ImmutableTable.Builder orderColumnsBy(Comparator columnComparator) { + this.columnComparator = (Comparator)Preconditions.checkNotNull(columnComparator); + return this; + } + + public ImmutableTable.Builder put(R rowKey, C columnKey, V value) { + this.cells.add(ImmutableTable.cellOf(rowKey, columnKey, value)); + return this; + } + + public ImmutableTable.Builder put(Table.Cell cell) { + if (cell instanceof Tables.ImmutableCell) { + Preconditions.checkNotNull(cell.getRowKey()); + Preconditions.checkNotNull(cell.getColumnKey()); + Preconditions.checkNotNull(cell.getValue()); + this.cells.add(cell); + } else { + this.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + + return this; + } + + public ImmutableTable.Builder putAll(Table table) { + Iterator i$ = table.cellSet().iterator(); + + while(i$.hasNext()) { + Table.Cell cell = (Table.Cell)i$.next(); + this.put(cell); + } + + return this; + } + + public ImmutableTable build() { + int size = this.cells.size(); + switch(size) { + case 0: + return ImmutableTable.of(); + case 1: + return new SingletonImmutableTable((Table.Cell)Iterables.getOnlyElement(this.cells)); + default: + return RegularImmutableTable.forCells(this.cells, this.rowComparator, this.columnComparator); + } + } + } +} diff --git a/src/main/com/google/common/collect/Interner.java b/src/main/com/google/common/collect/Interner.java new file mode 100644 index 0000000..489ee11 --- /dev/null +++ b/src/main/com/google/common/collect/Interner.java @@ -0,0 +1,8 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; + +@Beta +public interface Interner { + E intern(E var1); +} diff --git a/src/main/com/google/common/collect/Interners.java b/src/main/com/google/common/collect/Interners.java new file mode 100644 index 0000000..86c662a --- /dev/null +++ b/src/main/com/google/common/collect/Interners.java @@ -0,0 +1,92 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Equivalence; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import java.util.concurrent.ConcurrentMap; + +@Beta +public final class Interners { + private Interners() { + } + + public static Interner newStrongInterner() { + final ConcurrentMap map = (new MapMaker()).makeMap(); + return new Interner() { + public E intern(E sample) { + E canonical = map.putIfAbsent(Preconditions.checkNotNull(sample), sample); + return canonical == null ? sample : canonical; + } + }; + } + + @GwtIncompatible("java.lang.ref.WeakReference") + public static Interner newWeakInterner() { + return new Interners.WeakInterner(); + } + + public static Function asFunction(Interner interner) { + return new Interners.InternerFunction((Interner)Preconditions.checkNotNull(interner)); + } + + private static class InternerFunction implements Function { + private final Interner interner; + + public InternerFunction(Interner interner) { + this.interner = interner; + } + + public E apply(E input) { + return this.interner.intern(input); + } + + public int hashCode() { + return this.interner.hashCode(); + } + + public boolean equals(Object other) { + if (other instanceof Interners.InternerFunction) { + Interners.InternerFunction that = (Interners.InternerFunction)other; + return this.interner.equals(that.interner); + } else { + return false; + } + } + } + + private static class WeakInterner implements Interner { + private final MapMakerInternalMap map; + + private WeakInterner() { + this.map = (new MapMaker()).weakKeys().keyEquivalence(Equivalence.equals()).makeCustomMap(); + } + + public E intern(E sample) { + Interners.WeakInterner.Dummy sneaky; + do { + MapMakerInternalMap.ReferenceEntry entry = this.map.getEntry(sample); + if (entry != null) { + E canonical = entry.getKey(); + if (canonical != null) { + return canonical; + } + } + + sneaky = (Interners.WeakInterner.Dummy)this.map.putIfAbsent(sample, Interners.WeakInterner.Dummy.VALUE); + } while(sneaky != null); + + return sample; + } + + // $FF: synthetic method + WeakInterner(Object x0) { + this(); + } + + private static enum Dummy { + VALUE; + } + } +} diff --git a/src/main/com/google/common/collect/Iterables.java b/src/main/com/google/common/collect/Iterables.java new file mode 100644 index 0000000..8ab0c22 --- /dev/null +++ b/src/main/com/google/common/collect/Iterables.java @@ -0,0 +1,504 @@ +package com.google.common.collect; + +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.Preconditions; +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.NoSuchElementException; +import java.util.Queue; +import java.util.RandomAccess; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Iterables { + private Iterables() { + } + + public static Iterable unmodifiableIterable(Iterable iterable) { + Preconditions.checkNotNull(iterable); + return (Iterable)(!(iterable instanceof Iterables.UnmodifiableIterable) && !(iterable instanceof ImmutableCollection) ? new Iterables.UnmodifiableIterable(iterable) : iterable); + } + + /** @deprecated */ + @Deprecated + public static Iterable unmodifiableIterable(ImmutableCollection iterable) { + return (Iterable)Preconditions.checkNotNull(iterable); + } + + public static int size(Iterable iterable) { + return iterable instanceof Collection ? ((Collection)iterable).size() : Iterators.size(iterable.iterator()); + } + + public static boolean contains(Iterable iterable, @Nullable Object element) { + if (iterable instanceof Collection) { + Collection collection = (Collection)iterable; + return Collections2.safeContains(collection, element); + } else { + return Iterators.contains(iterable.iterator(), element); + } + } + + public static boolean removeAll(Iterable removeFrom, Collection elementsToRemove) { + return removeFrom instanceof Collection ? ((Collection)removeFrom).removeAll((Collection)Preconditions.checkNotNull(elementsToRemove)) : Iterators.removeAll(removeFrom.iterator(), elementsToRemove); + } + + public static boolean retainAll(Iterable removeFrom, Collection elementsToRetain) { + return removeFrom instanceof Collection ? ((Collection)removeFrom).retainAll((Collection)Preconditions.checkNotNull(elementsToRetain)) : Iterators.retainAll(removeFrom.iterator(), elementsToRetain); + } + + public static boolean removeIf(Iterable removeFrom, Predicate predicate) { + return removeFrom instanceof RandomAccess && removeFrom instanceof List ? removeIfFromRandomAccessList((List)removeFrom, (Predicate)Preconditions.checkNotNull(predicate)) : Iterators.removeIf(removeFrom.iterator(), predicate); + } + + private static boolean removeIfFromRandomAccessList(List list, Predicate predicate) { + int from = 0; + + int to; + for(to = 0; from < list.size(); ++from) { + T element = list.get(from); + if (!predicate.apply(element)) { + if (from > to) { + try { + list.set(to, element); + } catch (UnsupportedOperationException var6) { + slowRemoveIfForRemainingElements(list, predicate, to, from); + return true; + } + } + + ++to; + } + } + + list.subList(to, list.size()).clear(); + return from != to; + } + + private static void slowRemoveIfForRemainingElements(List list, Predicate predicate, int to, int from) { + int n; + for(n = list.size() - 1; n > from; --n) { + if (predicate.apply(list.get(n))) { + list.remove(n); + } + } + + for(n = from - 1; n >= to; --n) { + list.remove(n); + } + + } + + @Nullable + static T removeFirstMatching(Iterable removeFrom, Predicate predicate) { + Preconditions.checkNotNull(predicate); + Iterator iterator = removeFrom.iterator(); + + Object next; + do { + if (!iterator.hasNext()) { + return null; + } + + next = iterator.next(); + } while(!predicate.apply(next)); + + iterator.remove(); + return next; + } + + 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()); + } + + public static String toString(Iterable iterable) { + return Iterators.toString(iterable.iterator()); + } + + public static T getOnlyElement(Iterable iterable) { + return Iterators.getOnlyElement(iterable.iterator()); + } + + @Nullable + public static T getOnlyElement(Iterable iterable, @Nullable T defaultValue) { + return Iterators.getOnlyElement(iterable.iterator(), defaultValue); + } + + @GwtIncompatible("Array.newInstance(Class, int)") + public static T[] toArray(Iterable iterable, Class type) { + Collection collection = toCollection(iterable); + T[] array = ObjectArrays.newArray(type, collection.size()); + return collection.toArray(array); + } + + static Object[] toArray(Iterable iterable) { + return toCollection(iterable).toArray(); + } + + private static Collection toCollection(Iterable iterable) { + return (Collection)(iterable instanceof Collection ? (Collection)iterable : Lists.newArrayList(iterable.iterator())); + } + + public static boolean addAll(Collection addTo, Iterable elementsToAdd) { + if (elementsToAdd instanceof Collection) { + Collection c = Collections2.cast(elementsToAdd); + return addTo.addAll(c); + } else { + return Iterators.addAll(addTo, ((Iterable)Preconditions.checkNotNull(elementsToAdd)).iterator()); + } + } + + public static int frequency(Iterable iterable, @Nullable Object element) { + if (iterable instanceof Multiset) { + return ((Multiset)iterable).count(element); + } else if (iterable instanceof Set) { + return ((Set)iterable).contains(element) ? 1 : 0; + } else { + return Iterators.frequency(iterable.iterator(), element); + } + } + + public static Iterable cycle(final Iterable iterable) { + Preconditions.checkNotNull(iterable); + return new FluentIterable() { + public Iterator iterator() { + return Iterators.cycle(iterable); + } + + public String toString() { + return String.valueOf(iterable.toString()).concat(" (cycled)"); + } + }; + } + + public static Iterable cycle(T... elements) { + return cycle((Iterable)Lists.newArrayList(elements)); + } + + public static Iterable concat(Iterable a, Iterable b) { + return concat((Iterable)ImmutableList.of(a, b)); + } + + public static Iterable concat(Iterable a, Iterable b, Iterable c) { + return concat((Iterable)ImmutableList.of(a, b, c)); + } + + public static Iterable concat(Iterable a, Iterable b, Iterable c, Iterable d) { + return concat((Iterable)ImmutableList.of(a, b, c, d)); + } + + public static Iterable concat(Iterable... inputs) { + return concat((Iterable)ImmutableList.copyOf((Object[])inputs)); + } + + public static Iterable concat(final Iterable> inputs) { + Preconditions.checkNotNull(inputs); + return new FluentIterable() { + public Iterator iterator() { + return Iterators.concat(Iterables.iterators(inputs)); + } + }; + } + + private static Iterator> iterators(Iterable> iterables) { + return new TransformedIterator, Iterator>(iterables.iterator()) { + Iterator transform(Iterable from) { + return from.iterator(); + } + }; + } + + public static Iterable> partition(final Iterable iterable, final int size) { + Preconditions.checkNotNull(iterable); + Preconditions.checkArgument(size > 0); + return new FluentIterable>() { + public Iterator> iterator() { + return Iterators.partition(iterable.iterator(), size); + } + }; + } + + public static Iterable> paddedPartition(final Iterable iterable, final int size) { + Preconditions.checkNotNull(iterable); + Preconditions.checkArgument(size > 0); + return new FluentIterable>() { + public Iterator> iterator() { + return Iterators.paddedPartition(iterable.iterator(), size); + } + }; + } + + public static Iterable filter(final Iterable unfiltered, final Predicate predicate) { + Preconditions.checkNotNull(unfiltered); + Preconditions.checkNotNull(predicate); + return new FluentIterable() { + public Iterator iterator() { + return Iterators.filter(unfiltered.iterator(), predicate); + } + }; + } + + @GwtIncompatible("Class.isInstance") + public static Iterable filter(final Iterable unfiltered, final Class type) { + Preconditions.checkNotNull(unfiltered); + Preconditions.checkNotNull(type); + return new FluentIterable() { + public Iterator iterator() { + return Iterators.filter(unfiltered.iterator(), type); + } + }; + } + + public static boolean any(Iterable iterable, Predicate predicate) { + return Iterators.any(iterable.iterator(), predicate); + } + + public static boolean all(Iterable iterable, Predicate predicate) { + return Iterators.all(iterable.iterator(), predicate); + } + + public static T find(Iterable iterable, Predicate predicate) { + return Iterators.find(iterable.iterator(), predicate); + } + + @Nullable + public static T find(Iterable iterable, Predicate predicate, @Nullable T defaultValue) { + return Iterators.find(iterable.iterator(), predicate, defaultValue); + } + + public static Optional tryFind(Iterable iterable, Predicate predicate) { + return Iterators.tryFind(iterable.iterator(), predicate); + } + + public static int indexOf(Iterable iterable, Predicate predicate) { + return Iterators.indexOf(iterable.iterator(), predicate); + } + + public static Iterable transform(final Iterable fromIterable, final Function function) { + Preconditions.checkNotNull(fromIterable); + Preconditions.checkNotNull(function); + return new FluentIterable() { + public Iterator iterator() { + return Iterators.transform(fromIterable.iterator(), function); + } + }; + } + + public static T get(Iterable iterable, int position) { + Preconditions.checkNotNull(iterable); + return iterable instanceof List ? ((List)iterable).get(position) : Iterators.get(iterable.iterator(), position); + } + + @Nullable + public static T get(Iterable iterable, int position, @Nullable T defaultValue) { + Preconditions.checkNotNull(iterable); + Iterators.checkNonnegative(position); + if (iterable instanceof List) { + List list = Lists.cast(iterable); + return position < list.size() ? list.get(position) : defaultValue; + } else { + Iterator iterator = iterable.iterator(); + Iterators.advance(iterator, position); + return Iterators.getNext(iterator, defaultValue); + } + } + + @Nullable + public static T getFirst(Iterable iterable, @Nullable T defaultValue) { + return Iterators.getNext(iterable.iterator(), defaultValue); + } + + public static T getLast(Iterable iterable) { + if (iterable instanceof List) { + List list = (List)iterable; + if (list.isEmpty()) { + throw new NoSuchElementException(); + } else { + return getLastInNonemptyList(list); + } + } else { + return Iterators.getLast(iterable.iterator()); + } + } + + @Nullable + public static T getLast(Iterable iterable, @Nullable T defaultValue) { + if (iterable instanceof Collection) { + Collection c = Collections2.cast(iterable); + if (c.isEmpty()) { + return defaultValue; + } + + if (iterable instanceof List) { + return getLastInNonemptyList(Lists.cast(iterable)); + } + } + + return Iterators.getLast(iterable.iterator(), defaultValue); + } + + private static T getLastInNonemptyList(List list) { + return list.get(list.size() - 1); + } + + public static Iterable skip(final Iterable iterable, final int numberToSkip) { + Preconditions.checkNotNull(iterable); + Preconditions.checkArgument(numberToSkip >= 0, "number to skip cannot be negative"); + if (iterable instanceof List) { + final List list = (List)iterable; + return new FluentIterable() { + public Iterator iterator() { + int toSkip = Math.min(list.size(), numberToSkip); + return list.subList(toSkip, list.size()).iterator(); + } + }; + } else { + return new FluentIterable() { + public Iterator iterator() { + final Iterator iterator = iterable.iterator(); + Iterators.advance(iterator, numberToSkip); + return new Iterator() { + boolean atStart = true; + + public boolean hasNext() { + return iterator.hasNext(); + } + + public T next() { + T result = iterator.next(); + this.atStart = false; + return result; + } + + public void remove() { + CollectPreconditions.checkRemove(!this.atStart); + iterator.remove(); + } + }; + } + }; + } + } + + public static Iterable limit(final Iterable iterable, final int limitSize) { + Preconditions.checkNotNull(iterable); + Preconditions.checkArgument(limitSize >= 0, "limit is negative"); + return new FluentIterable() { + public Iterator iterator() { + return Iterators.limit(iterable.iterator(), limitSize); + } + }; + } + + public static Iterable consumingIterable(final Iterable iterable) { + if (iterable instanceof Queue) { + return new FluentIterable() { + public Iterator iterator() { + return new Iterables.ConsumingQueueIterator((Queue)iterable); + } + + public String toString() { + return "Iterables.consumingIterable(...)"; + } + }; + } else { + Preconditions.checkNotNull(iterable); + return new FluentIterable() { + public Iterator iterator() { + return Iterators.consumingIterator(iterable.iterator()); + } + + public String toString() { + return "Iterables.consumingIterable(...)"; + } + }; + } + } + + public static boolean isEmpty(Iterable iterable) { + if (iterable instanceof Collection) { + return ((Collection)iterable).isEmpty(); + } else { + return !iterable.iterator().hasNext(); + } + } + + @Beta + public static Iterable mergeSorted(final Iterable> iterables, final Comparator comparator) { + Preconditions.checkNotNull(iterables, "iterables"); + Preconditions.checkNotNull(comparator, "comparator"); + Iterable iterable = new FluentIterable() { + public Iterator iterator() { + return Iterators.mergeSorted(Iterables.transform(iterables, Iterables.toIterator()), comparator); + } + }; + return new Iterables.UnmodifiableIterable(iterable); + } + + private static Function, Iterator> toIterator() { + return new Function, Iterator>() { + public Iterator apply(Iterable iterable) { + return iterable.iterator(); + } + }; + } + + private static class ConsumingQueueIterator extends AbstractIterator { + private final Queue queue; + + private ConsumingQueueIterator(Queue queue) { + this.queue = queue; + } + + public T computeNext() { + try { + return this.queue.remove(); + } catch (NoSuchElementException var2) { + return this.endOfData(); + } + } + + // $FF: synthetic method + ConsumingQueueIterator(Queue x0, Object x1) { + this(x0); + } + } + + private static final class UnmodifiableIterable extends FluentIterable { + private final Iterable iterable; + + private UnmodifiableIterable(Iterable iterable) { + this.iterable = iterable; + } + + public Iterator iterator() { + return Iterators.unmodifiableIterator(this.iterable.iterator()); + } + + public String toString() { + return this.iterable.toString(); + } + + // $FF: synthetic method + UnmodifiableIterable(Iterable x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/collect/Iterators.java b/src/main/com/google/common/collect/Iterators.java new file mode 100644 index 0000000..c9c4dc7 --- /dev/null +++ b/src/main/com/google/common/collect/Iterators.java @@ -0,0 +1,694 @@ +package com.google.common.collect; + +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; +import com.google.common.base.Predicates; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Iterators { + static final UnmodifiableListIterator EMPTY_LIST_ITERATOR = new UnmodifiableListIterator() { + public boolean hasNext() { + return false; + } + + public Object next() { + throw new NoSuchElementException(); + } + + public boolean hasPrevious() { + return false; + } + + public Object previous() { + throw new NoSuchElementException(); + } + + public int nextIndex() { + return 0; + } + + public int previousIndex() { + return -1; + } + }; + private static final Iterator EMPTY_MODIFIABLE_ITERATOR = new Iterator() { + public boolean hasNext() { + return false; + } + + public Object next() { + throw new NoSuchElementException(); + } + + public void remove() { + CollectPreconditions.checkRemove(false); + } + }; + + private Iterators() { + } + + /** @deprecated */ + @Deprecated + public static UnmodifiableIterator emptyIterator() { + return emptyListIterator(); + } + + static UnmodifiableListIterator emptyListIterator() { + return EMPTY_LIST_ITERATOR; + } + + static Iterator emptyModifiableIterator() { + return EMPTY_MODIFIABLE_ITERATOR; + } + + public static UnmodifiableIterator unmodifiableIterator(final Iterator iterator) { + Preconditions.checkNotNull(iterator); + return iterator instanceof UnmodifiableIterator ? (UnmodifiableIterator)iterator : new UnmodifiableIterator() { + public boolean hasNext() { + return iterator.hasNext(); + } + + public T next() { + return iterator.next(); + } + }; + } + + /** @deprecated */ + @Deprecated + public static UnmodifiableIterator unmodifiableIterator(UnmodifiableIterator iterator) { + return (UnmodifiableIterator)Preconditions.checkNotNull(iterator); + } + + public static int size(Iterator iterator) { + int count; + for(count = 0; iterator.hasNext(); ++count) { + iterator.next(); + } + + return count; + } + + public static boolean contains(Iterator iterator, @Nullable Object element) { + return any(iterator, Predicates.equalTo(element)); + } + + public static boolean removeAll(Iterator removeFrom, Collection elementsToRemove) { + return removeIf(removeFrom, Predicates.in(elementsToRemove)); + } + + public static boolean removeIf(Iterator removeFrom, Predicate predicate) { + Preconditions.checkNotNull(predicate); + boolean modified = false; + + while(removeFrom.hasNext()) { + if (predicate.apply(removeFrom.next())) { + removeFrom.remove(); + modified = true; + } + } + + return modified; + } + + public static boolean retainAll(Iterator removeFrom, Collection elementsToRetain) { + return removeIf(removeFrom, Predicates.not(Predicates.in(elementsToRetain))); + } + + public static boolean elementsEqual(Iterator iterator1, Iterator iterator2) { + while(true) { + if (iterator1.hasNext()) { + if (!iterator2.hasNext()) { + return false; + } + + Object o1 = iterator1.next(); + Object o2 = iterator2.next(); + if (Objects.equal(o1, o2)) { + continue; + } + + return false; + } + + return !iterator2.hasNext(); + } + } + + public static String toString(Iterator iterator) { + return Collections2.STANDARD_JOINER.appendTo((new StringBuilder()).append('['), iterator).append(']').toString(); + } + + public static T getOnlyElement(Iterator iterator) { + T first = iterator.next(); + if (!iterator.hasNext()) { + return first; + } else { + StringBuilder sb = new StringBuilder(); + String var3 = String.valueOf(String.valueOf(first)); + sb.append((new StringBuilder(31 + var3.length())).append("expected one element but was: <").append(var3).toString()); + + for(int i = 0; i < 4 && iterator.hasNext(); ++i) { + String var5 = String.valueOf(String.valueOf(iterator.next())); + sb.append((new StringBuilder(2 + var5.length())).append(", ").append(var5).toString()); + } + + if (iterator.hasNext()) { + sb.append(", ..."); + } + + sb.append('>'); + throw new IllegalArgumentException(sb.toString()); + } + } + + @Nullable + public static T getOnlyElement(Iterator iterator, @Nullable T defaultValue) { + return iterator.hasNext() ? getOnlyElement(iterator) : defaultValue; + } + + @GwtIncompatible("Array.newInstance(Class, int)") + public static T[] toArray(Iterator iterator, Class type) { + List list = Lists.newArrayList(iterator); + return Iterables.toArray(list, type); + } + + public static boolean addAll(Collection addTo, Iterator iterator) { + Preconditions.checkNotNull(addTo); + Preconditions.checkNotNull(iterator); + + boolean wasModified; + for(wasModified = false; iterator.hasNext(); wasModified |= addTo.add(iterator.next())) { + } + + return wasModified; + } + + public static int frequency(Iterator iterator, @Nullable Object element) { + return size(filter(iterator, Predicates.equalTo(element))); + } + + public static Iterator cycle(final Iterable iterable) { + Preconditions.checkNotNull(iterable); + return new Iterator() { + Iterator iterator = Iterators.emptyIterator(); + Iterator removeFrom; + + public boolean hasNext() { + if (!this.iterator.hasNext()) { + this.iterator = iterable.iterator(); + } + + return this.iterator.hasNext(); + } + + public T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + this.removeFrom = this.iterator; + return this.iterator.next(); + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.removeFrom != null); + this.removeFrom.remove(); + this.removeFrom = null; + } + }; + } + + public static Iterator cycle(T... elements) { + return cycle((Iterable)Lists.newArrayList(elements)); + } + + public static Iterator concat(Iterator a, Iterator b) { + return concat((Iterator)ImmutableList.of(a, b).iterator()); + } + + public static Iterator concat(Iterator a, Iterator b, Iterator c) { + return concat((Iterator)ImmutableList.of(a, b, c).iterator()); + } + + public static Iterator concat(Iterator a, Iterator b, Iterator c, Iterator d) { + return concat((Iterator)ImmutableList.of(a, b, c, d).iterator()); + } + + public static Iterator concat(Iterator... inputs) { + return concat((Iterator)ImmutableList.copyOf((Object[])inputs).iterator()); + } + + public static Iterator concat(final Iterator> inputs) { + Preconditions.checkNotNull(inputs); + return new Iterator() { + Iterator current = Iterators.emptyIterator(); + Iterator removeFrom; + + public boolean hasNext() { + boolean currentHasNext; + while(!(currentHasNext = ((Iterator)Preconditions.checkNotNull(this.current)).hasNext()) && inputs.hasNext()) { + this.current = (Iterator)inputs.next(); + } + + return currentHasNext; + } + + public T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + this.removeFrom = this.current; + return this.current.next(); + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.removeFrom != null); + this.removeFrom.remove(); + this.removeFrom = null; + } + }; + } + + public static UnmodifiableIterator> partition(Iterator iterator, int size) { + return partitionImpl(iterator, size, false); + } + + public static UnmodifiableIterator> paddedPartition(Iterator iterator, int size) { + return partitionImpl(iterator, size, true); + } + + private static UnmodifiableIterator> partitionImpl(final Iterator iterator, final int size, final boolean pad) { + Preconditions.checkNotNull(iterator); + Preconditions.checkArgument(size > 0); + return new UnmodifiableIterator>() { + public boolean hasNext() { + return iterator.hasNext(); + } + + public List next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + Object[] array = new Object[size]; + + int count; + for(count = 0; count < size && iterator.hasNext(); ++count) { + array[count] = iterator.next(); + } + + for(int i = count; i < size; ++i) { + array[i] = null; + } + + List list = Collections.unmodifiableList(Arrays.asList(array)); + return !pad && count != size ? list.subList(0, count) : list; + } + } + }; + } + + public static UnmodifiableIterator filter(final Iterator unfiltered, final Predicate predicate) { + Preconditions.checkNotNull(unfiltered); + Preconditions.checkNotNull(predicate); + return new AbstractIterator() { + protected T computeNext() { + while(true) { + if (unfiltered.hasNext()) { + T element = unfiltered.next(); + if (!predicate.apply(element)) { + continue; + } + + return element; + } + + return this.endOfData(); + } + } + }; + } + + @GwtIncompatible("Class.isInstance") + public static UnmodifiableIterator filter(Iterator unfiltered, Class type) { + return filter(unfiltered, Predicates.instanceOf(type)); + } + + public static boolean any(Iterator iterator, Predicate predicate) { + return indexOf(iterator, predicate) != -1; + } + + public static boolean all(Iterator iterator, Predicate predicate) { + Preconditions.checkNotNull(predicate); + + Object element; + do { + if (!iterator.hasNext()) { + return true; + } + + element = iterator.next(); + } while(predicate.apply(element)); + + return false; + } + + public static T find(Iterator iterator, Predicate predicate) { + return filter(iterator, predicate).next(); + } + + @Nullable + public static T find(Iterator iterator, Predicate predicate, @Nullable T defaultValue) { + return getNext(filter(iterator, predicate), defaultValue); + } + + public static Optional tryFind(Iterator iterator, Predicate predicate) { + UnmodifiableIterator filteredIterator = filter(iterator, predicate); + return filteredIterator.hasNext() ? Optional.of(filteredIterator.next()) : Optional.absent(); + } + + public static int indexOf(Iterator iterator, Predicate predicate) { + Preconditions.checkNotNull(predicate, "predicate"); + + for(int i = 0; iterator.hasNext(); ++i) { + T current = iterator.next(); + if (predicate.apply(current)) { + return i; + } + } + + return -1; + } + + public static Iterator transform(Iterator fromIterator, final Function function) { + Preconditions.checkNotNull(function); + return new TransformedIterator(fromIterator) { + T transform(F from) { + return function.apply(from); + } + }; + } + + public static T get(Iterator iterator, int position) { + checkNonnegative(position); + int skipped = advance(iterator, position); + if (!iterator.hasNext()) { + throw new IndexOutOfBoundsException((new StringBuilder(91)).append("position (").append(position).append(") must be less than the number of elements that remained (").append(skipped).append(")").toString()); + } else { + return iterator.next(); + } + } + + static void checkNonnegative(int position) { + if (position < 0) { + throw new IndexOutOfBoundsException((new StringBuilder(43)).append("position (").append(position).append(") must not be negative").toString()); + } + } + + @Nullable + public static T get(Iterator iterator, int position, @Nullable T defaultValue) { + checkNonnegative(position); + advance(iterator, position); + return getNext(iterator, defaultValue); + } + + @Nullable + public static T getNext(Iterator iterator, @Nullable T defaultValue) { + return iterator.hasNext() ? iterator.next() : defaultValue; + } + + public static T getLast(Iterator iterator) { + Object current; + do { + current = iterator.next(); + } while(iterator.hasNext()); + + return current; + } + + @Nullable + public static T getLast(Iterator iterator, @Nullable T defaultValue) { + return iterator.hasNext() ? getLast(iterator) : defaultValue; + } + + public static int advance(Iterator iterator, int numberToAdvance) { + Preconditions.checkNotNull(iterator); + Preconditions.checkArgument(numberToAdvance >= 0, "numberToAdvance must be nonnegative"); + + int i; + for(i = 0; i < numberToAdvance && iterator.hasNext(); ++i) { + iterator.next(); + } + + return i; + } + + public static Iterator limit(final Iterator iterator, final int limitSize) { + Preconditions.checkNotNull(iterator); + Preconditions.checkArgument(limitSize >= 0, "limit is negative"); + return new Iterator() { + private int count; + + public boolean hasNext() { + return this.count < limitSize && iterator.hasNext(); + } + + public T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + ++this.count; + return iterator.next(); + } + } + + public void remove() { + iterator.remove(); + } + }; + } + + public static Iterator consumingIterator(final Iterator iterator) { + Preconditions.checkNotNull(iterator); + return new UnmodifiableIterator() { + public boolean hasNext() { + return iterator.hasNext(); + } + + public T next() { + T next = iterator.next(); + iterator.remove(); + return next; + } + + public String toString() { + return "Iterators.consumingIterator(...)"; + } + }; + } + + @Nullable + static T pollNext(Iterator iterator) { + if (iterator.hasNext()) { + T result = iterator.next(); + iterator.remove(); + return result; + } else { + return null; + } + } + + static void clear(Iterator iterator) { + Preconditions.checkNotNull(iterator); + + while(iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + + } + + public static UnmodifiableIterator forArray(T... array) { + return forArray(array, 0, array.length, 0); + } + + static UnmodifiableListIterator forArray(final T[] array, final int offset, int length, int index) { + Preconditions.checkArgument(length >= 0); + int end = offset + length; + Preconditions.checkPositionIndexes(offset, end, array.length); + Preconditions.checkPositionIndex(index, length); + return (UnmodifiableListIterator)(length == 0 ? emptyListIterator() : new AbstractIndexedListIterator(length, index) { + protected T get(int index) { + return array[offset + index]; + } + }); + } + + public static UnmodifiableIterator singletonIterator(@Nullable final T value) { + return new UnmodifiableIterator() { + boolean done; + + public boolean hasNext() { + return !this.done; + } + + public T next() { + if (this.done) { + throw new NoSuchElementException(); + } else { + this.done = true; + return value; + } + } + }; + } + + public static UnmodifiableIterator forEnumeration(final Enumeration enumeration) { + Preconditions.checkNotNull(enumeration); + return new UnmodifiableIterator() { + public boolean hasNext() { + return enumeration.hasMoreElements(); + } + + public T next() { + return enumeration.nextElement(); + } + }; + } + + public static Enumeration asEnumeration(final Iterator iterator) { + Preconditions.checkNotNull(iterator); + return new Enumeration() { + public boolean hasMoreElements() { + return iterator.hasNext(); + } + + public T nextElement() { + return iterator.next(); + } + }; + } + + public static PeekingIterator peekingIterator(Iterator iterator) { + if (iterator instanceof Iterators.PeekingImpl) { + Iterators.PeekingImpl peeking = (Iterators.PeekingImpl)iterator; + return peeking; + } else { + return new Iterators.PeekingImpl(iterator); + } + } + + /** @deprecated */ + @Deprecated + public static PeekingIterator peekingIterator(PeekingIterator iterator) { + return (PeekingIterator)Preconditions.checkNotNull(iterator); + } + + @Beta + public static UnmodifiableIterator mergeSorted(Iterable> iterators, Comparator comparator) { + Preconditions.checkNotNull(iterators, "iterators"); + Preconditions.checkNotNull(comparator, "comparator"); + return new Iterators.MergingIterator(iterators, comparator); + } + + static ListIterator cast(Iterator iterator) { + return (ListIterator)iterator; + } + + private static class MergingIterator extends UnmodifiableIterator { + final Queue> queue; + + public MergingIterator(Iterable> iterators, final Comparator itemComparator) { + Comparator> heapComparator = new Comparator>() { + public int compare(PeekingIterator o1, PeekingIterator o2) { + return itemComparator.compare(o1.peek(), o2.peek()); + } + }; + this.queue = new PriorityQueue(2, heapComparator); + Iterator i$ = iterators.iterator(); + + while(i$.hasNext()) { + Iterator iterator = (Iterator)i$.next(); + if (iterator.hasNext()) { + this.queue.add(Iterators.peekingIterator(iterator)); + } + } + + } + + public boolean hasNext() { + return !this.queue.isEmpty(); + } + + public T next() { + PeekingIterator nextIter = (PeekingIterator)this.queue.remove(); + T next = nextIter.next(); + if (nextIter.hasNext()) { + this.queue.add(nextIter); + } + + return next; + } + } + + private static class PeekingImpl implements PeekingIterator { + private final Iterator iterator; + private boolean hasPeeked; + private E peekedElement; + + public PeekingImpl(Iterator iterator) { + this.iterator = (Iterator)Preconditions.checkNotNull(iterator); + } + + public boolean hasNext() { + return this.hasPeeked || this.iterator.hasNext(); + } + + public E next() { + if (!this.hasPeeked) { + return this.iterator.next(); + } else { + E result = this.peekedElement; + this.hasPeeked = false; + this.peekedElement = null; + return result; + } + } + + public void remove() { + Preconditions.checkState(!this.hasPeeked, "Can't remove after you've peeked at next"); + this.iterator.remove(); + } + + public E peek() { + if (!this.hasPeeked) { + this.peekedElement = this.iterator.next(); + this.hasPeeked = true; + } + + return this.peekedElement; + } + } +} diff --git a/src/main/com/google/common/collect/LexicographicalOrdering.java b/src/main/com/google/common/collect/LexicographicalOrdering.java new file mode 100644 index 0000000..b8985cc --- /dev/null +++ b/src/main/com/google/common/collect/LexicographicalOrdering.java @@ -0,0 +1,62 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Iterator; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class LexicographicalOrdering extends Ordering> implements Serializable { + final Ordering elementOrder; + private static final long serialVersionUID = 0L; + + LexicographicalOrdering(Ordering elementOrder) { + this.elementOrder = elementOrder; + } + + public int compare(Iterable leftIterable, Iterable rightIterable) { + Iterator left = leftIterable.iterator(); + Iterator right = rightIterable.iterator(); + + int result; + do { + if (!left.hasNext()) { + if (right.hasNext()) { + return -1; + } + + return 0; + } + + if (!right.hasNext()) { + return 1; + } + + result = this.elementOrder.compare(left.next(), right.next()); + } while(result == 0); + + return result; + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (object instanceof LexicographicalOrdering) { + LexicographicalOrdering that = (LexicographicalOrdering)object; + return this.elementOrder.equals(that.elementOrder); + } else { + return false; + } + } + + public int hashCode() { + return this.elementOrder.hashCode() ^ 2075626741; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.elementOrder)); + return (new StringBuilder(18 + var1.length())).append(var1).append(".lexicographical()").toString(); + } +} diff --git a/src/main/com/google/common/collect/LinkedHashMultimap.java b/src/main/com/google/common/collect/LinkedHashMultimap.java new file mode 100644 index 0000000..7d2a1c9 --- /dev/null +++ b/src/main/com/google/common/collect/LinkedHashMultimap.java @@ -0,0 +1,428 @@ +package com.google.common.collect; + +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 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 java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public final class LinkedHashMultimap extends AbstractSetMultimap { + 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.0D; + @VisibleForTesting + transient int valueSetCapacity = 2; + private transient LinkedHashMultimap.ValueEntry multimapHeaderEntry; + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 1L; + + public static LinkedHashMultimap create() { + return new LinkedHashMultimap(16, 2); + } + + public static LinkedHashMultimap create(int expectedKeys, int expectedValuesPerKey) { + return new LinkedHashMultimap(Maps.capacity(expectedKeys), Maps.capacity(expectedValuesPerKey)); + } + + public static LinkedHashMultimap create(Multimap multimap) { + LinkedHashMultimap result = create(multimap.keySet().size(), 2); + result.putAll(multimap); + return result; + } + + private static void succeedsInValueSet(LinkedHashMultimap.ValueSetLink pred, LinkedHashMultimap.ValueSetLink succ) { + pred.setSuccessorInValueSet(succ); + succ.setPredecessorInValueSet(pred); + } + + private static void succeedsInMultimap(LinkedHashMultimap.ValueEntry pred, LinkedHashMultimap.ValueEntry succ) { + pred.setSuccessorInMultimap(succ); + succ.setPredecessorInMultimap(pred); + } + + private static void deleteFromValueSet(LinkedHashMultimap.ValueSetLink entry) { + succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet()); + } + + private static void deleteFromMultimap(LinkedHashMultimap.ValueEntry entry) { + succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap()); + } + + private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) { + super(new LinkedHashMap(keyCapacity)); + CollectPreconditions.checkNonnegative(valueSetCapacity, "expectedValuesPerKey"); + this.valueSetCapacity = valueSetCapacity; + this.multimapHeaderEntry = new LinkedHashMultimap.ValueEntry((Object)null, (Object)null, 0, (LinkedHashMultimap.ValueEntry)null); + succeedsInMultimap(this.multimapHeaderEntry, this.multimapHeaderEntry); + } + + Set createCollection() { + return new LinkedHashSet(this.valueSetCapacity); + } + + Collection createCollection(K key) { + return new LinkedHashMultimap.ValueSet(key, this.valueSetCapacity); + } + + public Set replaceValues(@Nullable K key, Iterable values) { + return super.replaceValues(key, values); + } + + public Set> entries() { + return super.entries(); + } + + public Collection values() { + return super.values(); + } + + Iterator> entryIterator() { + return new Iterator>() { + LinkedHashMultimap.ValueEntry nextEntry; + LinkedHashMultimap.ValueEntry toRemove; + + { + this.nextEntry = LinkedHashMultimap.this.multimapHeaderEntry.successorInMultimap; + } + + public boolean hasNext() { + return this.nextEntry != LinkedHashMultimap.this.multimapHeaderEntry; + } + + public Entry next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + LinkedHashMultimap.ValueEntry result = this.nextEntry; + this.toRemove = result; + this.nextEntry = this.nextEntry.successorInMultimap; + return result; + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.toRemove != null); + LinkedHashMultimap.this.remove(this.toRemove.getKey(), this.toRemove.getValue()); + this.toRemove = null; + } + }; + } + + Iterator valueIterator() { + return Maps.valueIterator(this.entryIterator()); + } + + public void clear() { + super.clear(); + succeedsInMultimap(this.multimapHeaderEntry, this.multimapHeaderEntry); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(this.valueSetCapacity); + stream.writeInt(this.keySet().size()); + Iterator i$ = this.keySet().iterator(); + + while(i$.hasNext()) { + K key = i$.next(); + stream.writeObject(key); + } + + stream.writeInt(this.size()); + i$ = this.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.multimapHeaderEntry = new LinkedHashMultimap.ValueEntry((Object)null, (Object)null, 0, (LinkedHashMultimap.ValueEntry)null); + succeedsInMultimap(this.multimapHeaderEntry, this.multimapHeaderEntry); + this.valueSetCapacity = stream.readInt(); + int distinctKeys = stream.readInt(); + Map> map = new LinkedHashMap(Maps.capacity(distinctKeys)); + + int entries; + for(entries = 0; entries < distinctKeys; ++entries) { + K key = stream.readObject(); + map.put(key, this.createCollection(key)); + } + + entries = stream.readInt(); + + for(int i = 0; i < entries; ++i) { + K key = stream.readObject(); + V value = stream.readObject(); + ((Collection)map.get(key)).add(value); + } + + this.setMap(map); + } + + @VisibleForTesting + final class ValueSet extends Sets.ImprovedAbstractSet implements LinkedHashMultimap.ValueSetLink { + private final K key; + @VisibleForTesting + LinkedHashMultimap.ValueEntry[] hashTable; + private int size = 0; + private int modCount = 0; + private LinkedHashMultimap.ValueSetLink firstEntry; + private LinkedHashMultimap.ValueSetLink lastEntry; + + ValueSet(K key, int expectedValues) { + this.key = key; + this.firstEntry = this; + this.lastEntry = this; + int tableSize = Hashing.closedTableSize(expectedValues, 1.0D); + LinkedHashMultimap.ValueEntry[] hashTable = new LinkedHashMultimap.ValueEntry[tableSize]; + this.hashTable = hashTable; + } + + private int mask() { + return this.hashTable.length - 1; + } + + public LinkedHashMultimap.ValueSetLink getPredecessorInValueSet() { + return this.lastEntry; + } + + public LinkedHashMultimap.ValueSetLink getSuccessorInValueSet() { + return this.firstEntry; + } + + public void setPredecessorInValueSet(LinkedHashMultimap.ValueSetLink entry) { + this.lastEntry = entry; + } + + public void setSuccessorInValueSet(LinkedHashMultimap.ValueSetLink entry) { + this.firstEntry = entry; + } + + public Iterator iterator() { + return new Iterator() { + LinkedHashMultimap.ValueSetLink nextEntry; + LinkedHashMultimap.ValueEntry toRemove; + int expectedModCount; + + { + this.nextEntry = ValueSet.this.firstEntry; + this.expectedModCount = ValueSet.this.modCount; + } + + private void checkForComodification() { + if (ValueSet.this.modCount != this.expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + public boolean hasNext() { + this.checkForComodification(); + return this.nextEntry != ValueSet.this; + } + + public V next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + LinkedHashMultimap.ValueEntry entry = (LinkedHashMultimap.ValueEntry)this.nextEntry; + V result = entry.getValue(); + this.toRemove = entry; + this.nextEntry = entry.getSuccessorInValueSet(); + return result; + } + } + + public void remove() { + this.checkForComodification(); + CollectPreconditions.checkRemove(this.toRemove != null); + ValueSet.this.remove(this.toRemove.getValue()); + this.expectedModCount = ValueSet.this.modCount; + this.toRemove = null; + } + }; + } + + public int size() { + return this.size; + } + + public boolean contains(@Nullable Object o) { + int smearedHash = Hashing.smearedHash(o); + + for(LinkedHashMultimap.ValueEntry entry = this.hashTable[smearedHash & this.mask()]; entry != null; entry = entry.nextInValueBucket) { + if (entry.matchesValue(o, smearedHash)) { + return true; + } + } + + return false; + } + + public boolean add(@Nullable V value) { + int smearedHash = Hashing.smearedHash(value); + int bucket = smearedHash & this.mask(); + LinkedHashMultimap.ValueEntry rowHead = this.hashTable[bucket]; + + LinkedHashMultimap.ValueEntry entry; + for(entry = rowHead; entry != null; entry = entry.nextInValueBucket) { + if (entry.matchesValue(value, smearedHash)) { + return false; + } + } + + entry = new LinkedHashMultimap.ValueEntry(this.key, value, smearedHash, rowHead); + LinkedHashMultimap.succeedsInValueSet(this.lastEntry, entry); + LinkedHashMultimap.succeedsInValueSet(entry, this); + LinkedHashMultimap.succeedsInMultimap(LinkedHashMultimap.this.multimapHeaderEntry.getPredecessorInMultimap(), entry); + LinkedHashMultimap.succeedsInMultimap(entry, LinkedHashMultimap.this.multimapHeaderEntry); + this.hashTable[bucket] = entry; + ++this.size; + ++this.modCount; + this.rehashIfNecessary(); + return true; + } + + private void rehashIfNecessary() { + if (Hashing.needsResizing(this.size, this.hashTable.length, 1.0D)) { + LinkedHashMultimap.ValueEntry[] hashTable = new LinkedHashMultimap.ValueEntry[this.hashTable.length * 2]; + this.hashTable = hashTable; + int mask = hashTable.length - 1; + + for(LinkedHashMultimap.ValueSetLink entry = this.firstEntry; entry != this; entry = entry.getSuccessorInValueSet()) { + LinkedHashMultimap.ValueEntry valueEntry = (LinkedHashMultimap.ValueEntry)entry; + int bucket = valueEntry.smearedValueHash & mask; + valueEntry.nextInValueBucket = hashTable[bucket]; + hashTable[bucket] = valueEntry; + } + } + + } + + public boolean remove(@Nullable Object o) { + int smearedHash = Hashing.smearedHash(o); + int bucket = smearedHash & this.mask(); + LinkedHashMultimap.ValueEntry prev = null; + + for(LinkedHashMultimap.ValueEntry entry = this.hashTable[bucket]; entry != null; entry = entry.nextInValueBucket) { + if (entry.matchesValue(o, smearedHash)) { + if (prev == null) { + this.hashTable[bucket] = entry.nextInValueBucket; + } else { + prev.nextInValueBucket = entry.nextInValueBucket; + } + + LinkedHashMultimap.deleteFromValueSet(entry); + LinkedHashMultimap.deleteFromMultimap(entry); + --this.size; + ++this.modCount; + return true; + } + + prev = entry; + } + + return false; + } + + public void clear() { + Arrays.fill(this.hashTable, (Object)null); + this.size = 0; + + for(LinkedHashMultimap.ValueSetLink entry = this.firstEntry; entry != this; entry = entry.getSuccessorInValueSet()) { + LinkedHashMultimap.ValueEntry valueEntry = (LinkedHashMultimap.ValueEntry)entry; + LinkedHashMultimap.deleteFromMultimap(valueEntry); + } + + LinkedHashMultimap.succeedsInValueSet(this, this); + ++this.modCount; + } + } + + @VisibleForTesting + static final class ValueEntry extends ImmutableEntry implements LinkedHashMultimap.ValueSetLink { + final int smearedValueHash; + @Nullable + LinkedHashMultimap.ValueEntry nextInValueBucket; + LinkedHashMultimap.ValueSetLink predecessorInValueSet; + LinkedHashMultimap.ValueSetLink successorInValueSet; + LinkedHashMultimap.ValueEntry predecessorInMultimap; + LinkedHashMultimap.ValueEntry successorInMultimap; + + ValueEntry(@Nullable K key, @Nullable V value, int smearedValueHash, @Nullable LinkedHashMultimap.ValueEntry nextInValueBucket) { + super(key, value); + this.smearedValueHash = smearedValueHash; + this.nextInValueBucket = nextInValueBucket; + } + + boolean matchesValue(@Nullable Object v, int smearedVHash) { + return this.smearedValueHash == smearedVHash && Objects.equal(this.getValue(), v); + } + + public LinkedHashMultimap.ValueSetLink getPredecessorInValueSet() { + return this.predecessorInValueSet; + } + + public LinkedHashMultimap.ValueSetLink getSuccessorInValueSet() { + return this.successorInValueSet; + } + + public void setPredecessorInValueSet(LinkedHashMultimap.ValueSetLink entry) { + this.predecessorInValueSet = entry; + } + + public void setSuccessorInValueSet(LinkedHashMultimap.ValueSetLink entry) { + this.successorInValueSet = entry; + } + + public LinkedHashMultimap.ValueEntry getPredecessorInMultimap() { + return this.predecessorInMultimap; + } + + public LinkedHashMultimap.ValueEntry getSuccessorInMultimap() { + return this.successorInMultimap; + } + + public void setSuccessorInMultimap(LinkedHashMultimap.ValueEntry multimapSuccessor) { + this.successorInMultimap = multimapSuccessor; + } + + public void setPredecessorInMultimap(LinkedHashMultimap.ValueEntry multimapPredecessor) { + this.predecessorInMultimap = multimapPredecessor; + } + } + + private interface ValueSetLink { + LinkedHashMultimap.ValueSetLink getPredecessorInValueSet(); + + LinkedHashMultimap.ValueSetLink getSuccessorInValueSet(); + + void setPredecessorInValueSet(LinkedHashMultimap.ValueSetLink var1); + + void setSuccessorInValueSet(LinkedHashMultimap.ValueSetLink var1); + } +} diff --git a/src/main/com/google/common/collect/LinkedHashMultiset.java b/src/main/com/google/common/collect/LinkedHashMultiset.java new file mode 100644 index 0000000..5dc64c3 --- /dev/null +++ b/src/main/com/google/common/collect/LinkedHashMultiset.java @@ -0,0 +1,53 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.LinkedHashMap; + +@GwtCompatible( + serializable = true, + emulated = true +) +public final class LinkedHashMultiset extends AbstractMapBasedMultiset { + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0L; + + public static LinkedHashMultiset create() { + return new LinkedHashMultiset(); + } + + public static LinkedHashMultiset create(int distinctElements) { + return new LinkedHashMultiset(distinctElements); + } + + public static LinkedHashMultiset create(Iterable elements) { + LinkedHashMultiset multiset = create(Multisets.inferDistinctElements(elements)); + Iterables.addAll(multiset, elements); + return multiset; + } + + private LinkedHashMultiset() { + super(new LinkedHashMap()); + } + + private LinkedHashMultiset(int distinctElements) { + super(new LinkedHashMap(Maps.capacity(distinctElements))); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + int distinctElements = Serialization.readCount(stream); + this.setBackingMap(new LinkedHashMap(Maps.capacity(distinctElements))); + Serialization.populateMultiset(this, stream, distinctElements); + } +} diff --git a/src/main/com/google/common/collect/LinkedListMultimap.java b/src/main/com/google/common/collect/LinkedListMultimap.java new file mode 100644 index 0000000..b8deb1d --- /dev/null +++ b/src/main/com/google/common/collect/LinkedListMultimap.java @@ -0,0 +1,615 @@ +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.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractSequentialList; +import java.util.Collection; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public class LinkedListMultimap extends AbstractMultimap implements ListMultimap, Serializable { + private transient LinkedListMultimap.Node head; + private transient LinkedListMultimap.Node tail; + private transient Map> keyToKeyList; + private transient int size; + private transient int modCount; + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 0L; + + public static LinkedListMultimap create() { + return new LinkedListMultimap(); + } + + public static LinkedListMultimap create(int expectedKeys) { + return new LinkedListMultimap(expectedKeys); + } + + public static LinkedListMultimap create(Multimap multimap) { + return new LinkedListMultimap(multimap); + } + + LinkedListMultimap() { + this.keyToKeyList = Maps.newHashMap(); + } + + private LinkedListMultimap(int expectedKeys) { + this.keyToKeyList = new HashMap(expectedKeys); + } + + private LinkedListMultimap(Multimap multimap) { + this(multimap.keySet().size()); + this.putAll(multimap); + } + + private LinkedListMultimap.Node addNode(@Nullable K key, @Nullable V value, @Nullable LinkedListMultimap.Node nextSibling) { + LinkedListMultimap.Node node = new LinkedListMultimap.Node(key, value); + if (this.head == null) { + this.head = this.tail = node; + this.keyToKeyList.put(key, new LinkedListMultimap.KeyList(node)); + ++this.modCount; + } else { + LinkedListMultimap.KeyList keyList; + if (nextSibling == null) { + this.tail.next = node; + node.previous = this.tail; + this.tail = node; + keyList = (LinkedListMultimap.KeyList)this.keyToKeyList.get(key); + if (keyList == null) { + this.keyToKeyList.put(key, new LinkedListMultimap.KeyList(node)); + ++this.modCount; + } else { + ++keyList.count; + LinkedListMultimap.Node keyTail = keyList.tail; + keyTail.nextSibling = node; + node.previousSibling = keyTail; + keyList.tail = node; + } + } else { + keyList = (LinkedListMultimap.KeyList)this.keyToKeyList.get(key); + ++keyList.count; + node.previous = nextSibling.previous; + node.previousSibling = nextSibling.previousSibling; + node.next = nextSibling; + node.nextSibling = nextSibling; + if (nextSibling.previousSibling == null) { + ((LinkedListMultimap.KeyList)this.keyToKeyList.get(key)).head = node; + } else { + nextSibling.previousSibling.nextSibling = node; + } + + if (nextSibling.previous == null) { + this.head = node; + } else { + nextSibling.previous.next = node; + } + + nextSibling.previous = node; + nextSibling.previousSibling = node; + } + } + + ++this.size; + return node; + } + + private void removeNode(LinkedListMultimap.Node node) { + if (node.previous != null) { + node.previous.next = node.next; + } else { + this.head = node.next; + } + + if (node.next != null) { + node.next.previous = node.previous; + } else { + this.tail = node.previous; + } + + LinkedListMultimap.KeyList keyList; + if (node.previousSibling == null && node.nextSibling == null) { + keyList = (LinkedListMultimap.KeyList)this.keyToKeyList.remove(node.key); + keyList.count = 0; + ++this.modCount; + } else { + keyList = (LinkedListMultimap.KeyList)this.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; + } + } + + --this.size; + } + + private void removeAllNodes(@Nullable Object key) { + Iterators.clear(new LinkedListMultimap.ValueForKeyIterator(key)); + } + + private static void checkElement(@Nullable Object node) { + if (node == null) { + throw new NoSuchElementException(); + } + } + + public int size() { + return this.size; + } + + public boolean isEmpty() { + return this.head == null; + } + + public boolean containsKey(@Nullable Object key) { + return this.keyToKeyList.containsKey(key); + } + + public boolean containsValue(@Nullable Object value) { + return this.values().contains(value); + } + + public boolean put(@Nullable K key, @Nullable V value) { + this.addNode(key, value, (LinkedListMultimap.Node)null); + return true; + } + + public List replaceValues(@Nullable K key, Iterable values) { + List oldValues = this.getCopy(key); + ListIterator keyValues = new LinkedListMultimap.ValueForKeyIterator(key); + Iterator newValues = values.iterator(); + + while(keyValues.hasNext() && newValues.hasNext()) { + keyValues.next(); + keyValues.set(newValues.next()); + } + + while(keyValues.hasNext()) { + keyValues.next(); + keyValues.remove(); + } + + while(newValues.hasNext()) { + keyValues.add(newValues.next()); + } + + return oldValues; + } + + private List getCopy(@Nullable Object key) { + return Collections.unmodifiableList(Lists.newArrayList((Iterator)(new LinkedListMultimap.ValueForKeyIterator(key)))); + } + + public List removeAll(@Nullable Object key) { + List oldValues = this.getCopy(key); + this.removeAllNodes(key); + return oldValues; + } + + public void clear() { + this.head = null; + this.tail = null; + this.keyToKeyList.clear(); + this.size = 0; + ++this.modCount; + } + + public List get(@Nullable final K key) { + return new AbstractSequentialList() { + public int size() { + LinkedListMultimap.KeyList keyList = (LinkedListMultimap.KeyList)LinkedListMultimap.this.keyToKeyList.get(key); + return keyList == null ? 0 : keyList.count; + } + + public ListIterator listIterator(int index) { + return LinkedListMultimap.this.new ValueForKeyIterator(key, index); + } + }; + } + + Set createKeySet() { + return new Sets.ImprovedAbstractSet() { + public int size() { + return LinkedListMultimap.this.keyToKeyList.size(); + } + + public Iterator iterator() { + return LinkedListMultimap.this.new DistinctKeyIterator(); + } + + public boolean contains(Object key) { + return LinkedListMultimap.this.containsKey(key); + } + + public boolean remove(Object o) { + return !LinkedListMultimap.this.removeAll(o).isEmpty(); + } + }; + } + + public List values() { + return (List)super.values(); + } + + List createValues() { + return new AbstractSequentialList() { + public int size() { + return LinkedListMultimap.this.size; + } + + public ListIterator listIterator(int index) { + final LinkedListMultimap.NodeIterator nodeItr = LinkedListMultimap.this.new NodeIterator(index); + return new TransformedListIterator, V>(nodeItr) { + V transform(Entry entry) { + return entry.getValue(); + } + + public void set(V value) { + nodeItr.setValue(value); + } + }; + } + }; + } + + public List> entries() { + return (List)super.entries(); + } + + List> createEntries() { + return new AbstractSequentialList>() { + public int size() { + return LinkedListMultimap.this.size; + } + + public ListIterator> listIterator(int index) { + return LinkedListMultimap.this.new NodeIterator(index); + } + }; + } + + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + Map> createAsMap() { + return new Multimaps.AsMap(this); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeInt(this.size()); + Iterator i$ = this.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.keyToKeyList = Maps.newLinkedHashMap(); + int size = stream.readInt(); + + for(int i = 0; i < size; ++i) { + K key = stream.readObject(); + V value = stream.readObject(); + this.put(key, value); + } + + } + + private class ValueForKeyIterator implements ListIterator { + final Object key; + int nextIndex; + LinkedListMultimap.Node next; + LinkedListMultimap.Node current; + LinkedListMultimap.Node previous; + + ValueForKeyIterator(@Nullable Object key) { + this.key = key; + LinkedListMultimap.KeyList keyList = (LinkedListMultimap.KeyList)LinkedListMultimap.this.keyToKeyList.get(key); + this.next = keyList == null ? null : keyList.head; + } + + public ValueForKeyIterator(@Nullable Object key, int index) { + LinkedListMultimap.KeyList keyList = (LinkedListMultimap.KeyList)LinkedListMultimap.this.keyToKeyList.get(key); + int size = keyList == null ? 0 : keyList.count; + Preconditions.checkPositionIndex(index, size); + if (index >= size / 2) { + this.previous = keyList == null ? null : keyList.tail; + this.nextIndex = size; + + while(index++ < size) { + this.previous(); + } + } else { + this.next = keyList == null ? null : keyList.head; + + while(index-- > 0) { + this.next(); + } + } + + this.key = key; + this.current = null; + } + + public boolean hasNext() { + return this.next != null; + } + + public V next() { + LinkedListMultimap.checkElement(this.next); + this.previous = this.current = this.next; + this.next = this.next.nextSibling; + ++this.nextIndex; + return this.current.value; + } + + public boolean hasPrevious() { + return this.previous != null; + } + + public V previous() { + LinkedListMultimap.checkElement(this.previous); + this.next = this.current = this.previous; + this.previous = this.previous.previousSibling; + --this.nextIndex; + return this.current.value; + } + + public int nextIndex() { + return this.nextIndex; + } + + public int previousIndex() { + return this.nextIndex - 1; + } + + public void remove() { + CollectPreconditions.checkRemove(this.current != null); + if (this.current != this.next) { + this.previous = this.current.previousSibling; + --this.nextIndex; + } else { + this.next = this.current.nextSibling; + } + + LinkedListMultimap.this.removeNode(this.current); + this.current = null; + } + + public void set(V value) { + Preconditions.checkState(this.current != null); + this.current.value = value; + } + + public void add(V value) { + this.previous = LinkedListMultimap.this.addNode(this.key, value, this.next); + ++this.nextIndex; + this.current = null; + } + } + + private class DistinctKeyIterator implements Iterator { + final Set seenKeys; + LinkedListMultimap.Node next; + LinkedListMultimap.Node current; + int expectedModCount; + + private DistinctKeyIterator() { + this.seenKeys = Sets.newHashSetWithExpectedSize(LinkedListMultimap.this.keySet().size()); + this.next = LinkedListMultimap.this.head; + this.expectedModCount = LinkedListMultimap.this.modCount; + } + + private void checkForConcurrentModification() { + if (LinkedListMultimap.this.modCount != this.expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + public boolean hasNext() { + this.checkForConcurrentModification(); + return this.next != null; + } + + public K next() { + this.checkForConcurrentModification(); + LinkedListMultimap.checkElement(this.next); + this.current = this.next; + this.seenKeys.add(this.current.key); + + do { + this.next = this.next.next; + } while(this.next != null && !this.seenKeys.add(this.next.key)); + + return this.current.key; + } + + public void remove() { + this.checkForConcurrentModification(); + CollectPreconditions.checkRemove(this.current != null); + LinkedListMultimap.this.removeAllNodes(this.current.key); + this.current = null; + this.expectedModCount = LinkedListMultimap.this.modCount; + } + + // $FF: synthetic method + DistinctKeyIterator(Object x1) { + this(); + } + } + + private class NodeIterator implements ListIterator> { + int nextIndex; + LinkedListMultimap.Node next; + LinkedListMultimap.Node current; + LinkedListMultimap.Node previous; + int expectedModCount; + + NodeIterator(int index) { + this.expectedModCount = LinkedListMultimap.this.modCount; + int size = LinkedListMultimap.this.size(); + Preconditions.checkPositionIndex(index, size); + if (index >= size / 2) { + this.previous = LinkedListMultimap.this.tail; + this.nextIndex = size; + + while(index++ < size) { + this.previous(); + } + } else { + this.next = LinkedListMultimap.this.head; + + while(index-- > 0) { + this.next(); + } + } + + this.current = null; + } + + private void checkForConcurrentModification() { + if (LinkedListMultimap.this.modCount != this.expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + public boolean hasNext() { + this.checkForConcurrentModification(); + return this.next != null; + } + + public LinkedListMultimap.Node next() { + this.checkForConcurrentModification(); + LinkedListMultimap.checkElement(this.next); + this.previous = this.current = this.next; + this.next = this.next.next; + ++this.nextIndex; + return this.current; + } + + public void remove() { + this.checkForConcurrentModification(); + CollectPreconditions.checkRemove(this.current != null); + if (this.current != this.next) { + this.previous = this.current.previous; + --this.nextIndex; + } else { + this.next = this.current.next; + } + + LinkedListMultimap.this.removeNode(this.current); + this.current = null; + this.expectedModCount = LinkedListMultimap.this.modCount; + } + + public boolean hasPrevious() { + this.checkForConcurrentModification(); + return this.previous != null; + } + + public LinkedListMultimap.Node previous() { + this.checkForConcurrentModification(); + LinkedListMultimap.checkElement(this.previous); + this.next = this.current = this.previous; + this.previous = this.previous.previous; + --this.nextIndex; + return this.current; + } + + public int nextIndex() { + return this.nextIndex; + } + + public int previousIndex() { + return this.nextIndex - 1; + } + + public void set(Entry e) { + throw new UnsupportedOperationException(); + } + + public void add(Entry e) { + throw new UnsupportedOperationException(); + } + + void setValue(V value) { + Preconditions.checkState(this.current != null); + this.current.value = value; + } + } + + private static class KeyList { + LinkedListMultimap.Node head; + LinkedListMultimap.Node tail; + int count; + + KeyList(LinkedListMultimap.Node firstNode) { + this.head = firstNode; + this.tail = firstNode; + firstNode.previousSibling = null; + firstNode.nextSibling = null; + this.count = 1; + } + } + + private static final class Node extends AbstractMapEntry { + final K key; + V value; + LinkedListMultimap.Node next; + LinkedListMultimap.Node previous; + LinkedListMultimap.Node nextSibling; + LinkedListMultimap.Node previousSibling; + + Node(@Nullable K key, @Nullable V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return this.key; + } + + public V getValue() { + return this.value; + } + + public V setValue(@Nullable V newValue) { + V result = this.value; + this.value = newValue; + return result; + } + } +} diff --git a/src/main/com/google/common/collect/ListMultimap.java b/src/main/com/google/common/collect/ListMultimap.java new file mode 100644 index 0000000..c85484f --- /dev/null +++ b/src/main/com/google/common/collect/ListMultimap.java @@ -0,0 +1,20 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible +public interface ListMultimap extends Multimap { + List get(@Nullable K var1); + + List removeAll(@Nullable Object var1); + + List replaceValues(K var1, Iterable var2); + + Map> asMap(); + + boolean equals(@Nullable Object var1); +} diff --git a/src/main/com/google/common/collect/Lists.java b/src/main/com/google/common/collect/Lists.java new file mode 100644 index 0000000..494bb13 --- /dev/null +++ b/src/main/com/google/common/collect/Lists.java @@ -0,0 +1,622 @@ +package com.google.common.collect; + +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; +import com.google.common.base.Preconditions; +import com.google.common.math.IntMath; +import com.google.common.primitives.Ints; +import java.io.Serializable; +import java.math.RoundingMode; +import java.util.AbstractList; +import java.util.AbstractSequentialList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.RandomAccess; +import java.util.concurrent.CopyOnWriteArrayList; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Lists { + private Lists() { + } + + @GwtCompatible( + serializable = true + ) + public static ArrayList newArrayList() { + return new ArrayList(); + } + + @GwtCompatible( + serializable = true + ) + public static ArrayList newArrayList(E... elements) { + Preconditions.checkNotNull(elements); + int capacity = computeArrayListCapacity(elements.length); + ArrayList list = new ArrayList(capacity); + Collections.addAll(list, elements); + return list; + } + + @VisibleForTesting + static int computeArrayListCapacity(int arraySize) { + CollectPreconditions.checkNonnegative(arraySize, "arraySize"); + return Ints.saturatedCast(5L + (long)arraySize + (long)(arraySize / 10)); + } + + @GwtCompatible( + serializable = true + ) + public static ArrayList newArrayList(Iterable elements) { + Preconditions.checkNotNull(elements); + return elements instanceof Collection ? new ArrayList(Collections2.cast(elements)) : newArrayList(elements.iterator()); + } + + @GwtCompatible( + serializable = true + ) + public static ArrayList newArrayList(Iterator elements) { + ArrayList list = newArrayList(); + Iterators.addAll(list, elements); + return list; + } + + @GwtCompatible( + serializable = true + ) + public static ArrayList newArrayListWithCapacity(int initialArraySize) { + CollectPreconditions.checkNonnegative(initialArraySize, "initialArraySize"); + return new ArrayList(initialArraySize); + } + + @GwtCompatible( + serializable = true + ) + public static ArrayList newArrayListWithExpectedSize(int estimatedSize) { + return new ArrayList(computeArrayListCapacity(estimatedSize)); + } + + @GwtCompatible( + serializable = true + ) + public static LinkedList newLinkedList() { + return new LinkedList(); + } + + @GwtCompatible( + serializable = true + ) + public static LinkedList newLinkedList(Iterable elements) { + LinkedList list = newLinkedList(); + Iterables.addAll(list, elements); + return list; + } + + @GwtIncompatible("CopyOnWriteArrayList") + public static CopyOnWriteArrayList newCopyOnWriteArrayList() { + return new CopyOnWriteArrayList(); + } + + @GwtIncompatible("CopyOnWriteArrayList") + public static CopyOnWriteArrayList newCopyOnWriteArrayList(Iterable elements) { + Collection elementsCollection = elements instanceof Collection ? Collections2.cast(elements) : newArrayList(elements); + return new CopyOnWriteArrayList((Collection)elementsCollection); + } + + public static List asList(@Nullable E first, E[] rest) { + return new Lists.OnePlusArrayList(first, rest); + } + + public static List asList(@Nullable E first, @Nullable E second, E[] rest) { + return new Lists.TwoPlusArrayList(first, second, rest); + } + + static List> cartesianProduct(List> lists) { + return CartesianList.create(lists); + } + + static List> cartesianProduct(List... lists) { + return cartesianProduct(Arrays.asList(lists)); + } + + public static List transform(List fromList, Function function) { + return (List)(fromList instanceof RandomAccess ? new Lists.TransformingRandomAccessList(fromList, function) : new Lists.TransformingSequentialList(fromList, function)); + } + + public static List> partition(List list, int size) { + Preconditions.checkNotNull(list); + Preconditions.checkArgument(size > 0); + return (List)(list instanceof RandomAccess ? new Lists.RandomAccessPartition(list, size) : new Lists.Partition(list, size)); + } + + @Beta + public static ImmutableList charactersOf(String string) { + return new Lists.StringAsImmutableList((String)Preconditions.checkNotNull(string)); + } + + @Beta + public static List charactersOf(CharSequence sequence) { + return new Lists.CharSequenceAsList((CharSequence)Preconditions.checkNotNull(sequence)); + } + + public static List reverse(List list) { + if (list instanceof ImmutableList) { + return ((ImmutableList)list).reverse(); + } else if (list instanceof Lists.ReverseList) { + return ((Lists.ReverseList)list).getForwardList(); + } else { + return (List)(list instanceof RandomAccess ? new Lists.RandomAccessReverseList(list) : new Lists.ReverseList(list)); + } + } + + static int hashCodeImpl(List list) { + int hashCode = 1; + + for(Iterator i$ = list.iterator(); i$.hasNext(); hashCode = ~(~hashCode)) { + Object o = i$.next(); + hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode()); + } + + return hashCode; + } + + static boolean equalsImpl(List list, @Nullable Object object) { + if (object == Preconditions.checkNotNull(list)) { + return true; + } else if (!(object instanceof List)) { + return false; + } else { + List o = (List)object; + return list.size() == o.size() && Iterators.elementsEqual(list.iterator(), o.iterator()); + } + } + + static boolean addAllImpl(List list, int index, Iterable elements) { + boolean changed = false; + ListIterator listIterator = list.listIterator(index); + + for(Iterator i$ = elements.iterator(); i$.hasNext(); changed = true) { + E e = i$.next(); + listIterator.add(e); + } + + return changed; + } + + static int indexOfImpl(List list, @Nullable Object element) { + ListIterator listIterator = list.listIterator(); + + do { + if (!listIterator.hasNext()) { + return -1; + } + } while(!Objects.equal(element, listIterator.next())); + + return listIterator.previousIndex(); + } + + static int lastIndexOfImpl(List list, @Nullable Object element) { + ListIterator listIterator = list.listIterator(list.size()); + + do { + if (!listIterator.hasPrevious()) { + return -1; + } + } while(!Objects.equal(element, listIterator.previous())); + + return listIterator.nextIndex(); + } + + static ListIterator listIteratorImpl(List list, int index) { + return (new Lists.AbstractListWrapper(list)).listIterator(index); + } + + static List subListImpl(List list, int fromIndex, int toIndex) { + Object wrapper; + if (list instanceof RandomAccess) { + wrapper = new Lists.RandomAccessListWrapper(list) { + private static final long serialVersionUID = 0L; + + public ListIterator listIterator(int index) { + return this.backingList.listIterator(index); + } + }; + } else { + wrapper = new Lists.AbstractListWrapper(list) { + private static final long serialVersionUID = 0L; + + public ListIterator listIterator(int index) { + return this.backingList.listIterator(index); + } + }; + } + + return ((List)wrapper).subList(fromIndex, toIndex); + } + + static List cast(Iterable iterable) { + return (List)iterable; + } + + private static class RandomAccessListWrapper extends Lists.AbstractListWrapper implements RandomAccess { + RandomAccessListWrapper(List backingList) { + super(backingList); + } + } + + private static class AbstractListWrapper extends AbstractList { + final List backingList; + + AbstractListWrapper(List backingList) { + this.backingList = (List)Preconditions.checkNotNull(backingList); + } + + public void add(int index, E element) { + this.backingList.add(index, element); + } + + public boolean addAll(int index, Collection c) { + return this.backingList.addAll(index, c); + } + + public E get(int index) { + return this.backingList.get(index); + } + + public E remove(int index) { + return this.backingList.remove(index); + } + + public E set(int index, E element) { + return this.backingList.set(index, element); + } + + public boolean contains(Object o) { + return this.backingList.contains(o); + } + + public int size() { + return this.backingList.size(); + } + } + + private static class RandomAccessReverseList extends Lists.ReverseList implements RandomAccess { + RandomAccessReverseList(List forwardList) { + super(forwardList); + } + } + + private static class ReverseList extends AbstractList { + private final List forwardList; + + ReverseList(List forwardList) { + this.forwardList = (List)Preconditions.checkNotNull(forwardList); + } + + List getForwardList() { + return this.forwardList; + } + + private int reverseIndex(int index) { + int size = this.size(); + Preconditions.checkElementIndex(index, size); + return size - 1 - index; + } + + private int reversePosition(int index) { + int size = this.size(); + Preconditions.checkPositionIndex(index, size); + return size - index; + } + + public void add(int index, @Nullable T element) { + this.forwardList.add(this.reversePosition(index), element); + } + + public void clear() { + this.forwardList.clear(); + } + + public T remove(int index) { + return this.forwardList.remove(this.reverseIndex(index)); + } + + protected void removeRange(int fromIndex, int toIndex) { + this.subList(fromIndex, toIndex).clear(); + } + + public T set(int index, @Nullable T element) { + return this.forwardList.set(this.reverseIndex(index), element); + } + + public T get(int index) { + return this.forwardList.get(this.reverseIndex(index)); + } + + public int size() { + return this.forwardList.size(); + } + + public List subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, this.size()); + return Lists.reverse(this.forwardList.subList(this.reversePosition(toIndex), this.reversePosition(fromIndex))); + } + + public Iterator iterator() { + return this.listIterator(); + } + + public ListIterator listIterator(int index) { + int start = this.reversePosition(index); + final ListIterator forwardIterator = this.forwardList.listIterator(start); + return new ListIterator() { + boolean canRemoveOrSet; + + public void add(T e) { + forwardIterator.add(e); + forwardIterator.previous(); + this.canRemoveOrSet = false; + } + + public boolean hasNext() { + return forwardIterator.hasPrevious(); + } + + public boolean hasPrevious() { + return forwardIterator.hasNext(); + } + + public T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + this.canRemoveOrSet = true; + return forwardIterator.previous(); + } + } + + public int nextIndex() { + return ReverseList.this.reversePosition(forwardIterator.nextIndex()); + } + + public T previous() { + if (!this.hasPrevious()) { + throw new NoSuchElementException(); + } else { + this.canRemoveOrSet = true; + return forwardIterator.next(); + } + } + + public int previousIndex() { + return this.nextIndex() - 1; + } + + public void remove() { + CollectPreconditions.checkRemove(this.canRemoveOrSet); + forwardIterator.remove(); + this.canRemoveOrSet = false; + } + + public void set(T e) { + Preconditions.checkState(this.canRemoveOrSet); + forwardIterator.set(e); + } + }; + } + } + + private static final class CharSequenceAsList extends AbstractList { + private final CharSequence sequence; + + CharSequenceAsList(CharSequence sequence) { + this.sequence = sequence; + } + + public Character get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.sequence.charAt(index); + } + + public int size() { + return this.sequence.length(); + } + } + + private static final class StringAsImmutableList extends ImmutableList { + private final String string; + + StringAsImmutableList(String string) { + this.string = string; + } + + public int indexOf(@Nullable Object object) { + return object instanceof Character ? this.string.indexOf((Character)object) : -1; + } + + public int lastIndexOf(@Nullable Object object) { + return object instanceof Character ? this.string.lastIndexOf((Character)object) : -1; + } + + public ImmutableList subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, this.size()); + return Lists.charactersOf(this.string.substring(fromIndex, toIndex)); + } + + boolean isPartialView() { + return false; + } + + public Character get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.string.charAt(index); + } + + public int size() { + return this.string.length(); + } + } + + private static class RandomAccessPartition extends Lists.Partition implements RandomAccess { + RandomAccessPartition(List list, int size) { + super(list, size); + } + } + + private static class Partition extends AbstractList> { + final List list; + final int size; + + Partition(List list, int size) { + this.list = list; + this.size = size; + } + + public List get(int index) { + Preconditions.checkElementIndex(index, this.size()); + int start = index * this.size; + int end = Math.min(start + this.size, this.list.size()); + return this.list.subList(start, end); + } + + public int size() { + return IntMath.divide(this.list.size(), this.size, RoundingMode.CEILING); + } + + public boolean isEmpty() { + return this.list.isEmpty(); + } + } + + private static class TransformingRandomAccessList extends AbstractList implements RandomAccess, Serializable { + final List fromList; + final Function function; + private static final long serialVersionUID = 0L; + + TransformingRandomAccessList(List fromList, Function function) { + this.fromList = (List)Preconditions.checkNotNull(fromList); + this.function = (Function)Preconditions.checkNotNull(function); + } + + public void clear() { + this.fromList.clear(); + } + + public T get(int index) { + return this.function.apply(this.fromList.get(index)); + } + + public Iterator iterator() { + return this.listIterator(); + } + + public ListIterator listIterator(int index) { + return new TransformedListIterator(this.fromList.listIterator(index)) { + T transform(F from) { + return TransformingRandomAccessList.this.function.apply(from); + } + }; + } + + public boolean isEmpty() { + return this.fromList.isEmpty(); + } + + public T remove(int index) { + return this.function.apply(this.fromList.remove(index)); + } + + public int size() { + return this.fromList.size(); + } + } + + private static class TransformingSequentialList extends AbstractSequentialList implements Serializable { + final List fromList; + final Function function; + private static final long serialVersionUID = 0L; + + TransformingSequentialList(List fromList, Function function) { + this.fromList = (List)Preconditions.checkNotNull(fromList); + this.function = (Function)Preconditions.checkNotNull(function); + } + + public void clear() { + this.fromList.clear(); + } + + public int size() { + return this.fromList.size(); + } + + public ListIterator listIterator(int index) { + return new TransformedListIterator(this.fromList.listIterator(index)) { + T transform(F from) { + return TransformingSequentialList.this.function.apply(from); + } + }; + } + } + + private static class TwoPlusArrayList extends AbstractList implements Serializable, RandomAccess { + final E first; + final E second; + final E[] rest; + private static final long serialVersionUID = 0L; + + TwoPlusArrayList(@Nullable E first, @Nullable E second, E[] rest) { + this.first = first; + this.second = second; + this.rest = (Object[])Preconditions.checkNotNull(rest); + } + + public int size() { + return this.rest.length + 2; + } + + public E get(int index) { + switch(index) { + case 0: + return this.first; + case 1: + return this.second; + default: + Preconditions.checkElementIndex(index, this.size()); + return this.rest[index - 2]; + } + } + } + + private static class OnePlusArrayList extends AbstractList implements Serializable, RandomAccess { + final E first; + final E[] rest; + private static final long serialVersionUID = 0L; + + OnePlusArrayList(@Nullable E first, E[] rest) { + this.first = first; + this.rest = (Object[])Preconditions.checkNotNull(rest); + } + + public int size() { + return this.rest.length + 1; + } + + public E get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return index == 0 ? this.first : this.rest[index - 1]; + } + } +} diff --git a/src/main/com/google/common/collect/MapConstraint.java b/src/main/com/google/common/collect/MapConstraint.java new file mode 100644 index 0000000..d2e0d55 --- /dev/null +++ b/src/main/com/google/common/collect/MapConstraint.java @@ -0,0 +1,13 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +@Beta +public interface MapConstraint { + void checkKeyValue(@Nullable K var1, @Nullable V var2); + + String toString(); +} diff --git a/src/main/com/google/common/collect/MapConstraints.java b/src/main/com/google/common/collect/MapConstraints.java new file mode 100644 index 0000000..2b4277e --- /dev/null +++ b/src/main/com/google/common/collect/MapConstraints.java @@ -0,0 +1,568 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public final class MapConstraints { + private MapConstraints() { + } + + public static MapConstraint notNull() { + return MapConstraints.NotNullMapConstraint.INSTANCE; + } + + public static Map constrainedMap(Map map, MapConstraint constraint) { + return new MapConstraints.ConstrainedMap(map, constraint); + } + + public static Multimap constrainedMultimap(Multimap multimap, MapConstraint constraint) { + return new MapConstraints.ConstrainedMultimap(multimap, constraint); + } + + public static ListMultimap constrainedListMultimap(ListMultimap multimap, MapConstraint constraint) { + return new MapConstraints.ConstrainedListMultimap(multimap, constraint); + } + + public static SetMultimap constrainedSetMultimap(SetMultimap multimap, MapConstraint constraint) { + return new MapConstraints.ConstrainedSetMultimap(multimap, constraint); + } + + public static SortedSetMultimap constrainedSortedSetMultimap(SortedSetMultimap multimap, MapConstraint constraint) { + return new MapConstraints.ConstrainedSortedSetMultimap(multimap, constraint); + } + + private static Entry constrainedEntry(final Entry entry, final MapConstraint constraint) { + Preconditions.checkNotNull(entry); + Preconditions.checkNotNull(constraint); + return new ForwardingMapEntry() { + protected Entry delegate() { + return entry; + } + + public V setValue(V value) { + constraint.checkKeyValue(this.getKey(), value); + return entry.setValue(value); + } + }; + } + + private static Entry> constrainedAsMapEntry(final Entry> entry, final MapConstraint constraint) { + Preconditions.checkNotNull(entry); + Preconditions.checkNotNull(constraint); + return new ForwardingMapEntry>() { + protected Entry> delegate() { + return entry; + } + + public Collection getValue() { + return Constraints.constrainedTypePreservingCollection((Collection)entry.getValue(), new Constraint() { + public V checkElement(V value) { + constraint.checkKeyValue(getKey(), value); + return value; + } + }); + } + }; + } + + private static Set>> constrainedAsMapEntries(Set>> entries, MapConstraint constraint) { + return new MapConstraints.ConstrainedAsMapEntries(entries, constraint); + } + + private static Collection> constrainedEntries(Collection> entries, MapConstraint constraint) { + return (Collection)(entries instanceof Set ? constrainedEntrySet((Set)entries, constraint) : new MapConstraints.ConstrainedEntries(entries, constraint)); + } + + private static Set> constrainedEntrySet(Set> entries, MapConstraint constraint) { + return new MapConstraints.ConstrainedEntrySet(entries, constraint); + } + + public static BiMap constrainedBiMap(BiMap map, MapConstraint constraint) { + return new MapConstraints.ConstrainedBiMap(map, (BiMap)null, constraint); + } + + private static Collection checkValues(K key, Iterable values, MapConstraint constraint) { + Collection copy = Lists.newArrayList(values); + Iterator i$ = copy.iterator(); + + while(i$.hasNext()) { + V value = i$.next(); + constraint.checkKeyValue(key, value); + } + + return copy; + } + + private static Map checkMap(Map map, MapConstraint constraint) { + Map copy = new LinkedHashMap(map); + Iterator i$ = copy.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + constraint.checkKeyValue(entry.getKey(), entry.getValue()); + } + + return copy; + } + + private static class ConstrainedSortedSetMultimap extends MapConstraints.ConstrainedSetMultimap implements SortedSetMultimap { + ConstrainedSortedSetMultimap(SortedSetMultimap delegate, MapConstraint constraint) { + super(delegate, constraint); + } + + public SortedSet get(K key) { + return (SortedSet)super.get(key); + } + + public SortedSet removeAll(Object key) { + return (SortedSet)super.removeAll(key); + } + + public SortedSet replaceValues(K key, Iterable values) { + return (SortedSet)super.replaceValues(key, values); + } + + public Comparator valueComparator() { + return ((SortedSetMultimap)this.delegate()).valueComparator(); + } + } + + private static class ConstrainedSetMultimap extends MapConstraints.ConstrainedMultimap implements SetMultimap { + ConstrainedSetMultimap(SetMultimap delegate, MapConstraint constraint) { + super(delegate, constraint); + } + + public Set get(K key) { + return (Set)super.get(key); + } + + public Set> entries() { + return (Set)super.entries(); + } + + public Set removeAll(Object key) { + return (Set)super.removeAll(key); + } + + public Set replaceValues(K key, Iterable values) { + return (Set)super.replaceValues(key, values); + } + } + + private static class ConstrainedListMultimap extends MapConstraints.ConstrainedMultimap implements ListMultimap { + ConstrainedListMultimap(ListMultimap delegate, MapConstraint constraint) { + super(delegate, constraint); + } + + public List get(K key) { + return (List)super.get(key); + } + + public List removeAll(Object key) { + return (List)super.removeAll(key); + } + + public List replaceValues(K key, Iterable values) { + return (List)super.replaceValues(key, values); + } + } + + static class ConstrainedAsMapEntries extends ForwardingSet>> { + private final MapConstraint constraint; + private final Set>> entries; + + ConstrainedAsMapEntries(Set>> entries, MapConstraint constraint) { + this.entries = entries; + this.constraint = constraint; + } + + protected Set>> delegate() { + return this.entries; + } + + public Iterator>> iterator() { + final Iterator>> iterator = this.entries.iterator(); + return new ForwardingIterator>>() { + public Entry> next() { + return MapConstraints.constrainedAsMapEntry((Entry)iterator.next(), ConstrainedAsMapEntries.this.constraint); + } + + protected Iterator>> delegate() { + return iterator; + } + }; + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + + public boolean contains(Object o) { + return Maps.containsEntryImpl(this.delegate(), o); + } + + public boolean containsAll(Collection c) { + return this.standardContainsAll(c); + } + + public boolean equals(@Nullable Object object) { + return this.standardEquals(object); + } + + public int hashCode() { + return this.standardHashCode(); + } + + public boolean remove(Object o) { + return Maps.removeEntryImpl(this.delegate(), o); + } + + public boolean removeAll(Collection c) { + return this.standardRemoveAll(c); + } + + public boolean retainAll(Collection c) { + return this.standardRetainAll(c); + } + } + + static class ConstrainedEntrySet extends MapConstraints.ConstrainedEntries implements Set> { + ConstrainedEntrySet(Set> entries, MapConstraint constraint) { + super(entries, constraint); + } + + public boolean equals(@Nullable Object object) { + return Sets.equalsImpl(this, object); + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + } + + private static class ConstrainedEntries extends ForwardingCollection> { + final MapConstraint constraint; + final Collection> entries; + + ConstrainedEntries(Collection> entries, MapConstraint constraint) { + this.entries = entries; + this.constraint = constraint; + } + + protected Collection> delegate() { + return this.entries; + } + + public Iterator> iterator() { + final Iterator> iterator = this.entries.iterator(); + return new ForwardingIterator>() { + public Entry next() { + return MapConstraints.constrainedEntry((Entry)iterator.next(), ConstrainedEntries.this.constraint); + } + + protected Iterator> delegate() { + return iterator; + } + }; + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + + public boolean contains(Object o) { + return Maps.containsEntryImpl(this.delegate(), o); + } + + public boolean containsAll(Collection c) { + return this.standardContainsAll(c); + } + + public boolean remove(Object o) { + return Maps.removeEntryImpl(this.delegate(), o); + } + + public boolean removeAll(Collection c) { + return this.standardRemoveAll(c); + } + + public boolean retainAll(Collection c) { + return this.standardRetainAll(c); + } + } + + private static class ConstrainedAsMapValues extends ForwardingCollection> { + final Collection> delegate; + final Set>> entrySet; + + ConstrainedAsMapValues(Collection> delegate, Set>> entrySet) { + this.delegate = delegate; + this.entrySet = entrySet; + } + + protected Collection> delegate() { + return this.delegate; + } + + public Iterator> iterator() { + final Iterator>> iterator = this.entrySet.iterator(); + return new Iterator>() { + public boolean hasNext() { + return iterator.hasNext(); + } + + public Collection next() { + return (Collection)((Entry)iterator.next()).getValue(); + } + + public void remove() { + iterator.remove(); + } + }; + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + + public boolean contains(Object o) { + return this.standardContains(o); + } + + public boolean containsAll(Collection c) { + return this.standardContainsAll(c); + } + + public boolean remove(Object o) { + return this.standardRemove(o); + } + + public boolean removeAll(Collection c) { + return this.standardRemoveAll(c); + } + + public boolean retainAll(Collection c) { + return this.standardRetainAll(c); + } + } + + private static class ConstrainedMultimap extends ForwardingMultimap implements Serializable { + final MapConstraint constraint; + final Multimap delegate; + transient Collection> entries; + transient Map> asMap; + + public ConstrainedMultimap(Multimap delegate, MapConstraint constraint) { + this.delegate = (Multimap)Preconditions.checkNotNull(delegate); + this.constraint = (MapConstraint)Preconditions.checkNotNull(constraint); + } + + protected Multimap delegate() { + return this.delegate; + } + + public Map> asMap() { + Map> result = this.asMap; + if (result == null) { + final Map> asMapDelegate = this.delegate.asMap(); + this.asMap = (Map)(result = new ForwardingMap>() { + Set>> entrySet; + Collection> values; + + protected Map> delegate() { + return asMapDelegate; + } + + public Set>> entrySet() { + Set>> result = this.entrySet; + if (result == null) { + this.entrySet = result = MapConstraints.constrainedAsMapEntries(asMapDelegate.entrySet(), ConstrainedMultimap.this.constraint); + } + + return result; + } + + public Collection get(Object key) { + try { + Collection collection = ConstrainedMultimap.this.get(key); + return collection.isEmpty() ? null : collection; + } catch (ClassCastException var3) { + return null; + } + } + + public Collection> values() { + Collection> result = this.values; + if (result == null) { + this.values = (Collection)(result = new MapConstraints.ConstrainedAsMapValues(this.delegate().values(), this.entrySet())); + } + + return (Collection)result; + } + + public boolean containsValue(Object o) { + return this.values().contains(o); + } + }); + } + + return (Map)result; + } + + public Collection> entries() { + Collection> result = this.entries; + if (result == null) { + this.entries = result = MapConstraints.constrainedEntries(this.delegate.entries(), this.constraint); + } + + return result; + } + + public Collection get(final K key) { + return Constraints.constrainedTypePreservingCollection(this.delegate.get(key), new Constraint() { + public V checkElement(V value) { + ConstrainedMultimap.this.constraint.checkKeyValue(key, value); + return value; + } + }); + } + + public boolean put(K key, V value) { + this.constraint.checkKeyValue(key, value); + return this.delegate.put(key, value); + } + + public boolean putAll(K key, Iterable values) { + return this.delegate.putAll(key, MapConstraints.checkValues(key, values, this.constraint)); + } + + public boolean putAll(Multimap multimap) { + boolean changed = false; + + Entry entry; + for(Iterator i$ = multimap.entries().iterator(); i$.hasNext(); changed |= this.put(entry.getKey(), entry.getValue())) { + entry = (Entry)i$.next(); + } + + return changed; + } + + public Collection replaceValues(K key, Iterable values) { + return this.delegate.replaceValues(key, MapConstraints.checkValues(key, values, this.constraint)); + } + } + + private static class InverseConstraint implements MapConstraint { + final MapConstraint constraint; + + public InverseConstraint(MapConstraint constraint) { + this.constraint = (MapConstraint)Preconditions.checkNotNull(constraint); + } + + public void checkKeyValue(K key, V value) { + this.constraint.checkKeyValue(value, key); + } + } + + private static class ConstrainedBiMap extends MapConstraints.ConstrainedMap implements BiMap { + volatile BiMap inverse; + + ConstrainedBiMap(BiMap delegate, @Nullable BiMap inverse, MapConstraint constraint) { + super(delegate, constraint); + this.inverse = inverse; + } + + protected BiMap delegate() { + return (BiMap)super.delegate(); + } + + public V forcePut(K key, V value) { + this.constraint.checkKeyValue(key, value); + return this.delegate().forcePut(key, value); + } + + public BiMap inverse() { + if (this.inverse == null) { + this.inverse = new MapConstraints.ConstrainedBiMap(this.delegate().inverse(), this, new MapConstraints.InverseConstraint(this.constraint)); + } + + return this.inverse; + } + + public Set values() { + return this.delegate().values(); + } + } + + static class ConstrainedMap extends ForwardingMap { + private final Map delegate; + final MapConstraint constraint; + private transient Set> entrySet; + + ConstrainedMap(Map delegate, MapConstraint constraint) { + this.delegate = (Map)Preconditions.checkNotNull(delegate); + this.constraint = (MapConstraint)Preconditions.checkNotNull(constraint); + } + + protected Map delegate() { + return this.delegate; + } + + public Set> entrySet() { + Set> result = this.entrySet; + if (result == null) { + this.entrySet = result = MapConstraints.constrainedEntrySet(this.delegate.entrySet(), this.constraint); + } + + return result; + } + + public V put(K key, V value) { + this.constraint.checkKeyValue(key, value); + return this.delegate.put(key, value); + } + + public void putAll(Map map) { + this.delegate.putAll(MapConstraints.checkMap(map, this.constraint)); + } + } + + private static enum NotNullMapConstraint implements MapConstraint { + INSTANCE; + + public void checkKeyValue(Object key, Object value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + } + + public String toString() { + return "Not null"; + } + } +} diff --git a/src/main/com/google/common/collect/MapDifference.java b/src/main/com/google/common/collect/MapDifference.java new file mode 100644 index 0000000..1fa0224 --- /dev/null +++ b/src/main/com/google/common/collect/MapDifference.java @@ -0,0 +1,32 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import javax.annotation.Nullable; + +@GwtCompatible +public interface MapDifference { + boolean areEqual(); + + Map entriesOnlyOnLeft(); + + Map entriesOnlyOnRight(); + + Map entriesInCommon(); + + Map> entriesDiffering(); + + boolean equals(@Nullable Object var1); + + int hashCode(); + + public interface ValueDifference { + V leftValue(); + + V rightValue(); + + boolean equals(@Nullable Object var1); + + int hashCode(); + } +} diff --git a/src/main/com/google/common/collect/MapMaker.java b/src/main/com/google/common/collect/MapMaker.java new file mode 100644 index 0000000..0569e0e --- /dev/null +++ b/src/main/com/google/common/collect/MapMaker.java @@ -0,0 +1,429 @@ +package com.google.common.collect; + +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.Function; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.google.common.base.Ticker; +import java.io.Serializable; +import java.util.AbstractMap; +import java.util.Collections; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class MapMaker extends GenericMapMaker { + private static final int DEFAULT_INITIAL_CAPACITY = 16; + private static final int DEFAULT_CONCURRENCY_LEVEL = 4; + private static final int DEFAULT_EXPIRATION_NANOS = 0; + static final int UNSET_INT = -1; + boolean useCustomMap; + int initialCapacity = -1; + int concurrencyLevel = -1; + int maximumSize = -1; + MapMakerInternalMap.Strength keyStrength; + MapMakerInternalMap.Strength valueStrength; + long expireAfterWriteNanos = -1L; + long expireAfterAccessNanos = -1L; + MapMaker.RemovalCause nullRemovalCause; + Equivalence keyEquivalence; + Ticker ticker; + + @GwtIncompatible("To be supported") + MapMaker keyEquivalence(Equivalence equivalence) { + Preconditions.checkState(this.keyEquivalence == null, "key equivalence was already set to %s", this.keyEquivalence); + this.keyEquivalence = (Equivalence)Preconditions.checkNotNull(equivalence); + this.useCustomMap = true; + return this; + } + + Equivalence getKeyEquivalence() { + return (Equivalence)MoreObjects.firstNonNull(this.keyEquivalence, this.getKeyStrength().defaultEquivalence()); + } + + public MapMaker initialCapacity(int initialCapacity) { + Preconditions.checkState(this.initialCapacity == -1, "initial capacity was already set to %s", this.initialCapacity); + Preconditions.checkArgument(initialCapacity >= 0); + this.initialCapacity = initialCapacity; + return this; + } + + int getInitialCapacity() { + return this.initialCapacity == -1 ? 16 : this.initialCapacity; + } + + /** @deprecated */ + @Deprecated + MapMaker maximumSize(int size) { + Preconditions.checkState(this.maximumSize == -1, "maximum size was already set to %s", this.maximumSize); + Preconditions.checkArgument(size >= 0, "maximum size must not be negative"); + this.maximumSize = size; + this.useCustomMap = true; + if (this.maximumSize == 0) { + this.nullRemovalCause = MapMaker.RemovalCause.SIZE; + } + + return this; + } + + public MapMaker concurrencyLevel(int concurrencyLevel) { + Preconditions.checkState(this.concurrencyLevel == -1, "concurrency level was already set to %s", this.concurrencyLevel); + Preconditions.checkArgument(concurrencyLevel > 0); + this.concurrencyLevel = concurrencyLevel; + return this; + } + + int getConcurrencyLevel() { + return this.concurrencyLevel == -1 ? 4 : this.concurrencyLevel; + } + + @GwtIncompatible("java.lang.ref.WeakReference") + public MapMaker weakKeys() { + return this.setKeyStrength(MapMakerInternalMap.Strength.WEAK); + } + + MapMaker setKeyStrength(MapMakerInternalMap.Strength strength) { + Preconditions.checkState(this.keyStrength == null, "Key strength was already set to %s", this.keyStrength); + this.keyStrength = (MapMakerInternalMap.Strength)Preconditions.checkNotNull(strength); + Preconditions.checkArgument(this.keyStrength != MapMakerInternalMap.Strength.SOFT, "Soft keys are not supported"); + if (strength != MapMakerInternalMap.Strength.STRONG) { + this.useCustomMap = true; + } + + return this; + } + + MapMakerInternalMap.Strength getKeyStrength() { + return (MapMakerInternalMap.Strength)MoreObjects.firstNonNull(this.keyStrength, MapMakerInternalMap.Strength.STRONG); + } + + @GwtIncompatible("java.lang.ref.WeakReference") + public MapMaker weakValues() { + return this.setValueStrength(MapMakerInternalMap.Strength.WEAK); + } + + /** @deprecated */ + @Deprecated + @GwtIncompatible("java.lang.ref.SoftReference") + public MapMaker softValues() { + return this.setValueStrength(MapMakerInternalMap.Strength.SOFT); + } + + MapMaker setValueStrength(MapMakerInternalMap.Strength strength) { + Preconditions.checkState(this.valueStrength == null, "Value strength was already set to %s", this.valueStrength); + this.valueStrength = (MapMakerInternalMap.Strength)Preconditions.checkNotNull(strength); + if (strength != MapMakerInternalMap.Strength.STRONG) { + this.useCustomMap = true; + } + + return this; + } + + MapMakerInternalMap.Strength getValueStrength() { + return (MapMakerInternalMap.Strength)MoreObjects.firstNonNull(this.valueStrength, MapMakerInternalMap.Strength.STRONG); + } + + /** @deprecated */ + @Deprecated + MapMaker expireAfterWrite(long duration, TimeUnit unit) { + this.checkExpiration(duration, unit); + this.expireAfterWriteNanos = unit.toNanos(duration); + if (duration == 0L && this.nullRemovalCause == null) { + this.nullRemovalCause = MapMaker.RemovalCause.EXPIRED; + } + + this.useCustomMap = true; + return this; + } + + private void checkExpiration(long duration, TimeUnit unit) { + Preconditions.checkState(this.expireAfterWriteNanos == -1L, "expireAfterWrite was already set to %s ns", this.expireAfterWriteNanos); + Preconditions.checkState(this.expireAfterAccessNanos == -1L, "expireAfterAccess was already set to %s ns", this.expireAfterAccessNanos); + Preconditions.checkArgument(duration >= 0L, "duration cannot be negative: %s %s", duration, unit); + } + + long getExpireAfterWriteNanos() { + return this.expireAfterWriteNanos == -1L ? 0L : this.expireAfterWriteNanos; + } + + /** @deprecated */ + @Deprecated + @GwtIncompatible("To be supported") + MapMaker expireAfterAccess(long duration, TimeUnit unit) { + this.checkExpiration(duration, unit); + this.expireAfterAccessNanos = unit.toNanos(duration); + if (duration == 0L && this.nullRemovalCause == null) { + this.nullRemovalCause = MapMaker.RemovalCause.EXPIRED; + } + + this.useCustomMap = true; + return this; + } + + long getExpireAfterAccessNanos() { + return this.expireAfterAccessNanos == -1L ? 0L : this.expireAfterAccessNanos; + } + + Ticker getTicker() { + return (Ticker)MoreObjects.firstNonNull(this.ticker, Ticker.systemTicker()); + } + + /** @deprecated */ + @Deprecated + @GwtIncompatible("To be supported") + GenericMapMaker removalListener(MapMaker.RemovalListener listener) { + Preconditions.checkState(this.removalListener == null); + super.removalListener = (MapMaker.RemovalListener)Preconditions.checkNotNull(listener); + this.useCustomMap = true; + return this; + } + + public ConcurrentMap makeMap() { + return (ConcurrentMap)(!this.useCustomMap ? new ConcurrentHashMap(this.getInitialCapacity(), 0.75F, this.getConcurrencyLevel()) : (ConcurrentMap)(this.nullRemovalCause == null ? new MapMakerInternalMap(this) : new MapMaker.NullConcurrentMap(this))); + } + + @GwtIncompatible("MapMakerInternalMap") + MapMakerInternalMap makeCustomMap() { + return new MapMakerInternalMap(this); + } + + /** @deprecated */ + @Deprecated + ConcurrentMap makeComputingMap(Function computingFunction) { + return (ConcurrentMap)(this.nullRemovalCause == null ? new MapMaker.ComputingMapAdapter(this, computingFunction) : new MapMaker.NullComputingConcurrentMap(this, computingFunction)); + } + + public String toString() { + MoreObjects.ToStringHelper s = MoreObjects.toStringHelper((Object)this); + if (this.initialCapacity != -1) { + s.add("initialCapacity", this.initialCapacity); + } + + if (this.concurrencyLevel != -1) { + s.add("concurrencyLevel", this.concurrencyLevel); + } + + if (this.maximumSize != -1) { + s.add("maximumSize", this.maximumSize); + } + + long var2; + if (this.expireAfterWriteNanos != -1L) { + var2 = this.expireAfterWriteNanos; + s.add("expireAfterWrite", (new StringBuilder(22)).append(var2).append("ns").toString()); + } + + if (this.expireAfterAccessNanos != -1L) { + var2 = this.expireAfterAccessNanos; + s.add("expireAfterAccess", (new StringBuilder(22)).append(var2).append("ns").toString()); + } + + if (this.keyStrength != null) { + s.add("keyStrength", Ascii.toLowerCase(this.keyStrength.toString())); + } + + if (this.valueStrength != null) { + s.add("valueStrength", Ascii.toLowerCase(this.valueStrength.toString())); + } + + if (this.keyEquivalence != null) { + s.addValue("keyEquivalence"); + } + + if (this.removalListener != null) { + s.addValue("removalListener"); + } + + return s.toString(); + } + + static final class ComputingMapAdapter extends ComputingConcurrentHashMap implements Serializable { + private static final long serialVersionUID = 0L; + + ComputingMapAdapter(MapMaker mapMaker, Function computingFunction) { + super(mapMaker, computingFunction); + } + + public V get(Object key) { + Object value; + try { + value = this.getOrCompute(key); + } catch (ExecutionException var5) { + Throwable cause = var5.getCause(); + Throwables.propagateIfInstanceOf(cause, ComputationException.class); + throw new ComputationException(cause); + } + + if (value == null) { + String var3 = String.valueOf(String.valueOf(this.computingFunction)); + String var6 = String.valueOf(String.valueOf(key)); + throw new NullPointerException((new StringBuilder(24 + var3.length() + var6.length())).append(var3).append(" returned null for key ").append(var6).append(".").toString()); + } else { + return value; + } + } + } + + static final class NullComputingConcurrentMap extends MapMaker.NullConcurrentMap { + private static final long serialVersionUID = 0L; + final Function computingFunction; + + NullComputingConcurrentMap(MapMaker mapMaker, Function computingFunction) { + super(mapMaker); + this.computingFunction = (Function)Preconditions.checkNotNull(computingFunction); + } + + public V get(Object k) { + V value = this.compute(k); + Preconditions.checkNotNull(value, "%s returned null for key %s.", this.computingFunction, k); + this.notifyRemoval(k, value); + return value; + } + + private V compute(K key) { + Preconditions.checkNotNull(key); + + try { + return this.computingFunction.apply(key); + } catch (ComputationException var3) { + throw var3; + } catch (Throwable var4) { + throw new ComputationException(var4); + } + } + } + + static class NullConcurrentMap extends AbstractMap implements ConcurrentMap, Serializable { + private static final long serialVersionUID = 0L; + private final MapMaker.RemovalListener removalListener; + private final MapMaker.RemovalCause removalCause; + + NullConcurrentMap(MapMaker mapMaker) { + this.removalListener = mapMaker.getRemovalListener(); + this.removalCause = mapMaker.nullRemovalCause; + } + + public boolean containsKey(@Nullable Object key) { + return false; + } + + public boolean containsValue(@Nullable Object value) { + return false; + } + + public V get(@Nullable Object key) { + return null; + } + + void notifyRemoval(K key, V value) { + MapMaker.RemovalNotification notification = new MapMaker.RemovalNotification(key, value, this.removalCause); + this.removalListener.onRemoval(notification); + } + + public V put(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + this.notifyRemoval(key, value); + return null; + } + + public V putIfAbsent(K key, V value) { + return this.put(key, value); + } + + public V remove(@Nullable Object key) { + return null; + } + + public boolean remove(@Nullable Object key, @Nullable Object value) { + return false; + } + + public V replace(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + return null; + } + + public boolean replace(K key, @Nullable V oldValue, V newValue) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(newValue); + return false; + } + + public Set> entrySet() { + return Collections.emptySet(); + } + } + + static enum RemovalCause { + EXPLICIT { + boolean wasEvicted() { + return false; + } + }, + REPLACED { + boolean wasEvicted() { + return false; + } + }, + COLLECTED { + boolean wasEvicted() { + return true; + } + }, + EXPIRED { + boolean wasEvicted() { + return true; + } + }, + SIZE { + boolean wasEvicted() { + return true; + } + }; + + private RemovalCause() { + } + + abstract boolean wasEvicted(); + + // $FF: synthetic method + RemovalCause(Object x2) { + this(); + } + } + + static final class RemovalNotification extends ImmutableEntry { + private static final long serialVersionUID = 0L; + private final MapMaker.RemovalCause cause; + + RemovalNotification(@Nullable K key, @Nullable V value, MapMaker.RemovalCause cause) { + super(key, value); + this.cause = cause; + } + + public MapMaker.RemovalCause getCause() { + return this.cause; + } + + public boolean wasEvicted() { + return this.cause.wasEvicted(); + } + } + + interface RemovalListener { + void onRemoval(MapMaker.RemovalNotification var1); + } +} diff --git a/src/main/com/google/common/collect/MapMakerInternalMap.java b/src/main/com/google/common/collect/MapMakerInternalMap.java new file mode 100644 index 0000000..226a63d --- /dev/null +++ b/src/main/com/google/common/collect/MapMakerInternalMap.java @@ -0,0 +1,2988 @@ +package com.google.common.collect; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Equivalence; +import com.google.common.base.Preconditions; +import com.google.common.base.Ticker; +import com.google.common.primitives.Ints; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +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; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; +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; + +class MapMakerInternalMap extends AbstractMap implements ConcurrentMap, Serializable { + static final int MAXIMUM_CAPACITY = 1073741824; + static final int MAX_SEGMENTS = 65536; + static final int CONTAINS_VALUE_RETRIES = 3; + static final int DRAIN_THRESHOLD = 63; + static final int DRAIN_MAX = 16; + static final long CLEANUP_EXECUTOR_DELAY_SECS = 60L; + private static final Logger logger = Logger.getLogger(MapMakerInternalMap.class.getName()); + final transient int segmentMask; + final transient int segmentShift; + final transient MapMakerInternalMap.Segment[] segments; + final int concurrencyLevel; + final Equivalence keyEquivalence; + final Equivalence valueEquivalence; + final MapMakerInternalMap.Strength keyStrength; + final MapMakerInternalMap.Strength valueStrength; + final int maximumSize; + final long expireAfterAccessNanos; + final long expireAfterWriteNanos; + final Queue> removalNotificationQueue; + final MapMaker.RemovalListener removalListener; + final transient MapMakerInternalMap.EntryFactory entryFactory; + final Ticker ticker; + static final MapMakerInternalMap.ValueReference UNSET = new MapMakerInternalMap.ValueReference() { + public Object get() { + return null; + } + + public MapMakerInternalMap.ReferenceEntry getEntry() { + return null; + } + + public MapMakerInternalMap.ValueReference copyFor(ReferenceQueue queue, @Nullable Object value, MapMakerInternalMap.ReferenceEntry entry) { + return this; + } + + public boolean isComputingReference() { + return false; + } + + public Object waitForValue() { + return null; + } + + public void clear(MapMakerInternalMap.ValueReference newValue) { + } + }; + static final Queue DISCARDING_QUEUE = new AbstractQueue() { + public boolean offer(Object o) { + return true; + } + + public Object peek() { + return null; + } + + public Object poll() { + return null; + } + + public int size() { + return 0; + } + + public Iterator iterator() { + return Iterators.emptyIterator(); + } + }; + transient Set keySet; + transient Collection values; + transient Set> entrySet; + private static final long serialVersionUID = 5L; + + MapMakerInternalMap(MapMaker builder) { + this.concurrencyLevel = Math.min(builder.getConcurrencyLevel(), 65536); + this.keyStrength = builder.getKeyStrength(); + this.valueStrength = builder.getValueStrength(); + this.keyEquivalence = builder.getKeyEquivalence(); + this.valueEquivalence = this.valueStrength.defaultEquivalence(); + this.maximumSize = builder.maximumSize; + this.expireAfterAccessNanos = builder.getExpireAfterAccessNanos(); + this.expireAfterWriteNanos = builder.getExpireAfterWriteNanos(); + this.entryFactory = MapMakerInternalMap.EntryFactory.getFactory(this.keyStrength, this.expires(), this.evictsBySize()); + this.ticker = builder.getTicker(); + this.removalListener = builder.getRemovalListener(); + this.removalNotificationQueue = (Queue)(this.removalListener == GenericMapMaker.NullListener.INSTANCE ? discardingQueue() : new ConcurrentLinkedQueue()); + int initialCapacity = Math.min(builder.getInitialCapacity(), 1073741824); + if (this.evictsBySize()) { + initialCapacity = Math.min(initialCapacity, this.maximumSize); + } + + int segmentShift = 0; + + int segmentCount; + for(segmentCount = 1; segmentCount < this.concurrencyLevel && (!this.evictsBySize() || segmentCount * 2 <= this.maximumSize); segmentCount <<= 1) { + ++segmentShift; + } + + this.segmentShift = 32 - segmentShift; + this.segmentMask = segmentCount - 1; + this.segments = this.newSegmentArray(segmentCount); + int segmentCapacity = initialCapacity / segmentCount; + if (segmentCapacity * segmentCount < initialCapacity) { + ++segmentCapacity; + } + + int segmentSize; + for(segmentSize = 1; segmentSize < segmentCapacity; segmentSize <<= 1) { + } + + int maximumSegmentSize; + if (this.evictsBySize()) { + maximumSegmentSize = this.maximumSize / segmentCount + 1; + int remainder = this.maximumSize % segmentCount; + + for(int i = 0; i < this.segments.length; ++i) { + if (i == remainder) { + --maximumSegmentSize; + } + + this.segments[i] = this.createSegment(segmentSize, maximumSegmentSize); + } + } else { + for(maximumSegmentSize = 0; maximumSegmentSize < this.segments.length; ++maximumSegmentSize) { + this.segments[maximumSegmentSize] = this.createSegment(segmentSize, -1); + } + } + + } + + boolean evictsBySize() { + return this.maximumSize != -1; + } + + boolean expires() { + return this.expiresAfterWrite() || this.expiresAfterAccess(); + } + + boolean expiresAfterWrite() { + return this.expireAfterWriteNanos > 0L; + } + + boolean expiresAfterAccess() { + return this.expireAfterAccessNanos > 0L; + } + + boolean usesKeyReferences() { + return this.keyStrength != MapMakerInternalMap.Strength.STRONG; + } + + boolean usesValueReferences() { + return this.valueStrength != MapMakerInternalMap.Strength.STRONG; + } + + static MapMakerInternalMap.ValueReference unset() { + return UNSET; + } + + static MapMakerInternalMap.ReferenceEntry nullEntry() { + return MapMakerInternalMap.NullEntry.INSTANCE; + } + + static Queue discardingQueue() { + return DISCARDING_QUEUE; + } + + static int rehash(int h) { + h += h << 15 ^ -12931; + h ^= h >>> 10; + h += h << 3; + h ^= h >>> 6; + h += (h << 2) + (h << 14); + return h ^ h >>> 16; + } + + @VisibleForTesting + MapMakerInternalMap.ReferenceEntry newEntry(K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return this.segmentFor(hash).newEntry(key, hash, next); + } + + @VisibleForTesting + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + int hash = original.getHash(); + return this.segmentFor(hash).copyEntry(original, newNext); + } + + @VisibleForTesting + MapMakerInternalMap.ValueReference newValueReference(MapMakerInternalMap.ReferenceEntry entry, V value) { + int hash = entry.getHash(); + return this.valueStrength.referenceValue(this.segmentFor(hash), entry, value); + } + + int hash(Object key) { + int h = this.keyEquivalence.hash(key); + return rehash(h); + } + + void reclaimValue(MapMakerInternalMap.ValueReference valueReference) { + MapMakerInternalMap.ReferenceEntry entry = valueReference.getEntry(); + int hash = entry.getHash(); + this.segmentFor(hash).reclaimValue(entry.getKey(), hash, valueReference); + } + + void reclaimKey(MapMakerInternalMap.ReferenceEntry entry) { + int hash = entry.getHash(); + this.segmentFor(hash).reclaimKey(entry, hash); + } + + @VisibleForTesting + boolean isLive(MapMakerInternalMap.ReferenceEntry entry) { + return this.segmentFor(entry.getHash()).getLiveValue(entry) != null; + } + + MapMakerInternalMap.Segment segmentFor(int hash) { + return this.segments[hash >>> this.segmentShift & this.segmentMask]; + } + + MapMakerInternalMap.Segment createSegment(int initialCapacity, int maxSegmentSize) { + return new MapMakerInternalMap.Segment(this, initialCapacity, maxSegmentSize); + } + + V getLiveValue(MapMakerInternalMap.ReferenceEntry entry) { + if (entry.getKey() == null) { + return null; + } else { + V value = entry.getValueReference().get(); + if (value == null) { + return null; + } else { + return this.expires() && this.isExpired(entry) ? null : value; + } + } + } + + boolean isExpired(MapMakerInternalMap.ReferenceEntry entry) { + return this.isExpired(entry, this.ticker.read()); + } + + boolean isExpired(MapMakerInternalMap.ReferenceEntry entry, long now) { + return now - entry.getExpirationTime() > 0L; + } + + static void connectExpirables(MapMakerInternalMap.ReferenceEntry previous, MapMakerInternalMap.ReferenceEntry next) { + previous.setNextExpirable(next); + next.setPreviousExpirable(previous); + } + + static void nullifyExpirable(MapMakerInternalMap.ReferenceEntry nulled) { + MapMakerInternalMap.ReferenceEntry nullEntry = nullEntry(); + nulled.setNextExpirable(nullEntry); + nulled.setPreviousExpirable(nullEntry); + } + + void processPendingNotifications() { + MapMaker.RemovalNotification notification; + while((notification = (MapMaker.RemovalNotification)this.removalNotificationQueue.poll()) != null) { + try { + this.removalListener.onRemoval(notification); + } catch (Exception var3) { + logger.log(Level.WARNING, "Exception thrown by removal listener", var3); + } + } + + } + + static void connectEvictables(MapMakerInternalMap.ReferenceEntry previous, MapMakerInternalMap.ReferenceEntry next) { + previous.setNextEvictable(next); + next.setPreviousEvictable(previous); + } + + static void nullifyEvictable(MapMakerInternalMap.ReferenceEntry nulled) { + MapMakerInternalMap.ReferenceEntry nullEntry = nullEntry(); + nulled.setNextEvictable(nullEntry); + nulled.setPreviousEvictable(nullEntry); + } + + final MapMakerInternalMap.Segment[] newSegmentArray(int ssize) { + return new MapMakerInternalMap.Segment[ssize]; + } + + public boolean isEmpty() { + long sum = 0L; + MapMakerInternalMap.Segment[] segments = this.segments; + + int i; + for(i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + + sum += (long)segments[i].modCount; + } + + if (sum != 0L) { + for(i = 0; i < segments.length; ++i) { + if (segments[i].count != 0) { + return false; + } + + sum -= (long)segments[i].modCount; + } + + if (sum != 0L) { + return false; + } + } + + return true; + } + + public int size() { + MapMakerInternalMap.Segment[] segments = this.segments; + long sum = 0L; + + for(int i = 0; i < segments.length; ++i) { + sum += (long)segments[i].count; + } + + return Ints.saturatedCast(sum); + } + + public V get(@Nullable Object key) { + if (key == null) { + return null; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).get(key, hash); + } + } + + MapMakerInternalMap.ReferenceEntry getEntry(@Nullable Object key) { + if (key == null) { + return null; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).getEntry(key, hash); + } + } + + public boolean containsKey(@Nullable Object key) { + if (key == null) { + return false; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).containsKey(key, hash); + } + } + + public boolean containsValue(@Nullable Object value) { + if (value == null) { + return false; + } else { + MapMakerInternalMap.Segment[] segments = this.segments; + long last = -1L; + + for(int i = 0; i < 3; ++i) { + long sum = 0L; + MapMakerInternalMap.Segment[] arr$ = segments; + int len$ = segments.length; + + for(int i$ = 0; i$ < len$; ++i$) { + MapMakerInternalMap.Segment segment = arr$[i$]; + int c = segment.count; + AtomicReferenceArray> table = segment.table; + + for(int j = 0; j < table.length(); ++j) { + for(MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)table.get(j); e != null; e = e.getNext()) { + V v = segment.getLiveValue(e); + if (v != null && this.valueEquivalence.equivalent(value, v)) { + return true; + } + } + } + + sum += (long)segment.modCount; + } + + if (sum == last) { + break; + } + + last = sum; + } + + return false; + } + } + + public V put(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + int hash = this.hash(key); + return this.segmentFor(hash).put(key, hash, value, false); + } + + public V putIfAbsent(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + int hash = this.hash(key); + return this.segmentFor(hash).put(key, hash, value, true); + } + + public void putAll(Map m) { + Iterator i$ = m.entrySet().iterator(); + + while(i$.hasNext()) { + Entry e = (Entry)i$.next(); + this.put(e.getKey(), e.getValue()); + } + + } + + public V remove(@Nullable Object key) { + if (key == null) { + return null; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).remove(key, hash); + } + } + + public boolean remove(@Nullable Object key, @Nullable Object value) { + if (key != null && value != null) { + int hash = this.hash(key); + return this.segmentFor(hash).remove(key, hash, value); + } else { + return false; + } + } + + public boolean replace(K key, @Nullable V oldValue, V newValue) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(newValue); + if (oldValue == null) { + return false; + } else { + int hash = this.hash(key); + return this.segmentFor(hash).replace(key, hash, oldValue, newValue); + } + } + + public V replace(K key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + int hash = this.hash(key); + return this.segmentFor(hash).replace(key, hash, value); + } + + public void clear() { + MapMakerInternalMap.Segment[] arr$ = this.segments; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + MapMakerInternalMap.Segment segment = arr$[i$]; + segment.clear(); + } + + } + + public Set keySet() { + Set ks = this.keySet; + return ks != null ? ks : (this.keySet = new MapMakerInternalMap.KeySet()); + } + + public Collection values() { + Collection vs = this.values; + return vs != null ? vs : (this.values = new MapMakerInternalMap.Values()); + } + + public Set> entrySet() { + Set> es = this.entrySet; + return es != null ? es : (this.entrySet = new MapMakerInternalMap.EntrySet()); + } + + Object writeReplace() { + return new MapMakerInternalMap.SerializationProxy(this.keyStrength, this.valueStrength, this.keyEquivalence, this.valueEquivalence, this.expireAfterWriteNanos, this.expireAfterAccessNanos, this.maximumSize, this.concurrencyLevel, this.removalListener, this); + } + + private static final class SerializationProxy extends MapMakerInternalMap.AbstractSerializationProxy { + private static final long serialVersionUID = 3L; + + SerializationProxy(MapMakerInternalMap.Strength keyStrength, MapMakerInternalMap.Strength valueStrength, Equivalence keyEquivalence, Equivalence valueEquivalence, long expireAfterWriteNanos, long expireAfterAccessNanos, int maximumSize, int concurrencyLevel, MapMaker.RemovalListener removalListener, ConcurrentMap delegate) { + super(keyStrength, valueStrength, keyEquivalence, valueEquivalence, expireAfterWriteNanos, expireAfterAccessNanos, maximumSize, concurrencyLevel, removalListener, delegate); + } + + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + this.writeMapTo(out); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + MapMaker mapMaker = this.readMapMaker(in); + this.delegate = mapMaker.makeMap(); + this.readEntries(in); + } + + private Object readResolve() { + return this.delegate; + } + } + + abstract static class AbstractSerializationProxy extends ForwardingConcurrentMap implements Serializable { + private static final long serialVersionUID = 3L; + final MapMakerInternalMap.Strength keyStrength; + final MapMakerInternalMap.Strength valueStrength; + final Equivalence keyEquivalence; + final Equivalence valueEquivalence; + final long expireAfterWriteNanos; + final long expireAfterAccessNanos; + final int maximumSize; + final int concurrencyLevel; + final MapMaker.RemovalListener removalListener; + transient ConcurrentMap delegate; + + AbstractSerializationProxy(MapMakerInternalMap.Strength keyStrength, MapMakerInternalMap.Strength valueStrength, Equivalence keyEquivalence, Equivalence valueEquivalence, long expireAfterWriteNanos, long expireAfterAccessNanos, int maximumSize, int concurrencyLevel, MapMaker.RemovalListener removalListener, ConcurrentMap delegate) { + this.keyStrength = keyStrength; + this.valueStrength = valueStrength; + this.keyEquivalence = keyEquivalence; + this.valueEquivalence = valueEquivalence; + this.expireAfterWriteNanos = expireAfterWriteNanos; + this.expireAfterAccessNanos = expireAfterAccessNanos; + this.maximumSize = maximumSize; + this.concurrencyLevel = concurrencyLevel; + this.removalListener = removalListener; + this.delegate = delegate; + } + + protected ConcurrentMap delegate() { + return this.delegate; + } + + void writeMapTo(ObjectOutputStream out) throws IOException { + out.writeInt(this.delegate.size()); + Iterator i$ = this.delegate.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + out.writeObject(entry.getKey()); + out.writeObject(entry.getValue()); + } + + out.writeObject((Object)null); + } + + MapMaker readMapMaker(ObjectInputStream in) throws IOException { + int size = in.readInt(); + MapMaker mapMaker = (new MapMaker()).initialCapacity(size).setKeyStrength(this.keyStrength).setValueStrength(this.valueStrength).keyEquivalence(this.keyEquivalence).concurrencyLevel(this.concurrencyLevel); + mapMaker.removalListener(this.removalListener); + if (this.expireAfterWriteNanos > 0L) { + mapMaker.expireAfterWrite(this.expireAfterWriteNanos, TimeUnit.NANOSECONDS); + } + + if (this.expireAfterAccessNanos > 0L) { + mapMaker.expireAfterAccess(this.expireAfterAccessNanos, TimeUnit.NANOSECONDS); + } + + if (this.maximumSize != -1) { + mapMaker.maximumSize(this.maximumSize); + } + + return mapMaker; + } + + void readEntries(ObjectInputStream in) throws IOException, ClassNotFoundException { + while(true) { + K key = in.readObject(); + if (key == null) { + return; + } + + V value = in.readObject(); + this.delegate.put(key, value); + } + } + } + + final class EntrySet extends AbstractSet> { + public Iterator> iterator() { + return MapMakerInternalMap.this.new EntryIterator(); + } + + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } else { + Entry e = (Entry)o; + Object key = e.getKey(); + if (key == null) { + return false; + } else { + V v = MapMakerInternalMap.this.get(key); + return v != null && MapMakerInternalMap.this.valueEquivalence.equivalent(e.getValue(), v); + } + } + } + + public boolean remove(Object o) { + if (!(o instanceof Entry)) { + return false; + } else { + Entry e = (Entry)o; + Object key = e.getKey(); + return key != null && MapMakerInternalMap.this.remove(key, e.getValue()); + } + } + + public int size() { + return MapMakerInternalMap.this.size(); + } + + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + + final class Values extends AbstractCollection { + public Iterator iterator() { + return MapMakerInternalMap.this.new ValueIterator(); + } + + public int size() { + return MapMakerInternalMap.this.size(); + } + + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + public boolean contains(Object o) { + return MapMakerInternalMap.this.containsValue(o); + } + + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + + final class KeySet extends AbstractSet { + public Iterator iterator() { + return MapMakerInternalMap.this.new KeyIterator(); + } + + public int size() { + return MapMakerInternalMap.this.size(); + } + + public boolean isEmpty() { + return MapMakerInternalMap.this.isEmpty(); + } + + public boolean contains(Object o) { + return MapMakerInternalMap.this.containsKey(o); + } + + public boolean remove(Object o) { + return MapMakerInternalMap.this.remove(o) != null; + } + + public void clear() { + MapMakerInternalMap.this.clear(); + } + } + + final class EntryIterator extends MapMakerInternalMap.HashIterator> { + EntryIterator() { + super(); + } + + public Entry next() { + return this.nextEntry(); + } + } + + final class WriteThroughEntry extends AbstractMapEntry { + final K key; + V value; + + WriteThroughEntry(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return this.key; + } + + public V getValue() { + return this.value; + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof Entry)) { + return false; + } else { + Entry that = (Entry)object; + return this.key.equals(that.getKey()) && this.value.equals(that.getValue()); + } + } + + public int hashCode() { + return this.key.hashCode() ^ this.value.hashCode(); + } + + public V setValue(V newValue) { + V oldValue = MapMakerInternalMap.this.put(this.key, newValue); + this.value = newValue; + return oldValue; + } + } + + final class ValueIterator extends MapMakerInternalMap.HashIterator { + ValueIterator() { + super(); + } + + public V next() { + return this.nextEntry().getValue(); + } + } + + final class KeyIterator extends MapMakerInternalMap.HashIterator { + KeyIterator() { + super(); + } + + public K next() { + return this.nextEntry().getKey(); + } + } + + abstract class HashIterator implements Iterator { + int nextSegmentIndex; + int nextTableIndex; + MapMakerInternalMap.Segment currentSegment; + AtomicReferenceArray> currentTable; + MapMakerInternalMap.ReferenceEntry nextEntry; + MapMakerInternalMap.WriteThroughEntry nextExternal; + MapMakerInternalMap.WriteThroughEntry lastReturned; + + HashIterator() { + this.nextSegmentIndex = MapMakerInternalMap.this.segments.length - 1; + this.nextTableIndex = -1; + this.advance(); + } + + public abstract E next(); + + final void advance() { + this.nextExternal = null; + if (!this.nextInChain()) { + if (!this.nextInTable()) { + while(this.nextSegmentIndex >= 0) { + this.currentSegment = MapMakerInternalMap.this.segments[this.nextSegmentIndex--]; + if (this.currentSegment.count != 0) { + this.currentTable = this.currentSegment.table; + this.nextTableIndex = this.currentTable.length() - 1; + if (this.nextInTable()) { + return; + } + } + } + + } + } + } + + boolean nextInChain() { + if (this.nextEntry != null) { + for(this.nextEntry = this.nextEntry.getNext(); this.nextEntry != null; this.nextEntry = this.nextEntry.getNext()) { + if (this.advanceTo(this.nextEntry)) { + return true; + } + } + } + + return false; + } + + boolean nextInTable() { + while(true) { + if (this.nextTableIndex >= 0) { + if ((this.nextEntry = (MapMakerInternalMap.ReferenceEntry)this.currentTable.get(this.nextTableIndex--)) == null || !this.advanceTo(this.nextEntry) && !this.nextInChain()) { + continue; + } + + return true; + } + + return false; + } + } + + boolean advanceTo(MapMakerInternalMap.ReferenceEntry entry) { + boolean var4; + try { + K key = entry.getKey(); + V value = MapMakerInternalMap.this.getLiveValue(entry); + if (value != null) { + this.nextExternal = MapMakerInternalMap.this.new WriteThroughEntry(key, value); + var4 = true; + return var4; + } + + var4 = false; + } finally { + this.currentSegment.postReadCleanup(); + } + + return var4; + } + + public boolean hasNext() { + return this.nextExternal != null; + } + + MapMakerInternalMap.WriteThroughEntry nextEntry() { + if (this.nextExternal == null) { + throw new NoSuchElementException(); + } else { + this.lastReturned = this.nextExternal; + this.advance(); + return this.lastReturned; + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.lastReturned != null); + MapMakerInternalMap.this.remove(this.lastReturned.getKey()); + this.lastReturned = null; + } + } + + static final class CleanupMapTask implements Runnable { + final WeakReference> mapReference; + + public CleanupMapTask(MapMakerInternalMap map) { + this.mapReference = new WeakReference(map); + } + + public void run() { + MapMakerInternalMap map = (MapMakerInternalMap)this.mapReference.get(); + if (map == null) { + throw new CancellationException(); + } else { + MapMakerInternalMap.Segment[] arr$ = map.segments; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + MapMakerInternalMap.Segment segment = arr$[i$]; + segment.runCleanup(); + } + + } + } + } + + static final class ExpirationQueue extends AbstractQueue> { + final MapMakerInternalMap.ReferenceEntry head = new MapMakerInternalMap.AbstractReferenceEntry() { + MapMakerInternalMap.ReferenceEntry nextExpirable = this; + MapMakerInternalMap.ReferenceEntry previousExpirable = this; + + public long getExpirationTime() { + return Long.MAX_VALUE; + } + + public void setExpirationTime(long time) { + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this.nextExpirable; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + this.nextExpirable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this.previousExpirable; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousExpirable = previous; + } + }; + + public boolean offer(MapMakerInternalMap.ReferenceEntry entry) { + MapMakerInternalMap.connectExpirables(entry.getPreviousExpirable(), entry.getNextExpirable()); + MapMakerInternalMap.connectExpirables(this.head.getPreviousExpirable(), entry); + MapMakerInternalMap.connectExpirables(entry, this.head); + return true; + } + + public MapMakerInternalMap.ReferenceEntry peek() { + MapMakerInternalMap.ReferenceEntry next = this.head.getNextExpirable(); + return next == this.head ? null : next; + } + + public MapMakerInternalMap.ReferenceEntry poll() { + MapMakerInternalMap.ReferenceEntry next = this.head.getNextExpirable(); + if (next == this.head) { + return null; + } else { + this.remove(next); + return next; + } + } + + public boolean remove(Object o) { + MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)o; + MapMakerInternalMap.ReferenceEntry previous = e.getPreviousExpirable(); + MapMakerInternalMap.ReferenceEntry next = e.getNextExpirable(); + MapMakerInternalMap.connectExpirables(previous, next); + MapMakerInternalMap.nullifyExpirable(e); + return next != MapMakerInternalMap.NullEntry.INSTANCE; + } + + public boolean contains(Object o) { + MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)o; + return e.getNextExpirable() != MapMakerInternalMap.NullEntry.INSTANCE; + } + + public boolean isEmpty() { + return this.head.getNextExpirable() == this.head; + } + + public int size() { + int size = 0; + + for(MapMakerInternalMap.ReferenceEntry e = this.head.getNextExpirable(); e != this.head; e = e.getNextExpirable()) { + ++size; + } + + return size; + } + + public void clear() { + MapMakerInternalMap.ReferenceEntry next; + for(MapMakerInternalMap.ReferenceEntry e = this.head.getNextExpirable(); e != this.head; e = next) { + next = e.getNextExpirable(); + MapMakerInternalMap.nullifyExpirable(e); + } + + this.head.setNextExpirable(this.head); + this.head.setPreviousExpirable(this.head); + } + + public Iterator> iterator() { + return new AbstractSequentialIterator>(this.peek()) { + protected MapMakerInternalMap.ReferenceEntry computeNext(MapMakerInternalMap.ReferenceEntry previous) { + MapMakerInternalMap.ReferenceEntry next = previous.getNextExpirable(); + return next == ExpirationQueue.this.head ? null : next; + } + }; + } + } + + static final class EvictionQueue extends AbstractQueue> { + final MapMakerInternalMap.ReferenceEntry head = new MapMakerInternalMap.AbstractReferenceEntry() { + MapMakerInternalMap.ReferenceEntry nextEvictable = this; + MapMakerInternalMap.ReferenceEntry previousEvictable = this; + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this.nextEvictable; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + this.nextEvictable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this.previousEvictable; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousEvictable = previous; + } + }; + + public boolean offer(MapMakerInternalMap.ReferenceEntry entry) { + MapMakerInternalMap.connectEvictables(entry.getPreviousEvictable(), entry.getNextEvictable()); + MapMakerInternalMap.connectEvictables(this.head.getPreviousEvictable(), entry); + MapMakerInternalMap.connectEvictables(entry, this.head); + return true; + } + + public MapMakerInternalMap.ReferenceEntry peek() { + MapMakerInternalMap.ReferenceEntry next = this.head.getNextEvictable(); + return next == this.head ? null : next; + } + + public MapMakerInternalMap.ReferenceEntry poll() { + MapMakerInternalMap.ReferenceEntry next = this.head.getNextEvictable(); + if (next == this.head) { + return null; + } else { + this.remove(next); + return next; + } + } + + public boolean remove(Object o) { + MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)o; + MapMakerInternalMap.ReferenceEntry previous = e.getPreviousEvictable(); + MapMakerInternalMap.ReferenceEntry next = e.getNextEvictable(); + MapMakerInternalMap.connectEvictables(previous, next); + MapMakerInternalMap.nullifyEvictable(e); + return next != MapMakerInternalMap.NullEntry.INSTANCE; + } + + public boolean contains(Object o) { + MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)o; + return e.getNextEvictable() != MapMakerInternalMap.NullEntry.INSTANCE; + } + + public boolean isEmpty() { + return this.head.getNextEvictable() == this.head; + } + + public int size() { + int size = 0; + + for(MapMakerInternalMap.ReferenceEntry e = this.head.getNextEvictable(); e != this.head; e = e.getNextEvictable()) { + ++size; + } + + return size; + } + + public void clear() { + MapMakerInternalMap.ReferenceEntry next; + for(MapMakerInternalMap.ReferenceEntry e = this.head.getNextEvictable(); e != this.head; e = next) { + next = e.getNextEvictable(); + MapMakerInternalMap.nullifyEvictable(e); + } + + this.head.setNextEvictable(this.head); + this.head.setPreviousEvictable(this.head); + } + + public Iterator> iterator() { + return new AbstractSequentialIterator>(this.peek()) { + protected MapMakerInternalMap.ReferenceEntry computeNext(MapMakerInternalMap.ReferenceEntry previous) { + MapMakerInternalMap.ReferenceEntry next = previous.getNextEvictable(); + return next == EvictionQueue.this.head ? null : next; + } + }; + } + } + + static class Segment extends ReentrantLock { + final MapMakerInternalMap map; + volatile int count; + int modCount; + int threshold; + volatile AtomicReferenceArray> table; + final int maxSegmentSize; + final ReferenceQueue keyReferenceQueue; + final ReferenceQueue valueReferenceQueue; + final Queue> recencyQueue; + final AtomicInteger readCount = new AtomicInteger(); + @GuardedBy("Segment.this") + final Queue> evictionQueue; + @GuardedBy("Segment.this") + final Queue> expirationQueue; + + Segment(MapMakerInternalMap map, int initialCapacity, int maxSegmentSize) { + this.map = map; + this.maxSegmentSize = maxSegmentSize; + this.initTable(this.newEntryArray(initialCapacity)); + this.keyReferenceQueue = map.usesKeyReferences() ? new ReferenceQueue() : null; + this.valueReferenceQueue = map.usesValueReferences() ? new ReferenceQueue() : null; + this.recencyQueue = (Queue)(!map.evictsBySize() && !map.expiresAfterAccess() ? MapMakerInternalMap.discardingQueue() : new ConcurrentLinkedQueue()); + this.evictionQueue = (Queue)(map.evictsBySize() ? new MapMakerInternalMap.EvictionQueue() : MapMakerInternalMap.discardingQueue()); + this.expirationQueue = (Queue)(map.expires() ? new MapMakerInternalMap.ExpirationQueue() : MapMakerInternalMap.discardingQueue()); + } + + AtomicReferenceArray> newEntryArray(int size) { + return new AtomicReferenceArray(size); + } + + void initTable(AtomicReferenceArray> newTable) { + this.threshold = newTable.length() * 3 / 4; + if (this.threshold == this.maxSegmentSize) { + ++this.threshold; + } + + this.table = newTable; + } + + @GuardedBy("Segment.this") + MapMakerInternalMap.ReferenceEntry newEntry(K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return this.map.entryFactory.newEntry(this, key, hash, next); + } + + @GuardedBy("Segment.this") + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + if (original.getKey() == null) { + return null; + } else { + MapMakerInternalMap.ValueReference valueReference = original.getValueReference(); + V value = valueReference.get(); + if (value == null && !valueReference.isComputingReference()) { + return null; + } else { + MapMakerInternalMap.ReferenceEntry newEntry = this.map.entryFactory.copyEntry(this, original, newNext); + newEntry.setValueReference(valueReference.copyFor(this.valueReferenceQueue, value, newEntry)); + return newEntry; + } + } + } + + @GuardedBy("Segment.this") + void setValue(MapMakerInternalMap.ReferenceEntry entry, V value) { + MapMakerInternalMap.ValueReference valueReference = this.map.valueStrength.referenceValue(this, entry, value); + entry.setValueReference(valueReference); + this.recordWrite(entry); + } + + void tryDrainReferenceQueues() { + if (this.tryLock()) { + try { + this.drainReferenceQueues(); + } finally { + this.unlock(); + } + } + + } + + @GuardedBy("Segment.this") + void drainReferenceQueues() { + if (this.map.usesKeyReferences()) { + this.drainKeyReferenceQueue(); + } + + if (this.map.usesValueReferences()) { + this.drainValueReferenceQueue(); + } + + } + + @GuardedBy("Segment.this") + void drainKeyReferenceQueue() { + int i = 0; + + Reference ref; + while((ref = this.keyReferenceQueue.poll()) != null) { + MapMakerInternalMap.ReferenceEntry entry = (MapMakerInternalMap.ReferenceEntry)ref; + this.map.reclaimKey(entry); + ++i; + if (i == 16) { + break; + } + } + + } + + @GuardedBy("Segment.this") + void drainValueReferenceQueue() { + int i = 0; + + Reference ref; + while((ref = this.valueReferenceQueue.poll()) != null) { + MapMakerInternalMap.ValueReference valueReference = (MapMakerInternalMap.ValueReference)ref; + this.map.reclaimValue(valueReference); + ++i; + if (i == 16) { + break; + } + } + + } + + void clearReferenceQueues() { + if (this.map.usesKeyReferences()) { + this.clearKeyReferenceQueue(); + } + + if (this.map.usesValueReferences()) { + this.clearValueReferenceQueue(); + } + + } + + void clearKeyReferenceQueue() { + while(this.keyReferenceQueue.poll() != null) { + } + + } + + void clearValueReferenceQueue() { + while(this.valueReferenceQueue.poll() != null) { + } + + } + + void recordRead(MapMakerInternalMap.ReferenceEntry entry) { + if (this.map.expiresAfterAccess()) { + this.recordExpirationTime(entry, this.map.expireAfterAccessNanos); + } + + this.recencyQueue.add(entry); + } + + @GuardedBy("Segment.this") + void recordLockedRead(MapMakerInternalMap.ReferenceEntry entry) { + this.evictionQueue.add(entry); + if (this.map.expiresAfterAccess()) { + this.recordExpirationTime(entry, this.map.expireAfterAccessNanos); + this.expirationQueue.add(entry); + } + + } + + @GuardedBy("Segment.this") + void recordWrite(MapMakerInternalMap.ReferenceEntry entry) { + this.drainRecencyQueue(); + this.evictionQueue.add(entry); + if (this.map.expires()) { + long expiration = this.map.expiresAfterAccess() ? this.map.expireAfterAccessNanos : this.map.expireAfterWriteNanos; + this.recordExpirationTime(entry, expiration); + this.expirationQueue.add(entry); + } + + } + + @GuardedBy("Segment.this") + void drainRecencyQueue() { + MapMakerInternalMap.ReferenceEntry e; + while((e = (MapMakerInternalMap.ReferenceEntry)this.recencyQueue.poll()) != null) { + if (this.evictionQueue.contains(e)) { + this.evictionQueue.add(e); + } + + if (this.map.expiresAfterAccess() && this.expirationQueue.contains(e)) { + this.expirationQueue.add(e); + } + } + + } + + void recordExpirationTime(MapMakerInternalMap.ReferenceEntry entry, long expirationNanos) { + entry.setExpirationTime(this.map.ticker.read() + expirationNanos); + } + + void tryExpireEntries() { + if (this.tryLock()) { + try { + this.expireEntries(); + } finally { + this.unlock(); + } + } + + } + + @GuardedBy("Segment.this") + void expireEntries() { + this.drainRecencyQueue(); + if (!this.expirationQueue.isEmpty()) { + long now = this.map.ticker.read(); + + MapMakerInternalMap.ReferenceEntry e; + while((e = (MapMakerInternalMap.ReferenceEntry)this.expirationQueue.peek()) != null && this.map.isExpired(e, now)) { + if (!this.removeEntry(e, e.getHash(), MapMaker.RemovalCause.EXPIRED)) { + throw new AssertionError(); + } + } + + } + } + + void enqueueNotification(MapMakerInternalMap.ReferenceEntry entry, MapMaker.RemovalCause cause) { + this.enqueueNotification(entry.getKey(), entry.getHash(), entry.getValueReference().get(), cause); + } + + void enqueueNotification(@Nullable K key, int hash, @Nullable V value, MapMaker.RemovalCause cause) { + if (this.map.removalNotificationQueue != MapMakerInternalMap.DISCARDING_QUEUE) { + MapMaker.RemovalNotification notification = new MapMaker.RemovalNotification(key, value, cause); + this.map.removalNotificationQueue.offer(notification); + } + + } + + @GuardedBy("Segment.this") + boolean evictEntries() { + if (this.map.evictsBySize() && this.count >= this.maxSegmentSize) { + this.drainRecencyQueue(); + MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)this.evictionQueue.remove(); + if (!this.removeEntry(e, e.getHash(), MapMaker.RemovalCause.SIZE)) { + throw new AssertionError(); + } else { + return true; + } + } else { + return false; + } + } + + MapMakerInternalMap.ReferenceEntry getFirst(int hash) { + AtomicReferenceArray> table = this.table; + return (MapMakerInternalMap.ReferenceEntry)table.get(hash & table.length() - 1); + } + + MapMakerInternalMap.ReferenceEntry getEntry(Object key, int hash) { + if (this.count != 0) { + for(MapMakerInternalMap.ReferenceEntry e = this.getFirst(hash); e != null; e = e.getNext()) { + if (e.getHash() == hash) { + K entryKey = e.getKey(); + if (entryKey == null) { + this.tryDrainReferenceQueues(); + } else if (this.map.keyEquivalence.equivalent(key, entryKey)) { + return e; + } + } + } + } + + return null; + } + + MapMakerInternalMap.ReferenceEntry getLiveEntry(Object key, int hash) { + MapMakerInternalMap.ReferenceEntry e = this.getEntry(key, hash); + if (e == null) { + return null; + } else if (this.map.expires() && this.map.isExpired(e)) { + this.tryExpireEntries(); + return null; + } else { + return e; + } + } + + V get(Object key, int hash) { + Object value; + try { + MapMakerInternalMap.ReferenceEntry e = this.getLiveEntry(key, hash); + if (e != null) { + value = e.getValueReference().get(); + if (value != null) { + this.recordRead(e); + } else { + this.tryDrainReferenceQueues(); + } + + Object var5 = value; + return var5; + } + + value = null; + } finally { + this.postReadCleanup(); + } + + return value; + } + + boolean containsKey(Object key, int hash) { + boolean var4; + try { + if (this.count == 0) { + boolean var8 = false; + return var8; + } + + MapMakerInternalMap.ReferenceEntry e = this.getLiveEntry(key, hash); + if (e != null) { + var4 = e.getValueReference().get() != null; + return var4; + } + + var4 = false; + } finally { + this.postReadCleanup(); + } + + return var4; + } + + @VisibleForTesting + boolean containsValue(Object value) { + try { + if (this.count != 0) { + AtomicReferenceArray> table = this.table; + int length = table.length(); + + for(int i = 0; i < length; ++i) { + for(MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)table.get(i); e != null; e = e.getNext()) { + V entryValue = this.getLiveValue(e); + if (entryValue != null && this.map.valueEquivalence.equivalent(value, entryValue)) { + boolean var7 = true; + return var7; + } + } + } + } + + boolean var11 = false; + return var11; + } finally { + this.postReadCleanup(); + } + } + + V put(K key, int hash, V value, boolean onlyIfAbsent) { + this.lock(); + + Object entryKey; + try { + this.preWriteCleanup(); + int newCount = this.count + 1; + if (newCount > this.threshold) { + this.expand(); + newCount = this.count + 1; + } + + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + MapMakerInternalMap.ReferenceEntry e; + for(e = first; e != null; e = e.getNext()) { + entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + Object var13; + if (entryValue == null) { + ++this.modCount; + this.setValue(e, value); + if (!valueReference.isComputingReference()) { + this.enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.COLLECTED); + newCount = this.count; + } else if (this.evictEntries()) { + newCount = this.count + 1; + } + + this.count = newCount; + var13 = null; + return var13; + } + + if (onlyIfAbsent) { + this.recordLockedRead(e); + var13 = entryValue; + return var13; + } + + ++this.modCount; + this.enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.REPLACED); + this.setValue(e, value); + var13 = entryValue; + return var13; + } + } + + ++this.modCount; + e = this.newEntry(key, hash, first); + this.setValue(e, value); + table.set(index, e); + if (this.evictEntries()) { + newCount = this.count + 1; + } + + this.count = newCount; + entryKey = null; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + + return entryKey; + } + + @GuardedBy("Segment.this") + void expand() { + AtomicReferenceArray> oldTable = this.table; + int oldCapacity = oldTable.length(); + if (oldCapacity < 1073741824) { + int newCount = this.count; + AtomicReferenceArray> newTable = this.newEntryArray(oldCapacity << 1); + this.threshold = newTable.length() * 3 / 4; + int newMask = newTable.length() - 1; + + for(int oldIndex = 0; oldIndex < oldCapacity; ++oldIndex) { + MapMakerInternalMap.ReferenceEntry head = (MapMakerInternalMap.ReferenceEntry)oldTable.get(oldIndex); + if (head != null) { + MapMakerInternalMap.ReferenceEntry next = head.getNext(); + int headIndex = head.getHash() & newMask; + if (next == null) { + newTable.set(headIndex, head); + } else { + MapMakerInternalMap.ReferenceEntry tail = head; + int tailIndex = headIndex; + + MapMakerInternalMap.ReferenceEntry e; + int newIndex; + for(e = next; e != null; e = e.getNext()) { + newIndex = e.getHash() & newMask; + if (newIndex != tailIndex) { + tailIndex = newIndex; + tail = e; + } + } + + newTable.set(tailIndex, tail); + + for(e = head; e != tail; e = e.getNext()) { + newIndex = e.getHash() & newMask; + MapMakerInternalMap.ReferenceEntry newNext = (MapMakerInternalMap.ReferenceEntry)newTable.get(newIndex); + MapMakerInternalMap.ReferenceEntry newFirst = this.copyEntry(e, newNext); + if (newFirst != null) { + newTable.set(newIndex, newFirst); + } else { + this.removeCollectedEntry(e); + --newCount; + } + } + } + } + } + + this.table = newTable; + this.count = newCount; + } + } + + boolean replace(K key, int hash, V oldValue, V newValue) { + this.lock(); + + try { + this.preWriteCleanup(); + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + for(MapMakerInternalMap.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + boolean var12; + if (entryValue == null) { + if (this.isCollected(valueReference)) { + int newCount = this.count - 1; + ++this.modCount; + this.enqueueNotification(entryKey, hash, entryValue, MapMaker.RemovalCause.COLLECTED); + MapMakerInternalMap.ReferenceEntry newFirst = this.removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + } + + var12 = false; + return var12; + } + + if (this.map.valueEquivalence.equivalent(oldValue, entryValue)) { + ++this.modCount; + this.enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.REPLACED); + this.setValue(e, newValue); + var12 = true; + return var12; + } + + this.recordLockedRead(e); + var12 = false; + return var12; + } + } + + boolean var17 = false; + return var17; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + V replace(K key, int hash, V newValue) { + this.lock(); + + try { + this.preWriteCleanup(); + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + MapMakerInternalMap.ReferenceEntry e; + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + Object var11; + if (entryValue == null) { + if (this.isCollected(valueReference)) { + int newCount = this.count - 1; + ++this.modCount; + this.enqueueNotification(entryKey, hash, entryValue, MapMaker.RemovalCause.COLLECTED); + MapMakerInternalMap.ReferenceEntry newFirst = this.removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + } + + var11 = null; + return var11; + } + + ++this.modCount; + this.enqueueNotification(key, hash, entryValue, MapMaker.RemovalCause.REPLACED); + this.setValue(e, newValue); + var11 = entryValue; + return var11; + } + } + + e = null; + return e; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + V remove(Object key, int hash) { + this.lock(); + + try { + this.preWriteCleanup(); + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + MapMakerInternalMap.ReferenceEntry e; + for(e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + MapMaker.RemovalCause cause; + MapMakerInternalMap.ReferenceEntry newFirst; + if (entryValue != null) { + cause = MapMaker.RemovalCause.EXPLICIT; + } else { + if (!this.isCollected(valueReference)) { + newFirst = null; + return newFirst; + } + + cause = MapMaker.RemovalCause.COLLECTED; + } + + ++this.modCount; + this.enqueueNotification(entryKey, hash, entryValue, cause); + newFirst = this.removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + Object var13 = entryValue; + return var13; + } + } + + e = null; + return e; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + boolean remove(Object key, int hash, Object value) { + this.lock(); + + try { + this.preWriteCleanup(); + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + for(MapMakerInternalMap.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference valueReference = e.getValueReference(); + V entryValue = valueReference.get(); + MapMaker.RemovalCause cause; + if (this.map.valueEquivalence.equivalent(value, entryValue)) { + cause = MapMaker.RemovalCause.EXPLICIT; + } else { + if (!this.isCollected(valueReference)) { + boolean var19 = false; + return var19; + } + + cause = MapMaker.RemovalCause.COLLECTED; + } + + ++this.modCount; + this.enqueueNotification(entryKey, hash, entryValue, cause); + MapMakerInternalMap.ReferenceEntry newFirst = this.removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + boolean var14 = cause == MapMaker.RemovalCause.EXPLICIT; + return var14; + } + } + + boolean var18 = false; + return var18; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + void clear() { + if (this.count != 0) { + this.lock(); + + try { + AtomicReferenceArray> table = this.table; + int i; + if (this.map.removalNotificationQueue != MapMakerInternalMap.DISCARDING_QUEUE) { + for(i = 0; i < table.length(); ++i) { + for(MapMakerInternalMap.ReferenceEntry e = (MapMakerInternalMap.ReferenceEntry)table.get(i); e != null; e = e.getNext()) { + if (!e.getValueReference().isComputingReference()) { + this.enqueueNotification(e, MapMaker.RemovalCause.EXPLICIT); + } + } + } + } + + for(i = 0; i < table.length(); ++i) { + table.set(i, (Object)null); + } + + this.clearReferenceQueues(); + this.evictionQueue.clear(); + this.expirationQueue.clear(); + this.readCount.set(0); + ++this.modCount; + this.count = 0; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + } + + @GuardedBy("Segment.this") + MapMakerInternalMap.ReferenceEntry removeFromChain(MapMakerInternalMap.ReferenceEntry first, MapMakerInternalMap.ReferenceEntry entry) { + this.evictionQueue.remove(entry); + this.expirationQueue.remove(entry); + int newCount = this.count; + MapMakerInternalMap.ReferenceEntry newFirst = entry.getNext(); + + for(MapMakerInternalMap.ReferenceEntry e = first; e != entry; e = e.getNext()) { + MapMakerInternalMap.ReferenceEntry next = this.copyEntry(e, newFirst); + if (next != null) { + newFirst = next; + } else { + this.removeCollectedEntry(e); + --newCount; + } + } + + this.count = newCount; + return newFirst; + } + + void removeCollectedEntry(MapMakerInternalMap.ReferenceEntry entry) { + this.enqueueNotification(entry, MapMaker.RemovalCause.COLLECTED); + this.evictionQueue.remove(entry); + this.expirationQueue.remove(entry); + } + + boolean reclaimKey(MapMakerInternalMap.ReferenceEntry entry, int hash) { + this.lock(); + + try { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + for(MapMakerInternalMap.ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++this.modCount; + this.enqueueNotification(e.getKey(), hash, e.getValueReference().get(), MapMaker.RemovalCause.COLLECTED); + MapMakerInternalMap.ReferenceEntry newFirst = this.removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + boolean var9 = true; + return var9; + } + } + + boolean var13 = false; + return var13; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + boolean reclaimValue(K key, int hash, MapMakerInternalMap.ValueReference valueReference) { + this.lock(); + + try { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + for(MapMakerInternalMap.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference v = e.getValueReference(); + if (v == valueReference) { + ++this.modCount; + this.enqueueNotification(key, hash, valueReference.get(), MapMaker.RemovalCause.COLLECTED); + MapMakerInternalMap.ReferenceEntry newFirst = this.removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + boolean var12 = true; + return var12; + } + + boolean var11 = false; + return var11; + } + } + + boolean var16 = false; + return var16; + } finally { + this.unlock(); + if (!this.isHeldByCurrentThread()) { + this.postWriteCleanup(); + } + + } + } + + boolean clearValue(K key, int hash, MapMakerInternalMap.ValueReference valueReference) { + this.lock(); + + try { + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + for(MapMakerInternalMap.ReferenceEntry e = first; e != null; e = e.getNext()) { + K entryKey = e.getKey(); + if (e.getHash() == hash && entryKey != null && this.map.keyEquivalence.equivalent(key, entryKey)) { + MapMakerInternalMap.ValueReference v = e.getValueReference(); + if (v != valueReference) { + boolean var15 = false; + return var15; + } + + MapMakerInternalMap.ReferenceEntry newFirst = this.removeFromChain(first, e); + table.set(index, newFirst); + boolean var11 = true; + return var11; + } + } + + boolean var16 = false; + return var16; + } finally { + this.unlock(); + this.postWriteCleanup(); + } + } + + @GuardedBy("Segment.this") + boolean removeEntry(MapMakerInternalMap.ReferenceEntry entry, int hash, MapMaker.RemovalCause cause) { + int newCount = this.count - 1; + AtomicReferenceArray> table = this.table; + int index = hash & table.length() - 1; + MapMakerInternalMap.ReferenceEntry first = (MapMakerInternalMap.ReferenceEntry)table.get(index); + + for(MapMakerInternalMap.ReferenceEntry e = first; e != null; e = e.getNext()) { + if (e == entry) { + ++this.modCount; + this.enqueueNotification(e.getKey(), hash, e.getValueReference().get(), cause); + MapMakerInternalMap.ReferenceEntry newFirst = this.removeFromChain(first, e); + newCount = this.count - 1; + table.set(index, newFirst); + this.count = newCount; + return true; + } + } + + return false; + } + + boolean isCollected(MapMakerInternalMap.ValueReference valueReference) { + if (valueReference.isComputingReference()) { + return false; + } else { + return valueReference.get() == null; + } + } + + V getLiveValue(MapMakerInternalMap.ReferenceEntry entry) { + if (entry.getKey() == null) { + this.tryDrainReferenceQueues(); + return null; + } else { + V value = entry.getValueReference().get(); + if (value == null) { + this.tryDrainReferenceQueues(); + return null; + } else if (this.map.expires() && this.map.isExpired(entry)) { + this.tryExpireEntries(); + return null; + } else { + return value; + } + } + } + + void postReadCleanup() { + if ((this.readCount.incrementAndGet() & 63) == 0) { + this.runCleanup(); + } + + } + + @GuardedBy("Segment.this") + void preWriteCleanup() { + this.runLockedCleanup(); + } + + void postWriteCleanup() { + this.runUnlockedCleanup(); + } + + void runCleanup() { + this.runLockedCleanup(); + this.runUnlockedCleanup(); + } + + void runLockedCleanup() { + if (this.tryLock()) { + try { + this.drainReferenceQueues(); + this.expireEntries(); + this.readCount.set(0); + } finally { + this.unlock(); + } + } + + } + + void runUnlockedCleanup() { + if (!this.isHeldByCurrentThread()) { + this.map.processPendingNotifications(); + } + + } + } + + static final class StrongValueReference implements MapMakerInternalMap.ValueReference { + final V referent; + + StrongValueReference(V referent) { + this.referent = referent; + } + + public V get() { + return this.referent; + } + + public MapMakerInternalMap.ReferenceEntry getEntry() { + return null; + } + + public MapMakerInternalMap.ValueReference copyFor(ReferenceQueue queue, V value, MapMakerInternalMap.ReferenceEntry entry) { + return this; + } + + public boolean isComputingReference() { + return false; + } + + public V waitForValue() { + return this.get(); + } + + public void clear(MapMakerInternalMap.ValueReference newValue) { + } + } + + static final class SoftValueReference extends SoftReference implements MapMakerInternalMap.ValueReference { + final MapMakerInternalMap.ReferenceEntry entry; + + SoftValueReference(ReferenceQueue queue, V referent, MapMakerInternalMap.ReferenceEntry entry) { + super(referent, queue); + this.entry = entry; + } + + public MapMakerInternalMap.ReferenceEntry getEntry() { + return this.entry; + } + + public void clear(MapMakerInternalMap.ValueReference newValue) { + this.clear(); + } + + public MapMakerInternalMap.ValueReference copyFor(ReferenceQueue queue, V value, MapMakerInternalMap.ReferenceEntry entry) { + return new MapMakerInternalMap.SoftValueReference(queue, value, entry); + } + + public boolean isComputingReference() { + return false; + } + + public V waitForValue() { + return this.get(); + } + } + + static final class WeakValueReference extends WeakReference implements MapMakerInternalMap.ValueReference { + final MapMakerInternalMap.ReferenceEntry entry; + + WeakValueReference(ReferenceQueue queue, V referent, MapMakerInternalMap.ReferenceEntry entry) { + super(referent, queue); + this.entry = entry; + } + + public MapMakerInternalMap.ReferenceEntry getEntry() { + return this.entry; + } + + public void clear(MapMakerInternalMap.ValueReference newValue) { + this.clear(); + } + + public MapMakerInternalMap.ValueReference copyFor(ReferenceQueue queue, V value, MapMakerInternalMap.ReferenceEntry entry) { + return new MapMakerInternalMap.WeakValueReference(queue, value, entry); + } + + public boolean isComputingReference() { + return false; + } + + public V waitForValue() { + return this.get(); + } + } + + static final class WeakExpirableEvictableEntry extends MapMakerInternalMap.WeakEntry implements MapMakerInternalMap.ReferenceEntry { + volatile long time = Long.MAX_VALUE; + MapMakerInternalMap.ReferenceEntry nextExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry nextEvictable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousEvictable = MapMakerInternalMap.nullEntry(); + + WeakExpirableEvictableEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public long getExpirationTime() { + return this.time; + } + + public void setExpirationTime(long time) { + this.time = time; + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this.nextExpirable; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + this.nextExpirable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this.previousExpirable; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousExpirable = previous; + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this.nextEvictable; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + this.nextEvictable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this.previousEvictable; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class WeakEvictableEntry extends MapMakerInternalMap.WeakEntry implements MapMakerInternalMap.ReferenceEntry { + MapMakerInternalMap.ReferenceEntry nextEvictable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousEvictable = MapMakerInternalMap.nullEntry(); + + WeakEvictableEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this.nextEvictable; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + this.nextEvictable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this.previousEvictable; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class WeakExpirableEntry extends MapMakerInternalMap.WeakEntry implements MapMakerInternalMap.ReferenceEntry { + volatile long time = Long.MAX_VALUE; + MapMakerInternalMap.ReferenceEntry nextExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousExpirable = MapMakerInternalMap.nullEntry(); + + WeakExpirableEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public long getExpirationTime() { + return this.time; + } + + public void setExpirationTime(long time) { + this.time = time; + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this.nextExpirable; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + this.nextExpirable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this.previousExpirable; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousExpirable = previous; + } + } + + static class WeakEntry extends WeakReference implements MapMakerInternalMap.ReferenceEntry { + final int hash; + final MapMakerInternalMap.ReferenceEntry next; + volatile MapMakerInternalMap.ValueReference valueReference = MapMakerInternalMap.unset(); + + WeakEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(key, queue); + this.hash = hash; + this.next = next; + } + + public K getKey() { + return this.get(); + } + + public long getExpirationTime() { + throw new UnsupportedOperationException(); + } + + public void setExpirationTime(long time) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + throw new UnsupportedOperationException(); + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + throw new UnsupportedOperationException(); + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ValueReference getValueReference() { + return this.valueReference; + } + + public void setValueReference(MapMakerInternalMap.ValueReference valueReference) { + MapMakerInternalMap.ValueReference previous = this.valueReference; + this.valueReference = valueReference; + previous.clear(valueReference); + } + + public int getHash() { + return this.hash; + } + + public MapMakerInternalMap.ReferenceEntry getNext() { + return this.next; + } + } + + static final class SoftExpirableEvictableEntry extends MapMakerInternalMap.SoftEntry implements MapMakerInternalMap.ReferenceEntry { + volatile long time = Long.MAX_VALUE; + MapMakerInternalMap.ReferenceEntry nextExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry nextEvictable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousEvictable = MapMakerInternalMap.nullEntry(); + + SoftExpirableEvictableEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public long getExpirationTime() { + return this.time; + } + + public void setExpirationTime(long time) { + this.time = time; + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this.nextExpirable; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + this.nextExpirable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this.previousExpirable; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousExpirable = previous; + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this.nextEvictable; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + this.nextEvictable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this.previousEvictable; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class SoftEvictableEntry extends MapMakerInternalMap.SoftEntry implements MapMakerInternalMap.ReferenceEntry { + MapMakerInternalMap.ReferenceEntry nextEvictable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousEvictable = MapMakerInternalMap.nullEntry(); + + SoftEvictableEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this.nextEvictable; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + this.nextEvictable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this.previousEvictable; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class SoftExpirableEntry extends MapMakerInternalMap.SoftEntry implements MapMakerInternalMap.ReferenceEntry { + volatile long time = Long.MAX_VALUE; + MapMakerInternalMap.ReferenceEntry nextExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousExpirable = MapMakerInternalMap.nullEntry(); + + SoftExpirableEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(queue, key, hash, next); + } + + public long getExpirationTime() { + return this.time; + } + + public void setExpirationTime(long time) { + this.time = time; + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this.nextExpirable; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + this.nextExpirable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this.previousExpirable; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousExpirable = previous; + } + } + + static class SoftEntry extends SoftReference implements MapMakerInternalMap.ReferenceEntry { + final int hash; + final MapMakerInternalMap.ReferenceEntry next; + volatile MapMakerInternalMap.ValueReference valueReference = MapMakerInternalMap.unset(); + + SoftEntry(ReferenceQueue queue, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(key, queue); + this.hash = hash; + this.next = next; + } + + public K getKey() { + return this.get(); + } + + public long getExpirationTime() { + throw new UnsupportedOperationException(); + } + + public void setExpirationTime(long time) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + throw new UnsupportedOperationException(); + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + throw new UnsupportedOperationException(); + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ValueReference getValueReference() { + return this.valueReference; + } + + public void setValueReference(MapMakerInternalMap.ValueReference valueReference) { + MapMakerInternalMap.ValueReference previous = this.valueReference; + this.valueReference = valueReference; + previous.clear(valueReference); + } + + public int getHash() { + return this.hash; + } + + public MapMakerInternalMap.ReferenceEntry getNext() { + return this.next; + } + } + + static final class StrongExpirableEvictableEntry extends MapMakerInternalMap.StrongEntry implements MapMakerInternalMap.ReferenceEntry { + volatile long time = Long.MAX_VALUE; + MapMakerInternalMap.ReferenceEntry nextExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry nextEvictable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousEvictable = MapMakerInternalMap.nullEntry(); + + StrongExpirableEvictableEntry(K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(key, hash, next); + } + + public long getExpirationTime() { + return this.time; + } + + public void setExpirationTime(long time) { + this.time = time; + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this.nextExpirable; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + this.nextExpirable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this.previousExpirable; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousExpirable = previous; + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this.nextEvictable; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + this.nextEvictable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this.previousEvictable; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class StrongEvictableEntry extends MapMakerInternalMap.StrongEntry implements MapMakerInternalMap.ReferenceEntry { + MapMakerInternalMap.ReferenceEntry nextEvictable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousEvictable = MapMakerInternalMap.nullEntry(); + + StrongEvictableEntry(K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(key, hash, next); + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this.nextEvictable; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + this.nextEvictable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this.previousEvictable; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousEvictable = previous; + } + } + + static final class StrongExpirableEntry extends MapMakerInternalMap.StrongEntry implements MapMakerInternalMap.ReferenceEntry { + volatile long time = Long.MAX_VALUE; + MapMakerInternalMap.ReferenceEntry nextExpirable = MapMakerInternalMap.nullEntry(); + MapMakerInternalMap.ReferenceEntry previousExpirable = MapMakerInternalMap.nullEntry(); + + StrongExpirableEntry(K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + super(key, hash, next); + } + + public long getExpirationTime() { + return this.time; + } + + public void setExpirationTime(long time) { + this.time = time; + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this.nextExpirable; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + this.nextExpirable = next; + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this.previousExpirable; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + this.previousExpirable = previous; + } + } + + static class StrongEntry implements MapMakerInternalMap.ReferenceEntry { + final K key; + final int hash; + final MapMakerInternalMap.ReferenceEntry next; + volatile MapMakerInternalMap.ValueReference valueReference = MapMakerInternalMap.unset(); + + StrongEntry(K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + this.key = key; + this.hash = hash; + this.next = next; + } + + public K getKey() { + return this.key; + } + + public long getExpirationTime() { + throw new UnsupportedOperationException(); + } + + public void setExpirationTime(long time) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + throw new UnsupportedOperationException(); + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + throw new UnsupportedOperationException(); + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ValueReference getValueReference() { + return this.valueReference; + } + + public void setValueReference(MapMakerInternalMap.ValueReference valueReference) { + MapMakerInternalMap.ValueReference previous = this.valueReference; + this.valueReference = valueReference; + previous.clear(valueReference); + } + + public int getHash() { + return this.hash; + } + + public MapMakerInternalMap.ReferenceEntry getNext() { + return this.next; + } + } + + abstract static class AbstractReferenceEntry implements MapMakerInternalMap.ReferenceEntry { + public MapMakerInternalMap.ValueReference getValueReference() { + throw new UnsupportedOperationException(); + } + + public void setValueReference(MapMakerInternalMap.ValueReference valueReference) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNext() { + throw new UnsupportedOperationException(); + } + + public int getHash() { + throw new UnsupportedOperationException(); + } + + public K getKey() { + throw new UnsupportedOperationException(); + } + + public long getExpirationTime() { + throw new UnsupportedOperationException(); + } + + public void setExpirationTime(long time) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + throw new UnsupportedOperationException(); + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + throw new UnsupportedOperationException(); + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + throw new UnsupportedOperationException(); + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + throw new UnsupportedOperationException(); + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + throw new UnsupportedOperationException(); + } + } + + private static enum NullEntry implements MapMakerInternalMap.ReferenceEntry { + INSTANCE; + + public MapMakerInternalMap.ValueReference getValueReference() { + return null; + } + + public void setValueReference(MapMakerInternalMap.ValueReference valueReference) { + } + + public MapMakerInternalMap.ReferenceEntry getNext() { + return null; + } + + public int getHash() { + return 0; + } + + public Object getKey() { + return null; + } + + public long getExpirationTime() { + return 0L; + } + + public void setExpirationTime(long time) { + } + + public MapMakerInternalMap.ReferenceEntry getNextExpirable() { + return this; + } + + public void setNextExpirable(MapMakerInternalMap.ReferenceEntry next) { + } + + public MapMakerInternalMap.ReferenceEntry getPreviousExpirable() { + return this; + } + + public void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry previous) { + } + + public MapMakerInternalMap.ReferenceEntry getNextEvictable() { + return this; + } + + public void setNextEvictable(MapMakerInternalMap.ReferenceEntry next) { + } + + public MapMakerInternalMap.ReferenceEntry getPreviousEvictable() { + return this; + } + + public void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry previous) { + } + } + + interface ReferenceEntry { + MapMakerInternalMap.ValueReference getValueReference(); + + void setValueReference(MapMakerInternalMap.ValueReference var1); + + MapMakerInternalMap.ReferenceEntry getNext(); + + int getHash(); + + K getKey(); + + long getExpirationTime(); + + void setExpirationTime(long var1); + + MapMakerInternalMap.ReferenceEntry getNextExpirable(); + + void setNextExpirable(MapMakerInternalMap.ReferenceEntry var1); + + MapMakerInternalMap.ReferenceEntry getPreviousExpirable(); + + void setPreviousExpirable(MapMakerInternalMap.ReferenceEntry var1); + + MapMakerInternalMap.ReferenceEntry getNextEvictable(); + + void setNextEvictable(MapMakerInternalMap.ReferenceEntry var1); + + MapMakerInternalMap.ReferenceEntry getPreviousEvictable(); + + void setPreviousEvictable(MapMakerInternalMap.ReferenceEntry var1); + } + + interface ValueReference { + V get(); + + V waitForValue() throws ExecutionException; + + MapMakerInternalMap.ReferenceEntry getEntry(); + + MapMakerInternalMap.ValueReference copyFor(ReferenceQueue var1, @Nullable V var2, MapMakerInternalMap.ReferenceEntry var3); + + void clear(@Nullable MapMakerInternalMap.ValueReference var1); + + boolean isComputingReference(); + } + + static enum EntryFactory { + STRONG { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.StrongEntry(key, hash, next); + } + }, + STRONG_EXPIRABLE { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.StrongExpirableEntry(key, hash, next); + } + + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + MapMakerInternalMap.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyExpirableEntry(original, newEntry); + return newEntry; + } + }, + STRONG_EVICTABLE { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.StrongEvictableEntry(key, hash, next); + } + + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + MapMakerInternalMap.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + STRONG_EXPIRABLE_EVICTABLE { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.StrongExpirableEvictableEntry(key, hash, next); + } + + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + MapMakerInternalMap.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyExpirableEntry(original, newEntry); + this.copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + WEAK { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.WeakEntry(segment.keyReferenceQueue, key, hash, next); + } + }, + WEAK_EXPIRABLE { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.WeakExpirableEntry(segment.keyReferenceQueue, key, hash, next); + } + + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + MapMakerInternalMap.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyExpirableEntry(original, newEntry); + return newEntry; + } + }, + WEAK_EVICTABLE { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.WeakEvictableEntry(segment.keyReferenceQueue, key, hash, next); + } + + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + MapMakerInternalMap.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyEvictableEntry(original, newEntry); + return newEntry; + } + }, + WEAK_EXPIRABLE_EVICTABLE { + MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment segment, K key, int hash, @Nullable MapMakerInternalMap.ReferenceEntry next) { + return new MapMakerInternalMap.WeakExpirableEvictableEntry(segment.keyReferenceQueue, key, hash, next); + } + + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + MapMakerInternalMap.ReferenceEntry newEntry = super.copyEntry(segment, original, newNext); + this.copyExpirableEntry(original, newEntry); + this.copyEvictableEntry(original, newEntry); + return newEntry; + } + }; + + static final int EXPIRABLE_MASK = 1; + static final int EVICTABLE_MASK = 2; + static final MapMakerInternalMap.EntryFactory[][] factories = new MapMakerInternalMap.EntryFactory[][]{{STRONG, STRONG_EXPIRABLE, STRONG_EVICTABLE, STRONG_EXPIRABLE_EVICTABLE}, new MapMakerInternalMap.EntryFactory[0], {WEAK, WEAK_EXPIRABLE, WEAK_EVICTABLE, WEAK_EXPIRABLE_EVICTABLE}}; + + private EntryFactory() { + } + + static MapMakerInternalMap.EntryFactory getFactory(MapMakerInternalMap.Strength keyStrength, boolean expireAfterWrite, boolean evictsBySize) { + int flags = (expireAfterWrite ? 1 : 0) | (evictsBySize ? 2 : 0); + return factories[keyStrength.ordinal()][flags]; + } + + abstract MapMakerInternalMap.ReferenceEntry newEntry(MapMakerInternalMap.Segment var1, K var2, int var3, @Nullable MapMakerInternalMap.ReferenceEntry var4); + + MapMakerInternalMap.ReferenceEntry copyEntry(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newNext) { + return this.newEntry(segment, original.getKey(), original.getHash(), newNext); + } + + void copyExpirableEntry(MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newEntry) { + newEntry.setExpirationTime(original.getExpirationTime()); + MapMakerInternalMap.connectExpirables(original.getPreviousExpirable(), newEntry); + MapMakerInternalMap.connectExpirables(newEntry, original.getNextExpirable()); + MapMakerInternalMap.nullifyExpirable(original); + } + + void copyEvictableEntry(MapMakerInternalMap.ReferenceEntry original, MapMakerInternalMap.ReferenceEntry newEntry) { + MapMakerInternalMap.connectEvictables(original.getPreviousEvictable(), newEntry); + MapMakerInternalMap.connectEvictables(newEntry, original.getNextEvictable()); + MapMakerInternalMap.nullifyEvictable(original); + } + + // $FF: synthetic method + EntryFactory(Object x2) { + this(); + } + } + + static enum Strength { + STRONG { + MapMakerInternalMap.ValueReference referenceValue(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry entry, V value) { + return new MapMakerInternalMap.StrongValueReference(value); + } + + Equivalence defaultEquivalence() { + return Equivalence.equals(); + } + }, + SOFT { + MapMakerInternalMap.ValueReference referenceValue(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry entry, V value) { + return new MapMakerInternalMap.SoftValueReference(segment.valueReferenceQueue, value, entry); + } + + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }, + WEAK { + MapMakerInternalMap.ValueReference referenceValue(MapMakerInternalMap.Segment segment, MapMakerInternalMap.ReferenceEntry entry, V value) { + return new MapMakerInternalMap.WeakValueReference(segment.valueReferenceQueue, value, entry); + } + + Equivalence defaultEquivalence() { + return Equivalence.identity(); + } + }; + + private Strength() { + } + + abstract MapMakerInternalMap.ValueReference referenceValue(MapMakerInternalMap.Segment var1, MapMakerInternalMap.ReferenceEntry var2, V var3); + + abstract Equivalence defaultEquivalence(); + + // $FF: synthetic method + Strength(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/Maps.java b/src/main/com/google/common/collect/Maps.java new file mode 100644 index 0000000..4abf6d2 --- /dev/null +++ b/src/main/com/google/common/collect/Maps.java @@ -0,0 +1,2323 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Converter; +import com.google.common.base.Equivalence; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +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 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.EnumMap; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +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.Map.Entry; +import java.util.concurrent.ConcurrentMap; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Maps { + static final Joiner.MapJoiner STANDARD_JOINER; + + private Maps() { + } + + static Function, K> keyFunction() { + return Maps.EntryFunction.KEY; + } + + static Function, V> valueFunction() { + return Maps.EntryFunction.VALUE; + } + + static Iterator keyIterator(Iterator> entryIterator) { + return Iterators.transform(entryIterator, keyFunction()); + } + + static Iterator valueIterator(Iterator> entryIterator) { + return Iterators.transform(entryIterator, valueFunction()); + } + + static UnmodifiableIterator valueIterator(final UnmodifiableIterator> entryIterator) { + return new UnmodifiableIterator() { + public boolean hasNext() { + return entryIterator.hasNext(); + } + + public V next() { + return ((Entry)entryIterator.next()).getValue(); + } + }; + } + + @GwtCompatible( + serializable = true + ) + @Beta + public static , V> ImmutableMap immutableEnumMap(Map map) { + if (map instanceof ImmutableEnumMap) { + ImmutableEnumMap result = (ImmutableEnumMap)map; + return result; + } else if (map.isEmpty()) { + return ImmutableMap.of(); + } else { + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + Preconditions.checkNotNull(entry.getKey()); + Preconditions.checkNotNull(entry.getValue()); + } + + return ImmutableEnumMap.asImmutable(new EnumMap(map)); + } + } + + public static HashMap newHashMap() { + return new HashMap(); + } + + public static HashMap newHashMapWithExpectedSize(int expectedSize) { + return new HashMap(capacity(expectedSize)); + } + + static int capacity(int expectedSize) { + if (expectedSize < 3) { + CollectPreconditions.checkNonnegative(expectedSize, "expectedSize"); + return expectedSize + 1; + } else { + return expectedSize < 1073741824 ? expectedSize + expectedSize / 3 : Integer.MAX_VALUE; + } + } + + public static HashMap newHashMap(Map map) { + return new HashMap(map); + } + + public static LinkedHashMap newLinkedHashMap() { + return new LinkedHashMap(); + } + + public static LinkedHashMap newLinkedHashMap(Map map) { + return new LinkedHashMap(map); + } + + public static ConcurrentMap newConcurrentMap() { + return (new MapMaker()).makeMap(); + } + + public static TreeMap newTreeMap() { + return new TreeMap(); + } + + public static TreeMap newTreeMap(SortedMap map) { + return new TreeMap(map); + } + + public static TreeMap newTreeMap(@Nullable Comparator comparator) { + return new TreeMap(comparator); + } + + public static , V> EnumMap newEnumMap(Class type) { + return new EnumMap((Class)Preconditions.checkNotNull(type)); + } + + public static , V> EnumMap newEnumMap(Map map) { + return new EnumMap(map); + } + + public static IdentityHashMap newIdentityHashMap() { + return new IdentityHashMap(); + } + + public static MapDifference difference(Map left, Map right) { + if (left instanceof SortedMap) { + SortedMap sortedLeft = (SortedMap)left; + SortedMapDifference result = difference(sortedLeft, right); + return result; + } else { + return difference(left, right, Equivalence.equals()); + } + } + + @Beta + public static MapDifference difference(Map left, Map right, Equivalence valueEquivalence) { + Preconditions.checkNotNull(valueEquivalence); + Map onlyOnLeft = newHashMap(); + Map onlyOnRight = new HashMap(right); + Map onBoth = newHashMap(); + Map> differences = newHashMap(); + doDifference(left, right, valueEquivalence, onlyOnLeft, onlyOnRight, onBoth, differences); + return new Maps.MapDifferenceImpl(onlyOnLeft, onlyOnRight, onBoth, differences); + } + + private static void doDifference(Map left, Map right, Equivalence valueEquivalence, Map onlyOnLeft, Map onlyOnRight, Map onBoth, Map> differences) { + Iterator i$ = left.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + K leftKey = entry.getKey(); + V leftValue = entry.getValue(); + if (right.containsKey(leftKey)) { + V rightValue = onlyOnRight.remove(leftKey); + if (valueEquivalence.equivalent(leftValue, rightValue)) { + onBoth.put(leftKey, leftValue); + } else { + differences.put(leftKey, Maps.ValueDifferenceImpl.create(leftValue, rightValue)); + } + } else { + onlyOnLeft.put(leftKey, leftValue); + } + } + + } + + private static Map unmodifiableMap(Map map) { + return (Map)(map instanceof SortedMap ? Collections.unmodifiableSortedMap((SortedMap)map) : Collections.unmodifiableMap(map)); + } + + public static SortedMapDifference difference(SortedMap left, Map right) { + Preconditions.checkNotNull(left); + Preconditions.checkNotNull(right); + Comparator comparator = orNaturalOrder(left.comparator()); + SortedMap onlyOnLeft = newTreeMap(comparator); + SortedMap onlyOnRight = newTreeMap(comparator); + onlyOnRight.putAll(right); + SortedMap onBoth = newTreeMap(comparator); + SortedMap> differences = newTreeMap(comparator); + doDifference(left, right, Equivalence.equals(), onlyOnLeft, onlyOnRight, onBoth, differences); + return new Maps.SortedMapDifferenceImpl(onlyOnLeft, onlyOnRight, onBoth, differences); + } + + static Comparator orNaturalOrder(@Nullable Comparator comparator) { + return (Comparator)(comparator != null ? comparator : Ordering.natural()); + } + + @Beta + public static Map asMap(Set set, Function function) { + return (Map)(set instanceof SortedSet ? asMap((SortedSet)set, function) : new Maps.AsMapView(set, function)); + } + + @Beta + public static SortedMap asMap(SortedSet set, Function function) { + return Platform.mapsAsMapSortedSet(set, function); + } + + static SortedMap asMapSortedIgnoreNavigable(SortedSet set, Function function) { + return new Maps.SortedAsMapView(set, function); + } + + @Beta + @GwtIncompatible("NavigableMap") + public static NavigableMap asMap(NavigableSet set, Function function) { + return new Maps.NavigableAsMapView(set, function); + } + + static Iterator> asMapEntryIterator(Set set, final Function function) { + return new TransformedIterator>(set.iterator()) { + Entry transform(K key) { + return Maps.immutableEntry(key, function.apply(key)); + } + }; + } + + private static Set removeOnlySet(final Set set) { + return new ForwardingSet() { + protected Set delegate() { + return set; + } + + public boolean add(E element) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + }; + } + + private static SortedSet removeOnlySortedSet(final SortedSet set) { + return new ForwardingSortedSet() { + protected SortedSet delegate() { + return set; + } + + public boolean add(E element) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + + public SortedSet headSet(E toElement) { + return Maps.removeOnlySortedSet(super.headSet(toElement)); + } + + public SortedSet subSet(E fromElement, E toElement) { + return Maps.removeOnlySortedSet(super.subSet(fromElement, toElement)); + } + + public SortedSet tailSet(E fromElement) { + return Maps.removeOnlySortedSet(super.tailSet(fromElement)); + } + }; + } + + @GwtIncompatible("NavigableSet") + private static NavigableSet removeOnlyNavigableSet(final NavigableSet set) { + return new ForwardingNavigableSet() { + protected NavigableSet delegate() { + return set; + } + + public boolean add(E element) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection es) { + throw new UnsupportedOperationException(); + } + + public SortedSet headSet(E toElement) { + return Maps.removeOnlySortedSet(super.headSet(toElement)); + } + + public SortedSet subSet(E fromElement, E toElement) { + return Maps.removeOnlySortedSet(super.subSet(fromElement, toElement)); + } + + public SortedSet tailSet(E fromElement) { + return Maps.removeOnlySortedSet(super.tailSet(fromElement)); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return Maps.removeOnlyNavigableSet(super.headSet(toElement, inclusive)); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return Maps.removeOnlyNavigableSet(super.tailSet(fromElement, inclusive)); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return Maps.removeOnlyNavigableSet(super.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + public NavigableSet descendingSet() { + return Maps.removeOnlyNavigableSet(super.descendingSet()); + } + }; + } + + @Beta + public static ImmutableMap toMap(Iterable keys, Function valueFunction) { + return toMap(keys.iterator(), valueFunction); + } + + @Beta + public static ImmutableMap toMap(Iterator keys, Function valueFunction) { + Preconditions.checkNotNull(valueFunction); + LinkedHashMap builder = newLinkedHashMap(); + + while(keys.hasNext()) { + K key = keys.next(); + builder.put(key, valueFunction.apply(key)); + } + + return ImmutableMap.copyOf(builder); + } + + public static ImmutableMap uniqueIndex(Iterable values, Function keyFunction) { + return uniqueIndex(values.iterator(), keyFunction); + } + + public static ImmutableMap uniqueIndex(Iterator values, Function keyFunction) { + Preconditions.checkNotNull(keyFunction); + ImmutableMap.Builder builder = ImmutableMap.builder(); + + while(values.hasNext()) { + V value = values.next(); + builder.put(keyFunction.apply(value), value); + } + + return builder.build(); + } + + @GwtIncompatible("java.util.Properties") + public static ImmutableMap fromProperties(Properties properties) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + Enumeration e = properties.propertyNames(); + + while(e.hasMoreElements()) { + String key = (String)e.nextElement(); + builder.put(key, properties.getProperty(key)); + } + + return builder.build(); + } + + @GwtCompatible( + serializable = true + ) + public static Entry immutableEntry(@Nullable K key, @Nullable V value) { + return new ImmutableEntry(key, value); + } + + static Set> unmodifiableEntrySet(Set> entrySet) { + return new Maps.UnmodifiableEntrySet(Collections.unmodifiableSet(entrySet)); + } + + static Entry unmodifiableEntry(final Entry entry) { + Preconditions.checkNotNull(entry); + return new AbstractMapEntry() { + public K getKey() { + return entry.getKey(); + } + + public V getValue() { + return entry.getValue(); + } + }; + } + + @Beta + public static Converter asConverter(BiMap bimap) { + return new Maps.BiMapConverter(bimap); + } + + public static BiMap synchronizedBiMap(BiMap bimap) { + return Synchronized.biMap(bimap, (Object)null); + } + + public static BiMap unmodifiableBiMap(BiMap bimap) { + return new Maps.UnmodifiableBiMap(bimap, (BiMap)null); + } + + public static Map transformValues(Map fromMap, Function function) { + return transformEntries(fromMap, asEntryTransformer(function)); + } + + public static SortedMap transformValues(SortedMap fromMap, Function function) { + return transformEntries(fromMap, asEntryTransformer(function)); + } + + @GwtIncompatible("NavigableMap") + public static NavigableMap transformValues(NavigableMap fromMap, Function function) { + return transformEntries(fromMap, asEntryTransformer(function)); + } + + public static Map transformEntries(Map fromMap, Maps.EntryTransformer transformer) { + return (Map)(fromMap instanceof SortedMap ? transformEntries((SortedMap)fromMap, transformer) : new Maps.TransformedEntriesMap(fromMap, transformer)); + } + + public static SortedMap transformEntries(SortedMap fromMap, Maps.EntryTransformer transformer) { + return Platform.mapsTransformEntriesSortedMap(fromMap, transformer); + } + + @GwtIncompatible("NavigableMap") + public static NavigableMap transformEntries(NavigableMap fromMap, Maps.EntryTransformer transformer) { + return new Maps.TransformedEntriesNavigableMap(fromMap, transformer); + } + + static SortedMap transformEntriesIgnoreNavigable(SortedMap fromMap, Maps.EntryTransformer transformer) { + return new Maps.TransformedEntriesSortedMap(fromMap, transformer); + } + + static Maps.EntryTransformer asEntryTransformer(final Function function) { + Preconditions.checkNotNull(function); + return new Maps.EntryTransformer() { + public V2 transformEntry(K key, V1 value) { + return function.apply(value); + } + }; + } + + static Function asValueToValueFunction(final Maps.EntryTransformer transformer, final K key) { + Preconditions.checkNotNull(transformer); + return new Function() { + public V2 apply(@Nullable V1 v1) { + return transformer.transformEntry(key, v1); + } + }; + } + + static Function, V2> asEntryToValueFunction(final Maps.EntryTransformer transformer) { + Preconditions.checkNotNull(transformer); + return new Function, V2>() { + public V2 apply(Entry entry) { + return transformer.transformEntry(entry.getKey(), entry.getValue()); + } + }; + } + + static Entry transformEntry(final Maps.EntryTransformer transformer, final Entry entry) { + Preconditions.checkNotNull(transformer); + Preconditions.checkNotNull(entry); + return new AbstractMapEntry() { + public K getKey() { + return entry.getKey(); + } + + public V2 getValue() { + return transformer.transformEntry(entry.getKey(), entry.getValue()); + } + }; + } + + static Function, Entry> asEntryToEntryFunction(final Maps.EntryTransformer transformer) { + Preconditions.checkNotNull(transformer); + return new Function, Entry>() { + public Entry apply(Entry entry) { + return Maps.transformEntry(transformer, entry); + } + }; + } + + static Predicate> keyPredicateOnEntries(Predicate keyPredicate) { + return Predicates.compose(keyPredicate, keyFunction()); + } + + static Predicate> valuePredicateOnEntries(Predicate valuePredicate) { + return Predicates.compose(valuePredicate, valueFunction()); + } + + public static Map filterKeys(Map unfiltered, Predicate keyPredicate) { + if (unfiltered instanceof SortedMap) { + return filterKeys((SortedMap)unfiltered, keyPredicate); + } else if (unfiltered instanceof BiMap) { + return filterKeys((BiMap)unfiltered, keyPredicate); + } else { + Preconditions.checkNotNull(keyPredicate); + Predicate> entryPredicate = keyPredicateOnEntries(keyPredicate); + return (Map)(unfiltered instanceof Maps.AbstractFilteredMap ? filterFiltered((Maps.AbstractFilteredMap)unfiltered, entryPredicate) : new Maps.FilteredKeyMap((Map)Preconditions.checkNotNull(unfiltered), keyPredicate, entryPredicate)); + } + } + + public static SortedMap filterKeys(SortedMap unfiltered, Predicate keyPredicate) { + return filterEntries(unfiltered, keyPredicateOnEntries(keyPredicate)); + } + + @GwtIncompatible("NavigableMap") + public static NavigableMap filterKeys(NavigableMap unfiltered, Predicate keyPredicate) { + return filterEntries(unfiltered, keyPredicateOnEntries(keyPredicate)); + } + + public static BiMap filterKeys(BiMap unfiltered, Predicate keyPredicate) { + Preconditions.checkNotNull(keyPredicate); + return filterEntries(unfiltered, keyPredicateOnEntries(keyPredicate)); + } + + public static Map filterValues(Map unfiltered, Predicate valuePredicate) { + if (unfiltered instanceof SortedMap) { + return filterValues((SortedMap)unfiltered, valuePredicate); + } else { + return (Map)(unfiltered instanceof BiMap ? filterValues((BiMap)unfiltered, valuePredicate) : filterEntries(unfiltered, valuePredicateOnEntries(valuePredicate))); + } + } + + public static SortedMap filterValues(SortedMap unfiltered, Predicate valuePredicate) { + return filterEntries(unfiltered, valuePredicateOnEntries(valuePredicate)); + } + + @GwtIncompatible("NavigableMap") + public static NavigableMap filterValues(NavigableMap unfiltered, Predicate valuePredicate) { + return filterEntries(unfiltered, valuePredicateOnEntries(valuePredicate)); + } + + public static BiMap filterValues(BiMap unfiltered, Predicate valuePredicate) { + return filterEntries(unfiltered, valuePredicateOnEntries(valuePredicate)); + } + + public static Map filterEntries(Map unfiltered, Predicate> entryPredicate) { + if (unfiltered instanceof SortedMap) { + return filterEntries((SortedMap)unfiltered, entryPredicate); + } else if (unfiltered instanceof BiMap) { + return filterEntries((BiMap)unfiltered, entryPredicate); + } else { + Preconditions.checkNotNull(entryPredicate); + return (Map)(unfiltered instanceof Maps.AbstractFilteredMap ? filterFiltered((Maps.AbstractFilteredMap)unfiltered, entryPredicate) : new Maps.FilteredEntryMap((Map)Preconditions.checkNotNull(unfiltered), entryPredicate)); + } + } + + public static SortedMap filterEntries(SortedMap unfiltered, Predicate> entryPredicate) { + return Platform.mapsFilterSortedMap(unfiltered, entryPredicate); + } + + static SortedMap filterSortedIgnoreNavigable(SortedMap unfiltered, Predicate> entryPredicate) { + Preconditions.checkNotNull(entryPredicate); + return (SortedMap)(unfiltered instanceof Maps.FilteredEntrySortedMap ? filterFiltered((Maps.FilteredEntrySortedMap)unfiltered, entryPredicate) : new Maps.FilteredEntrySortedMap((SortedMap)Preconditions.checkNotNull(unfiltered), entryPredicate)); + } + + @GwtIncompatible("NavigableMap") + public static NavigableMap filterEntries(NavigableMap unfiltered, Predicate> entryPredicate) { + Preconditions.checkNotNull(entryPredicate); + return (NavigableMap)(unfiltered instanceof Maps.FilteredEntryNavigableMap ? filterFiltered((Maps.FilteredEntryNavigableMap)unfiltered, entryPredicate) : new Maps.FilteredEntryNavigableMap((NavigableMap)Preconditions.checkNotNull(unfiltered), entryPredicate)); + } + + public static BiMap filterEntries(BiMap unfiltered, Predicate> entryPredicate) { + Preconditions.checkNotNull(unfiltered); + Preconditions.checkNotNull(entryPredicate); + return (BiMap)(unfiltered instanceof Maps.FilteredEntryBiMap ? filterFiltered((Maps.FilteredEntryBiMap)unfiltered, entryPredicate) : new Maps.FilteredEntryBiMap(unfiltered, entryPredicate)); + } + + private static Map filterFiltered(Maps.AbstractFilteredMap map, Predicate> entryPredicate) { + return new Maps.FilteredEntryMap(map.unfiltered, Predicates.and(map.predicate, entryPredicate)); + } + + private static SortedMap filterFiltered(Maps.FilteredEntrySortedMap map, Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(map.predicate, entryPredicate); + return new Maps.FilteredEntrySortedMap(map.sortedMap(), predicate); + } + + @GwtIncompatible("NavigableMap") + private static NavigableMap filterFiltered(Maps.FilteredEntryNavigableMap map, Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(map.entryPredicate, entryPredicate); + return new Maps.FilteredEntryNavigableMap(map.unfiltered, predicate); + } + + private static BiMap filterFiltered(Maps.FilteredEntryBiMap map, Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(map.predicate, entryPredicate); + return new Maps.FilteredEntryBiMap(map.unfiltered(), predicate); + } + + @GwtIncompatible("NavigableMap") + public static NavigableMap unmodifiableNavigableMap(NavigableMap map) { + Preconditions.checkNotNull(map); + return (NavigableMap)(map instanceof Maps.UnmodifiableNavigableMap ? map : new Maps.UnmodifiableNavigableMap(map)); + } + + @Nullable + private static Entry unmodifiableOrNull(@Nullable Entry entry) { + return entry == null ? null : unmodifiableEntry(entry); + } + + @GwtIncompatible("NavigableMap") + public static NavigableMap synchronizedNavigableMap(NavigableMap navigableMap) { + return Synchronized.navigableMap(navigableMap); + } + + static V safeGet(Map map, @Nullable Object key) { + Preconditions.checkNotNull(map); + + try { + return map.get(key); + } catch (ClassCastException var3) { + return null; + } catch (NullPointerException var4) { + return null; + } + } + + static boolean safeContainsKey(Map map, Object key) { + Preconditions.checkNotNull(map); + + try { + return map.containsKey(key); + } catch (ClassCastException var3) { + return false; + } catch (NullPointerException var4) { + return false; + } + } + + static V safeRemove(Map map, Object key) { + Preconditions.checkNotNull(map); + + try { + return map.remove(key); + } catch (ClassCastException var3) { + return null; + } catch (NullPointerException var4) { + return null; + } + } + + static boolean containsKeyImpl(Map map, @Nullable Object key) { + return Iterators.contains(keyIterator(map.entrySet().iterator()), key); + } + + static boolean containsValueImpl(Map map, @Nullable Object value) { + return Iterators.contains(valueIterator(map.entrySet().iterator()), value); + } + + static boolean containsEntryImpl(Collection> c, Object o) { + return !(o instanceof Entry) ? false : c.contains(unmodifiableEntry((Entry)o)); + } + + static boolean removeEntryImpl(Collection> c, Object o) { + return !(o instanceof Entry) ? false : c.remove(unmodifiableEntry((Entry)o)); + } + + static boolean equalsImpl(Map map, Object object) { + if (map == object) { + return true; + } else if (object instanceof Map) { + Map o = (Map)object; + return map.entrySet().equals(o.entrySet()); + } else { + return false; + } + } + + static String toStringImpl(Map map) { + StringBuilder sb = Collections2.newStringBuilderForCollection(map.size()).append('{'); + STANDARD_JOINER.appendTo(sb, map); + return sb.append('}').toString(); + } + + static void putAllImpl(Map self, Map map) { + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + self.put(entry.getKey(), entry.getValue()); + } + + } + + @Nullable + static K keyOrNull(@Nullable Entry entry) { + return entry == null ? null : entry.getKey(); + } + + @Nullable + static V valueOrNull(@Nullable Entry entry) { + return entry == null ? null : entry.getValue(); + } + + static { + STANDARD_JOINER = Collections2.STANDARD_JOINER.withKeyValueSeparator("="); + } + + @GwtIncompatible("NavigableMap") + abstract static class DescendingMap extends ForwardingMap implements NavigableMap { + private transient Comparator comparator; + private transient Set> entrySet; + private transient NavigableSet navigableKeySet; + + abstract NavigableMap forward(); + + protected final Map delegate() { + return this.forward(); + } + + public Comparator comparator() { + Comparator result = this.comparator; + if (result == null) { + Comparator forwardCmp = this.forward().comparator(); + if (forwardCmp == null) { + forwardCmp = Ordering.natural(); + } + + result = this.comparator = reverse((Comparator)forwardCmp); + } + + return result; + } + + private static Ordering reverse(Comparator forward) { + return Ordering.from(forward).reverse(); + } + + public K firstKey() { + return this.forward().lastKey(); + } + + public K lastKey() { + return this.forward().firstKey(); + } + + public Entry lowerEntry(K key) { + return this.forward().higherEntry(key); + } + + public K lowerKey(K key) { + return this.forward().higherKey(key); + } + + public Entry floorEntry(K key) { + return this.forward().ceilingEntry(key); + } + + public K floorKey(K key) { + return this.forward().ceilingKey(key); + } + + public Entry ceilingEntry(K key) { + return this.forward().floorEntry(key); + } + + public K ceilingKey(K key) { + return this.forward().floorKey(key); + } + + public Entry higherEntry(K key) { + return this.forward().lowerEntry(key); + } + + public K higherKey(K key) { + return this.forward().lowerKey(key); + } + + public Entry firstEntry() { + return this.forward().lastEntry(); + } + + public Entry lastEntry() { + return this.forward().firstEntry(); + } + + public Entry pollFirstEntry() { + return this.forward().pollLastEntry(); + } + + public Entry pollLastEntry() { + return this.forward().pollFirstEntry(); + } + + public NavigableMap descendingMap() { + return this.forward(); + } + + public Set> entrySet() { + Set> result = this.entrySet; + return result == null ? (this.entrySet = this.createEntrySet()) : result; + } + + abstract Iterator> entryIterator(); + + Set> createEntrySet() { + return new Maps.EntrySet() { + Map map() { + return DescendingMap.this; + } + + public Iterator> iterator() { + return DescendingMap.this.entryIterator(); + } + }; + } + + public Set keySet() { + return this.navigableKeySet(); + } + + public NavigableSet navigableKeySet() { + NavigableSet result = this.navigableKeySet; + return result == null ? (this.navigableKeySet = new Maps.NavigableKeySet(this)) : result; + } + + public NavigableSet descendingKeySet() { + return this.forward().navigableKeySet(); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return this.forward().subMap(toKey, toInclusive, fromKey, fromInclusive).descendingMap(); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return this.forward().tailMap(toKey, inclusive).descendingMap(); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return this.forward().headMap(fromKey, inclusive).descendingMap(); + } + + public SortedMap subMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public SortedMap headMap(K toKey) { + return this.headMap(toKey, false); + } + + public SortedMap tailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + + public Collection values() { + return new Maps.Values(this); + } + + public String toString() { + return this.standardToString(); + } + } + + abstract static class EntrySet extends Sets.ImprovedAbstractSet> { + abstract Map map(); + + public int size() { + return this.map().size(); + } + + public void clear() { + this.map().clear(); + } + + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } else { + Entry entry = (Entry)o; + Object key = entry.getKey(); + V value = Maps.safeGet(this.map(), key); + return Objects.equal(value, entry.getValue()) && (value != null || this.map().containsKey(key)); + } + } + + public boolean isEmpty() { + return this.map().isEmpty(); + } + + public boolean remove(Object o) { + if (this.contains(o)) { + Entry entry = (Entry)o; + return this.map().keySet().remove(entry.getKey()); + } else { + return false; + } + } + + public boolean removeAll(Collection c) { + try { + return super.removeAll((Collection)Preconditions.checkNotNull(c)); + } catch (UnsupportedOperationException var3) { + return Sets.removeAllImpl(this, (Iterator)c.iterator()); + } + } + + public boolean retainAll(Collection c) { + try { + return super.retainAll((Collection)Preconditions.checkNotNull(c)); + } catch (UnsupportedOperationException var7) { + Set keys = Sets.newHashSetWithExpectedSize(c.size()); + Iterator i$ = c.iterator(); + + while(i$.hasNext()) { + Object o = i$.next(); + if (this.contains(o)) { + Entry entry = (Entry)o; + keys.add(entry.getKey()); + } + } + + return this.map().keySet().retainAll(keys); + } + } + } + + static class Values extends AbstractCollection { + final Map map; + + Values(Map map) { + this.map = (Map)Preconditions.checkNotNull(map); + } + + final Map map() { + return this.map; + } + + public Iterator iterator() { + return Maps.valueIterator(this.map().entrySet().iterator()); + } + + public boolean remove(Object o) { + try { + return super.remove(o); + } catch (UnsupportedOperationException var5) { + Iterator i$ = this.map().entrySet().iterator(); + + Entry entry; + do { + if (!i$.hasNext()) { + return false; + } + + entry = (Entry)i$.next(); + } while(!Objects.equal(o, entry.getValue())); + + this.map().remove(entry.getKey()); + return true; + } + } + + public boolean removeAll(Collection c) { + try { + return super.removeAll((Collection)Preconditions.checkNotNull(c)); + } catch (UnsupportedOperationException var6) { + Set toRemove = Sets.newHashSet(); + Iterator i$ = this.map().entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + if (c.contains(entry.getValue())) { + toRemove.add(entry.getKey()); + } + } + + return this.map().keySet().removeAll(toRemove); + } + } + + public boolean retainAll(Collection c) { + try { + return super.retainAll((Collection)Preconditions.checkNotNull(c)); + } catch (UnsupportedOperationException var6) { + Set toRetain = Sets.newHashSet(); + Iterator i$ = this.map().entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + if (c.contains(entry.getValue())) { + toRetain.add(entry.getKey()); + } + } + + return this.map().keySet().retainAll(toRetain); + } + } + + public int size() { + return this.map().size(); + } + + public boolean isEmpty() { + return this.map().isEmpty(); + } + + public boolean contains(@Nullable Object o) { + return this.map().containsValue(o); + } + + public void clear() { + this.map().clear(); + } + } + + @GwtIncompatible("NavigableMap") + static class NavigableKeySet extends Maps.SortedKeySet implements NavigableSet { + NavigableKeySet(NavigableMap map) { + super(map); + } + + NavigableMap map() { + return (NavigableMap)this.map; + } + + public K lower(K e) { + return this.map().lowerKey(e); + } + + public K floor(K e) { + return this.map().floorKey(e); + } + + public K ceiling(K e) { + return this.map().ceilingKey(e); + } + + public K higher(K e) { + return this.map().higherKey(e); + } + + public K pollFirst() { + return Maps.keyOrNull(this.map().pollFirstEntry()); + } + + public K pollLast() { + return Maps.keyOrNull(this.map().pollLastEntry()); + } + + public NavigableSet descendingSet() { + return this.map().descendingKeySet(); + } + + public Iterator descendingIterator() { + return this.descendingSet().iterator(); + } + + public NavigableSet subSet(K fromElement, boolean fromInclusive, K toElement, boolean toInclusive) { + return this.map().subMap(fromElement, fromInclusive, toElement, toInclusive).navigableKeySet(); + } + + public NavigableSet headSet(K toElement, boolean inclusive) { + return this.map().headMap(toElement, inclusive).navigableKeySet(); + } + + public NavigableSet tailSet(K fromElement, boolean inclusive) { + return this.map().tailMap(fromElement, inclusive).navigableKeySet(); + } + + public SortedSet subSet(K fromElement, K toElement) { + return this.subSet(fromElement, true, toElement, false); + } + + public SortedSet headSet(K toElement) { + return this.headSet(toElement, false); + } + + public SortedSet tailSet(K fromElement) { + return this.tailSet(fromElement, true); + } + } + + static class SortedKeySet extends Maps.KeySet implements SortedSet { + SortedKeySet(SortedMap map) { + super(map); + } + + SortedMap map() { + return (SortedMap)super.map(); + } + + public Comparator comparator() { + return this.map().comparator(); + } + + public SortedSet subSet(K fromElement, K toElement) { + return new Maps.SortedKeySet(this.map().subMap(fromElement, toElement)); + } + + public SortedSet headSet(K toElement) { + return new Maps.SortedKeySet(this.map().headMap(toElement)); + } + + public SortedSet tailSet(K fromElement) { + return new Maps.SortedKeySet(this.map().tailMap(fromElement)); + } + + public K first() { + return this.map().firstKey(); + } + + public K last() { + return this.map().lastKey(); + } + } + + static class KeySet extends Sets.ImprovedAbstractSet { + final Map map; + + KeySet(Map map) { + this.map = (Map)Preconditions.checkNotNull(map); + } + + Map map() { + return this.map; + } + + public Iterator iterator() { + return Maps.keyIterator(this.map().entrySet().iterator()); + } + + public int size() { + return this.map().size(); + } + + public boolean isEmpty() { + return this.map().isEmpty(); + } + + public boolean contains(Object o) { + return this.map().containsKey(o); + } + + public boolean remove(Object o) { + if (this.contains(o)) { + this.map().remove(o); + return true; + } else { + return false; + } + } + + public void clear() { + this.map().clear(); + } + } + + @GwtCompatible + abstract static class ImprovedAbstractMap extends AbstractMap { + private transient Set> entrySet; + private transient Set keySet; + private transient Collection values; + + abstract Set> createEntrySet(); + + public Set> entrySet() { + Set> result = this.entrySet; + return result == null ? (this.entrySet = this.createEntrySet()) : result; + } + + public Set keySet() { + Set result = this.keySet; + return result == null ? (this.keySet = this.createKeySet()) : result; + } + + Set createKeySet() { + return new Maps.KeySet(this); + } + + public Collection values() { + Collection result = this.values; + return result == null ? (this.values = this.createValues()) : result; + } + + Collection createValues() { + return new Maps.Values(this); + } + } + + @GwtIncompatible("NavigableMap") + static class UnmodifiableNavigableMap extends ForwardingSortedMap implements NavigableMap, Serializable { + private final NavigableMap delegate; + private transient Maps.UnmodifiableNavigableMap descendingMap; + + UnmodifiableNavigableMap(NavigableMap delegate) { + this.delegate = delegate; + } + + UnmodifiableNavigableMap(NavigableMap delegate, Maps.UnmodifiableNavigableMap descendingMap) { + this.delegate = delegate; + this.descendingMap = descendingMap; + } + + protected SortedMap delegate() { + return Collections.unmodifiableSortedMap(this.delegate); + } + + public Entry lowerEntry(K key) { + return Maps.unmodifiableOrNull(this.delegate.lowerEntry(key)); + } + + public K lowerKey(K key) { + return this.delegate.lowerKey(key); + } + + public Entry floorEntry(K key) { + return Maps.unmodifiableOrNull(this.delegate.floorEntry(key)); + } + + public K floorKey(K key) { + return this.delegate.floorKey(key); + } + + public Entry ceilingEntry(K key) { + return Maps.unmodifiableOrNull(this.delegate.ceilingEntry(key)); + } + + public K ceilingKey(K key) { + return this.delegate.ceilingKey(key); + } + + public Entry higherEntry(K key) { + return Maps.unmodifiableOrNull(this.delegate.higherEntry(key)); + } + + public K higherKey(K key) { + return this.delegate.higherKey(key); + } + + public Entry firstEntry() { + return Maps.unmodifiableOrNull(this.delegate.firstEntry()); + } + + public Entry lastEntry() { + return Maps.unmodifiableOrNull(this.delegate.lastEntry()); + } + + public final Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + public final Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + public NavigableMap descendingMap() { + Maps.UnmodifiableNavigableMap result = this.descendingMap; + return result == null ? (this.descendingMap = new Maps.UnmodifiableNavigableMap(this.delegate.descendingMap(), this)) : result; + } + + public Set keySet() { + return this.navigableKeySet(); + } + + public NavigableSet navigableKeySet() { + return Sets.unmodifiableNavigableSet(this.delegate.navigableKeySet()); + } + + public NavigableSet descendingKeySet() { + return Sets.unmodifiableNavigableSet(this.delegate.descendingKeySet()); + } + + public SortedMap subMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public SortedMap headMap(K toKey) { + return this.headMap(toKey, false); + } + + public SortedMap tailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return Maps.unmodifiableNavigableMap(this.delegate.subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return Maps.unmodifiableNavigableMap(this.delegate.headMap(toKey, inclusive)); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return Maps.unmodifiableNavigableMap(this.delegate.tailMap(fromKey, inclusive)); + } + } + + static final class FilteredEntryBiMap extends Maps.FilteredEntryMap implements BiMap { + private final BiMap inverse; + + private static Predicate> inversePredicate(final Predicate> forwardPredicate) { + return new Predicate>() { + public boolean apply(Entry input) { + return forwardPredicate.apply(Maps.immutableEntry(input.getValue(), input.getKey())); + } + }; + } + + FilteredEntryBiMap(BiMap delegate, Predicate> predicate) { + super(delegate, predicate); + this.inverse = new Maps.FilteredEntryBiMap(delegate.inverse(), inversePredicate(predicate), this); + } + + private FilteredEntryBiMap(BiMap delegate, Predicate> predicate, BiMap inverse) { + super(delegate, predicate); + this.inverse = inverse; + } + + BiMap unfiltered() { + return (BiMap)this.unfiltered; + } + + public V forcePut(@Nullable K key, @Nullable V value) { + Preconditions.checkArgument(this.apply(key, value)); + return this.unfiltered().forcePut(key, value); + } + + public BiMap inverse() { + return this.inverse; + } + + public Set values() { + return this.inverse.keySet(); + } + } + + @GwtIncompatible("NavigableMap") + private static class FilteredEntryNavigableMap extends AbstractNavigableMap { + private final NavigableMap unfiltered; + private final Predicate> entryPredicate; + private final Map filteredDelegate; + + FilteredEntryNavigableMap(NavigableMap unfiltered, Predicate> entryPredicate) { + this.unfiltered = (NavigableMap)Preconditions.checkNotNull(unfiltered); + this.entryPredicate = entryPredicate; + this.filteredDelegate = new Maps.FilteredEntryMap(unfiltered, entryPredicate); + } + + public Comparator comparator() { + return this.unfiltered.comparator(); + } + + public NavigableSet navigableKeySet() { + return new Maps.NavigableKeySet(this) { + public boolean removeAll(Collection c) { + return Iterators.removeIf(FilteredEntryNavigableMap.this.unfiltered.entrySet().iterator(), Predicates.and(FilteredEntryNavigableMap.this.entryPredicate, Maps.keyPredicateOnEntries(Predicates.in(c)))); + } + + public boolean retainAll(Collection c) { + return Iterators.removeIf(FilteredEntryNavigableMap.this.unfiltered.entrySet().iterator(), Predicates.and(FilteredEntryNavigableMap.this.entryPredicate, Maps.keyPredicateOnEntries(Predicates.not(Predicates.in(c))))); + } + }; + } + + public Collection values() { + return new Maps.FilteredMapValues(this, this.unfiltered, this.entryPredicate); + } + + Iterator> entryIterator() { + return Iterators.filter(this.unfiltered.entrySet().iterator(), this.entryPredicate); + } + + Iterator> descendingEntryIterator() { + return Iterators.filter(this.unfiltered.descendingMap().entrySet().iterator(), this.entryPredicate); + } + + public int size() { + return this.filteredDelegate.size(); + } + + public boolean isEmpty() { + return !Iterables.any(this.unfiltered.entrySet(), this.entryPredicate); + } + + @Nullable + public V get(@Nullable Object key) { + return this.filteredDelegate.get(key); + } + + public boolean containsKey(@Nullable Object key) { + return this.filteredDelegate.containsKey(key); + } + + public V put(K key, V value) { + return this.filteredDelegate.put(key, value); + } + + public V remove(@Nullable Object key) { + return this.filteredDelegate.remove(key); + } + + public void putAll(Map m) { + this.filteredDelegate.putAll(m); + } + + public void clear() { + this.filteredDelegate.clear(); + } + + public Set> entrySet() { + return this.filteredDelegate.entrySet(); + } + + public Entry pollFirstEntry() { + return (Entry)Iterables.removeFirstMatching(this.unfiltered.entrySet(), this.entryPredicate); + } + + public Entry pollLastEntry() { + return (Entry)Iterables.removeFirstMatching(this.unfiltered.descendingMap().entrySet(), this.entryPredicate); + } + + public NavigableMap descendingMap() { + return Maps.filterEntries(this.unfiltered.descendingMap(), this.entryPredicate); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return Maps.filterEntries(this.unfiltered.subMap(fromKey, fromInclusive, toKey, toInclusive), this.entryPredicate); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return Maps.filterEntries(this.unfiltered.headMap(toKey, inclusive), this.entryPredicate); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return Maps.filterEntries(this.unfiltered.tailMap(fromKey, inclusive), this.entryPredicate); + } + } + + private static class FilteredEntrySortedMap extends Maps.FilteredEntryMap implements SortedMap { + FilteredEntrySortedMap(SortedMap unfiltered, Predicate> entryPredicate) { + super(unfiltered, entryPredicate); + } + + SortedMap sortedMap() { + return (SortedMap)this.unfiltered; + } + + public SortedSet keySet() { + return (SortedSet)super.keySet(); + } + + SortedSet createKeySet() { + return new Maps.FilteredEntrySortedMap.SortedKeySet(); + } + + public Comparator comparator() { + return this.sortedMap().comparator(); + } + + public K firstKey() { + return this.keySet().iterator().next(); + } + + public K lastKey() { + SortedMap headMap = this.sortedMap(); + + while(true) { + K key = headMap.lastKey(); + if (this.apply(key, this.unfiltered.get(key))) { + return key; + } + + headMap = this.sortedMap().headMap(key); + } + } + + public SortedMap headMap(K toKey) { + return new Maps.FilteredEntrySortedMap(this.sortedMap().headMap(toKey), this.predicate); + } + + public SortedMap subMap(K fromKey, K toKey) { + return new Maps.FilteredEntrySortedMap(this.sortedMap().subMap(fromKey, toKey), this.predicate); + } + + public SortedMap tailMap(K fromKey) { + return new Maps.FilteredEntrySortedMap(this.sortedMap().tailMap(fromKey), this.predicate); + } + + class SortedKeySet extends Maps.FilteredEntryMap.KeySet implements SortedSet { + SortedKeySet() { + super(); + } + + public Comparator comparator() { + return FilteredEntrySortedMap.this.sortedMap().comparator(); + } + + public SortedSet subSet(K fromElement, K toElement) { + return (SortedSet)FilteredEntrySortedMap.this.subMap(fromElement, toElement).keySet(); + } + + public SortedSet headSet(K toElement) { + return (SortedSet)FilteredEntrySortedMap.this.headMap(toElement).keySet(); + } + + public SortedSet tailSet(K fromElement) { + return (SortedSet)FilteredEntrySortedMap.this.tailMap(fromElement).keySet(); + } + + public K first() { + return FilteredEntrySortedMap.this.firstKey(); + } + + public K last() { + return FilteredEntrySortedMap.this.lastKey(); + } + } + } + + static class FilteredEntryMap extends Maps.AbstractFilteredMap { + final Set> filteredEntrySet; + + FilteredEntryMap(Map unfiltered, Predicate> entryPredicate) { + super(unfiltered, entryPredicate); + this.filteredEntrySet = Sets.filter(unfiltered.entrySet(), this.predicate); + } + + protected Set> createEntrySet() { + return new Maps.FilteredEntryMap.EntrySet(); + } + + Set createKeySet() { + return new Maps.FilteredEntryMap.KeySet(); + } + + class KeySet extends Maps.KeySet { + KeySet() { + super(FilteredEntryMap.this); + } + + public boolean remove(Object o) { + if (FilteredEntryMap.this.containsKey(o)) { + FilteredEntryMap.this.unfiltered.remove(o); + return true; + } else { + return false; + } + } + + private boolean removeIf(Predicate keyPredicate) { + return Iterables.removeIf(FilteredEntryMap.this.unfiltered.entrySet(), Predicates.and(FilteredEntryMap.this.predicate, Maps.keyPredicateOnEntries(keyPredicate))); + } + + public boolean removeAll(Collection c) { + return this.removeIf(Predicates.in(c)); + } + + public boolean retainAll(Collection c) { + return this.removeIf(Predicates.not(Predicates.in(c))); + } + + public Object[] toArray() { + return Lists.newArrayList(this.iterator()).toArray(); + } + + public T[] toArray(T[] array) { + return Lists.newArrayList(this.iterator()).toArray(array); + } + } + + private class EntrySet extends ForwardingSet> { + private EntrySet() { + } + + protected Set> delegate() { + return FilteredEntryMap.this.filteredEntrySet; + } + + public Iterator> iterator() { + return new TransformedIterator, Entry>(FilteredEntryMap.this.filteredEntrySet.iterator()) { + Entry transform(final Entry entry) { + return new ForwardingMapEntry() { + protected Entry delegate() { + return entry; + } + + public V setValue(V newValue) { + Preconditions.checkArgument(FilteredEntryMap.this.apply(this.getKey(), newValue)); + return super.setValue(newValue); + } + }; + } + }; + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } + } + + private static class FilteredKeyMap extends Maps.AbstractFilteredMap { + Predicate keyPredicate; + + FilteredKeyMap(Map unfiltered, Predicate keyPredicate, Predicate> entryPredicate) { + super(unfiltered, entryPredicate); + this.keyPredicate = keyPredicate; + } + + protected Set> createEntrySet() { + return Sets.filter(this.unfiltered.entrySet(), this.predicate); + } + + Set createKeySet() { + return Sets.filter(this.unfiltered.keySet(), this.keyPredicate); + } + + public boolean containsKey(Object key) { + return this.unfiltered.containsKey(key) && this.keyPredicate.apply(key); + } + } + + private static final class FilteredMapValues extends Maps.Values { + Map unfiltered; + Predicate> predicate; + + FilteredMapValues(Map filteredMap, Map unfiltered, Predicate> predicate) { + super(filteredMap); + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + public boolean remove(Object o) { + return Iterables.removeFirstMatching(this.unfiltered.entrySet(), Predicates.and(this.predicate, Maps.valuePredicateOnEntries(Predicates.equalTo(o)))) != null; + } + + private boolean removeIf(Predicate valuePredicate) { + return Iterables.removeIf(this.unfiltered.entrySet(), Predicates.and(this.predicate, Maps.valuePredicateOnEntries(valuePredicate))); + } + + public boolean removeAll(Collection collection) { + return this.removeIf(Predicates.in(collection)); + } + + public boolean retainAll(Collection collection) { + return this.removeIf(Predicates.not(Predicates.in(collection))); + } + + public Object[] toArray() { + return Lists.newArrayList(this.iterator()).toArray(); + } + + public T[] toArray(T[] array) { + return Lists.newArrayList(this.iterator()).toArray(array); + } + } + + private abstract static class AbstractFilteredMap extends Maps.ImprovedAbstractMap { + final Map unfiltered; + final Predicate> predicate; + + AbstractFilteredMap(Map unfiltered, Predicate> predicate) { + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + boolean apply(@Nullable Object key, @Nullable V value) { + return this.predicate.apply(Maps.immutableEntry(key, value)); + } + + public V put(K key, V value) { + Preconditions.checkArgument(this.apply(key, value)); + return this.unfiltered.put(key, value); + } + + public void putAll(Map map) { + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + Preconditions.checkArgument(this.apply(entry.getKey(), entry.getValue())); + } + + this.unfiltered.putAll(map); + } + + public boolean containsKey(Object key) { + return this.unfiltered.containsKey(key) && this.apply(key, this.unfiltered.get(key)); + } + + public V get(Object key) { + V value = this.unfiltered.get(key); + return value != null && this.apply(key, value) ? value : null; + } + + public boolean isEmpty() { + return this.entrySet().isEmpty(); + } + + public V remove(Object key) { + return this.containsKey(key) ? this.unfiltered.remove(key) : null; + } + + Collection createValues() { + return new Maps.FilteredMapValues(this, this.unfiltered, this.predicate); + } + } + + @GwtIncompatible("NavigableMap") + private static class TransformedEntriesNavigableMap extends Maps.TransformedEntriesSortedMap implements NavigableMap { + TransformedEntriesNavigableMap(NavigableMap fromMap, Maps.EntryTransformer transformer) { + super(fromMap, transformer); + } + + public Entry ceilingEntry(K key) { + return this.transformEntry(this.fromMap().ceilingEntry(key)); + } + + public K ceilingKey(K key) { + return this.fromMap().ceilingKey(key); + } + + public NavigableSet descendingKeySet() { + return this.fromMap().descendingKeySet(); + } + + public NavigableMap descendingMap() { + return Maps.transformEntries(this.fromMap().descendingMap(), this.transformer); + } + + public Entry firstEntry() { + return this.transformEntry(this.fromMap().firstEntry()); + } + + public Entry floorEntry(K key) { + return this.transformEntry(this.fromMap().floorEntry(key)); + } + + public K floorKey(K key) { + return this.fromMap().floorKey(key); + } + + public NavigableMap headMap(K toKey) { + return this.headMap(toKey, false); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return Maps.transformEntries(this.fromMap().headMap(toKey, inclusive), this.transformer); + } + + public Entry higherEntry(K key) { + return this.transformEntry(this.fromMap().higherEntry(key)); + } + + public K higherKey(K key) { + return this.fromMap().higherKey(key); + } + + public Entry lastEntry() { + return this.transformEntry(this.fromMap().lastEntry()); + } + + public Entry lowerEntry(K key) { + return this.transformEntry(this.fromMap().lowerEntry(key)); + } + + public K lowerKey(K key) { + return this.fromMap().lowerKey(key); + } + + public NavigableSet navigableKeySet() { + return this.fromMap().navigableKeySet(); + } + + public Entry pollFirstEntry() { + return this.transformEntry(this.fromMap().pollFirstEntry()); + } + + public Entry pollLastEntry() { + return this.transformEntry(this.fromMap().pollLastEntry()); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return Maps.transformEntries(this.fromMap().subMap(fromKey, fromInclusive, toKey, toInclusive), this.transformer); + } + + public NavigableMap subMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public NavigableMap tailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return Maps.transformEntries(this.fromMap().tailMap(fromKey, inclusive), this.transformer); + } + + @Nullable + private Entry transformEntry(@Nullable Entry entry) { + return entry == null ? null : Maps.transformEntry(this.transformer, entry); + } + + protected NavigableMap fromMap() { + return (NavigableMap)super.fromMap(); + } + } + + static class TransformedEntriesSortedMap extends Maps.TransformedEntriesMap implements SortedMap { + protected SortedMap fromMap() { + return (SortedMap)this.fromMap; + } + + TransformedEntriesSortedMap(SortedMap fromMap, Maps.EntryTransformer transformer) { + super(fromMap, transformer); + } + + public Comparator comparator() { + return this.fromMap().comparator(); + } + + public K firstKey() { + return this.fromMap().firstKey(); + } + + public SortedMap headMap(K toKey) { + return Maps.transformEntries(this.fromMap().headMap(toKey), this.transformer); + } + + public K lastKey() { + return this.fromMap().lastKey(); + } + + public SortedMap subMap(K fromKey, K toKey) { + return Maps.transformEntries(this.fromMap().subMap(fromKey, toKey), this.transformer); + } + + public SortedMap tailMap(K fromKey) { + return Maps.transformEntries(this.fromMap().tailMap(fromKey), this.transformer); + } + } + + static class TransformedEntriesMap extends Maps.ImprovedAbstractMap { + final Map fromMap; + final Maps.EntryTransformer transformer; + + TransformedEntriesMap(Map fromMap, Maps.EntryTransformer transformer) { + this.fromMap = (Map)Preconditions.checkNotNull(fromMap); + this.transformer = (Maps.EntryTransformer)Preconditions.checkNotNull(transformer); + } + + public int size() { + return this.fromMap.size(); + } + + public boolean containsKey(Object key) { + return this.fromMap.containsKey(key); + } + + public V2 get(Object key) { + V1 value = this.fromMap.get(key); + return value == null && !this.fromMap.containsKey(key) ? null : this.transformer.transformEntry(key, value); + } + + public V2 remove(Object key) { + return this.fromMap.containsKey(key) ? this.transformer.transformEntry(key, this.fromMap.remove(key)) : null; + } + + public void clear() { + this.fromMap.clear(); + } + + public Set keySet() { + return this.fromMap.keySet(); + } + + protected Set> createEntrySet() { + return new Maps.EntrySet() { + Map map() { + return TransformedEntriesMap.this; + } + + public Iterator> iterator() { + return Iterators.transform(TransformedEntriesMap.this.fromMap.entrySet().iterator(), Maps.asEntryToEntryFunction(TransformedEntriesMap.this.transformer)); + } + }; + } + } + + public interface EntryTransformer { + V2 transformEntry(@Nullable K var1, @Nullable V1 var2); + } + + private static class UnmodifiableBiMap extends ForwardingMap implements BiMap, Serializable { + final Map unmodifiableMap; + final BiMap delegate; + BiMap inverse; + transient Set values; + private static final long serialVersionUID = 0L; + + UnmodifiableBiMap(BiMap delegate, @Nullable BiMap inverse) { + this.unmodifiableMap = Collections.unmodifiableMap(delegate); + this.delegate = delegate; + this.inverse = inverse; + } + + protected Map delegate() { + return this.unmodifiableMap; + } + + public V forcePut(K key, V value) { + throw new UnsupportedOperationException(); + } + + public BiMap inverse() { + BiMap result = this.inverse; + return result == null ? (this.inverse = new Maps.UnmodifiableBiMap(this.delegate.inverse(), this)) : result; + } + + public Set values() { + Set result = this.values; + return result == null ? (this.values = Collections.unmodifiableSet(this.delegate.values())) : result; + } + } + + private static final class BiMapConverter extends Converter implements Serializable { + private final BiMap bimap; + private static final long serialVersionUID = 0L; + + BiMapConverter(BiMap bimap) { + this.bimap = (BiMap)Preconditions.checkNotNull(bimap); + } + + protected B doForward(A a) { + return convert(this.bimap, a); + } + + protected A doBackward(B b) { + return convert(this.bimap.inverse(), b); + } + + private static Y convert(BiMap bimap, X input) { + Y output = bimap.get(input); + Preconditions.checkArgument(output != null, "No non-null mapping present for input: %s", input); + return output; + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Maps.BiMapConverter) { + Maps.BiMapConverter that = (Maps.BiMapConverter)object; + return this.bimap.equals(that.bimap); + } else { + return false; + } + } + + public int hashCode() { + return this.bimap.hashCode(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.bimap)); + return (new StringBuilder(18 + var1.length())).append("Maps.asConverter(").append(var1).append(")").toString(); + } + } + + static class UnmodifiableEntrySet extends Maps.UnmodifiableEntries implements Set> { + UnmodifiableEntrySet(Set> entries) { + super(entries); + } + + public boolean equals(@Nullable Object object) { + return Sets.equalsImpl(this, object); + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + } + + static class UnmodifiableEntries extends ForwardingCollection> { + private final Collection> entries; + + UnmodifiableEntries(Collection> entries) { + this.entries = entries; + } + + protected Collection> delegate() { + return this.entries; + } + + public Iterator> iterator() { + final Iterator> delegate = super.iterator(); + return new UnmodifiableIterator>() { + public boolean hasNext() { + return delegate.hasNext(); + } + + public Entry next() { + return Maps.unmodifiableEntry((Entry)delegate.next()); + } + }; + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + } + + @GwtIncompatible("NavigableMap") + private static final class NavigableAsMapView extends AbstractNavigableMap { + private final NavigableSet set; + private final Function function; + + NavigableAsMapView(NavigableSet ks, Function vFunction) { + this.set = (NavigableSet)Preconditions.checkNotNull(ks); + this.function = (Function)Preconditions.checkNotNull(vFunction); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return Maps.asMap(this.set.subSet(fromKey, fromInclusive, toKey, toInclusive), this.function); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return Maps.asMap(this.set.headSet(toKey, inclusive), this.function); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return Maps.asMap(this.set.tailSet(fromKey, inclusive), this.function); + } + + public Comparator comparator() { + return this.set.comparator(); + } + + @Nullable + public V get(@Nullable Object key) { + return Collections2.safeContains(this.set, key) ? this.function.apply(key) : null; + } + + public void clear() { + this.set.clear(); + } + + Iterator> entryIterator() { + return Maps.asMapEntryIterator(this.set, this.function); + } + + Iterator> descendingEntryIterator() { + return this.descendingMap().entrySet().iterator(); + } + + public NavigableSet navigableKeySet() { + return Maps.removeOnlyNavigableSet(this.set); + } + + public int size() { + return this.set.size(); + } + + public NavigableMap descendingMap() { + return Maps.asMap(this.set.descendingSet(), this.function); + } + } + + private static class SortedAsMapView extends Maps.AsMapView implements SortedMap { + SortedAsMapView(SortedSet set, Function function) { + super(set, function); + } + + SortedSet backingSet() { + return (SortedSet)super.backingSet(); + } + + public Comparator comparator() { + return this.backingSet().comparator(); + } + + public Set keySet() { + return Maps.removeOnlySortedSet(this.backingSet()); + } + + public SortedMap subMap(K fromKey, K toKey) { + return Maps.asMap(this.backingSet().subSet(fromKey, toKey), this.function); + } + + public SortedMap headMap(K toKey) { + return Maps.asMap(this.backingSet().headSet(toKey), this.function); + } + + public SortedMap tailMap(K fromKey) { + return Maps.asMap(this.backingSet().tailSet(fromKey), this.function); + } + + public K firstKey() { + return this.backingSet().first(); + } + + public K lastKey() { + return this.backingSet().last(); + } + } + + private static class AsMapView extends Maps.ImprovedAbstractMap { + private final Set set; + final Function function; + + Set backingSet() { + return this.set; + } + + AsMapView(Set set, Function function) { + this.set = (Set)Preconditions.checkNotNull(set); + this.function = (Function)Preconditions.checkNotNull(function); + } + + public Set createKeySet() { + return Maps.removeOnlySet(this.backingSet()); + } + + Collection createValues() { + return Collections2.transform(this.set, this.function); + } + + public int size() { + return this.backingSet().size(); + } + + public boolean containsKey(@Nullable Object key) { + return this.backingSet().contains(key); + } + + public V get(@Nullable Object key) { + return Collections2.safeContains(this.backingSet(), key) ? this.function.apply(key) : null; + } + + public V remove(@Nullable Object key) { + return this.backingSet().remove(key) ? this.function.apply(key) : null; + } + + public void clear() { + this.backingSet().clear(); + } + + protected Set> createEntrySet() { + return new Maps.EntrySet() { + Map map() { + return AsMapView.this; + } + + public Iterator> iterator() { + return Maps.asMapEntryIterator(AsMapView.this.backingSet(), AsMapView.this.function); + } + }; + } + } + + static class SortedMapDifferenceImpl extends Maps.MapDifferenceImpl implements SortedMapDifference { + SortedMapDifferenceImpl(SortedMap onlyOnLeft, SortedMap onlyOnRight, SortedMap onBoth, SortedMap> differences) { + super(onlyOnLeft, onlyOnRight, onBoth, differences); + } + + public SortedMap> entriesDiffering() { + return (SortedMap)super.entriesDiffering(); + } + + public SortedMap entriesInCommon() { + return (SortedMap)super.entriesInCommon(); + } + + public SortedMap entriesOnlyOnLeft() { + return (SortedMap)super.entriesOnlyOnLeft(); + } + + public SortedMap entriesOnlyOnRight() { + return (SortedMap)super.entriesOnlyOnRight(); + } + } + + static class ValueDifferenceImpl implements MapDifference.ValueDifference { + private final V left; + private final V right; + + static MapDifference.ValueDifference create(@Nullable V left, @Nullable V right) { + return new Maps.ValueDifferenceImpl(left, right); + } + + private ValueDifferenceImpl(@Nullable V left, @Nullable V right) { + this.left = left; + this.right = right; + } + + public V leftValue() { + return this.left; + } + + public V rightValue() { + return this.right; + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof MapDifference.ValueDifference)) { + return false; + } else { + MapDifference.ValueDifference that = (MapDifference.ValueDifference)object; + return Objects.equal(this.left, that.leftValue()) && Objects.equal(this.right, that.rightValue()); + } + } + + public int hashCode() { + return Objects.hashCode(this.left, this.right); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.left)); + String var2 = String.valueOf(String.valueOf(this.right)); + return (new StringBuilder(4 + var1.length() + var2.length())).append("(").append(var1).append(", ").append(var2).append(")").toString(); + } + } + + static class MapDifferenceImpl implements MapDifference { + final Map onlyOnLeft; + final Map onlyOnRight; + final Map onBoth; + final Map> differences; + + MapDifferenceImpl(Map onlyOnLeft, Map onlyOnRight, Map onBoth, Map> differences) { + this.onlyOnLeft = Maps.unmodifiableMap(onlyOnLeft); + this.onlyOnRight = Maps.unmodifiableMap(onlyOnRight); + this.onBoth = Maps.unmodifiableMap(onBoth); + this.differences = Maps.unmodifiableMap(differences); + } + + public boolean areEqual() { + return this.onlyOnLeft.isEmpty() && this.onlyOnRight.isEmpty() && this.differences.isEmpty(); + } + + public Map entriesOnlyOnLeft() { + return this.onlyOnLeft; + } + + public Map entriesOnlyOnRight() { + return this.onlyOnRight; + } + + public Map entriesInCommon() { + return this.onBoth; + } + + public Map> entriesDiffering() { + return this.differences; + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (!(object instanceof MapDifference)) { + return false; + } else { + MapDifference other = (MapDifference)object; + return this.entriesOnlyOnLeft().equals(other.entriesOnlyOnLeft()) && this.entriesOnlyOnRight().equals(other.entriesOnlyOnRight()) && this.entriesInCommon().equals(other.entriesInCommon()) && this.entriesDiffering().equals(other.entriesDiffering()); + } + } + + public int hashCode() { + return Objects.hashCode(this.entriesOnlyOnLeft(), this.entriesOnlyOnRight(), this.entriesInCommon(), this.entriesDiffering()); + } + + public String toString() { + if (this.areEqual()) { + return "equal"; + } else { + StringBuilder result = new StringBuilder("not equal"); + if (!this.onlyOnLeft.isEmpty()) { + result.append(": only on left=").append(this.onlyOnLeft); + } + + if (!this.onlyOnRight.isEmpty()) { + result.append(": only on right=").append(this.onlyOnRight); + } + + if (!this.differences.isEmpty()) { + result.append(": value differences=").append(this.differences); + } + + return result.toString(); + } + } + } + + private static enum EntryFunction implements Function, Object> { + KEY { + @Nullable + public Object apply(Entry entry) { + return entry.getKey(); + } + }, + VALUE { + @Nullable + public Object apply(Entry entry) { + return entry.getValue(); + } + }; + + private EntryFunction() { + } + + // $FF: synthetic method + EntryFunction(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/MinMaxPriorityQueue.java b/src/main/com/google/common/collect/MinMaxPriorityQueue.java new file mode 100644 index 0000000..0d21a5e --- /dev/null +++ b/src/main/com/google/common/collect/MinMaxPriorityQueue.java @@ -0,0 +1,621 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +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.List; +import java.util.NoSuchElementException; +import java.util.Queue; + +@Beta +public final class MinMaxPriorityQueue extends AbstractQueue { + private final MinMaxPriorityQueue.Heap minHeap; + private final MinMaxPriorityQueue.Heap maxHeap; + @VisibleForTesting + final int maximumSize; + private Object[] queue; + private int size; + private int modCount; + private static final int EVEN_POWERS_OF_TWO = 1431655765; + private static final int ODD_POWERS_OF_TWO = -1431655766; + private static final int DEFAULT_CAPACITY = 11; + + public static > MinMaxPriorityQueue create() { + return (new MinMaxPriorityQueue.Builder(Ordering.natural())).create(); + } + + public static > MinMaxPriorityQueue create(Iterable initialContents) { + return (new MinMaxPriorityQueue.Builder(Ordering.natural())).create(initialContents); + } + + public static MinMaxPriorityQueue.Builder orderedBy(Comparator comparator) { + return new MinMaxPriorityQueue.Builder(comparator); + } + + public static MinMaxPriorityQueue.Builder expectedSize(int expectedSize) { + return (new MinMaxPriorityQueue.Builder(Ordering.natural())).expectedSize(expectedSize); + } + + public static MinMaxPriorityQueue.Builder maximumSize(int maximumSize) { + return (new MinMaxPriorityQueue.Builder(Ordering.natural())).maximumSize(maximumSize); + } + + private MinMaxPriorityQueue(MinMaxPriorityQueue.Builder builder, int queueSize) { + Ordering ordering = builder.ordering(); + this.minHeap = new MinMaxPriorityQueue.Heap(ordering); + this.maxHeap = new MinMaxPriorityQueue.Heap(ordering.reverse()); + this.minHeap.otherHeap = this.maxHeap; + this.maxHeap.otherHeap = this.minHeap; + this.maximumSize = builder.maximumSize; + this.queue = new Object[queueSize]; + } + + public int size() { + return this.size; + } + + public boolean add(E element) { + this.offer(element); + return true; + } + + public boolean addAll(Collection newElements) { + boolean modified = false; + + for(Iterator i$ = newElements.iterator(); i$.hasNext(); modified = true) { + E element = i$.next(); + this.offer(element); + } + + return modified; + } + + public boolean offer(E element) { + Preconditions.checkNotNull(element); + ++this.modCount; + int insertIndex = this.size++; + this.growIfNeeded(); + this.heapForIndex(insertIndex).bubbleUp(insertIndex, element); + return this.size <= this.maximumSize || this.pollLast() != element; + } + + public E poll() { + return this.isEmpty() ? null : this.removeAndGet(0); + } + + E elementData(int index) { + return this.queue[index]; + } + + public E peek() { + return this.isEmpty() ? null : this.elementData(0); + } + + private int getMaxElementIndex() { + switch(this.size) { + case 1: + return 0; + case 2: + return 1; + default: + return this.maxHeap.compareElements(1, 2) <= 0 ? 1 : 2; + } + } + + public E pollFirst() { + return this.poll(); + } + + public E removeFirst() { + return this.remove(); + } + + public E peekFirst() { + return this.peek(); + } + + public E pollLast() { + return this.isEmpty() ? null : this.removeAndGet(this.getMaxElementIndex()); + } + + public E removeLast() { + if (this.isEmpty()) { + throw new NoSuchElementException(); + } else { + return this.removeAndGet(this.getMaxElementIndex()); + } + } + + public E peekLast() { + return this.isEmpty() ? null : this.elementData(this.getMaxElementIndex()); + } + + @VisibleForTesting + MinMaxPriorityQueue.MoveDesc removeAt(int index) { + Preconditions.checkPositionIndex(index, this.size); + ++this.modCount; + --this.size; + if (this.size == index) { + this.queue[this.size] = null; + return null; + } else { + E actualLastElement = this.elementData(this.size); + int lastElementAt = this.heapForIndex(this.size).getCorrectLastElement(actualLastElement); + E toTrickle = this.elementData(this.size); + this.queue[this.size] = null; + MinMaxPriorityQueue.MoveDesc changes = this.fillHole(index, toTrickle); + if (lastElementAt < index) { + return changes == null ? new MinMaxPriorityQueue.MoveDesc(actualLastElement, toTrickle) : new MinMaxPriorityQueue.MoveDesc(actualLastElement, changes.replaced); + } else { + return changes; + } + } + } + + private MinMaxPriorityQueue.MoveDesc fillHole(int index, E toTrickle) { + MinMaxPriorityQueue.Heap heap = this.heapForIndex(index); + int vacated = heap.fillHoleAt(index); + int bubbledTo = heap.bubbleUpAlternatingLevels(vacated, toTrickle); + if (bubbledTo == vacated) { + return heap.tryCrossOverAndBubbleUp(index, vacated, toTrickle); + } else { + return bubbledTo < index ? new MinMaxPriorityQueue.MoveDesc(toTrickle, this.elementData(index)) : null; + } + } + + private E removeAndGet(int index) { + E value = this.elementData(index); + this.removeAt(index); + return value; + } + + private MinMaxPriorityQueue.Heap heapForIndex(int i) { + return isEvenLevel(i) ? this.minHeap : this.maxHeap; + } + + @VisibleForTesting + static boolean isEvenLevel(int index) { + int oneBased = index + 1; + Preconditions.checkState(oneBased > 0, "negative index"); + return (oneBased & 1431655765) > (oneBased & -1431655766); + } + + @VisibleForTesting + boolean isIntact() { + for(int i = 1; i < this.size; ++i) { + if (!this.heapForIndex(i).verifyIndex(i)) { + return false; + } + } + + return true; + } + + public Iterator iterator() { + return new MinMaxPriorityQueue.QueueIterator(); + } + + public void clear() { + for(int i = 0; i < this.size; ++i) { + this.queue[i] = null; + } + + this.size = 0; + } + + public Object[] toArray() { + Object[] copyTo = new Object[this.size]; + System.arraycopy(this.queue, 0, copyTo, 0, this.size); + return copyTo; + } + + public Comparator comparator() { + return this.minHeap.ordering; + } + + @VisibleForTesting + int capacity() { + return this.queue.length; + } + + @VisibleForTesting + static int initialQueueSize(int configuredExpectedSize, int maximumSize, Iterable initialContents) { + int result = configuredExpectedSize == -1 ? 11 : configuredExpectedSize; + if (initialContents instanceof Collection) { + int initialSize = ((Collection)initialContents).size(); + result = Math.max(result, initialSize); + } + + return capAtMaximumSize(result, maximumSize); + } + + private void growIfNeeded() { + if (this.size > this.queue.length) { + int newCapacity = this.calculateNewCapacity(); + Object[] newQueue = new Object[newCapacity]; + System.arraycopy(this.queue, 0, newQueue, 0, this.queue.length); + this.queue = newQueue; + } + + } + + private int calculateNewCapacity() { + int oldCapacity = this.queue.length; + int newCapacity = oldCapacity < 64 ? (oldCapacity + 1) * 2 : IntMath.checkedMultiply(oldCapacity / 2, 3); + return capAtMaximumSize(newCapacity, this.maximumSize); + } + + private static int capAtMaximumSize(int queueSize, int maximumSize) { + return Math.min(queueSize - 1, maximumSize) + 1; + } + + // $FF: synthetic method + MinMaxPriorityQueue(MinMaxPriorityQueue.Builder x0, int x1, Object x2) { + this(x0, x1); + } + + private class QueueIterator implements Iterator { + private int cursor; + private int expectedModCount; + private Queue forgetMeNot; + private List skipMe; + private E lastFromForgetMeNot; + private boolean canRemove; + + private QueueIterator() { + this.cursor = -1; + this.expectedModCount = MinMaxPriorityQueue.this.modCount; + } + + public boolean hasNext() { + this.checkModCount(); + return this.nextNotInSkipMe(this.cursor + 1) < MinMaxPriorityQueue.this.size() || this.forgetMeNot != null && !this.forgetMeNot.isEmpty(); + } + + public E next() { + this.checkModCount(); + int tempCursor = this.nextNotInSkipMe(this.cursor + 1); + if (tempCursor < MinMaxPriorityQueue.this.size()) { + this.cursor = tempCursor; + this.canRemove = true; + return MinMaxPriorityQueue.this.elementData(this.cursor); + } else { + if (this.forgetMeNot != null) { + this.cursor = MinMaxPriorityQueue.this.size(); + this.lastFromForgetMeNot = this.forgetMeNot.poll(); + if (this.lastFromForgetMeNot != null) { + this.canRemove = true; + return this.lastFromForgetMeNot; + } + } + + throw new NoSuchElementException("iterator moved past last element in queue."); + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.canRemove); + this.checkModCount(); + this.canRemove = false; + ++this.expectedModCount; + if (this.cursor < MinMaxPriorityQueue.this.size()) { + MinMaxPriorityQueue.MoveDesc moved = MinMaxPriorityQueue.this.removeAt(this.cursor); + if (moved != null) { + if (this.forgetMeNot == null) { + this.forgetMeNot = new ArrayDeque(); + this.skipMe = new ArrayList(3); + } + + this.forgetMeNot.add(moved.toTrickle); + this.skipMe.add(moved.replaced); + } + + --this.cursor; + } else { + Preconditions.checkState(this.removeExact(this.lastFromForgetMeNot)); + this.lastFromForgetMeNot = null; + } + + } + + private boolean containsExact(Iterable elements, E target) { + Iterator i$ = elements.iterator(); + + Object element; + do { + if (!i$.hasNext()) { + return false; + } + + element = i$.next(); + } while(element != target); + + return true; + } + + boolean removeExact(Object target) { + for(int i = 0; i < MinMaxPriorityQueue.this.size; ++i) { + if (MinMaxPriorityQueue.this.queue[i] == target) { + MinMaxPriorityQueue.this.removeAt(i); + return true; + } + } + + return false; + } + + void checkModCount() { + if (MinMaxPriorityQueue.this.modCount != this.expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + private int nextNotInSkipMe(int c) { + if (this.skipMe != null) { + while(c < MinMaxPriorityQueue.this.size() && this.containsExact(this.skipMe, MinMaxPriorityQueue.this.elementData(c))) { + ++c; + } + } + + return c; + } + + // $FF: synthetic method + QueueIterator(Object x1) { + this(); + } + } + + private class Heap { + final Ordering ordering; + MinMaxPriorityQueue.Heap otherHeap; + + Heap(Ordering ordering) { + this.ordering = ordering; + } + + int compareElements(int a, int b) { + return this.ordering.compare(MinMaxPriorityQueue.this.elementData(a), MinMaxPriorityQueue.this.elementData(b)); + } + + MinMaxPriorityQueue.MoveDesc tryCrossOverAndBubbleUp(int removeIndex, int vacated, E toTrickle) { + int crossOver = this.crossOver(vacated, toTrickle); + if (crossOver == vacated) { + return null; + } else { + Object parent; + if (crossOver < removeIndex) { + parent = MinMaxPriorityQueue.this.elementData(removeIndex); + } else { + parent = MinMaxPriorityQueue.this.elementData(this.getParentIndex(removeIndex)); + } + + return this.otherHeap.bubbleUpAlternatingLevels(crossOver, toTrickle) < removeIndex ? new MinMaxPriorityQueue.MoveDesc(toTrickle, parent) : null; + } + } + + void bubbleUp(int index, E x) { + int crossOver = this.crossOverUp(index, x); + MinMaxPriorityQueue.Heap heap; + if (crossOver == index) { + heap = this; + } else { + index = crossOver; + heap = this.otherHeap; + } + + heap.bubbleUpAlternatingLevels(index, x); + } + + int bubbleUpAlternatingLevels(int index, E x) { + while(true) { + if (index > 2) { + int grandParentIndex = this.getGrandparentIndex(index); + E e = MinMaxPriorityQueue.this.elementData(grandParentIndex); + if (this.ordering.compare(e, x) > 0) { + MinMaxPriorityQueue.this.queue[index] = e; + index = grandParentIndex; + continue; + } + } + + MinMaxPriorityQueue.this.queue[index] = x; + return index; + } + } + + int findMin(int index, int len) { + if (index >= MinMaxPriorityQueue.this.size) { + return -1; + } else { + Preconditions.checkState(index > 0); + int limit = Math.min(index, MinMaxPriorityQueue.this.size - len) + len; + int minIndex = index; + + for(int i = index + 1; i < limit; ++i) { + if (this.compareElements(i, minIndex) < 0) { + minIndex = i; + } + } + + return minIndex; + } + } + + int findMinChild(int index) { + return this.findMin(this.getLeftChildIndex(index), 2); + } + + int findMinGrandChild(int index) { + int leftChildIndex = this.getLeftChildIndex(index); + return leftChildIndex < 0 ? -1 : this.findMin(this.getLeftChildIndex(leftChildIndex), 4); + } + + int crossOverUp(int index, E x) { + if (index == 0) { + MinMaxPriorityQueue.this.queue[0] = x; + return 0; + } else { + int parentIndex = this.getParentIndex(index); + E parentElement = MinMaxPriorityQueue.this.elementData(parentIndex); + if (parentIndex != 0) { + int grandparentIndex = this.getParentIndex(parentIndex); + int uncleIndex = this.getRightChildIndex(grandparentIndex); + if (uncleIndex != parentIndex && this.getLeftChildIndex(uncleIndex) >= MinMaxPriorityQueue.this.size) { + E uncleElement = MinMaxPriorityQueue.this.elementData(uncleIndex); + if (this.ordering.compare(uncleElement, parentElement) < 0) { + parentIndex = uncleIndex; + parentElement = uncleElement; + } + } + } + + if (this.ordering.compare(parentElement, x) < 0) { + MinMaxPriorityQueue.this.queue[index] = parentElement; + MinMaxPriorityQueue.this.queue[parentIndex] = x; + return parentIndex; + } else { + MinMaxPriorityQueue.this.queue[index] = x; + return index; + } + } + } + + int getCorrectLastElement(E actualLastElement) { + int parentIndex = this.getParentIndex(MinMaxPriorityQueue.this.size); + if (parentIndex != 0) { + int grandparentIndex = this.getParentIndex(parentIndex); + int uncleIndex = this.getRightChildIndex(grandparentIndex); + if (uncleIndex != parentIndex && this.getLeftChildIndex(uncleIndex) >= MinMaxPriorityQueue.this.size) { + E uncleElement = MinMaxPriorityQueue.this.elementData(uncleIndex); + if (this.ordering.compare(uncleElement, actualLastElement) < 0) { + MinMaxPriorityQueue.this.queue[uncleIndex] = actualLastElement; + MinMaxPriorityQueue.this.queue[MinMaxPriorityQueue.this.size] = uncleElement; + return uncleIndex; + } + } + } + + return MinMaxPriorityQueue.this.size; + } + + int crossOver(int index, E x) { + int minChildIndex = this.findMinChild(index); + if (minChildIndex > 0 && this.ordering.compare(MinMaxPriorityQueue.this.elementData(minChildIndex), x) < 0) { + MinMaxPriorityQueue.this.queue[index] = MinMaxPriorityQueue.this.elementData(minChildIndex); + MinMaxPriorityQueue.this.queue[minChildIndex] = x; + return minChildIndex; + } else { + return this.crossOverUp(index, x); + } + } + + int fillHoleAt(int index) { + int minGrandchildIndex; + while((minGrandchildIndex = this.findMinGrandChild(index)) > 0) { + MinMaxPriorityQueue.this.queue[index] = MinMaxPriorityQueue.this.elementData(minGrandchildIndex); + index = minGrandchildIndex; + } + + return index; + } + + private boolean verifyIndex(int i) { + if (this.getLeftChildIndex(i) < MinMaxPriorityQueue.this.size && this.compareElements(i, this.getLeftChildIndex(i)) > 0) { + return false; + } else if (this.getRightChildIndex(i) < MinMaxPriorityQueue.this.size && this.compareElements(i, this.getRightChildIndex(i)) > 0) { + return false; + } else if (i > 0 && this.compareElements(i, this.getParentIndex(i)) > 0) { + return false; + } else { + return i <= 2 || this.compareElements(this.getGrandparentIndex(i), i) <= 0; + } + } + + private int getLeftChildIndex(int i) { + return i * 2 + 1; + } + + private int getRightChildIndex(int i) { + return i * 2 + 2; + } + + private int getParentIndex(int i) { + return (i - 1) / 2; + } + + private int getGrandparentIndex(int i) { + return this.getParentIndex(this.getParentIndex(i)); + } + } + + static class MoveDesc { + final E toTrickle; + final E replaced; + + MoveDesc(E toTrickle, E replaced) { + this.toTrickle = toTrickle; + this.replaced = replaced; + } + } + + @Beta + public static final class Builder { + private static final int UNSET_EXPECTED_SIZE = -1; + private final Comparator comparator; + private int expectedSize; + private int maximumSize; + + private Builder(Comparator comparator) { + this.expectedSize = -1; + this.maximumSize = Integer.MAX_VALUE; + this.comparator = (Comparator)Preconditions.checkNotNull(comparator); + } + + public MinMaxPriorityQueue.Builder expectedSize(int expectedSize) { + Preconditions.checkArgument(expectedSize >= 0); + this.expectedSize = expectedSize; + return this; + } + + public MinMaxPriorityQueue.Builder maximumSize(int maximumSize) { + Preconditions.checkArgument(maximumSize > 0); + this.maximumSize = maximumSize; + return this; + } + + public MinMaxPriorityQueue create() { + return this.create(Collections.emptySet()); + } + + public MinMaxPriorityQueue create(Iterable initialContents) { + MinMaxPriorityQueue queue = new MinMaxPriorityQueue(this, MinMaxPriorityQueue.initialQueueSize(this.expectedSize, this.maximumSize, initialContents)); + Iterator i$ = initialContents.iterator(); + + while(i$.hasNext()) { + T element = i$.next(); + queue.offer(element); + } + + return queue; + } + + private Ordering ordering() { + return Ordering.from(this.comparator); + } + + // $FF: synthetic method + Builder(Comparator x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/collect/Multimap.java b/src/main/com/google/common/collect/Multimap.java new file mode 100644 index 0000000..c04f1ba --- /dev/null +++ b/src/main/com/google/common/collect/Multimap.java @@ -0,0 +1,51 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +public interface Multimap { + int size(); + + boolean isEmpty(); + + boolean containsKey(@Nullable Object var1); + + boolean containsValue(@Nullable Object var1); + + boolean containsEntry(@Nullable Object var1, @Nullable Object var2); + + boolean put(@Nullable K var1, @Nullable V var2); + + boolean remove(@Nullable Object var1, @Nullable Object var2); + + boolean putAll(@Nullable K var1, Iterable var2); + + boolean putAll(Multimap var1); + + Collection replaceValues(@Nullable K var1, Iterable var2); + + Collection removeAll(@Nullable Object var1); + + void clear(); + + Collection get(@Nullable K var1); + + Set keySet(); + + Multiset keys(); + + Collection values(); + + Collection> entries(); + + Map> asMap(); + + boolean equals(@Nullable Object var1); + + int hashCode(); +} diff --git a/src/main/com/google/common/collect/MultimapBuilder.java b/src/main/com/google/common/collect/MultimapBuilder.java new file mode 100644 index 0000000..9252f5d --- /dev/null +++ b/src/main/com/google/common/collect/MultimapBuilder.java @@ -0,0 +1,280 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +@Beta +@GwtCompatible +public abstract class MultimapBuilder { + private static final int DEFAULT_EXPECTED_KEYS = 8; + + private MultimapBuilder() { + } + + public static MultimapBuilder.MultimapBuilderWithKeys hashKeys() { + return hashKeys(8); + } + + public static MultimapBuilder.MultimapBuilderWithKeys hashKeys(final int expectedKeys) { + CollectPreconditions.checkNonnegative(expectedKeys, "expectedKeys"); + return new MultimapBuilder.MultimapBuilderWithKeys() { + Map> createMap() { + return new HashMap(expectedKeys); + } + }; + } + + public static MultimapBuilder.MultimapBuilderWithKeys linkedHashKeys() { + return linkedHashKeys(8); + } + + public static MultimapBuilder.MultimapBuilderWithKeys linkedHashKeys(final int expectedKeys) { + CollectPreconditions.checkNonnegative(expectedKeys, "expectedKeys"); + return new MultimapBuilder.MultimapBuilderWithKeys() { + Map> createMap() { + return new LinkedHashMap(expectedKeys); + } + }; + } + + public static MultimapBuilder.MultimapBuilderWithKeys treeKeys() { + return treeKeys(Ordering.natural()); + } + + public static MultimapBuilder.MultimapBuilderWithKeys treeKeys(final Comparator comparator) { + Preconditions.checkNotNull(comparator); + return new MultimapBuilder.MultimapBuilderWithKeys() { + Map> createMap() { + return new TreeMap(comparator); + } + }; + } + + public static > MultimapBuilder.MultimapBuilderWithKeys enumKeys(final Class keyClass) { + Preconditions.checkNotNull(keyClass); + return new MultimapBuilder.MultimapBuilderWithKeys() { + Map> createMap() { + return new EnumMap(keyClass); + } + }; + } + + public abstract Multimap build(); + + public Multimap build(Multimap multimap) { + Multimap result = this.build(); + result.putAll(multimap); + return result; + } + + // $FF: synthetic method + MultimapBuilder(Object x0) { + this(); + } + + public abstract static class SortedSetMultimapBuilder extends MultimapBuilder.SetMultimapBuilder { + SortedSetMultimapBuilder() { + } + + public abstract SortedSetMultimap build(); + + public SortedSetMultimap build(Multimap multimap) { + return (SortedSetMultimap)super.build(multimap); + } + } + + public abstract static class SetMultimapBuilder extends MultimapBuilder { + SetMultimapBuilder() { + super(null); + } + + public abstract SetMultimap build(); + + public SetMultimap build(Multimap multimap) { + return (SetMultimap)super.build(multimap); + } + } + + public abstract static class ListMultimapBuilder extends MultimapBuilder { + ListMultimapBuilder() { + super(null); + } + + public abstract ListMultimap build(); + + public ListMultimap build(Multimap multimap) { + return (ListMultimap)super.build(multimap); + } + } + + public abstract static class MultimapBuilderWithKeys { + private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2; + + MultimapBuilderWithKeys() { + } + + abstract Map> createMap(); + + public MultimapBuilder.ListMultimapBuilder arrayListValues() { + return this.arrayListValues(2); + } + + public MultimapBuilder.ListMultimapBuilder arrayListValues(final int expectedValuesPerKey) { + CollectPreconditions.checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new MultimapBuilder.ListMultimapBuilder() { + public ListMultimap build() { + return Multimaps.newListMultimap(MultimapBuilderWithKeys.this.createMap(), new MultimapBuilder.ArrayListSupplier(expectedValuesPerKey)); + } + }; + } + + public MultimapBuilder.ListMultimapBuilder linkedListValues() { + return new MultimapBuilder.ListMultimapBuilder() { + public ListMultimap build() { + return Multimaps.newListMultimap(MultimapBuilderWithKeys.this.createMap(), MultimapBuilder.LinkedListSupplier.instance()); + } + }; + } + + public MultimapBuilder.SetMultimapBuilder hashSetValues() { + return this.hashSetValues(2); + } + + public MultimapBuilder.SetMultimapBuilder hashSetValues(final int expectedValuesPerKey) { + CollectPreconditions.checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new MultimapBuilder.SetMultimapBuilder() { + public SetMultimap build() { + return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), new MultimapBuilder.HashSetSupplier(expectedValuesPerKey)); + } + }; + } + + public MultimapBuilder.SetMultimapBuilder linkedHashSetValues() { + return this.linkedHashSetValues(2); + } + + public MultimapBuilder.SetMultimapBuilder linkedHashSetValues(final int expectedValuesPerKey) { + CollectPreconditions.checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + return new MultimapBuilder.SetMultimapBuilder() { + public SetMultimap build() { + return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), new MultimapBuilder.LinkedHashSetSupplier(expectedValuesPerKey)); + } + }; + } + + public MultimapBuilder.SortedSetMultimapBuilder treeSetValues() { + return this.treeSetValues(Ordering.natural()); + } + + public MultimapBuilder.SortedSetMultimapBuilder treeSetValues(final Comparator comparator) { + Preconditions.checkNotNull(comparator, "comparator"); + return new MultimapBuilder.SortedSetMultimapBuilder() { + public SortedSetMultimap build() { + return Multimaps.newSortedSetMultimap(MultimapBuilderWithKeys.this.createMap(), new MultimapBuilder.TreeSetSupplier(comparator)); + } + }; + } + + public > MultimapBuilder.SetMultimapBuilder enumSetValues(final Class valueClass) { + Preconditions.checkNotNull(valueClass, "valueClass"); + return new MultimapBuilder.SetMultimapBuilder() { + public SetMultimap build() { + Supplier> factory = new MultimapBuilder.EnumSetSupplier(valueClass); + return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), factory); + } + }; + } + } + + private static final class EnumSetSupplier> implements Supplier>, Serializable { + private final Class clazz; + + EnumSetSupplier(Class clazz) { + this.clazz = (Class)Preconditions.checkNotNull(clazz); + } + + public Set get() { + return EnumSet.noneOf(this.clazz); + } + } + + private static final class TreeSetSupplier implements Supplier>, Serializable { + private final Comparator comparator; + + TreeSetSupplier(Comparator comparator) { + this.comparator = (Comparator)Preconditions.checkNotNull(comparator); + } + + public SortedSet get() { + return new TreeSet(this.comparator); + } + } + + private static final class LinkedHashSetSupplier implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + LinkedHashSetSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = CollectPreconditions.checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + public Set get() { + return new LinkedHashSet(this.expectedValuesPerKey); + } + } + + private static final class HashSetSupplier implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + HashSetSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = CollectPreconditions.checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + public Set get() { + return new HashSet(this.expectedValuesPerKey); + } + } + + private static enum LinkedListSupplier implements Supplier> { + INSTANCE; + + public static Supplier> instance() { + Supplier> result = INSTANCE; + return result; + } + + public List get() { + return new LinkedList(); + } + } + + private static final class ArrayListSupplier implements Supplier>, Serializable { + private final int expectedValuesPerKey; + + ArrayListSupplier(int expectedValuesPerKey) { + this.expectedValuesPerKey = CollectPreconditions.checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); + } + + public List get() { + return new ArrayList(this.expectedValuesPerKey); + } + } +} diff --git a/src/main/com/google/common/collect/Multimaps.java b/src/main/com/google/common/collect/Multimaps.java new file mode 100644 index 0000000..2663bf8 --- /dev/null +++ b/src/main/com/google/common/collect/Multimaps.java @@ -0,0 +1,1013 @@ +package com.google.common.collect; + +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.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractCollection; +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.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedSet; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Multimaps { + private Multimaps() { + } + + public static Multimap newMultimap(Map> map, Supplier> factory) { + return new Multimaps.CustomMultimap(map, factory); + } + + public static ListMultimap newListMultimap(Map> map, Supplier> factory) { + return new Multimaps.CustomListMultimap(map, factory); + } + + public static SetMultimap newSetMultimap(Map> map, Supplier> factory) { + return new Multimaps.CustomSetMultimap(map, factory); + } + + public static SortedSetMultimap newSortedSetMultimap(Map> map, Supplier> factory) { + return new Multimaps.CustomSortedSetMultimap(map, factory); + } + + public static > M invertFrom(Multimap source, M dest) { + Preconditions.checkNotNull(dest); + Iterator i$ = source.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + dest.put(entry.getValue(), entry.getKey()); + } + + return dest; + } + + public static Multimap synchronizedMultimap(Multimap multimap) { + return Synchronized.multimap(multimap, (Object)null); + } + + public static Multimap unmodifiableMultimap(Multimap delegate) { + return (Multimap)(!(delegate instanceof Multimaps.UnmodifiableMultimap) && !(delegate instanceof ImmutableMultimap) ? new Multimaps.UnmodifiableMultimap(delegate) : delegate); + } + + /** @deprecated */ + @Deprecated + public static Multimap unmodifiableMultimap(ImmutableMultimap delegate) { + return (Multimap)Preconditions.checkNotNull(delegate); + } + + public static SetMultimap synchronizedSetMultimap(SetMultimap multimap) { + return Synchronized.setMultimap(multimap, (Object)null); + } + + public static SetMultimap unmodifiableSetMultimap(SetMultimap delegate) { + return (SetMultimap)(!(delegate instanceof Multimaps.UnmodifiableSetMultimap) && !(delegate instanceof ImmutableSetMultimap) ? new Multimaps.UnmodifiableSetMultimap(delegate) : delegate); + } + + /** @deprecated */ + @Deprecated + public static SetMultimap unmodifiableSetMultimap(ImmutableSetMultimap delegate) { + return (SetMultimap)Preconditions.checkNotNull(delegate); + } + + public static SortedSetMultimap synchronizedSortedSetMultimap(SortedSetMultimap multimap) { + return Synchronized.sortedSetMultimap(multimap, (Object)null); + } + + public static SortedSetMultimap unmodifiableSortedSetMultimap(SortedSetMultimap delegate) { + return (SortedSetMultimap)(delegate instanceof Multimaps.UnmodifiableSortedSetMultimap ? delegate : new Multimaps.UnmodifiableSortedSetMultimap(delegate)); + } + + public static ListMultimap synchronizedListMultimap(ListMultimap multimap) { + return Synchronized.listMultimap(multimap, (Object)null); + } + + public static ListMultimap unmodifiableListMultimap(ListMultimap delegate) { + return (ListMultimap)(!(delegate instanceof Multimaps.UnmodifiableListMultimap) && !(delegate instanceof ImmutableListMultimap) ? new Multimaps.UnmodifiableListMultimap(delegate) : delegate); + } + + /** @deprecated */ + @Deprecated + public static ListMultimap unmodifiableListMultimap(ImmutableListMultimap delegate) { + return (ListMultimap)Preconditions.checkNotNull(delegate); + } + + private static Collection unmodifiableValueCollection(Collection collection) { + if (collection instanceof SortedSet) { + return Collections.unmodifiableSortedSet((SortedSet)collection); + } else if (collection instanceof Set) { + return Collections.unmodifiableSet((Set)collection); + } else { + return (Collection)(collection instanceof List ? Collections.unmodifiableList((List)collection) : Collections.unmodifiableCollection(collection)); + } + } + + private static Collection> unmodifiableEntries(Collection> entries) { + return (Collection)(entries instanceof Set ? Maps.unmodifiableEntrySet((Set)entries) : new Maps.UnmodifiableEntries(Collections.unmodifiableCollection(entries))); + } + + @Beta + public static Map> asMap(ListMultimap multimap) { + return multimap.asMap(); + } + + @Beta + public static Map> asMap(SetMultimap multimap) { + return multimap.asMap(); + } + + @Beta + public static Map> asMap(SortedSetMultimap multimap) { + return multimap.asMap(); + } + + @Beta + public static Map> asMap(Multimap multimap) { + return multimap.asMap(); + } + + public static SetMultimap forMap(Map map) { + return new Multimaps.MapMultimap(map); + } + + public static Multimap transformValues(Multimap fromMultimap, Function function) { + Preconditions.checkNotNull(function); + Maps.EntryTransformer transformer = Maps.asEntryTransformer(function); + return transformEntries(fromMultimap, transformer); + } + + public static Multimap transformEntries(Multimap fromMap, Maps.EntryTransformer transformer) { + return new Multimaps.TransformedEntriesMultimap(fromMap, transformer); + } + + public static ListMultimap transformValues(ListMultimap fromMultimap, Function function) { + Preconditions.checkNotNull(function); + Maps.EntryTransformer transformer = Maps.asEntryTransformer(function); + return transformEntries(fromMultimap, transformer); + } + + public static ListMultimap transformEntries(ListMultimap fromMap, Maps.EntryTransformer transformer) { + return new Multimaps.TransformedEntriesListMultimap(fromMap, transformer); + } + + public static ImmutableListMultimap index(Iterable values, Function keyFunction) { + return index(values.iterator(), keyFunction); + } + + public static ImmutableListMultimap index(Iterator values, Function keyFunction) { + Preconditions.checkNotNull(keyFunction); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + + while(values.hasNext()) { + V value = values.next(); + Preconditions.checkNotNull(value, values); + builder.put(keyFunction.apply(value), value); + } + + return builder.build(); + } + + public static Multimap filterKeys(Multimap unfiltered, Predicate keyPredicate) { + if (unfiltered instanceof SetMultimap) { + return filterKeys((SetMultimap)unfiltered, keyPredicate); + } else if (unfiltered instanceof ListMultimap) { + return filterKeys((ListMultimap)unfiltered, keyPredicate); + } else if (unfiltered instanceof FilteredKeyMultimap) { + FilteredKeyMultimap prev = (FilteredKeyMultimap)unfiltered; + return new FilteredKeyMultimap(prev.unfiltered, Predicates.and(prev.keyPredicate, keyPredicate)); + } else if (unfiltered instanceof FilteredMultimap) { + FilteredMultimap prev = (FilteredMultimap)unfiltered; + return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); + } else { + return new FilteredKeyMultimap(unfiltered, keyPredicate); + } + } + + public static SetMultimap filterKeys(SetMultimap unfiltered, Predicate keyPredicate) { + if (unfiltered instanceof FilteredKeySetMultimap) { + FilteredKeySetMultimap prev = (FilteredKeySetMultimap)unfiltered; + return new FilteredKeySetMultimap(prev.unfiltered(), Predicates.and(prev.keyPredicate, keyPredicate)); + } else if (unfiltered instanceof FilteredSetMultimap) { + FilteredSetMultimap prev = (FilteredSetMultimap)unfiltered; + return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); + } else { + return new FilteredKeySetMultimap(unfiltered, keyPredicate); + } + } + + public static ListMultimap filterKeys(ListMultimap unfiltered, Predicate keyPredicate) { + if (unfiltered instanceof FilteredKeyListMultimap) { + FilteredKeyListMultimap prev = (FilteredKeyListMultimap)unfiltered; + return new FilteredKeyListMultimap(prev.unfiltered(), Predicates.and(prev.keyPredicate, keyPredicate)); + } else { + return new FilteredKeyListMultimap(unfiltered, keyPredicate); + } + } + + public static Multimap filterValues(Multimap unfiltered, Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + public static SetMultimap filterValues(SetMultimap unfiltered, Predicate valuePredicate) { + return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); + } + + public static Multimap filterEntries(Multimap unfiltered, Predicate> entryPredicate) { + Preconditions.checkNotNull(entryPredicate); + if (unfiltered instanceof SetMultimap) { + return filterEntries((SetMultimap)unfiltered, entryPredicate); + } else { + return (Multimap)(unfiltered instanceof FilteredMultimap ? filterFiltered((FilteredMultimap)unfiltered, entryPredicate) : new FilteredEntryMultimap((Multimap)Preconditions.checkNotNull(unfiltered), entryPredicate)); + } + } + + public static SetMultimap filterEntries(SetMultimap unfiltered, Predicate> entryPredicate) { + Preconditions.checkNotNull(entryPredicate); + return (SetMultimap)(unfiltered instanceof FilteredSetMultimap ? filterFiltered((FilteredSetMultimap)unfiltered, entryPredicate) : new FilteredEntrySetMultimap((SetMultimap)Preconditions.checkNotNull(unfiltered), entryPredicate)); + } + + private static Multimap filterFiltered(FilteredMultimap multimap, Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(multimap.entryPredicate(), entryPredicate); + return new FilteredEntryMultimap(multimap.unfiltered(), predicate); + } + + private static SetMultimap filterFiltered(FilteredSetMultimap multimap, Predicate> entryPredicate) { + Predicate> predicate = Predicates.and(multimap.entryPredicate(), entryPredicate); + return new FilteredEntrySetMultimap(multimap.unfiltered(), predicate); + } + + static boolean equalsImpl(Multimap multimap, @Nullable Object object) { + if (object == multimap) { + return true; + } else if (object instanceof Multimap) { + Multimap that = (Multimap)object; + return multimap.asMap().equals(that.asMap()); + } else { + return false; + } + } + + static final class AsMap extends Maps.ImprovedAbstractMap> { + private final Multimap multimap; + + AsMap(Multimap multimap) { + this.multimap = (Multimap)Preconditions.checkNotNull(multimap); + } + + public int size() { + return this.multimap.keySet().size(); + } + + protected Set>> createEntrySet() { + return new Multimaps.AsMap.EntrySet(); + } + + void removeValuesForKey(Object key) { + this.multimap.keySet().remove(key); + } + + public Collection get(Object key) { + return this.containsKey(key) ? this.multimap.get(key) : null; + } + + public Collection remove(Object key) { + return this.containsKey(key) ? this.multimap.removeAll(key) : null; + } + + public Set keySet() { + return this.multimap.keySet(); + } + + public boolean isEmpty() { + return this.multimap.isEmpty(); + } + + public boolean containsKey(Object key) { + return this.multimap.containsKey(key); + } + + public void clear() { + this.multimap.clear(); + } + + class EntrySet extends Maps.EntrySet> { + Map> map() { + return AsMap.this; + } + + public Iterator>> iterator() { + return Maps.asMapEntryIterator(AsMap.this.multimap.keySet(), new Function>() { + public Collection apply(K key) { + return AsMap.this.multimap.get(key); + } + }); + } + + public boolean remove(Object o) { + if (!this.contains(o)) { + return false; + } else { + Entry entry = (Entry)o; + AsMap.this.removeValuesForKey(entry.getKey()); + return true; + } + } + } + } + + abstract static class Entries extends AbstractCollection> { + abstract Multimap multimap(); + + public int size() { + return this.multimap().size(); + } + + public boolean contains(@Nullable Object o) { + if (o instanceof Entry) { + Entry entry = (Entry)o; + return this.multimap().containsEntry(entry.getKey(), entry.getValue()); + } else { + return false; + } + } + + public boolean remove(@Nullable Object o) { + if (o instanceof Entry) { + Entry entry = (Entry)o; + return this.multimap().remove(entry.getKey(), entry.getValue()); + } else { + return false; + } + } + + public void clear() { + this.multimap().clear(); + } + } + + static class Keys extends AbstractMultiset { + final Multimap multimap; + + Keys(Multimap multimap) { + this.multimap = multimap; + } + + Iterator> entryIterator() { + return new TransformedIterator>, Multiset.Entry>(this.multimap.asMap().entrySet().iterator()) { + Multiset.Entry transform(final Entry> backingEntry) { + return new Multisets.AbstractEntry() { + public K getElement() { + return backingEntry.getKey(); + } + + public int getCount() { + return ((Collection)backingEntry.getValue()).size(); + } + }; + } + }; + } + + int distinctElements() { + return this.multimap.asMap().size(); + } + + Set> createEntrySet() { + return new Multimaps.Keys.KeysEntrySet(); + } + + public boolean contains(@Nullable Object element) { + return this.multimap.containsKey(element); + } + + public Iterator iterator() { + return Maps.keyIterator(this.multimap.entries().iterator()); + } + + public int count(@Nullable Object element) { + Collection values = (Collection)Maps.safeGet(this.multimap.asMap(), element); + return values == null ? 0 : values.size(); + } + + public int remove(@Nullable Object element, int occurrences) { + CollectPreconditions.checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return this.count(element); + } else { + Collection values = (Collection)Maps.safeGet(this.multimap.asMap(), element); + if (values == null) { + return 0; + } else { + int oldCount = values.size(); + if (occurrences >= oldCount) { + values.clear(); + } else { + Iterator iterator = values.iterator(); + + for(int i = 0; i < occurrences; ++i) { + iterator.next(); + iterator.remove(); + } + } + + return oldCount; + } + } + } + + public void clear() { + this.multimap.clear(); + } + + public Set elementSet() { + return this.multimap.keySet(); + } + + class KeysEntrySet extends Multisets.EntrySet { + Multiset multiset() { + return Keys.this; + } + + public Iterator> iterator() { + return Keys.this.entryIterator(); + } + + public int size() { + return Keys.this.distinctElements(); + } + + public boolean isEmpty() { + return Keys.this.multimap.isEmpty(); + } + + public boolean contains(@Nullable Object o) { + if (!(o instanceof Multiset.Entry)) { + return false; + } else { + Multiset.Entry entry = (Multiset.Entry)o; + Collection collection = (Collection)Keys.this.multimap.asMap().get(entry.getElement()); + return collection != null && collection.size() == entry.getCount(); + } + } + + public boolean remove(@Nullable Object o) { + if (o instanceof Multiset.Entry) { + Multiset.Entry entry = (Multiset.Entry)o; + Collection collection = (Collection)Keys.this.multimap.asMap().get(entry.getElement()); + if (collection != null && collection.size() == entry.getCount()) { + collection.clear(); + return true; + } + } + + return false; + } + } + } + + private static final class TransformedEntriesListMultimap extends Multimaps.TransformedEntriesMultimap implements ListMultimap { + TransformedEntriesListMultimap(ListMultimap fromMultimap, Maps.EntryTransformer transformer) { + super(fromMultimap, transformer); + } + + List transform(K key, Collection values) { + return Lists.transform((List)values, Maps.asValueToValueFunction(this.transformer, key)); + } + + public List get(K key) { + return this.transform(key, this.fromMultimap.get(key)); + } + + public List removeAll(Object key) { + return this.transform(key, this.fromMultimap.removeAll(key)); + } + + public List replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + } + + private static class TransformedEntriesMultimap extends AbstractMultimap { + final Multimap fromMultimap; + final Maps.EntryTransformer transformer; + + TransformedEntriesMultimap(Multimap fromMultimap, Maps.EntryTransformer transformer) { + this.fromMultimap = (Multimap)Preconditions.checkNotNull(fromMultimap); + this.transformer = (Maps.EntryTransformer)Preconditions.checkNotNull(transformer); + } + + Collection transform(K key, Collection values) { + Function function = Maps.asValueToValueFunction(this.transformer, key); + return (Collection)(values instanceof List ? Lists.transform((List)values, function) : Collections2.transform(values, function)); + } + + Map> createAsMap() { + return Maps.transformEntries(this.fromMultimap.asMap(), new Maps.EntryTransformer, Collection>() { + public Collection transformEntry(K key, Collection value) { + return TransformedEntriesMultimap.this.transform(key, value); + } + }); + } + + public void clear() { + this.fromMultimap.clear(); + } + + public boolean containsKey(Object key) { + return this.fromMultimap.containsKey(key); + } + + Iterator> entryIterator() { + return Iterators.transform(this.fromMultimap.entries().iterator(), Maps.asEntryToEntryFunction(this.transformer)); + } + + public Collection get(K key) { + return this.transform(key, this.fromMultimap.get(key)); + } + + public boolean isEmpty() { + return this.fromMultimap.isEmpty(); + } + + public Set keySet() { + return this.fromMultimap.keySet(); + } + + public Multiset keys() { + return this.fromMultimap.keys(); + } + + public boolean put(K key, V2 value) { + throw new UnsupportedOperationException(); + } + + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object key, Object value) { + return this.get(key).remove(value); + } + + public Collection removeAll(Object key) { + return this.transform(key, this.fromMultimap.removeAll(key)); + } + + public Collection replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public int size() { + return this.fromMultimap.size(); + } + + Collection createValues() { + return Collections2.transform(this.fromMultimap.entries(), Maps.asEntryToValueFunction(this.transformer)); + } + } + + private static class MapMultimap extends AbstractMultimap implements SetMultimap, Serializable { + final Map map; + private static final long serialVersionUID = 7845222491160860175L; + + MapMultimap(Map map) { + this.map = (Map)Preconditions.checkNotNull(map); + } + + public int size() { + return this.map.size(); + } + + public boolean containsKey(Object key) { + return this.map.containsKey(key); + } + + public boolean containsValue(Object value) { + return this.map.containsValue(value); + } + + public boolean containsEntry(Object key, Object value) { + return this.map.entrySet().contains(Maps.immutableEntry(key, value)); + } + + public Set get(final K key) { + return new Sets.ImprovedAbstractSet() { + public Iterator iterator() { + return new Iterator() { + int i; + + public boolean hasNext() { + return this.i == 0 && MapMultimap.this.map.containsKey(key); + } + + public V next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + ++this.i; + return MapMultimap.this.map.get(key); + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.i == 1); + this.i = -1; + MapMultimap.this.map.remove(key); + } + }; + } + + public int size() { + return MapMultimap.this.map.containsKey(key) ? 1 : 0; + } + }; + } + + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + public Set replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object key, Object value) { + return this.map.entrySet().remove(Maps.immutableEntry(key, value)); + } + + public Set removeAll(Object key) { + Set values = new HashSet(2); + if (!this.map.containsKey(key)) { + return values; + } else { + values.add(this.map.remove(key)); + return values; + } + } + + public void clear() { + this.map.clear(); + } + + public Set keySet() { + return this.map.keySet(); + } + + public Collection values() { + return this.map.values(); + } + + public Set> entries() { + return this.map.entrySet(); + } + + Iterator> entryIterator() { + return this.map.entrySet().iterator(); + } + + Map> createAsMap() { + return new Multimaps.AsMap(this); + } + + public int hashCode() { + return this.map.hashCode(); + } + } + + private static class UnmodifiableSortedSetMultimap extends Multimaps.UnmodifiableSetMultimap implements SortedSetMultimap { + private static final long serialVersionUID = 0L; + + UnmodifiableSortedSetMultimap(SortedSetMultimap delegate) { + super(delegate); + } + + public SortedSetMultimap delegate() { + return (SortedSetMultimap)super.delegate(); + } + + public SortedSet get(K key) { + return Collections.unmodifiableSortedSet(this.delegate().get(key)); + } + + public SortedSet removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + public SortedSet replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public Comparator valueComparator() { + return this.delegate().valueComparator(); + } + } + + private static class UnmodifiableSetMultimap extends Multimaps.UnmodifiableMultimap implements SetMultimap { + private static final long serialVersionUID = 0L; + + UnmodifiableSetMultimap(SetMultimap delegate) { + super(delegate); + } + + public SetMultimap delegate() { + return (SetMultimap)super.delegate(); + } + + public Set get(K key) { + return Collections.unmodifiableSet(this.delegate().get(key)); + } + + public Set> entries() { + return Maps.unmodifiableEntrySet(this.delegate().entries()); + } + + public Set removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + public Set replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + } + + private static class UnmodifiableListMultimap extends Multimaps.UnmodifiableMultimap implements ListMultimap { + private static final long serialVersionUID = 0L; + + UnmodifiableListMultimap(ListMultimap delegate) { + super(delegate); + } + + public ListMultimap delegate() { + return (ListMultimap)super.delegate(); + } + + public List get(K key) { + return Collections.unmodifiableList(this.delegate().get(key)); + } + + public List removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + public List replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + } + + private static class UnmodifiableMultimap extends ForwardingMultimap implements Serializable { + final Multimap delegate; + transient Collection> entries; + transient Multiset keys; + transient Set keySet; + transient Collection values; + transient Map> map; + private static final long serialVersionUID = 0L; + + UnmodifiableMultimap(Multimap delegate) { + this.delegate = (Multimap)Preconditions.checkNotNull(delegate); + } + + protected Multimap delegate() { + return this.delegate; + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public Map> asMap() { + Map> result = this.map; + if (result == null) { + result = this.map = Collections.unmodifiableMap(Maps.transformValues(this.delegate.asMap(), new Function, Collection>() { + public Collection apply(Collection collection) { + return Multimaps.unmodifiableValueCollection(collection); + } + })); + } + + return result; + } + + public Collection> entries() { + Collection> result = this.entries; + if (result == null) { + this.entries = result = Multimaps.unmodifiableEntries(this.delegate.entries()); + } + + return result; + } + + public Collection get(K key) { + return Multimaps.unmodifiableValueCollection(this.delegate.get(key)); + } + + public Multiset keys() { + Multiset result = this.keys; + if (result == null) { + this.keys = result = Multisets.unmodifiableMultiset(this.delegate.keys()); + } + + return result; + } + + public Set keySet() { + Set result = this.keySet; + if (result == null) { + this.keySet = result = Collections.unmodifiableSet(this.delegate.keySet()); + } + + return result; + } + + public boolean put(K key, V value) { + throw new UnsupportedOperationException(); + } + + public boolean putAll(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public boolean putAll(Multimap multimap) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + public Collection removeAll(Object key) { + throw new UnsupportedOperationException(); + } + + public Collection replaceValues(K key, Iterable values) { + throw new UnsupportedOperationException(); + } + + public Collection values() { + Collection result = this.values; + if (result == null) { + this.values = result = Collections.unmodifiableCollection(this.delegate.values()); + } + + return result; + } + } + + private static class CustomSortedSetMultimap extends AbstractSortedSetMultimap { + transient Supplier> factory; + transient Comparator valueComparator; + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0L; + + CustomSortedSetMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = (Supplier)Preconditions.checkNotNull(factory); + this.valueComparator = ((SortedSet)factory.get()).comparator(); + } + + protected SortedSet createCollection() { + return (SortedSet)this.factory.get(); + } + + public Comparator valueComparator() { + return this.valueComparator; + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.factory); + stream.writeObject(this.backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.factory = (Supplier)stream.readObject(); + this.valueComparator = ((SortedSet)this.factory.get()).comparator(); + Map> map = (Map)stream.readObject(); + this.setMap(map); + } + } + + private static class CustomSetMultimap extends AbstractSetMultimap { + transient Supplier> factory; + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0L; + + CustomSetMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = (Supplier)Preconditions.checkNotNull(factory); + } + + protected Set createCollection() { + return (Set)this.factory.get(); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.factory); + stream.writeObject(this.backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.factory = (Supplier)stream.readObject(); + Map> map = (Map)stream.readObject(); + this.setMap(map); + } + } + + private static class CustomListMultimap extends AbstractListMultimap { + transient Supplier> factory; + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 0L; + + CustomListMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = (Supplier)Preconditions.checkNotNull(factory); + } + + protected List createCollection() { + return (List)this.factory.get(); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.factory); + stream.writeObject(this.backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.factory = (Supplier)stream.readObject(); + Map> map = (Map)stream.readObject(); + this.setMap(map); + } + } + + private static class CustomMultimap extends AbstractMapBasedMultimap { + transient Supplier> factory; + @GwtIncompatible("java serialization not supported") + private static final long serialVersionUID = 0L; + + CustomMultimap(Map> map, Supplier> factory) { + super(map); + this.factory = (Supplier)Preconditions.checkNotNull(factory); + } + + protected Collection createCollection() { + return (Collection)this.factory.get(); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.factory); + stream.writeObject(this.backingMap()); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.factory = (Supplier)stream.readObject(); + Map> map = (Map)stream.readObject(); + this.setMap(map); + } + } +} diff --git a/src/main/com/google/common/collect/Multiset.java b/src/main/com/google/common/collect/Multiset.java new file mode 100644 index 0000000..47f47a6 --- /dev/null +++ b/src/main/com/google/common/collect/Multiset.java @@ -0,0 +1,56 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +public interface Multiset extends Collection { + int count(@Nullable Object var1); + + int add(@Nullable E var1, int var2); + + int remove(@Nullable Object var1, int var2); + + int setCount(E var1, int var2); + + boolean setCount(E var1, int var2, int var3); + + Set elementSet(); + + Set> entrySet(); + + boolean equals(@Nullable Object var1); + + int hashCode(); + + String toString(); + + Iterator iterator(); + + boolean contains(@Nullable Object var1); + + boolean containsAll(Collection var1); + + boolean add(E var1); + + boolean remove(@Nullable Object var1); + + boolean removeAll(Collection var1); + + boolean retainAll(Collection var1); + + public interface Entry { + E getElement(); + + int getCount(); + + boolean equals(Object var1); + + int hashCode(); + + String toString(); + } +} diff --git a/src/main/com/google/common/collect/Multisets.java b/src/main/com/google/common/collect/Multisets.java new file mode 100644 index 0000000..8319f70 --- /dev/null +++ b/src/main/com/google/common/collect/Multisets.java @@ -0,0 +1,753 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +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.primitives.Ints; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Multisets { + private static final Ordering> DECREASING_COUNT_ORDERING = new Ordering>() { + public int compare(Multiset.Entry entry1, Multiset.Entry entry2) { + return Ints.compare(entry2.getCount(), entry1.getCount()); + } + }; + + private Multisets() { + } + + public static Multiset unmodifiableMultiset(Multiset multiset) { + return (Multiset)(!(multiset instanceof Multisets.UnmodifiableMultiset) && !(multiset instanceof ImmutableMultiset) ? new Multisets.UnmodifiableMultiset((Multiset)Preconditions.checkNotNull(multiset)) : multiset); + } + + /** @deprecated */ + @Deprecated + public static Multiset unmodifiableMultiset(ImmutableMultiset multiset) { + return (Multiset)Preconditions.checkNotNull(multiset); + } + + @Beta + public static SortedMultiset unmodifiableSortedMultiset(SortedMultiset sortedMultiset) { + return new UnmodifiableSortedMultiset((SortedMultiset)Preconditions.checkNotNull(sortedMultiset)); + } + + public static Multiset.Entry immutableEntry(@Nullable E e, int n) { + return new Multisets.ImmutableEntry(e, n); + } + + @Beta + public static Multiset filter(Multiset unfiltered, Predicate predicate) { + if (unfiltered instanceof Multisets.FilteredMultiset) { + Multisets.FilteredMultiset filtered = (Multisets.FilteredMultiset)unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new Multisets.FilteredMultiset(filtered.unfiltered, combinedPredicate); + } else { + return new Multisets.FilteredMultiset(unfiltered, predicate); + } + } + + static int inferDistinctElements(Iterable elements) { + return elements instanceof Multiset ? ((Multiset)elements).elementSet().size() : 11; + } + + @Beta + public static Multiset union(final Multiset multiset1, final Multiset multiset2) { + Preconditions.checkNotNull(multiset1); + Preconditions.checkNotNull(multiset2); + return new AbstractMultiset() { + public boolean contains(@Nullable Object element) { + return multiset1.contains(element) || multiset2.contains(element); + } + + public boolean isEmpty() { + return multiset1.isEmpty() && multiset2.isEmpty(); + } + + public int count(Object element) { + return Math.max(multiset1.count(element), multiset2.count(element)); + } + + Set createElementSet() { + return Sets.union(multiset1.elementSet(), multiset2.elementSet()); + } + + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + final Iterator> iterator2 = multiset2.entrySet().iterator(); + return new AbstractIterator>() { + protected Multiset.Entry computeNext() { + Multiset.Entry entry2; + Object element; + if (iterator1.hasNext()) { + entry2 = (Multiset.Entry)iterator1.next(); + element = entry2.getElement(); + int count = Math.max(entry2.getCount(), multiset2.count(element)); + return Multisets.immutableEntry(element, count); + } else { + do { + if (!iterator2.hasNext()) { + return (Multiset.Entry)this.endOfData(); + } + + entry2 = (Multiset.Entry)iterator2.next(); + element = entry2.getElement(); + } while(multiset1.contains(element)); + + return Multisets.immutableEntry(element, entry2.getCount()); + } + } + }; + } + + int distinctElements() { + return this.elementSet().size(); + } + }; + } + + public static Multiset intersection(final Multiset multiset1, final Multiset multiset2) { + Preconditions.checkNotNull(multiset1); + Preconditions.checkNotNull(multiset2); + return new AbstractMultiset() { + public int count(Object element) { + int count1 = multiset1.count(element); + return count1 == 0 ? 0 : Math.min(count1, multiset2.count(element)); + } + + Set createElementSet() { + return Sets.intersection(multiset1.elementSet(), multiset2.elementSet()); + } + + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + return new AbstractIterator>() { + protected Multiset.Entry computeNext() { + while(true) { + if (iterator1.hasNext()) { + Multiset.Entry entry1 = (Multiset.Entry)iterator1.next(); + E element = entry1.getElement(); + int count = Math.min(entry1.getCount(), multiset2.count(element)); + if (count <= 0) { + continue; + } + + return Multisets.immutableEntry(element, count); + } + + return (Multiset.Entry)this.endOfData(); + } + } + }; + } + + int distinctElements() { + return this.elementSet().size(); + } + }; + } + + @Beta + public static Multiset sum(final Multiset multiset1, final Multiset multiset2) { + Preconditions.checkNotNull(multiset1); + Preconditions.checkNotNull(multiset2); + return new AbstractMultiset() { + public boolean contains(@Nullable Object element) { + return multiset1.contains(element) || multiset2.contains(element); + } + + public boolean isEmpty() { + return multiset1.isEmpty() && multiset2.isEmpty(); + } + + public int size() { + return multiset1.size() + multiset2.size(); + } + + public int count(Object element) { + return multiset1.count(element) + multiset2.count(element); + } + + Set createElementSet() { + return Sets.union(multiset1.elementSet(), multiset2.elementSet()); + } + + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + final Iterator> iterator2 = multiset2.entrySet().iterator(); + return new AbstractIterator>() { + protected Multiset.Entry computeNext() { + Multiset.Entry entry2; + Object element; + if (iterator1.hasNext()) { + entry2 = (Multiset.Entry)iterator1.next(); + element = entry2.getElement(); + int count = entry2.getCount() + multiset2.count(element); + return Multisets.immutableEntry(element, count); + } else { + do { + if (!iterator2.hasNext()) { + return (Multiset.Entry)this.endOfData(); + } + + entry2 = (Multiset.Entry)iterator2.next(); + element = entry2.getElement(); + } while(multiset1.contains(element)); + + return Multisets.immutableEntry(element, entry2.getCount()); + } + } + }; + } + + int distinctElements() { + return this.elementSet().size(); + } + }; + } + + @Beta + public static Multiset difference(final Multiset multiset1, final Multiset multiset2) { + Preconditions.checkNotNull(multiset1); + Preconditions.checkNotNull(multiset2); + return new AbstractMultiset() { + public int count(@Nullable Object element) { + int count1 = multiset1.count(element); + return count1 == 0 ? 0 : Math.max(0, count1 - multiset2.count(element)); + } + + Iterator> entryIterator() { + final Iterator> iterator1 = multiset1.entrySet().iterator(); + return new AbstractIterator>() { + protected Multiset.Entry computeNext() { + while(true) { + if (iterator1.hasNext()) { + Multiset.Entry entry1 = (Multiset.Entry)iterator1.next(); + E element = entry1.getElement(); + int count = entry1.getCount() - multiset2.count(element); + if (count <= 0) { + continue; + } + + return Multisets.immutableEntry(element, count); + } + + return (Multiset.Entry)this.endOfData(); + } + } + }; + } + + int distinctElements() { + return Iterators.size(this.entryIterator()); + } + }; + } + + public static boolean containsOccurrences(Multiset superMultiset, Multiset subMultiset) { + Preconditions.checkNotNull(superMultiset); + Preconditions.checkNotNull(subMultiset); + Iterator i$ = subMultiset.entrySet().iterator(); + + Multiset.Entry entry; + int superCount; + do { + if (!i$.hasNext()) { + return true; + } + + entry = (Multiset.Entry)i$.next(); + superCount = superMultiset.count(entry.getElement()); + } while(superCount >= entry.getCount()); + + return false; + } + + public static boolean retainOccurrences(Multiset multisetToModify, Multiset multisetToRetain) { + return retainOccurrencesImpl(multisetToModify, multisetToRetain); + } + + private static boolean retainOccurrencesImpl(Multiset multisetToModify, Multiset occurrencesToRetain) { + Preconditions.checkNotNull(multisetToModify); + Preconditions.checkNotNull(occurrencesToRetain); + Iterator> entryIterator = multisetToModify.entrySet().iterator(); + boolean changed = false; + + while(entryIterator.hasNext()) { + Multiset.Entry entry = (Multiset.Entry)entryIterator.next(); + int retainCount = occurrencesToRetain.count(entry.getElement()); + if (retainCount == 0) { + entryIterator.remove(); + changed = true; + } else if (retainCount < entry.getCount()) { + multisetToModify.setCount(entry.getElement(), retainCount); + changed = true; + } + } + + return changed; + } + + public static boolean removeOccurrences(Multiset multisetToModify, Iterable occurrencesToRemove) { + return occurrencesToRemove instanceof Multiset ? removeOccurrencesImpl(multisetToModify, (Multiset)occurrencesToRemove) : removeOccurrencesImpl(multisetToModify, occurrencesToRemove); + } + + private static boolean removeOccurrencesImpl(Multiset multisetToModify, Iterable occurrencesToRemove) { + Preconditions.checkNotNull(multisetToModify); + Preconditions.checkNotNull(occurrencesToRemove); + boolean changed = false; + + Object o; + for(Iterator i$ = occurrencesToRemove.iterator(); i$.hasNext(); changed |= multisetToModify.remove(o)) { + o = i$.next(); + } + + return changed; + } + + private static boolean removeOccurrencesImpl(Multiset multisetToModify, Multiset occurrencesToRemove) { + Preconditions.checkNotNull(multisetToModify); + Preconditions.checkNotNull(occurrencesToRemove); + boolean changed = false; + Iterator entryIterator = multisetToModify.entrySet().iterator(); + + while(entryIterator.hasNext()) { + Multiset.Entry entry = (Multiset.Entry)entryIterator.next(); + int removeCount = occurrencesToRemove.count(entry.getElement()); + if (removeCount >= entry.getCount()) { + entryIterator.remove(); + changed = true; + } else if (removeCount > 0) { + multisetToModify.remove(entry.getElement(), removeCount); + changed = true; + } + } + + return changed; + } + + static boolean equalsImpl(Multiset multiset, @Nullable Object object) { + if (object == multiset) { + return true; + } else if (object instanceof Multiset) { + Multiset that = (Multiset)object; + if (multiset.size() == that.size() && multiset.entrySet().size() == that.entrySet().size()) { + Iterator i$ = that.entrySet().iterator(); + + Multiset.Entry entry; + do { + if (!i$.hasNext()) { + return true; + } + + entry = (Multiset.Entry)i$.next(); + } while(multiset.count(entry.getElement()) == entry.getCount()); + + return false; + } else { + return false; + } + } else { + return false; + } + } + + static boolean addAllImpl(Multiset self, Collection elements) { + if (elements.isEmpty()) { + return false; + } else { + if (elements instanceof Multiset) { + Multiset that = cast(elements); + Iterator i$ = that.entrySet().iterator(); + + while(i$.hasNext()) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + self.add(entry.getElement(), entry.getCount()); + } + } else { + Iterators.addAll(self, elements.iterator()); + } + + return true; + } + } + + static boolean removeAllImpl(Multiset self, Collection elementsToRemove) { + Collection collection = elementsToRemove instanceof Multiset ? ((Multiset)elementsToRemove).elementSet() : elementsToRemove; + return self.elementSet().removeAll((Collection)collection); + } + + static boolean retainAllImpl(Multiset self, Collection elementsToRetain) { + Preconditions.checkNotNull(elementsToRetain); + Collection collection = elementsToRetain instanceof Multiset ? ((Multiset)elementsToRetain).elementSet() : elementsToRetain; + return self.elementSet().retainAll((Collection)collection); + } + + static int setCountImpl(Multiset self, E element, int count) { + CollectPreconditions.checkNonnegative(count, "count"); + int oldCount = self.count(element); + int delta = count - oldCount; + if (delta > 0) { + self.add(element, delta); + } else if (delta < 0) { + self.remove(element, -delta); + } + + return oldCount; + } + + static boolean setCountImpl(Multiset self, E element, int oldCount, int newCount) { + CollectPreconditions.checkNonnegative(oldCount, "oldCount"); + CollectPreconditions.checkNonnegative(newCount, "newCount"); + if (self.count(element) == oldCount) { + self.setCount(element, newCount); + return true; + } else { + return false; + } + } + + static Iterator iteratorImpl(Multiset multiset) { + return new Multisets.MultisetIteratorImpl(multiset, multiset.entrySet().iterator()); + } + + static int sizeImpl(Multiset multiset) { + long size = 0L; + + Multiset.Entry entry; + for(Iterator i$ = multiset.entrySet().iterator(); i$.hasNext(); size += (long)entry.getCount()) { + entry = (Multiset.Entry)i$.next(); + } + + return Ints.saturatedCast(size); + } + + static Multiset cast(Iterable iterable) { + return (Multiset)iterable; + } + + @Beta + public static ImmutableMultiset copyHighestCountFirst(Multiset multiset) { + List> sortedEntries = DECREASING_COUNT_ORDERING.immutableSortedCopy(multiset.entrySet()); + return ImmutableMultiset.copyFromEntries(sortedEntries); + } + + static final class MultisetIteratorImpl implements Iterator { + private final Multiset multiset; + private final Iterator> entryIterator; + private Multiset.Entry currentEntry; + private int laterCount; + private int totalCount; + private boolean canRemove; + + MultisetIteratorImpl(Multiset multiset, Iterator> entryIterator) { + this.multiset = multiset; + this.entryIterator = entryIterator; + } + + public boolean hasNext() { + return this.laterCount > 0 || this.entryIterator.hasNext(); + } + + public E next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + if (this.laterCount == 0) { + this.currentEntry = (Multiset.Entry)this.entryIterator.next(); + this.totalCount = this.laterCount = this.currentEntry.getCount(); + } + + --this.laterCount; + this.canRemove = true; + return this.currentEntry.getElement(); + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.canRemove); + if (this.totalCount == 1) { + this.entryIterator.remove(); + } else { + this.multiset.remove(this.currentEntry.getElement()); + } + + --this.totalCount; + this.canRemove = false; + } + } + + abstract static class EntrySet extends Sets.ImprovedAbstractSet> { + abstract Multiset multiset(); + + public boolean contains(@Nullable Object o) { + if (o instanceof Multiset.Entry) { + Multiset.Entry entry = (Multiset.Entry)o; + if (entry.getCount() <= 0) { + return false; + } else { + int count = this.multiset().count(entry.getElement()); + return count == entry.getCount(); + } + } else { + return false; + } + } + + 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) { + Multiset multiset = this.multiset(); + return multiset.setCount(element, entryCount, 0); + } + } + + return false; + } + + public void clear() { + this.multiset().clear(); + } + } + + abstract static class ElementSet extends Sets.ImprovedAbstractSet { + abstract Multiset multiset(); + + public void clear() { + this.multiset().clear(); + } + + public boolean contains(Object o) { + return this.multiset().contains(o); + } + + public boolean containsAll(Collection c) { + return this.multiset().containsAll(c); + } + + public boolean isEmpty() { + return this.multiset().isEmpty(); + } + + public Iterator iterator() { + return new TransformedIterator, E>(this.multiset().entrySet().iterator()) { + E transform(Multiset.Entry entry) { + return entry.getElement(); + } + }; + } + + public boolean remove(Object o) { + int count = this.multiset().count(o); + if (count > 0) { + this.multiset().remove(o, count); + return true; + } else { + return false; + } + } + + public int size() { + return this.multiset().entrySet().size(); + } + } + + abstract static class AbstractEntry implements Multiset.Entry { + public boolean equals(@Nullable Object object) { + if (!(object instanceof Multiset.Entry)) { + return false; + } else { + Multiset.Entry that = (Multiset.Entry)object; + return this.getCount() == that.getCount() && Objects.equal(this.getElement(), that.getElement()); + } + } + + public int hashCode() { + E e = this.getElement(); + return (e == null ? 0 : e.hashCode()) ^ this.getCount(); + } + + public String toString() { + String text = String.valueOf(this.getElement()); + int n = this.getCount(); + String var10000; + if (n == 1) { + var10000 = text; + } else { + String var3 = String.valueOf(String.valueOf(text)); + var10000 = (new StringBuilder(14 + var3.length())).append(var3).append(" x ").append(n).toString(); + } + + return var10000; + } + } + + private static final class FilteredMultiset extends AbstractMultiset { + final Multiset unfiltered; + final Predicate predicate; + + FilteredMultiset(Multiset unfiltered, Predicate predicate) { + this.unfiltered = (Multiset)Preconditions.checkNotNull(unfiltered); + this.predicate = (Predicate)Preconditions.checkNotNull(predicate); + } + + public UnmodifiableIterator iterator() { + return Iterators.filter(this.unfiltered.iterator(), this.predicate); + } + + Set createElementSet() { + return Sets.filter(this.unfiltered.elementSet(), this.predicate); + } + + Set> createEntrySet() { + return Sets.filter(this.unfiltered.entrySet(), new Predicate>() { + public boolean apply(Multiset.Entry entry) { + return FilteredMultiset.this.predicate.apply(entry.getElement()); + } + }); + } + + Iterator> entryIterator() { + throw new AssertionError("should never be called"); + } + + int distinctElements() { + return this.elementSet().size(); + } + + public int count(@Nullable Object element) { + int count = this.unfiltered.count(element); + if (count > 0) { + return this.predicate.apply(element) ? count : 0; + } else { + return 0; + } + } + + public int add(@Nullable E element, int occurrences) { + Preconditions.checkArgument(this.predicate.apply(element), "Element %s does not match predicate %s", element, this.predicate); + return this.unfiltered.add(element, occurrences); + } + + public int remove(@Nullable Object element, int occurrences) { + CollectPreconditions.checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return this.count(element); + } else { + return this.contains(element) ? this.unfiltered.remove(element, occurrences) : 0; + } + } + + public void clear() { + this.elementSet().clear(); + } + } + + static final class ImmutableEntry extends Multisets.AbstractEntry implements Serializable { + @Nullable + final E element; + final int count; + private static final long serialVersionUID = 0L; + + ImmutableEntry(@Nullable E element, int count) { + this.element = element; + this.count = count; + CollectPreconditions.checkNonnegative(count, "count"); + } + + @Nullable + public E getElement() { + return this.element; + } + + public int getCount() { + return this.count; + } + } + + static class UnmodifiableMultiset extends ForwardingMultiset implements Serializable { + final Multiset delegate; + transient Set elementSet; + transient Set> entrySet; + private static final long serialVersionUID = 0L; + + UnmodifiableMultiset(Multiset delegate) { + this.delegate = delegate; + } + + protected Multiset delegate() { + return this.delegate; + } + + Set createElementSet() { + return Collections.unmodifiableSet(this.delegate.elementSet()); + } + + public Set elementSet() { + Set es = this.elementSet; + return es == null ? (this.elementSet = this.createElementSet()) : es; + } + + public Set> entrySet() { + Set> es = this.entrySet; + return es == null ? (this.entrySet = Collections.unmodifiableSet(this.delegate.entrySet())) : es; + } + + public Iterator iterator() { + return Iterators.unmodifiableIterator(this.delegate.iterator()); + } + + public boolean add(E element) { + throw new UnsupportedOperationException(); + } + + public int add(E element, int occurences) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection elementsToAdd) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object element) { + throw new UnsupportedOperationException(); + } + + public int remove(Object element, int occurrences) { + throw new UnsupportedOperationException(); + } + + public boolean removeAll(Collection elementsToRemove) { + throw new UnsupportedOperationException(); + } + + public boolean retainAll(Collection elementsToRetain) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public int setCount(E element, int count) { + throw new UnsupportedOperationException(); + } + + public boolean setCount(E element, int oldCount, int newCount) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/src/main/com/google/common/collect/MutableClassToInstanceMap.java b/src/main/com/google/common/collect/MutableClassToInstanceMap.java new file mode 100644 index 0000000..6c60abb --- /dev/null +++ b/src/main/com/google/common/collect/MutableClassToInstanceMap.java @@ -0,0 +1,38 @@ +package com.google.common.collect; + +import com.google.common.primitives.Primitives; +import java.util.HashMap; +import java.util.Map; + +public final class MutableClassToInstanceMap extends MapConstraints.ConstrainedMap, B> implements ClassToInstanceMap { + private static final MapConstraint, Object> VALUE_CAN_BE_CAST_TO_KEY = new MapConstraint, Object>() { + public void checkKeyValue(Class key, Object value) { + MutableClassToInstanceMap.cast(key, value); + } + }; + private static final long serialVersionUID = 0L; + + public static MutableClassToInstanceMap create() { + return new MutableClassToInstanceMap(new HashMap()); + } + + public static MutableClassToInstanceMap create(Map, B> backingMap) { + return new MutableClassToInstanceMap(backingMap); + } + + private MutableClassToInstanceMap(Map, B> delegate) { + super(delegate, VALUE_CAN_BE_CAST_TO_KEY); + } + + public T putInstance(Class type, T value) { + return cast(type, this.put(type, value)); + } + + public T getInstance(Class type) { + return cast(type, this.get(type)); + } + + private static T cast(Class type, B value) { + return Primitives.wrap(type).cast(value); + } +} diff --git a/src/main/com/google/common/collect/NaturalOrdering.java b/src/main/com/google/common/collect/NaturalOrdering.java new file mode 100644 index 0000000..3d15649 --- /dev/null +++ b/src/main/com/google/common/collect/NaturalOrdering.java @@ -0,0 +1,34 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; + +@GwtCompatible( + serializable = true +) +final class NaturalOrdering extends Ordering implements Serializable { + static final NaturalOrdering INSTANCE = new NaturalOrdering(); + private static final long serialVersionUID = 0L; + + public int compare(Comparable left, Comparable right) { + Preconditions.checkNotNull(left); + Preconditions.checkNotNull(right); + return left.compareTo(right); + } + + public Ordering reverse() { + return ReverseNaturalOrdering.INSTANCE; + } + + private Object readResolve() { + return INSTANCE; + } + + public String toString() { + return "Ordering.natural()"; + } + + private NaturalOrdering() { + } +} diff --git a/src/main/com/google/common/collect/NullsFirstOrdering.java b/src/main/com/google/common/collect/NullsFirstOrdering.java new file mode 100644 index 0000000..a688dfc --- /dev/null +++ b/src/main/com/google/common/collect/NullsFirstOrdering.java @@ -0,0 +1,59 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class NullsFirstOrdering extends Ordering implements Serializable { + final Ordering ordering; + private static final long serialVersionUID = 0L; + + NullsFirstOrdering(Ordering ordering) { + this.ordering = ordering; + } + + public int compare(@Nullable T left, @Nullable T right) { + if (left == right) { + return 0; + } else if (left == null) { + return -1; + } else { + return right == null ? 1 : this.ordering.compare(left, right); + } + } + + public Ordering reverse() { + return this.ordering.reverse().nullsLast(); + } + + public Ordering nullsFirst() { + return this; + } + + public Ordering nullsLast() { + return this.ordering.nullsLast(); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (object instanceof NullsFirstOrdering) { + NullsFirstOrdering that = (NullsFirstOrdering)object; + return this.ordering.equals(that.ordering); + } else { + return false; + } + } + + public int hashCode() { + return this.ordering.hashCode() ^ 957692532; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.ordering)); + return (new StringBuilder(13 + var1.length())).append(var1).append(".nullsFirst()").toString(); + } +} diff --git a/src/main/com/google/common/collect/NullsLastOrdering.java b/src/main/com/google/common/collect/NullsLastOrdering.java new file mode 100644 index 0000000..b21ba05 --- /dev/null +++ b/src/main/com/google/common/collect/NullsLastOrdering.java @@ -0,0 +1,59 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class NullsLastOrdering extends Ordering implements Serializable { + final Ordering ordering; + private static final long serialVersionUID = 0L; + + NullsLastOrdering(Ordering ordering) { + this.ordering = ordering; + } + + public int compare(@Nullable T left, @Nullable T right) { + if (left == right) { + return 0; + } else if (left == null) { + return 1; + } else { + return right == null ? -1 : this.ordering.compare(left, right); + } + } + + public Ordering reverse() { + return this.ordering.reverse().nullsFirst(); + } + + public Ordering nullsFirst() { + return this.ordering.nullsFirst(); + } + + public Ordering nullsLast() { + return this; + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (object instanceof NullsLastOrdering) { + NullsLastOrdering that = (NullsLastOrdering)object; + return this.ordering.equals(that.ordering); + } else { + return false; + } + } + + public int hashCode() { + return this.ordering.hashCode() ^ -921210296; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.ordering)); + return (new StringBuilder(12 + var1.length())).append(var1).append(".nullsLast()").toString(); + } +} diff --git a/src/main/com/google/common/collect/ObjectArrays.java b/src/main/com/google/common/collect/ObjectArrays.java new file mode 100644 index 0000000..11367e0 --- /dev/null +++ b/src/main/com/google/common/collect/ObjectArrays.java @@ -0,0 +1,133 @@ +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.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class ObjectArrays { + static final Object[] EMPTY_ARRAY = new Object[0]; + + private ObjectArrays() { + } + + @GwtIncompatible("Array.newInstance(Class, int)") + public static T[] newArray(Class type, int length) { + return (Object[])((Object[])Array.newInstance(type, length)); + } + + public static T[] newArray(T[] reference, int length) { + return Platform.newArray(reference, length); + } + + @GwtIncompatible("Array.newInstance(Class, int)") + public static T[] concat(T[] first, T[] second, Class 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); + return result; + } + + public static 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); + return result; + } + + public static T[] concat(T[] array, @Nullable T element) { + T[] result = arraysCopyOf(array, array.length + 1); + result[array.length] = element; + return result; + } + + static T[] arraysCopyOf(T[] original, int newLength) { + T[] copy = newArray(original, newLength); + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + static T[] toArrayImpl(Collection c, T[] array) { + int size = c.size(); + if (array.length < size) { + array = newArray(array, size); + } + + fillArray(c, array); + if (array.length > size) { + array[size] = null; + } + + return array; + } + + static T[] toArrayImpl(Object[] src, int offset, int len, T[] dst) { + Preconditions.checkPositionIndexes(offset, offset + len, src.length); + if (dst.length < len) { + dst = newArray(dst, len); + } else if (dst.length > len) { + dst[len] = null; + } + + System.arraycopy(src, offset, dst, 0, len); + return dst; + } + + static Object[] toArrayImpl(Collection c) { + return fillArray(c, new Object[c.size()]); + } + + static Object[] copyAsObjectArray(Object[] elements, int offset, int length) { + Preconditions.checkPositionIndexes(offset, offset + length, elements.length); + if (length == 0) { + return EMPTY_ARRAY; + } else { + Object[] result = new Object[length]; + System.arraycopy(elements, offset, result, 0, length); + return result; + } + } + + private static Object[] fillArray(Iterable elements, Object[] array) { + int i = 0; + + Object element; + for(Iterator i$ = elements.iterator(); i$.hasNext(); array[i++] = element) { + element = i$.next(); + } + + return array; + } + + static void swap(Object[] array, int i, int j) { + Object temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + + static Object[] checkElementsNotNull(Object... array) { + return checkElementsNotNull(array, array.length); + } + + static Object[] checkElementsNotNull(Object[] array, int length) { + for(int i = 0; i < length; ++i) { + checkElementNotNull(array[i], i); + } + + return array; + } + + static Object checkElementNotNull(Object element, int index) { + if (element == null) { + throw new NullPointerException((new StringBuilder(20)).append("at index ").append(index).toString()); + } else { + return element; + } + } +} diff --git a/src/main/com/google/common/collect/Ordering.java b/src/main/com/google/common/collect/Ordering.java new file mode 100644 index 0000000..1394895 --- /dev/null +++ b/src/main/com/google/common/collect/Ordering.java @@ -0,0 +1,422 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import java.util.ArrayList; +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.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; + +@GwtCompatible +public abstract class Ordering implements Comparator { + static final int LEFT_IS_GREATER = 1; + static final int RIGHT_IS_GREATER = -1; + + @GwtCompatible( + serializable = true + ) + public static Ordering natural() { + return NaturalOrdering.INSTANCE; + } + + @GwtCompatible( + serializable = true + ) + public static Ordering from(Comparator comparator) { + return (Ordering)(comparator instanceof Ordering ? (Ordering)comparator : new ComparatorOrdering(comparator)); + } + + /** @deprecated */ + @Deprecated + @GwtCompatible( + serializable = true + ) + public static Ordering from(Ordering ordering) { + return (Ordering)Preconditions.checkNotNull(ordering); + } + + @GwtCompatible( + serializable = true + ) + public static Ordering explicit(List valuesInOrder) { + return new ExplicitOrdering(valuesInOrder); + } + + @GwtCompatible( + serializable = true + ) + public static Ordering explicit(T leastValue, T... remainingValuesInOrder) { + return explicit(Lists.asList(leastValue, remainingValuesInOrder)); + } + + @GwtCompatible( + serializable = true + ) + public static Ordering allEqual() { + return AllEqualOrdering.INSTANCE; + } + + @GwtCompatible( + serializable = true + ) + public static Ordering usingToString() { + return UsingToStringOrdering.INSTANCE; + } + + public static Ordering arbitrary() { + return Ordering.ArbitraryOrderingHolder.ARBITRARY_ORDERING; + } + + protected Ordering() { + } + + @GwtCompatible( + serializable = true + ) + public Ordering reverse() { + return new ReverseOrdering(this); + } + + @GwtCompatible( + serializable = true + ) + public Ordering nullsFirst() { + return new NullsFirstOrdering(this); + } + + @GwtCompatible( + serializable = true + ) + public Ordering nullsLast() { + return new NullsLastOrdering(this); + } + + @GwtCompatible( + serializable = true + ) + public Ordering onResultOf(Function function) { + return new ByFunctionOrdering(function, this); + } + + Ordering> onKeys() { + return this.onResultOf(Maps.keyFunction()); + } + + @GwtCompatible( + serializable = true + ) + public Ordering compound(Comparator secondaryComparator) { + return new CompoundOrdering(this, (Comparator)Preconditions.checkNotNull(secondaryComparator)); + } + + @GwtCompatible( + serializable = true + ) + public static Ordering compound(Iterable> comparators) { + return new CompoundOrdering(comparators); + } + + @GwtCompatible( + serializable = true + ) + public Ordering> lexicographical() { + return new LexicographicalOrdering(this); + } + + public abstract int compare(@Nullable T var1, @Nullable T var2); + + public E min(Iterator iterator) { + Object minSoFar; + for(minSoFar = iterator.next(); iterator.hasNext(); minSoFar = this.min(minSoFar, iterator.next())) { + } + + return minSoFar; + } + + public E min(Iterable iterable) { + return this.min(iterable.iterator()); + } + + public E min(@Nullable E a, @Nullable E b) { + return this.compare(a, b) <= 0 ? a : b; + } + + public E min(@Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E minSoFar = this.min(this.min(a, b), c); + Object[] arr$ = rest; + int len$ = rest.length; + + for(int i$ = 0; i$ < len$; ++i$) { + E r = arr$[i$]; + minSoFar = this.min(minSoFar, r); + } + + return minSoFar; + } + + public E max(Iterator iterator) { + Object maxSoFar; + for(maxSoFar = iterator.next(); iterator.hasNext(); maxSoFar = this.max(maxSoFar, iterator.next())) { + } + + return maxSoFar; + } + + public E max(Iterable iterable) { + return this.max(iterable.iterator()); + } + + public E max(@Nullable E a, @Nullable E b) { + return this.compare(a, b) >= 0 ? a : b; + } + + public E max(@Nullable E a, @Nullable E b, @Nullable E c, E... rest) { + E maxSoFar = this.max(this.max(a, b), c); + Object[] arr$ = rest; + int len$ = rest.length; + + for(int i$ = 0; i$ < len$; ++i$) { + E r = arr$[i$]; + maxSoFar = this.max(maxSoFar, r); + } + + return maxSoFar; + } + + public List leastOf(Iterable iterable, int k) { + if (iterable instanceof Collection) { + Collection collection = (Collection)iterable; + if ((long)collection.size() <= 2L * (long)k) { + E[] array = (Object[])collection.toArray(); + Arrays.sort(array, this); + if (array.length > k) { + array = ObjectArrays.arraysCopyOf(array, k); + } + + return Collections.unmodifiableList(Arrays.asList(array)); + } + } + + return this.leastOf(iterable.iterator(), k); + } + + public List leastOf(Iterator elements, int k) { + Preconditions.checkNotNull(elements); + CollectPreconditions.checkNonnegative(k, "k"); + if (k != 0 && elements.hasNext()) { + if (k >= 1073741823) { + ArrayList list = Lists.newArrayList(elements); + Collections.sort(list, this); + if (list.size() > k) { + list.subList(k, list.size()).clear(); + } + + list.trimToSize(); + return Collections.unmodifiableList(list); + } else { + int bufferCap = k * 2; + E[] buffer = (Object[])(new Object[bufferCap]); + E threshold = elements.next(); + buffer[0] = threshold; + + int bufferSize; + Object e; + for(bufferSize = 1; bufferSize < k && elements.hasNext(); threshold = this.max(threshold, e)) { + e = elements.next(); + buffer[bufferSize++] = e; + } + + while(true) { + do { + do { + if (!elements.hasNext()) { + Arrays.sort(buffer, 0, bufferSize, this); + bufferSize = Math.min(bufferSize, k); + return Collections.unmodifiableList(Arrays.asList(ObjectArrays.arraysCopyOf(buffer, bufferSize))); + } + + e = elements.next(); + } while(this.compare(e, threshold) >= 0); + + buffer[bufferSize++] = e; + } while(bufferSize != bufferCap); + + int left = 0; + int right = bufferCap - 1; + int minThresholdPosition = 0; + + int i; + while(left < right) { + i = left + right + 1 >>> 1; + int pivotNewIndex = this.partition(buffer, left, right, i); + if (pivotNewIndex > k) { + right = pivotNewIndex - 1; + } else { + if (pivotNewIndex >= k) { + break; + } + + left = Math.max(pivotNewIndex, left + 1); + minThresholdPosition = pivotNewIndex; + } + } + + bufferSize = k; + threshold = buffer[minThresholdPosition]; + + for(i = minThresholdPosition + 1; i < bufferSize; ++i) { + threshold = this.max(threshold, buffer[i]); + } + } + } + } else { + return ImmutableList.of(); + } + } + + private int partition(E[] values, int left, int right, int pivotIndex) { + E pivotValue = values[pivotIndex]; + values[pivotIndex] = values[right]; + values[right] = pivotValue; + int storeIndex = left; + + for(int i = left; i < right; ++i) { + if (this.compare(values[i], pivotValue) < 0) { + ObjectArrays.swap(values, storeIndex, i); + ++storeIndex; + } + } + + ObjectArrays.swap(values, right, storeIndex); + return storeIndex; + } + + public List greatestOf(Iterable iterable, int k) { + return this.reverse().leastOf(iterable, k); + } + + public List greatestOf(Iterator iterator, int k) { + return this.reverse().leastOf(iterator, k); + } + + public List sortedCopy(Iterable elements) { + E[] array = (Object[])Iterables.toArray(elements); + Arrays.sort(array, this); + return Lists.newArrayList((Iterable)Arrays.asList(array)); + } + + public ImmutableList immutableSortedCopy(Iterable elements) { + E[] array = (Object[])Iterables.toArray(elements); + Object[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + E e = arr$[i$]; + Preconditions.checkNotNull(e); + } + + Arrays.sort(array, this); + return ImmutableList.asImmutableList(array); + } + + public boolean isOrdered(Iterable iterable) { + Iterator it = iterable.iterator(); + Object next; + if (it.hasNext()) { + for(Object prev = it.next(); it.hasNext(); prev = next) { + next = it.next(); + if (this.compare(prev, next) > 0) { + return false; + } + } + } + + return true; + } + + public boolean isStrictlyOrdered(Iterable iterable) { + Iterator it = iterable.iterator(); + Object next; + if (it.hasNext()) { + for(Object prev = it.next(); it.hasNext(); prev = next) { + next = it.next(); + if (this.compare(prev, next) >= 0) { + return false; + } + } + } + + return true; + } + + public int binarySearch(List sortedList, @Nullable T key) { + return Collections.binarySearch(sortedList, key, this); + } + + @VisibleForTesting + static class IncomparableValueException extends ClassCastException { + final Object value; + private static final long serialVersionUID = 0L; + + IncomparableValueException(Object value) { + String var2 = String.valueOf(String.valueOf(value)); + super((new StringBuilder(22 + var2.length())).append("Cannot compare value: ").append(var2).toString()); + this.value = value; + } + } + + @VisibleForTesting + static class ArbitraryOrdering extends Ordering { + private Map uids = Platform.tryWeakKeys(new MapMaker()).makeComputingMap(new Function() { + final AtomicInteger counter = new AtomicInteger(0); + + public Integer apply(Object from) { + return this.counter.getAndIncrement(); + } + }); + + public int compare(Object left, Object right) { + if (left == right) { + return 0; + } else if (left == null) { + return -1; + } else if (right == null) { + return 1; + } else { + int leftCode = this.identityHashCode(left); + int rightCode = this.identityHashCode(right); + if (leftCode != rightCode) { + return leftCode < rightCode ? -1 : 1; + } else { + int result = ((Integer)this.uids.get(left)).compareTo((Integer)this.uids.get(right)); + if (result == 0) { + throw new AssertionError(); + } else { + return result; + } + } + } + } + + public String toString() { + return "Ordering.arbitrary()"; + } + + int identityHashCode(Object object) { + return System.identityHashCode(object); + } + } + + private static class ArbitraryOrderingHolder { + static final Ordering ARBITRARY_ORDERING = new Ordering.ArbitraryOrdering(); + } +} diff --git a/src/main/com/google/common/collect/PeekingIterator.java b/src/main/com/google/common/collect/PeekingIterator.java new file mode 100644 index 0000000..35206e8 --- /dev/null +++ b/src/main/com/google/common/collect/PeekingIterator.java @@ -0,0 +1,13 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; + +@GwtCompatible +public interface PeekingIterator extends Iterator { + E peek(); + + E next(); + + void remove(); +} diff --git a/src/main/com/google/common/collect/Platform.java b/src/main/com/google/common/collect/Platform.java new file mode 100644 index 0000000..e964c49 --- /dev/null +++ b/src/main/com/google/common/collect/Platform.java @@ -0,0 +1,52 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.Map; +import java.util.NavigableMap; +import java.util.NavigableSet; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Map.Entry; + +@GwtCompatible( + emulated = true +) +final class Platform { + static T[] newArray(T[] reference, int length) { + Class type = reference.getClass().getComponentType(); + T[] result = (Object[])((Object[])Array.newInstance(type, length)); + return result; + } + + static Set newSetFromMap(Map map) { + return Collections.newSetFromMap(map); + } + + static MapMaker tryWeakKeys(MapMaker mapMaker) { + return mapMaker.weakKeys(); + } + + static SortedMap mapsTransformEntriesSortedMap(SortedMap fromMap, Maps.EntryTransformer transformer) { + return (SortedMap)(fromMap instanceof NavigableMap ? Maps.transformEntries((NavigableMap)fromMap, transformer) : Maps.transformEntriesIgnoreNavigable(fromMap, transformer)); + } + + static SortedMap mapsAsMapSortedSet(SortedSet set, Function function) { + return (SortedMap)(set instanceof NavigableSet ? Maps.asMap((NavigableSet)set, function) : Maps.asMapSortedIgnoreNavigable(set, function)); + } + + static SortedSet setsFilterSortedSet(SortedSet set, Predicate predicate) { + return (SortedSet)(set instanceof NavigableSet ? Sets.filter((NavigableSet)set, predicate) : Sets.filterSortedIgnoreNavigable(set, predicate)); + } + + static SortedMap mapsFilterSortedMap(SortedMap map, Predicate> predicate) { + return (SortedMap)(map instanceof NavigableMap ? Maps.filterEntries((NavigableMap)map, predicate) : Maps.filterSortedIgnoreNavigable(map, predicate)); + } + + private Platform() { + } +} diff --git a/src/main/com/google/common/collect/Queues.java b/src/main/com/google/common/collect/Queues.java new file mode 100644 index 0000000..a61a89f --- /dev/null +++ b/src/main/com/google/common/collect/Queues.java @@ -0,0 +1,191 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +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; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; + +public final class Queues { + private Queues() { + } + + public static ArrayBlockingQueue newArrayBlockingQueue(int capacity) { + return new ArrayBlockingQueue(capacity); + } + + public static ArrayDeque newArrayDeque() { + return new ArrayDeque(); + } + + public static ArrayDeque newArrayDeque(Iterable elements) { + if (elements instanceof Collection) { + return new ArrayDeque(Collections2.cast(elements)); + } else { + ArrayDeque deque = new ArrayDeque(); + Iterables.addAll(deque, elements); + return deque; + } + } + + public static ConcurrentLinkedQueue newConcurrentLinkedQueue() { + return new ConcurrentLinkedQueue(); + } + + public static ConcurrentLinkedQueue newConcurrentLinkedQueue(Iterable elements) { + if (elements instanceof Collection) { + return new ConcurrentLinkedQueue(Collections2.cast(elements)); + } else { + ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue(); + Iterables.addAll(queue, elements); + return queue; + } + } + + public static LinkedBlockingDeque newLinkedBlockingDeque() { + return new LinkedBlockingDeque(); + } + + public static LinkedBlockingDeque newLinkedBlockingDeque(int capacity) { + return new LinkedBlockingDeque(capacity); + } + + public static LinkedBlockingDeque newLinkedBlockingDeque(Iterable elements) { + if (elements instanceof Collection) { + return new LinkedBlockingDeque(Collections2.cast(elements)); + } else { + LinkedBlockingDeque deque = new LinkedBlockingDeque(); + Iterables.addAll(deque, elements); + return deque; + } + } + + public static LinkedBlockingQueue newLinkedBlockingQueue() { + return new LinkedBlockingQueue(); + } + + public static LinkedBlockingQueue newLinkedBlockingQueue(int capacity) { + return new LinkedBlockingQueue(capacity); + } + + public static LinkedBlockingQueue newLinkedBlockingQueue(Iterable elements) { + if (elements instanceof Collection) { + return new LinkedBlockingQueue(Collections2.cast(elements)); + } else { + LinkedBlockingQueue queue = new LinkedBlockingQueue(); + Iterables.addAll(queue, elements); + return queue; + } + } + + public static PriorityBlockingQueue newPriorityBlockingQueue() { + return new PriorityBlockingQueue(); + } + + public static PriorityBlockingQueue newPriorityBlockingQueue(Iterable elements) { + if (elements instanceof Collection) { + return new PriorityBlockingQueue(Collections2.cast(elements)); + } else { + PriorityBlockingQueue queue = new PriorityBlockingQueue(); + Iterables.addAll(queue, elements); + return queue; + } + } + + public static PriorityQueue newPriorityQueue() { + return new PriorityQueue(); + } + + public static PriorityQueue newPriorityQueue(Iterable elements) { + if (elements instanceof Collection) { + return new PriorityQueue(Collections2.cast(elements)); + } else { + PriorityQueue queue = new PriorityQueue(); + Iterables.addAll(queue, elements); + return queue; + } + } + + public static SynchronousQueue newSynchronousQueue() { + return new SynchronousQueue(); + } + + @Beta + public static int drain(BlockingQueue q, Collection buffer, int numElements, long timeout, TimeUnit unit) throws InterruptedException { + Preconditions.checkNotNull(buffer); + long deadline = System.nanoTime() + unit.toNanos(timeout); + int added = 0; + + while(added < numElements) { + added += q.drainTo(buffer, numElements - added); + if (added < numElements) { + E e = q.poll(deadline - System.nanoTime(), TimeUnit.NANOSECONDS); + if (e == null) { + break; + } + + buffer.add(e); + ++added; + } + } + + return added; + } + + @Beta + public static int drainUninterruptibly(BlockingQueue q, Collection buffer, int numElements, long timeout, TimeUnit unit) { + Preconditions.checkNotNull(buffer); + long deadline = System.nanoTime() + unit.toNanos(timeout); + int added = 0; + boolean interrupted = false; + + try { + while(added < numElements) { + added += q.drainTo(buffer, numElements - added); + if (added < numElements) { + Object e; + while(true) { + try { + e = q.poll(deadline - System.nanoTime(), TimeUnit.NANOSECONDS); + break; + } catch (InterruptedException var15) { + interrupted = true; + } + } + + if (e == null) { + break; + } + + buffer.add(e); + ++added; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + + return added; + } + + public static Queue synchronizedQueue(Queue queue) { + return Synchronized.queue(queue, (Object)null); + } + + public static Deque synchronizedDeque(Deque deque) { + return Synchronized.deque(deque, (Object)null); + } +} diff --git a/src/main/com/google/common/collect/Range.java b/src/main/com/google/common/collect/Range.java new file mode 100644 index 0000000..605ed39 --- /dev/null +++ b/src/main/com/google/common/collect/Range.java @@ -0,0 +1,302 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.SortedSet; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Range implements Predicate, Serializable { + private static final Function LOWER_BOUND_FN = new Function() { + public Cut apply(Range range) { + return range.lowerBound; + } + }; + private static final Function UPPER_BOUND_FN = new Function() { + public Cut apply(Range range) { + return range.upperBound; + } + }; + static final Ordering> RANGE_LEX_ORDERING = new Ordering>() { + public int compare(Range left, Range right) { + return ComparisonChain.start().compare(left.lowerBound, right.lowerBound).compare(left.upperBound, right.upperBound).result(); + } + }; + private static final Range ALL = new Range(Cut.belowAll(), Cut.aboveAll()); + final Cut lowerBound; + final Cut upperBound; + private static final long serialVersionUID = 0L; + + static > Function, Cut> lowerBoundFn() { + return LOWER_BOUND_FN; + } + + static > Function, Cut> upperBoundFn() { + return UPPER_BOUND_FN; + } + + static > Range create(Cut lowerBound, Cut upperBound) { + return new Range(lowerBound, upperBound); + } + + public static > Range open(C lower, C upper) { + return create(Cut.aboveValue(lower), Cut.belowValue(upper)); + } + + public static > Range closed(C lower, C upper) { + return create(Cut.belowValue(lower), Cut.aboveValue(upper)); + } + + public static > Range closedOpen(C lower, C upper) { + return create(Cut.belowValue(lower), Cut.belowValue(upper)); + } + + public static > Range openClosed(C lower, C upper) { + return create(Cut.aboveValue(lower), Cut.aboveValue(upper)); + } + + public static > Range range(C lower, BoundType lowerType, C upper, BoundType upperType) { + Preconditions.checkNotNull(lowerType); + Preconditions.checkNotNull(upperType); + Cut lowerBound = lowerType == BoundType.OPEN ? Cut.aboveValue(lower) : Cut.belowValue(lower); + Cut upperBound = upperType == BoundType.OPEN ? Cut.belowValue(upper) : Cut.aboveValue(upper); + return create(lowerBound, upperBound); + } + + public static > Range lessThan(C endpoint) { + return create(Cut.belowAll(), Cut.belowValue(endpoint)); + } + + public static > Range atMost(C endpoint) { + return create(Cut.belowAll(), Cut.aboveValue(endpoint)); + } + + public static > Range upTo(C endpoint, BoundType boundType) { + switch(boundType) { + case OPEN: + return lessThan(endpoint); + case CLOSED: + return atMost(endpoint); + default: + throw new AssertionError(); + } + } + + public static > Range greaterThan(C endpoint) { + return create(Cut.aboveValue(endpoint), Cut.aboveAll()); + } + + public static > Range atLeast(C endpoint) { + return create(Cut.belowValue(endpoint), Cut.aboveAll()); + } + + public static > Range downTo(C endpoint, BoundType boundType) { + switch(boundType) { + case OPEN: + return greaterThan(endpoint); + case CLOSED: + return atLeast(endpoint); + default: + throw new AssertionError(); + } + } + + public static > Range all() { + return ALL; + } + + public static > Range singleton(C value) { + return closed(value, value); + } + + public static > Range encloseAll(Iterable values) { + Preconditions.checkNotNull(values); + if (values instanceof ContiguousSet) { + return ((ContiguousSet)values).range(); + } else { + Iterator valueIterator = values.iterator(); + C min = (Comparable)Preconditions.checkNotNull(valueIterator.next()); + + Comparable max; + Comparable value; + for(max = min; valueIterator.hasNext(); max = (Comparable)Ordering.natural().max(max, value)) { + value = (Comparable)Preconditions.checkNotNull(valueIterator.next()); + min = (Comparable)Ordering.natural().min(min, value); + } + + return closed(min, max); + } + } + + private Range(Cut lowerBound, Cut upperBound) { + if (lowerBound.compareTo(upperBound) <= 0 && lowerBound != Cut.aboveAll() && upperBound != Cut.belowAll()) { + this.lowerBound = (Cut)Preconditions.checkNotNull(lowerBound); + this.upperBound = (Cut)Preconditions.checkNotNull(upperBound); + } else { + IllegalArgumentException var10000 = new IllegalArgumentException; + String var10003 = String.valueOf(toString(lowerBound, upperBound)); + String var10002; + if (var10003.length() != 0) { + var10002 = "Invalid range: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Invalid range: "); + } + + var10000.(var10002); + throw var10000; + } + } + + public boolean hasLowerBound() { + return this.lowerBound != Cut.belowAll(); + } + + public C lowerEndpoint() { + return this.lowerBound.endpoint(); + } + + public BoundType lowerBoundType() { + return this.lowerBound.typeAsLowerBound(); + } + + public boolean hasUpperBound() { + return this.upperBound != Cut.aboveAll(); + } + + public C upperEndpoint() { + return this.upperBound.endpoint(); + } + + public BoundType upperBoundType() { + return this.upperBound.typeAsUpperBound(); + } + + public boolean isEmpty() { + return this.lowerBound.equals(this.upperBound); + } + + public boolean contains(C value) { + Preconditions.checkNotNull(value); + return this.lowerBound.isLessThan(value) && !this.upperBound.isLessThan(value); + } + + /** @deprecated */ + @Deprecated + public boolean apply(C input) { + return this.contains(input); + } + + public boolean containsAll(Iterable values) { + if (Iterables.isEmpty(values)) { + return true; + } else { + if (values instanceof SortedSet) { + SortedSet set = cast(values); + Comparator comparator = set.comparator(); + if (Ordering.natural().equals(comparator) || comparator == null) { + return this.contains((Comparable)set.first()) && this.contains((Comparable)set.last()); + } + } + + Iterator i$ = values.iterator(); + + Comparable value; + do { + if (!i$.hasNext()) { + return true; + } + + value = (Comparable)i$.next(); + } while(this.contains(value)); + + return false; + } + } + + public boolean encloses(Range other) { + return this.lowerBound.compareTo(other.lowerBound) <= 0 && this.upperBound.compareTo(other.upperBound) >= 0; + } + + public boolean isConnected(Range other) { + return this.lowerBound.compareTo(other.upperBound) <= 0 && other.lowerBound.compareTo(this.upperBound) <= 0; + } + + public Range intersection(Range connectedRange) { + int lowerCmp = this.lowerBound.compareTo(connectedRange.lowerBound); + int upperCmp = this.upperBound.compareTo(connectedRange.upperBound); + if (lowerCmp >= 0 && upperCmp <= 0) { + return this; + } else if (lowerCmp <= 0 && upperCmp >= 0) { + return connectedRange; + } else { + Cut newLower = lowerCmp >= 0 ? this.lowerBound : connectedRange.lowerBound; + Cut newUpper = upperCmp <= 0 ? this.upperBound : connectedRange.upperBound; + return create(newLower, newUpper); + } + } + + public Range span(Range other) { + int lowerCmp = this.lowerBound.compareTo(other.lowerBound); + int upperCmp = this.upperBound.compareTo(other.upperBound); + if (lowerCmp <= 0 && upperCmp >= 0) { + return this; + } else if (lowerCmp >= 0 && upperCmp <= 0) { + return other; + } else { + Cut newLower = lowerCmp <= 0 ? this.lowerBound : other.lowerBound; + Cut newUpper = upperCmp >= 0 ? this.upperBound : other.upperBound; + return create(newLower, newUpper); + } + } + + public Range canonical(DiscreteDomain domain) { + Preconditions.checkNotNull(domain); + Cut lower = this.lowerBound.canonical(domain); + Cut upper = this.upperBound.canonical(domain); + return lower == this.lowerBound && upper == this.upperBound ? this : create(lower, upper); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof Range)) { + return false; + } else { + Range other = (Range)object; + return this.lowerBound.equals(other.lowerBound) && this.upperBound.equals(other.upperBound); + } + } + + public int hashCode() { + return this.lowerBound.hashCode() * 31 + this.upperBound.hashCode(); + } + + public String toString() { + return toString(this.lowerBound, this.upperBound); + } + + private static String toString(Cut lowerBound, Cut upperBound) { + StringBuilder sb = new StringBuilder(16); + lowerBound.describeAsLowerBound(sb); + sb.append('‥'); + upperBound.describeAsUpperBound(sb); + return sb.toString(); + } + + private static SortedSet cast(Iterable iterable) { + return (SortedSet)iterable; + } + + Object readResolve() { + return this.equals(ALL) ? all() : this; + } + + static int compareOrThrow(Comparable left, Comparable right) { + return left.compareTo(right); + } +} diff --git a/src/main/com/google/common/collect/RangeMap.java b/src/main/com/google/common/collect/RangeMap.java new file mode 100644 index 0000000..dfe995f --- /dev/null +++ b/src/main/com/google/common/collect/RangeMap.java @@ -0,0 +1,35 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +public interface RangeMap { + @Nullable + V get(K var1); + + @Nullable + Entry, V> getEntry(K var1); + + Range span(); + + void put(Range var1, V var2); + + void putAll(RangeMap var1); + + void clear(); + + void remove(Range var1); + + Map, V> asMapOfRanges(); + + RangeMap subRangeMap(Range var1); + + boolean equals(@Nullable Object var1); + + int hashCode(); + + String toString(); +} diff --git a/src/main/com/google/common/collect/RangeSet.java b/src/main/com/google/common/collect/RangeSet.java new file mode 100644 index 0000000..e04f898 --- /dev/null +++ b/src/main/com/google/common/collect/RangeSet.java @@ -0,0 +1,42 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import java.util.Set; +import javax.annotation.Nullable; + +@Beta +public interface RangeSet { + boolean contains(C var1); + + Range rangeContaining(C var1); + + boolean encloses(Range var1); + + boolean enclosesAll(RangeSet var1); + + boolean isEmpty(); + + Range span(); + + Set> asRanges(); + + RangeSet complement(); + + RangeSet subRangeSet(Range var1); + + void add(Range var1); + + void remove(Range var1); + + void clear(); + + void addAll(RangeSet var1); + + void removeAll(RangeSet var1); + + boolean equals(@Nullable Object var1); + + int hashCode(); + + String toString(); +} diff --git a/src/main/com/google/common/collect/RegularContiguousSet.java b/src/main/com/google/common/collect/RegularContiguousSet.java new file mode 100644 index 0000000..a9e5622 --- /dev/null +++ b/src/main/com/google/common/collect/RegularContiguousSet.java @@ -0,0 +1,168 @@ +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.io.Serializable; +import java.util.Collection; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class RegularContiguousSet extends ContiguousSet { + private final Range range; + private static final long serialVersionUID = 0L; + + RegularContiguousSet(Range range, DiscreteDomain domain) { + super(domain); + this.range = range; + } + + private ContiguousSet intersectionInCurrentDomain(Range other) { + return (ContiguousSet)(this.range.isConnected(other) ? ContiguousSet.create(this.range.intersection(other), this.domain) : new EmptyContiguousSet(this.domain)); + } + + ContiguousSet headSetImpl(C toElement, boolean inclusive) { + return this.intersectionInCurrentDomain(Range.upTo(toElement, BoundType.forBoolean(inclusive))); + } + + ContiguousSet subSetImpl(C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) { + return (ContiguousSet)(fromElement.compareTo(toElement) == 0 && !fromInclusive && !toInclusive ? new EmptyContiguousSet(this.domain) : this.intersectionInCurrentDomain(Range.range(fromElement, BoundType.forBoolean(fromInclusive), toElement, BoundType.forBoolean(toInclusive)))); + } + + ContiguousSet tailSetImpl(C fromElement, boolean inclusive) { + return this.intersectionInCurrentDomain(Range.downTo(fromElement, BoundType.forBoolean(inclusive))); + } + + @GwtIncompatible("not used by GWT emulation") + int indexOf(Object target) { + return this.contains(target) ? (int)this.domain.distance(this.first(), (Comparable)target) : -1; + } + + public UnmodifiableIterator iterator() { + return new AbstractSequentialIterator(this.first()) { + final C last = RegularContiguousSet.this.last(); + + protected C computeNext(C previous) { + return RegularContiguousSet.equalsOrThrow(previous, this.last) ? null : RegularContiguousSet.this.domain.next(previous); + } + }; + } + + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return new AbstractSequentialIterator(this.last()) { + final C first = RegularContiguousSet.this.first(); + + protected C computeNext(C previous) { + return RegularContiguousSet.equalsOrThrow(previous, this.first) ? null : RegularContiguousSet.this.domain.previous(previous); + } + }; + } + + private static boolean equalsOrThrow(Comparable left, @Nullable Comparable right) { + return right != null && Range.compareOrThrow(left, right) == 0; + } + + boolean isPartialView() { + return false; + } + + public C first() { + return this.range.lowerBound.leastValueAbove(this.domain); + } + + public C last() { + return this.range.upperBound.greatestValueBelow(this.domain); + } + + public int size() { + long distance = this.domain.distance(this.first(), this.last()); + return distance >= 2147483647L ? Integer.MAX_VALUE : (int)distance + 1; + } + + public boolean contains(@Nullable Object object) { + if (object == null) { + return false; + } else { + try { + return this.range.contains((Comparable)object); + } catch (ClassCastException var3) { + return false; + } + } + } + + public boolean containsAll(Collection targets) { + return Collections2.containsAllImpl(this, targets); + } + + public boolean isEmpty() { + return false; + } + + public ContiguousSet intersection(ContiguousSet other) { + Preconditions.checkNotNull(other); + Preconditions.checkArgument(this.domain.equals(other.domain)); + if (other.isEmpty()) { + return other; + } else { + C lowerEndpoint = (Comparable)Ordering.natural().max(this.first(), other.first()); + C upperEndpoint = (Comparable)Ordering.natural().min(this.last(), other.last()); + return (ContiguousSet)(lowerEndpoint.compareTo(upperEndpoint) < 0 ? ContiguousSet.create(Range.closed(lowerEndpoint, upperEndpoint), this.domain) : new EmptyContiguousSet(this.domain)); + } + } + + public Range range() { + return this.range(BoundType.CLOSED, BoundType.CLOSED); + } + + public Range range(BoundType lowerBoundType, BoundType upperBoundType) { + return Range.create(this.range.lowerBound.withLowerBoundType(lowerBoundType, this.domain), this.range.upperBound.withUpperBoundType(upperBoundType, this.domain)); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else { + if (object instanceof RegularContiguousSet) { + RegularContiguousSet that = (RegularContiguousSet)object; + if (this.domain.equals(that.domain)) { + return this.first().equals(that.first()) && this.last().equals(that.last()); + } + } + + return super.equals(object); + } + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + @GwtIncompatible("serialization") + Object writeReplace() { + return new RegularContiguousSet.SerializedForm(this.range, this.domain); + } + + @GwtIncompatible("serialization") + private static final class SerializedForm implements Serializable { + final Range range; + final DiscreteDomain domain; + + private SerializedForm(Range range, DiscreteDomain domain) { + this.range = range; + this.domain = domain; + } + + private Object readResolve() { + return new RegularContiguousSet(this.range, this.domain); + } + + // $FF: synthetic method + SerializedForm(Range x0, DiscreteDomain x1, Object x2) { + this(x0, x1); + } + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableAsList.java b/src/main/com/google/common/collect/RegularImmutableAsList.java new file mode 100644 index 0000000..bab2676 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableAsList.java @@ -0,0 +1,42 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; + +@GwtCompatible( + emulated = true +) +class RegularImmutableAsList extends ImmutableAsList { + private final ImmutableCollection delegate; + private final ImmutableList delegateList; + + RegularImmutableAsList(ImmutableCollection delegate, ImmutableList delegateList) { + this.delegate = delegate; + this.delegateList = delegateList; + } + + RegularImmutableAsList(ImmutableCollection delegate, Object[] array) { + this(delegate, ImmutableList.asImmutableList(array)); + } + + ImmutableCollection delegateCollection() { + return this.delegate; + } + + ImmutableList delegateList() { + return this.delegateList; + } + + public UnmodifiableListIterator listIterator(int index) { + return this.delegateList.listIterator(index); + } + + @GwtIncompatible("not present in emulated superclass") + int copyIntoArray(Object[] dst, int offset) { + return this.delegateList.copyIntoArray(dst, offset); + } + + public E get(int index) { + return this.delegateList.get(index); + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableBiMap.java b/src/main/com/google/common/collect/RegularImmutableBiMap.java new file mode 100644 index 0000000..1dbad0d --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableBiMap.java @@ -0,0 +1,287 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +class RegularImmutableBiMap extends ImmutableBiMap { + static final double MAX_LOAD_FACTOR = 1.2D; + private final transient ImmutableMapEntry[] keyTable; + private final transient ImmutableMapEntry[] valueTable; + private final transient ImmutableMapEntry[] entries; + private final transient int mask; + private final transient int hashCode; + private transient ImmutableBiMap inverse; + + RegularImmutableBiMap(ImmutableMapEntry.TerminalEntry... entriesToAdd) { + this(entriesToAdd.length, entriesToAdd); + } + + RegularImmutableBiMap(int n, ImmutableMapEntry.TerminalEntry[] entriesToAdd) { + int tableSize = Hashing.closedTableSize(n, 1.2D); + this.mask = tableSize - 1; + ImmutableMapEntry[] keyTable = createEntryArray(tableSize); + ImmutableMapEntry[] valueTable = createEntryArray(tableSize); + ImmutableMapEntry[] entries = createEntryArray(n); + int hashCode = 0; + + for(int i = 0; i < n; ++i) { + ImmutableMapEntry.TerminalEntry entry = entriesToAdd[i]; + K key = entry.getKey(); + V value = entry.getValue(); + int keyHash = key.hashCode(); + int valueHash = value.hashCode(); + int keyBucket = Hashing.smear(keyHash) & this.mask; + int valueBucket = Hashing.smear(valueHash) & this.mask; + ImmutableMapEntry nextInKeyBucket = keyTable[keyBucket]; + + ImmutableMapEntry nextInValueBucket; + for(nextInValueBucket = nextInKeyBucket; nextInValueBucket != null; nextInValueBucket = nextInValueBucket.getNextInKeyBucket()) { + checkNoConflict(!key.equals(nextInValueBucket.getKey()), "key", entry, nextInValueBucket); + } + + nextInValueBucket = valueTable[valueBucket]; + + for(ImmutableMapEntry valueEntry = nextInValueBucket; valueEntry != null; valueEntry = valueEntry.getNextInValueBucket()) { + checkNoConflict(!value.equals(valueEntry.getValue()), "value", entry, valueEntry); + } + + ImmutableMapEntry newEntry = nextInKeyBucket == null && nextInValueBucket == null ? entry : new RegularImmutableBiMap.NonTerminalBiMapEntry(entry, nextInKeyBucket, nextInValueBucket); + keyTable[keyBucket] = (ImmutableMapEntry)newEntry; + valueTable[valueBucket] = (ImmutableMapEntry)newEntry; + entries[i] = (ImmutableMapEntry)newEntry; + hashCode += keyHash ^ valueHash; + } + + this.keyTable = keyTable; + this.valueTable = valueTable; + this.entries = entries; + this.hashCode = hashCode; + } + + RegularImmutableBiMap(Entry[] entriesToAdd) { + int n = entriesToAdd.length; + int tableSize = Hashing.closedTableSize(n, 1.2D); + this.mask = tableSize - 1; + ImmutableMapEntry[] keyTable = createEntryArray(tableSize); + ImmutableMapEntry[] valueTable = createEntryArray(tableSize); + ImmutableMapEntry[] entries = createEntryArray(n); + int hashCode = 0; + + for(int i = 0; i < n; ++i) { + Entry entry = entriesToAdd[i]; + K key = entry.getKey(); + V value = entry.getValue(); + CollectPreconditions.checkEntryNotNull(key, value); + int keyHash = key.hashCode(); + int valueHash = value.hashCode(); + int keyBucket = Hashing.smear(keyHash) & this.mask; + int valueBucket = Hashing.smear(valueHash) & this.mask; + ImmutableMapEntry nextInKeyBucket = keyTable[keyBucket]; + + ImmutableMapEntry nextInValueBucket; + for(nextInValueBucket = nextInKeyBucket; nextInValueBucket != null; nextInValueBucket = nextInValueBucket.getNextInKeyBucket()) { + checkNoConflict(!key.equals(nextInValueBucket.getKey()), "key", entry, nextInValueBucket); + } + + nextInValueBucket = valueTable[valueBucket]; + + for(ImmutableMapEntry valueEntry = nextInValueBucket; valueEntry != null; valueEntry = valueEntry.getNextInValueBucket()) { + checkNoConflict(!value.equals(valueEntry.getValue()), "value", entry, valueEntry); + } + + ImmutableMapEntry newEntry = nextInKeyBucket == null && nextInValueBucket == null ? new ImmutableMapEntry.TerminalEntry(key, value) : new RegularImmutableBiMap.NonTerminalBiMapEntry(key, value, nextInKeyBucket, nextInValueBucket); + keyTable[keyBucket] = (ImmutableMapEntry)newEntry; + valueTable[valueBucket] = (ImmutableMapEntry)newEntry; + entries[i] = (ImmutableMapEntry)newEntry; + hashCode += keyHash ^ valueHash; + } + + this.keyTable = keyTable; + this.valueTable = valueTable; + this.entries = entries; + this.hashCode = hashCode; + } + + private static ImmutableMapEntry[] createEntryArray(int length) { + return new ImmutableMapEntry[length]; + } + + @Nullable + public V get(@Nullable Object key) { + if (key == null) { + return null; + } else { + int bucket = Hashing.smear(key.hashCode()) & this.mask; + + for(ImmutableMapEntry entry = this.keyTable[bucket]; entry != null; entry = entry.getNextInKeyBucket()) { + if (key.equals(entry.getKey())) { + return entry.getValue(); + } + } + + return null; + } + } + + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet() { + ImmutableMap map() { + return RegularImmutableBiMap.this; + } + + public UnmodifiableIterator> iterator() { + return this.asList().iterator(); + } + + ImmutableList> createAsList() { + return new RegularImmutableAsList(this, RegularImmutableBiMap.this.entries); + } + + boolean isHashCodeFast() { + return true; + } + + public int hashCode() { + return RegularImmutableBiMap.this.hashCode; + } + }; + } + + boolean isPartialView() { + return false; + } + + public int size() { + return this.entries.length; + } + + public ImmutableBiMap inverse() { + ImmutableBiMap result = this.inverse; + return result == null ? (this.inverse = new RegularImmutableBiMap.Inverse()) : result; + } + + private static class InverseSerializedForm implements Serializable { + private final ImmutableBiMap forward; + private static final long serialVersionUID = 1L; + + InverseSerializedForm(ImmutableBiMap forward) { + this.forward = forward; + } + + Object readResolve() { + return this.forward.inverse(); + } + } + + private final class Inverse extends ImmutableBiMap { + private Inverse() { + } + + public int size() { + return this.inverse().size(); + } + + public ImmutableBiMap inverse() { + return RegularImmutableBiMap.this; + } + + public K get(@Nullable Object value) { + if (value == null) { + return null; + } else { + int bucket = Hashing.smear(value.hashCode()) & RegularImmutableBiMap.this.mask; + + for(ImmutableMapEntry entry = RegularImmutableBiMap.this.valueTable[bucket]; entry != null; entry = entry.getNextInValueBucket()) { + if (value.equals(entry.getValue())) { + return entry.getKey(); + } + } + + return null; + } + } + + ImmutableSet> createEntrySet() { + return new RegularImmutableBiMap.Inverse.InverseEntrySet(); + } + + boolean isPartialView() { + return false; + } + + Object writeReplace() { + return new RegularImmutableBiMap.InverseSerializedForm(RegularImmutableBiMap.this); + } + + // $FF: synthetic method + Inverse(Object x1) { + this(); + } + + final class InverseEntrySet extends ImmutableMapEntrySet { + ImmutableMap map() { + return Inverse.this; + } + + boolean isHashCodeFast() { + return true; + } + + public int hashCode() { + return RegularImmutableBiMap.this.hashCode; + } + + public UnmodifiableIterator> iterator() { + return this.asList().iterator(); + } + + ImmutableList> createAsList() { + return new ImmutableAsList>() { + public Entry get(int index) { + Entry entry = RegularImmutableBiMap.this.entries[index]; + return Maps.immutableEntry(entry.getValue(), entry.getKey()); + } + + ImmutableCollection> delegateCollection() { + return InverseEntrySet.this; + } + }; + } + } + } + + private static final class NonTerminalBiMapEntry extends ImmutableMapEntry { + @Nullable + private final ImmutableMapEntry nextInKeyBucket; + @Nullable + private final ImmutableMapEntry nextInValueBucket; + + NonTerminalBiMapEntry(K key, V value, @Nullable ImmutableMapEntry nextInKeyBucket, @Nullable ImmutableMapEntry nextInValueBucket) { + super(key, value); + this.nextInKeyBucket = nextInKeyBucket; + this.nextInValueBucket = nextInValueBucket; + } + + NonTerminalBiMapEntry(ImmutableMapEntry contents, @Nullable ImmutableMapEntry nextInKeyBucket, @Nullable ImmutableMapEntry nextInValueBucket) { + super(contents); + this.nextInKeyBucket = nextInKeyBucket; + this.nextInValueBucket = nextInValueBucket; + } + + @Nullable + ImmutableMapEntry getNextInKeyBucket() { + return this.nextInKeyBucket; + } + + @Nullable + ImmutableMapEntry getNextInValueBucket() { + return this.nextInValueBucket; + } + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableList.java b/src/main/com/google/common/collect/RegularImmutableList.java new file mode 100644 index 0000000..cc5a16b --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableList.java @@ -0,0 +1,79 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +class RegularImmutableList extends ImmutableList { + private final transient int offset; + private final transient int size; + private final transient Object[] array; + + RegularImmutableList(Object[] array, int offset, int size) { + this.offset = offset; + this.size = size; + this.array = array; + } + + RegularImmutableList(Object[] array) { + this(array, 0, array.length); + } + + public int size() { + return this.size; + } + + boolean isPartialView() { + return this.size != this.array.length; + } + + int copyIntoArray(Object[] dst, int dstOff) { + System.arraycopy(this.array, this.offset, dst, dstOff, this.size); + return dstOff + this.size; + } + + public E get(int index) { + Preconditions.checkElementIndex(index, this.size); + return this.array[index + this.offset]; + } + + public int indexOf(@Nullable Object object) { + if (object == null) { + return -1; + } else { + for(int i = 0; i < this.size; ++i) { + if (this.array[this.offset + i].equals(object)) { + return i; + } + } + + return -1; + } + } + + public int lastIndexOf(@Nullable Object object) { + if (object == null) { + return -1; + } else { + for(int i = this.size - 1; i >= 0; --i) { + if (this.array[this.offset + i].equals(object)) { + return i; + } + } + + return -1; + } + } + + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + return new RegularImmutableList(this.array, this.offset + fromIndex, toIndex - fromIndex); + } + + public UnmodifiableListIterator listIterator(int index) { + return Iterators.forArray(this.array, this.offset, this.size, index); + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableMap.java b/src/main/com/google/common/collect/RegularImmutableMap.java new file mode 100644 index 0000000..7974dd2 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableMap.java @@ -0,0 +1,148 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class RegularImmutableMap extends ImmutableMap { + private final transient ImmutableMapEntry[] entries; + private final transient ImmutableMapEntry[] table; + private final transient int mask; + private static final double MAX_LOAD_FACTOR = 1.2D; + private static final long serialVersionUID = 0L; + + RegularImmutableMap(ImmutableMapEntry.TerminalEntry... theEntries) { + this(theEntries.length, theEntries); + } + + RegularImmutableMap(int size, ImmutableMapEntry.TerminalEntry[] theEntries) { + this.entries = this.createEntryArray(size); + int tableSize = Hashing.closedTableSize(size, 1.2D); + this.table = this.createEntryArray(tableSize); + this.mask = tableSize - 1; + + for(int entryIndex = 0; entryIndex < size; ++entryIndex) { + ImmutableMapEntry.TerminalEntry entry = theEntries[entryIndex]; + K key = entry.getKey(); + int tableIndex = Hashing.smear(key.hashCode()) & this.mask; + ImmutableMapEntry existing = this.table[tableIndex]; + ImmutableMapEntry newEntry = existing == null ? entry : new RegularImmutableMap.NonTerminalMapEntry(entry, existing); + this.table[tableIndex] = (ImmutableMapEntry)newEntry; + this.entries[entryIndex] = (ImmutableMapEntry)newEntry; + this.checkNoConflictInBucket(key, (ImmutableMapEntry)newEntry, existing); + } + + } + + RegularImmutableMap(Entry[] theEntries) { + int size = theEntries.length; + this.entries = this.createEntryArray(size); + int tableSize = Hashing.closedTableSize(size, 1.2D); + this.table = this.createEntryArray(tableSize); + this.mask = tableSize - 1; + + for(int entryIndex = 0; entryIndex < size; ++entryIndex) { + Entry entry = theEntries[entryIndex]; + K key = entry.getKey(); + V value = entry.getValue(); + CollectPreconditions.checkEntryNotNull(key, value); + int tableIndex = Hashing.smear(key.hashCode()) & this.mask; + ImmutableMapEntry existing = this.table[tableIndex]; + ImmutableMapEntry newEntry = existing == null ? new ImmutableMapEntry.TerminalEntry(key, value) : new RegularImmutableMap.NonTerminalMapEntry(key, value, existing); + this.table[tableIndex] = (ImmutableMapEntry)newEntry; + this.entries[entryIndex] = (ImmutableMapEntry)newEntry; + this.checkNoConflictInBucket(key, (ImmutableMapEntry)newEntry, existing); + } + + } + + private void checkNoConflictInBucket(K key, ImmutableMapEntry entry, ImmutableMapEntry bucketHead) { + while(bucketHead != null) { + checkNoConflict(!key.equals(bucketHead.getKey()), "key", entry, bucketHead); + bucketHead = bucketHead.getNextInKeyBucket(); + } + + } + + private ImmutableMapEntry[] createEntryArray(int size) { + return new ImmutableMapEntry[size]; + } + + public V get(@Nullable Object key) { + if (key == null) { + return null; + } else { + int index = Hashing.smear(key.hashCode()) & this.mask; + + for(ImmutableMapEntry entry = this.table[index]; entry != null; entry = entry.getNextInKeyBucket()) { + K candidateKey = entry.getKey(); + if (key.equals(candidateKey)) { + return entry.getValue(); + } + } + + return null; + } + } + + public int size() { + return this.entries.length; + } + + boolean isPartialView() { + return false; + } + + ImmutableSet> createEntrySet() { + return new RegularImmutableMap.EntrySet(); + } + + private class EntrySet extends ImmutableMapEntrySet { + private EntrySet() { + } + + ImmutableMap map() { + return RegularImmutableMap.this; + } + + public UnmodifiableIterator> iterator() { + return this.asList().iterator(); + } + + ImmutableList> createAsList() { + return new RegularImmutableAsList(this, RegularImmutableMap.this.entries); + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } + + private static final class NonTerminalMapEntry extends ImmutableMapEntry { + private final ImmutableMapEntry nextInKeyBucket; + + NonTerminalMapEntry(K key, V value, ImmutableMapEntry nextInKeyBucket) { + super(key, value); + this.nextInKeyBucket = nextInKeyBucket; + } + + NonTerminalMapEntry(ImmutableMapEntry contents, ImmutableMapEntry nextInKeyBucket) { + super(contents); + this.nextInKeyBucket = nextInKeyBucket; + } + + ImmutableMapEntry getNextInKeyBucket() { + return this.nextInKeyBucket; + } + + @Nullable + ImmutableMapEntry getNextInValueBucket() { + return null; + } + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableMultiset.java b/src/main/com/google/common/collect/RegularImmutableMultiset.java new file mode 100644 index 0000000..229c6d5 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableMultiset.java @@ -0,0 +1,47 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +class RegularImmutableMultiset extends ImmutableMultiset { + private final transient ImmutableMap map; + private final transient int size; + + RegularImmutableMultiset(ImmutableMap map, int size) { + this.map = map; + this.size = size; + } + + boolean isPartialView() { + return this.map.isPartialView(); + } + + public int count(@Nullable Object element) { + Integer value = (Integer)this.map.get(element); + return value == null ? 0 : value; + } + + public int size() { + return this.size; + } + + public boolean contains(@Nullable Object element) { + return this.map.containsKey(element); + } + + public ImmutableSet elementSet() { + return this.map.keySet(); + } + + Multiset.Entry getEntry(int index) { + java.util.Map.Entry mapEntry = (java.util.Map.Entry)this.map.entrySet().asList().get(index); + return Multisets.immutableEntry(mapEntry.getKey(), (Integer)mapEntry.getValue()); + } + + public int hashCode() { + return this.map.hashCode(); + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableSet.java b/src/main/com/google/common/collect/RegularImmutableSet.java new file mode 100644 index 0000000..5a79094 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableSet.java @@ -0,0 +1,73 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class RegularImmutableSet extends ImmutableSet { + private final Object[] elements; + @VisibleForTesting + final transient Object[] table; + private final transient int mask; + private final transient int hashCode; + + RegularImmutableSet(Object[] elements, int hashCode, Object[] table, int mask) { + this.elements = elements; + this.table = table; + this.mask = mask; + this.hashCode = hashCode; + } + + public boolean contains(Object target) { + if (target == null) { + return false; + } else { + int i = Hashing.smear(target.hashCode()); + + while(true) { + Object candidate = this.table[i & this.mask]; + if (candidate == null) { + return false; + } + + if (candidate.equals(target)) { + return true; + } + + ++i; + } + } + } + + public int size() { + return this.elements.length; + } + + public UnmodifiableIterator iterator() { + return Iterators.forArray(this.elements); + } + + int copyIntoArray(Object[] dst, int offset) { + System.arraycopy(this.elements, 0, dst, offset, this.elements.length); + return offset + this.elements.length; + } + + ImmutableList createAsList() { + return new RegularImmutableAsList(this, this.elements); + } + + boolean isPartialView() { + return false; + } + + public int hashCode() { + return this.hashCode; + } + + boolean isHashCodeFast() { + return true; + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableSortedMap.java b/src/main/com/google/common/collect/RegularImmutableSortedMap.java new file mode 100644 index 0000000..3f93e10 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableSortedMap.java @@ -0,0 +1,94 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class RegularImmutableSortedMap extends ImmutableSortedMap { + private final transient RegularImmutableSortedSet keySet; + private final transient ImmutableList valueList; + + RegularImmutableSortedMap(RegularImmutableSortedSet keySet, ImmutableList valueList) { + this.keySet = keySet; + this.valueList = valueList; + } + + RegularImmutableSortedMap(RegularImmutableSortedSet keySet, ImmutableList valueList, ImmutableSortedMap descendingMap) { + super(descendingMap); + this.keySet = keySet; + this.valueList = valueList; + } + + ImmutableSet> createEntrySet() { + return new RegularImmutableSortedMap.EntrySet(); + } + + public ImmutableSortedSet keySet() { + return this.keySet; + } + + public ImmutableCollection values() { + return this.valueList; + } + + public V get(@Nullable Object key) { + int index = this.keySet.indexOf(key); + return index == -1 ? null : this.valueList.get(index); + } + + private ImmutableSortedMap getSubMap(int fromIndex, int toIndex) { + if (fromIndex == 0 && toIndex == this.size()) { + return this; + } else { + return fromIndex == toIndex ? emptyMap(this.comparator()) : from(this.keySet.getSubSet(fromIndex, toIndex), this.valueList.subList(fromIndex, toIndex)); + } + } + + public ImmutableSortedMap headMap(K toKey, boolean inclusive) { + return this.getSubMap(0, this.keySet.headIndex(Preconditions.checkNotNull(toKey), inclusive)); + } + + public ImmutableSortedMap tailMap(K fromKey, boolean inclusive) { + return this.getSubMap(this.keySet.tailIndex(Preconditions.checkNotNull(fromKey), inclusive), this.size()); + } + + ImmutableSortedMap createDescendingMap() { + return new RegularImmutableSortedMap((RegularImmutableSortedSet)this.keySet.descendingSet(), this.valueList.reverse(), this); + } + + private class EntrySet extends ImmutableMapEntrySet { + private EntrySet() { + } + + public UnmodifiableIterator> iterator() { + return this.asList().iterator(); + } + + ImmutableList> createAsList() { + return new ImmutableAsList>() { + private final ImmutableList keyList = RegularImmutableSortedMap.this.keySet().asList(); + + public Entry get(int index) { + return Maps.immutableEntry(this.keyList.get(index), RegularImmutableSortedMap.this.valueList.get(index)); + } + + ImmutableCollection> delegateCollection() { + return EntrySet.this; + } + }; + } + + ImmutableMap map() { + return RegularImmutableSortedMap.this; + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableSortedMultiset.java b/src/main/com/google/common/collect/RegularImmutableSortedMultiset.java new file mode 100644 index 0000000..befcf63 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableSortedMultiset.java @@ -0,0 +1,71 @@ +package com.google.common.collect; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; +import javax.annotation.Nullable; + +final class RegularImmutableSortedMultiset extends ImmutableSortedMultiset { + private final transient RegularImmutableSortedSet elementSet; + private final transient int[] counts; + private final transient long[] cumulativeCounts; + private final transient int offset; + private final transient int length; + + RegularImmutableSortedMultiset(RegularImmutableSortedSet elementSet, int[] counts, long[] cumulativeCounts, int offset, int length) { + this.elementSet = elementSet; + this.counts = counts; + this.cumulativeCounts = cumulativeCounts; + this.offset = offset; + this.length = length; + } + + Multiset.Entry getEntry(int index) { + return Multisets.immutableEntry(this.elementSet.asList().get(index), this.counts[this.offset + index]); + } + + public Multiset.Entry firstEntry() { + return this.getEntry(0); + } + + public Multiset.Entry lastEntry() { + return this.getEntry(this.length - 1); + } + + public int count(@Nullable Object element) { + int index = this.elementSet.indexOf(element); + return index == -1 ? 0 : this.counts[index + this.offset]; + } + + public int size() { + long size = this.cumulativeCounts[this.offset + this.length] - this.cumulativeCounts[this.offset]; + return Ints.saturatedCast(size); + } + + public ImmutableSortedSet elementSet() { + return this.elementSet; + } + + public ImmutableSortedMultiset headMultiset(E upperBound, BoundType boundType) { + return this.getSubMultiset(0, this.elementSet.headIndex(upperBound, Preconditions.checkNotNull(boundType) == BoundType.CLOSED)); + } + + public ImmutableSortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return this.getSubMultiset(this.elementSet.tailIndex(lowerBound, Preconditions.checkNotNull(boundType) == BoundType.CLOSED), this.length); + } + + ImmutableSortedMultiset getSubMultiset(int from, int to) { + Preconditions.checkPositionIndexes(from, to, this.length); + if (from == to) { + return emptyMultiset(this.comparator()); + } else if (from == 0 && to == this.length) { + return this; + } else { + RegularImmutableSortedSet subElementSet = (RegularImmutableSortedSet)this.elementSet.getSubSet(from, to); + return new RegularImmutableSortedMultiset(subElementSet, this.counts, this.cumulativeCounts, this.offset + from, to - from); + } + } + + boolean isPartialView() { + return this.offset > 0 || this.length < this.counts.length; + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableSortedSet.java b/src/main/com/google/common/collect/RegularImmutableSortedSet.java new file mode 100644 index 0000000..f0fb6c1 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableSortedSet.java @@ -0,0 +1,221 @@ +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.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class RegularImmutableSortedSet extends ImmutableSortedSet { + private final transient ImmutableList elements; + + RegularImmutableSortedSet(ImmutableList elements, Comparator comparator) { + super(comparator); + this.elements = elements; + Preconditions.checkArgument(!elements.isEmpty()); + } + + public UnmodifiableIterator iterator() { + return this.elements.iterator(); + } + + @GwtIncompatible("NavigableSet") + public UnmodifiableIterator descendingIterator() { + return this.elements.reverse().iterator(); + } + + public boolean isEmpty() { + return false; + } + + public int size() { + return this.elements.size(); + } + + public boolean contains(Object o) { + try { + return o != null && this.unsafeBinarySearch(o) >= 0; + } catch (ClassCastException var3) { + return false; + } + } + + public boolean containsAll(Collection targets) { + if (targets instanceof Multiset) { + targets = ((Multiset)targets).elementSet(); + } + + if (SortedIterables.hasSameComparator(this.comparator(), (Iterable)targets) && ((Collection)targets).size() > 1) { + PeekingIterator thisIterator = Iterators.peekingIterator((Iterator)this.iterator()); + Iterator thatIterator = ((Collection)targets).iterator(); + Object target = thatIterator.next(); + + try { + while(thisIterator.hasNext()) { + int cmp = this.unsafeCompare(thisIterator.peek(), target); + if (cmp < 0) { + thisIterator.next(); + } else if (cmp == 0) { + if (!thatIterator.hasNext()) { + return true; + } + + target = thatIterator.next(); + } else if (cmp > 0) { + return false; + } + } + + return false; + } catch (NullPointerException var6) { + return false; + } catch (ClassCastException var7) { + return false; + } + } else { + return super.containsAll((Collection)targets); + } + } + + private int unsafeBinarySearch(Object key) throws ClassCastException { + return Collections.binarySearch(this.elements, key, this.unsafeComparator()); + } + + boolean isPartialView() { + return this.elements.isPartialView(); + } + + int copyIntoArray(Object[] dst, int offset) { + return this.elements.copyIntoArray(dst, offset); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (!(object instanceof Set)) { + return false; + } else { + Set that = (Set)object; + if (this.size() != that.size()) { + return false; + } else if (SortedIterables.hasSameComparator(this.comparator, that)) { + Iterator otherIterator = that.iterator(); + + try { + UnmodifiableIterator iterator = this.iterator(); + + Object element; + Object otherElement; + do { + if (!iterator.hasNext()) { + return true; + } + + element = iterator.next(); + otherElement = otherIterator.next(); + } while(otherElement != null && this.unsafeCompare(element, otherElement) == 0); + + return false; + } catch (ClassCastException var7) { + return false; + } catch (NoSuchElementException var8) { + return false; + } + } else { + return this.containsAll(that); + } + } + } + + public E first() { + return this.elements.get(0); + } + + public E last() { + return this.elements.get(this.size() - 1); + } + + public E lower(E element) { + int index = this.headIndex(element, false) - 1; + return index == -1 ? null : this.elements.get(index); + } + + public E floor(E element) { + int index = this.headIndex(element, true) - 1; + return index == -1 ? null : this.elements.get(index); + } + + public E ceiling(E element) { + int index = this.tailIndex(element, true); + return index == this.size() ? null : this.elements.get(index); + } + + public E higher(E element) { + int index = this.tailIndex(element, false); + return index == this.size() ? null : this.elements.get(index); + } + + ImmutableSortedSet headSetImpl(E toElement, boolean inclusive) { + return this.getSubSet(0, this.headIndex(toElement, inclusive)); + } + + int headIndex(E toElement, boolean inclusive) { + return SortedLists.binarySearch(this.elements, (Object)Preconditions.checkNotNull(toElement), (Comparator)this.comparator(), inclusive ? SortedLists.KeyPresentBehavior.FIRST_AFTER : SortedLists.KeyPresentBehavior.FIRST_PRESENT, SortedLists.KeyAbsentBehavior.NEXT_HIGHER); + } + + ImmutableSortedSet subSetImpl(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return this.tailSetImpl(fromElement, fromInclusive).headSetImpl(toElement, toInclusive); + } + + ImmutableSortedSet tailSetImpl(E fromElement, boolean inclusive) { + return this.getSubSet(this.tailIndex(fromElement, inclusive), this.size()); + } + + int tailIndex(E fromElement, boolean inclusive) { + return SortedLists.binarySearch(this.elements, (Object)Preconditions.checkNotNull(fromElement), (Comparator)this.comparator(), inclusive ? SortedLists.KeyPresentBehavior.FIRST_PRESENT : SortedLists.KeyPresentBehavior.FIRST_AFTER, SortedLists.KeyAbsentBehavior.NEXT_HIGHER); + } + + Comparator unsafeComparator() { + return this.comparator; + } + + ImmutableSortedSet getSubSet(int newFromIndex, int newToIndex) { + if (newFromIndex == 0 && newToIndex == this.size()) { + return this; + } else { + return (ImmutableSortedSet)(newFromIndex < newToIndex ? new RegularImmutableSortedSet(this.elements.subList(newFromIndex, newToIndex), this.comparator) : emptySet(this.comparator)); + } + } + + int indexOf(@Nullable Object target) { + if (target == null) { + return -1; + } else { + int position; + try { + position = SortedLists.binarySearch(this.elements, (Object)target, (Comparator)this.unsafeComparator(), SortedLists.KeyPresentBehavior.ANY_PRESENT, SortedLists.KeyAbsentBehavior.INVERTED_INSERTION_INDEX); + } catch (ClassCastException var4) { + return -1; + } + + return position >= 0 ? position : -1; + } + } + + ImmutableList createAsList() { + return new ImmutableSortedAsList(this, this.elements); + } + + ImmutableSortedSet createDescendingSet() { + return new RegularImmutableSortedSet(this.elements.reverse(), Ordering.from(this.comparator).reverse()); + } +} diff --git a/src/main/com/google/common/collect/RegularImmutableTable.java b/src/main/com/google/common/collect/RegularImmutableTable.java new file mode 100644 index 0000000..0e7efd0 --- /dev/null +++ b/src/main/com/google/common/collect/RegularImmutableTable.java @@ -0,0 +1,143 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +@GwtCompatible +abstract class RegularImmutableTable extends ImmutableTable { + abstract Table.Cell getCell(int var1); + + final ImmutableSet> createCellSet() { + return (ImmutableSet)(this.isEmpty() ? ImmutableSet.of() : new RegularImmutableTable.CellSet()); + } + + abstract V getValue(int var1); + + final ImmutableCollection createValues() { + return (ImmutableCollection)(this.isEmpty() ? ImmutableList.of() : new RegularImmutableTable.Values()); + } + + static RegularImmutableTable forCells(List> cells, @Nullable final Comparator rowComparator, @Nullable final Comparator columnComparator) { + Preconditions.checkNotNull(cells); + if (rowComparator != null || columnComparator != null) { + Comparator> comparator = new Comparator>() { + public int compare(Table.Cell cell1, Table.Cell cell2) { + int rowCompare = rowComparator == null ? 0 : rowComparator.compare(cell1.getRowKey(), cell2.getRowKey()); + if (rowCompare != 0) { + return rowCompare; + } else { + return columnComparator == null ? 0 : columnComparator.compare(cell1.getColumnKey(), cell2.getColumnKey()); + } + } + }; + Collections.sort(cells, comparator); + } + + return forCellsInternal(cells, rowComparator, columnComparator); + } + + static RegularImmutableTable forCells(Iterable> cells) { + return forCellsInternal(cells, (Comparator)null, (Comparator)null); + } + + private static final RegularImmutableTable forCellsInternal(Iterable> cells, @Nullable Comparator rowComparator, @Nullable Comparator columnComparator) { + ImmutableSet.Builder rowSpaceBuilder = ImmutableSet.builder(); + ImmutableSet.Builder columnSpaceBuilder = ImmutableSet.builder(); + ImmutableList> cellList = ImmutableList.copyOf(cells); + Iterator i$ = cellList.iterator(); + + while(i$.hasNext()) { + Table.Cell cell = (Table.Cell)i$.next(); + rowSpaceBuilder.add(cell.getRowKey()); + columnSpaceBuilder.add(cell.getColumnKey()); + } + + ImmutableSet rowSpace = rowSpaceBuilder.build(); + if (rowComparator != null) { + List rowList = Lists.newArrayList((Iterable)rowSpace); + Collections.sort(rowList, rowComparator); + rowSpace = ImmutableSet.copyOf((Collection)rowList); + } + + ImmutableSet columnSpace = columnSpaceBuilder.build(); + if (columnComparator != null) { + List columnList = Lists.newArrayList((Iterable)columnSpace); + Collections.sort(columnList, columnComparator); + columnSpace = ImmutableSet.copyOf((Collection)columnList); + } + + return (RegularImmutableTable)((long)cellList.size() > (long)rowSpace.size() * (long)columnSpace.size() / 2L ? new DenseImmutableTable(cellList, rowSpace, columnSpace) : new SparseImmutableTable(cellList, rowSpace, columnSpace)); + } + + private final class Values extends ImmutableList { + private Values() { + } + + public int size() { + return RegularImmutableTable.this.size(); + } + + public V get(int index) { + return RegularImmutableTable.this.getValue(index); + } + + boolean isPartialView() { + return true; + } + + // $FF: synthetic method + Values(Object x1) { + this(); + } + } + + private final class CellSet extends ImmutableSet> { + private CellSet() { + } + + public int size() { + return RegularImmutableTable.this.size(); + } + + public UnmodifiableIterator> iterator() { + return this.asList().iterator(); + } + + ImmutableList> createAsList() { + return new ImmutableAsList>() { + public Table.Cell get(int index) { + return RegularImmutableTable.this.getCell(index); + } + + ImmutableCollection> delegateCollection() { + return CellSet.this; + } + }; + } + + public boolean contains(@Nullable Object object) { + if (!(object instanceof Table.Cell)) { + return false; + } else { + Table.Cell cell = (Table.Cell)object; + Object value = RegularImmutableTable.this.get(cell.getRowKey(), cell.getColumnKey()); + return value != null && value.equals(cell.getValue()); + } + } + + boolean isPartialView() { + return false; + } + + // $FF: synthetic method + CellSet(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/ReverseNaturalOrdering.java b/src/main/com/google/common/collect/ReverseNaturalOrdering.java new file mode 100644 index 0000000..5c01f87 --- /dev/null +++ b/src/main/com/google/common/collect/ReverseNaturalOrdering.java @@ -0,0 +1,66 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Iterator; + +@GwtCompatible( + serializable = true +) +final class ReverseNaturalOrdering extends Ordering implements Serializable { + static final ReverseNaturalOrdering INSTANCE = new ReverseNaturalOrdering(); + private static final long serialVersionUID = 0L; + + public int compare(Comparable left, Comparable right) { + Preconditions.checkNotNull(left); + return left == right ? 0 : right.compareTo(left); + } + + public Ordering reverse() { + return Ordering.natural(); + } + + public E min(E a, E b) { + return (Comparable)NaturalOrdering.INSTANCE.max(a, b); + } + + public E min(E a, E b, E c, E... rest) { + return (Comparable)NaturalOrdering.INSTANCE.max(a, b, c, rest); + } + + public E min(Iterator iterator) { + return (Comparable)NaturalOrdering.INSTANCE.max(iterator); + } + + public E min(Iterable iterable) { + return (Comparable)NaturalOrdering.INSTANCE.max(iterable); + } + + public E max(E a, E b) { + return (Comparable)NaturalOrdering.INSTANCE.min(a, b); + } + + public E max(E a, E b, E c, E... rest) { + return (Comparable)NaturalOrdering.INSTANCE.min(a, b, c, rest); + } + + public E max(Iterator iterator) { + return (Comparable)NaturalOrdering.INSTANCE.min(iterator); + } + + public E max(Iterable iterable) { + return (Comparable)NaturalOrdering.INSTANCE.min(iterable); + } + + private Object readResolve() { + return INSTANCE; + } + + public String toString() { + return "Ordering.natural().reverse()"; + } + + private ReverseNaturalOrdering() { + } +} diff --git a/src/main/com/google/common/collect/ReverseOrdering.java b/src/main/com/google/common/collect/ReverseOrdering.java new file mode 100644 index 0000000..e348235 --- /dev/null +++ b/src/main/com/google/common/collect/ReverseOrdering.java @@ -0,0 +1,79 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.Iterator; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +final class ReverseOrdering extends Ordering implements Serializable { + final Ordering forwardOrder; + private static final long serialVersionUID = 0L; + + ReverseOrdering(Ordering forwardOrder) { + this.forwardOrder = (Ordering)Preconditions.checkNotNull(forwardOrder); + } + + public int compare(T a, T b) { + return this.forwardOrder.compare(b, a); + } + + public Ordering reverse() { + return this.forwardOrder; + } + + public E min(E a, E b) { + return this.forwardOrder.max(a, b); + } + + public E min(E a, E b, E c, E... rest) { + return this.forwardOrder.max(a, b, c, rest); + } + + public E min(Iterator iterator) { + return this.forwardOrder.max(iterator); + } + + public E min(Iterable iterable) { + return this.forwardOrder.max(iterable); + } + + public E max(E a, E b) { + return this.forwardOrder.min(a, b); + } + + public E max(E a, E b, E c, E... rest) { + return this.forwardOrder.min(a, b, c, rest); + } + + public E max(Iterator iterator) { + return this.forwardOrder.min(iterator); + } + + public E max(Iterable iterable) { + return this.forwardOrder.min(iterable); + } + + public int hashCode() { + return -this.forwardOrder.hashCode(); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (object instanceof ReverseOrdering) { + ReverseOrdering that = (ReverseOrdering)object; + return this.forwardOrder.equals(that.forwardOrder); + } else { + return false; + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.forwardOrder)); + return (new StringBuilder(10 + var1.length())).append(var1).append(".reverse()").toString(); + } +} diff --git a/src/main/com/google/common/collect/RowSortedTable.java b/src/main/com/google/common/collect/RowSortedTable.java new file mode 100644 index 0000000..671b590 --- /dev/null +++ b/src/main/com/google/common/collect/RowSortedTable.java @@ -0,0 +1,15 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.util.Map; +import java.util.SortedMap; +import java.util.SortedSet; + +@GwtCompatible +@Beta +public interface RowSortedTable extends Table { + SortedSet rowKeySet(); + + SortedMap> rowMap(); +} diff --git a/src/main/com/google/common/collect/Serialization.java b/src/main/com/google/common/collect/Serialization.java new file mode 100644 index 0000000..bad2ddc --- /dev/null +++ b/src/main/com/google/common/collect/Serialization.java @@ -0,0 +1,148 @@ +package com.google.common.collect; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +final class Serialization { + private Serialization() { + } + + static int readCount(ObjectInputStream stream) throws IOException { + return stream.readInt(); + } + + static void writeMap(Map map, ObjectOutputStream stream) throws IOException { + stream.writeInt(map.size()); + Iterator i$ = map.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + + } + + static void populateMap(Map map, ObjectInputStream stream) throws IOException, ClassNotFoundException { + int size = stream.readInt(); + populateMap(map, stream, size); + } + + static void populateMap(Map map, ObjectInputStream stream, int size) throws IOException, ClassNotFoundException { + for(int i = 0; i < size; ++i) { + K key = stream.readObject(); + V value = stream.readObject(); + map.put(key, value); + } + + } + + static void writeMultiset(Multiset multiset, ObjectOutputStream stream) throws IOException { + int entryCount = multiset.entrySet().size(); + stream.writeInt(entryCount); + Iterator i$ = multiset.entrySet().iterator(); + + while(i$.hasNext()) { + Multiset.Entry entry = (Multiset.Entry)i$.next(); + stream.writeObject(entry.getElement()); + stream.writeInt(entry.getCount()); + } + + } + + static void populateMultiset(Multiset multiset, ObjectInputStream stream) throws IOException, ClassNotFoundException { + int distinctElements = stream.readInt(); + populateMultiset(multiset, stream, distinctElements); + } + + static void populateMultiset(Multiset multiset, ObjectInputStream stream, int distinctElements) throws IOException, ClassNotFoundException { + for(int i = 0; i < distinctElements; ++i) { + E element = stream.readObject(); + int count = stream.readInt(); + multiset.add(element, count); + } + + } + + static void writeMultimap(Multimap multimap, ObjectOutputStream stream) throws IOException { + stream.writeInt(multimap.asMap().size()); + Iterator i$ = multimap.asMap().entrySet().iterator(); + + while(i$.hasNext()) { + Entry> entry = (Entry)i$.next(); + stream.writeObject(entry.getKey()); + stream.writeInt(((Collection)entry.getValue()).size()); + Iterator i$ = ((Collection)entry.getValue()).iterator(); + + while(i$.hasNext()) { + V value = i$.next(); + stream.writeObject(value); + } + } + + } + + static void populateMultimap(Multimap multimap, ObjectInputStream stream) throws IOException, ClassNotFoundException { + int distinctKeys = stream.readInt(); + populateMultimap(multimap, stream, distinctKeys); + } + + static void populateMultimap(Multimap multimap, ObjectInputStream stream, int distinctKeys) throws IOException, ClassNotFoundException { + for(int i = 0; i < distinctKeys; ++i) { + K key = stream.readObject(); + Collection values = multimap.get(key); + int valueCount = stream.readInt(); + + for(int j = 0; j < valueCount; ++j) { + V value = stream.readObject(); + values.add(value); + } + } + + } + + static Serialization.FieldSetter getFieldSetter(Class clazz, String fieldName) { + try { + Field field = clazz.getDeclaredField(fieldName); + return new Serialization.FieldSetter(field); + } catch (NoSuchFieldException var3) { + throw new AssertionError(var3); + } + } + + static final class FieldSetter { + private final Field field; + + private FieldSetter(Field field) { + this.field = field; + field.setAccessible(true); + } + + void set(T instance, Object value) { + try { + this.field.set(instance, value); + } catch (IllegalAccessException var4) { + throw new AssertionError(var4); + } + } + + void set(T instance, int value) { + try { + this.field.set(instance, value); + } catch (IllegalAccessException var4) { + throw new AssertionError(var4); + } + } + + // $FF: synthetic method + FieldSetter(Field x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/collect/SetMultimap.java b/src/main/com/google/common/collect/SetMultimap.java new file mode 100644 index 0000000..49a6460 --- /dev/null +++ b/src/main/com/google/common/collect/SetMultimap.java @@ -0,0 +1,23 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +public interface SetMultimap extends Multimap { + Set get(@Nullable K var1); + + Set removeAll(@Nullable Object var1); + + Set replaceValues(K var1, Iterable var2); + + Set> entries(); + + Map> asMap(); + + boolean equals(@Nullable Object var1); +} diff --git a/src/main/com/google/common/collect/Sets.java b/src/main/com/google/common/collect/Sets.java new file mode 100644 index 0000000..0f8621f --- /dev/null +++ b/src/main/com/google/common/collect/Sets.java @@ -0,0 +1,862 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.HashSet; +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.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Sets { + private Sets() { + } + + @GwtCompatible( + serializable = true + ) + public static > ImmutableSet immutableEnumSet(E anElement, E... otherElements) { + return ImmutableEnumSet.asImmutable(EnumSet.of(anElement, otherElements)); + } + + @GwtCompatible( + serializable = true + ) + public static > ImmutableSet immutableEnumSet(Iterable elements) { + if (elements instanceof ImmutableEnumSet) { + return (ImmutableEnumSet)elements; + } else if (elements instanceof Collection) { + Collection collection = (Collection)elements; + return collection.isEmpty() ? ImmutableSet.of() : ImmutableEnumSet.asImmutable(EnumSet.copyOf(collection)); + } else { + Iterator itr = elements.iterator(); + if (itr.hasNext()) { + EnumSet enumSet = EnumSet.of((Enum)itr.next()); + Iterators.addAll(enumSet, itr); + return ImmutableEnumSet.asImmutable(enumSet); + } else { + return ImmutableSet.of(); + } + } + } + + public static > EnumSet newEnumSet(Iterable iterable, Class elementType) { + EnumSet set = EnumSet.noneOf(elementType); + Iterables.addAll(set, iterable); + return set; + } + + public static HashSet newHashSet() { + return new HashSet(); + } + + public static HashSet newHashSet(E... elements) { + HashSet set = newHashSetWithExpectedSize(elements.length); + Collections.addAll(set, elements); + return set; + } + + public static HashSet newHashSetWithExpectedSize(int expectedSize) { + return new HashSet(Maps.capacity(expectedSize)); + } + + public static HashSet newHashSet(Iterable elements) { + return elements instanceof Collection ? new HashSet(Collections2.cast(elements)) : newHashSet(elements.iterator()); + } + + public static HashSet newHashSet(Iterator elements) { + HashSet set = newHashSet(); + Iterators.addAll(set, elements); + return set; + } + + public static Set newConcurrentHashSet() { + return newSetFromMap(new ConcurrentHashMap()); + } + + public static Set newConcurrentHashSet(Iterable elements) { + Set set = newConcurrentHashSet(); + Iterables.addAll(set, elements); + return set; + } + + public static LinkedHashSet newLinkedHashSet() { + return new LinkedHashSet(); + } + + public static LinkedHashSet newLinkedHashSetWithExpectedSize(int expectedSize) { + return new LinkedHashSet(Maps.capacity(expectedSize)); + } + + public static LinkedHashSet newLinkedHashSet(Iterable elements) { + if (elements instanceof Collection) { + return new LinkedHashSet(Collections2.cast(elements)); + } else { + LinkedHashSet set = newLinkedHashSet(); + Iterables.addAll(set, elements); + return set; + } + } + + public static TreeSet newTreeSet() { + return new TreeSet(); + } + + public static TreeSet newTreeSet(Iterable elements) { + TreeSet set = newTreeSet(); + Iterables.addAll(set, elements); + return set; + } + + public static TreeSet newTreeSet(Comparator comparator) { + return new TreeSet((Comparator)Preconditions.checkNotNull(comparator)); + } + + public static Set newIdentityHashSet() { + return newSetFromMap(Maps.newIdentityHashMap()); + } + + @GwtIncompatible("CopyOnWriteArraySet") + public static CopyOnWriteArraySet newCopyOnWriteArraySet() { + return new CopyOnWriteArraySet(); + } + + @GwtIncompatible("CopyOnWriteArraySet") + public static CopyOnWriteArraySet newCopyOnWriteArraySet(Iterable elements) { + Collection elementsCollection = elements instanceof Collection ? Collections2.cast(elements) : Lists.newArrayList(elements); + return new CopyOnWriteArraySet((Collection)elementsCollection); + } + + public static > EnumSet complementOf(Collection collection) { + if (collection instanceof EnumSet) { + return EnumSet.complementOf((EnumSet)collection); + } else { + Preconditions.checkArgument(!collection.isEmpty(), "collection is empty; use the other version of this method"); + Class type = ((Enum)collection.iterator().next()).getDeclaringClass(); + return makeComplementByHand(collection, type); + } + } + + public static > EnumSet complementOf(Collection collection, Class type) { + Preconditions.checkNotNull(collection); + return collection instanceof EnumSet ? EnumSet.complementOf((EnumSet)collection) : makeComplementByHand(collection, type); + } + + private static > EnumSet makeComplementByHand(Collection collection, Class type) { + EnumSet result = EnumSet.allOf(type); + result.removeAll(collection); + return result; + } + + public static Set newSetFromMap(Map map) { + return Platform.newSetFromMap(map); + } + + public static Sets.SetView union(final Set set1, final Set set2) { + Preconditions.checkNotNull(set1, "set1"); + Preconditions.checkNotNull(set2, "set2"); + final Set set2minus1 = difference(set2, set1); + return new Sets.SetView() { + public int size() { + return set1.size() + set2minus1.size(); + } + + public boolean isEmpty() { + return set1.isEmpty() && set2.isEmpty(); + } + + public Iterator iterator() { + return Iterators.unmodifiableIterator(Iterators.concat(set1.iterator(), set2minus1.iterator())); + } + + public boolean contains(Object object) { + return set1.contains(object) || set2.contains(object); + } + + public > S copyInto(S set) { + set.addAll(set1); + set.addAll(set2); + return set; + } + + public ImmutableSet immutableCopy() { + return (new ImmutableSet.Builder()).addAll((Iterable)set1).addAll((Iterable)set2).build(); + } + }; + } + + public static Sets.SetView intersection(final Set set1, final Set set2) { + Preconditions.checkNotNull(set1, "set1"); + Preconditions.checkNotNull(set2, "set2"); + final Predicate inSet2 = Predicates.in(set2); + return new Sets.SetView() { + public Iterator iterator() { + return Iterators.filter(set1.iterator(), inSet2); + } + + public int size() { + return Iterators.size(this.iterator()); + } + + public boolean isEmpty() { + return !this.iterator().hasNext(); + } + + public boolean contains(Object object) { + return set1.contains(object) && set2.contains(object); + } + + public boolean containsAll(Collection collection) { + return set1.containsAll(collection) && set2.containsAll(collection); + } + }; + } + + public static Sets.SetView difference(final Set set1, final Set set2) { + Preconditions.checkNotNull(set1, "set1"); + Preconditions.checkNotNull(set2, "set2"); + final Predicate notInSet2 = Predicates.not(Predicates.in(set2)); + return new Sets.SetView() { + public Iterator iterator() { + return Iterators.filter(set1.iterator(), notInSet2); + } + + public int size() { + return Iterators.size(this.iterator()); + } + + public boolean isEmpty() { + return set2.containsAll(set1); + } + + public boolean contains(Object element) { + return set1.contains(element) && !set2.contains(element); + } + }; + } + + public static Sets.SetView symmetricDifference(Set set1, Set set2) { + Preconditions.checkNotNull(set1, "set1"); + Preconditions.checkNotNull(set2, "set2"); + return difference(union(set1, set2), intersection(set1, set2)); + } + + public static Set filter(Set unfiltered, Predicate predicate) { + if (unfiltered instanceof SortedSet) { + return filter((SortedSet)unfiltered, predicate); + } else if (unfiltered instanceof Sets.FilteredSet) { + Sets.FilteredSet filtered = (Sets.FilteredSet)unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new Sets.FilteredSet((Set)filtered.unfiltered, combinedPredicate); + } else { + return new Sets.FilteredSet((Set)Preconditions.checkNotNull(unfiltered), (Predicate)Preconditions.checkNotNull(predicate)); + } + } + + public static SortedSet filter(SortedSet unfiltered, Predicate predicate) { + return Platform.setsFilterSortedSet(unfiltered, predicate); + } + + static SortedSet filterSortedIgnoreNavigable(SortedSet unfiltered, Predicate predicate) { + if (unfiltered instanceof Sets.FilteredSet) { + Sets.FilteredSet filtered = (Sets.FilteredSet)unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new Sets.FilteredSortedSet((SortedSet)filtered.unfiltered, combinedPredicate); + } else { + return new Sets.FilteredSortedSet((SortedSet)Preconditions.checkNotNull(unfiltered), (Predicate)Preconditions.checkNotNull(predicate)); + } + } + + @GwtIncompatible("NavigableSet") + public static NavigableSet filter(NavigableSet unfiltered, Predicate predicate) { + if (unfiltered instanceof Sets.FilteredSet) { + Sets.FilteredSet filtered = (Sets.FilteredSet)unfiltered; + Predicate combinedPredicate = Predicates.and(filtered.predicate, predicate); + return new Sets.FilteredNavigableSet((NavigableSet)filtered.unfiltered, combinedPredicate); + } else { + return new Sets.FilteredNavigableSet((NavigableSet)Preconditions.checkNotNull(unfiltered), (Predicate)Preconditions.checkNotNull(predicate)); + } + } + + public static Set> cartesianProduct(List> sets) { + return Sets.CartesianSet.create(sets); + } + + public static Set> cartesianProduct(Set... sets) { + return cartesianProduct(Arrays.asList(sets)); + } + + @GwtCompatible( + serializable = false + ) + public static Set> powerSet(Set set) { + return new Sets.PowerSet(set); + } + + static int hashCodeImpl(Set s) { + int hashCode = 0; + + for(Iterator i$ = s.iterator(); i$.hasNext(); hashCode = ~(~hashCode)) { + Object o = i$.next(); + hashCode += o != null ? o.hashCode() : 0; + } + + return hashCode; + } + + static boolean equalsImpl(Set s, @Nullable Object object) { + if (s == object) { + return true; + } else if (object instanceof Set) { + Set o = (Set)object; + + try { + return s.size() == o.size() && s.containsAll(o); + } catch (NullPointerException var4) { + return false; + } catch (ClassCastException var5) { + return false; + } + } else { + return false; + } + } + + @GwtIncompatible("NavigableSet") + public static NavigableSet unmodifiableNavigableSet(NavigableSet set) { + return (NavigableSet)(!(set instanceof ImmutableSortedSet) && !(set instanceof Sets.UnmodifiableNavigableSet) ? new Sets.UnmodifiableNavigableSet(set) : set); + } + + @GwtIncompatible("NavigableSet") + public static NavigableSet synchronizedNavigableSet(NavigableSet navigableSet) { + return Synchronized.navigableSet(navigableSet); + } + + static boolean removeAllImpl(Set set, Iterator iterator) { + boolean changed; + for(changed = false; iterator.hasNext(); changed |= set.remove(iterator.next())) { + } + + return changed; + } + + static boolean removeAllImpl(Set set, Collection collection) { + Preconditions.checkNotNull(collection); + if (collection instanceof Multiset) { + collection = ((Multiset)collection).elementSet(); + } + + return collection instanceof Set && ((Collection)collection).size() > set.size() ? Iterators.removeAll(set.iterator(), (Collection)collection) : removeAllImpl(set, ((Collection)collection).iterator()); + } + + @GwtIncompatible("NavigableSet") + static class DescendingSet extends ForwardingNavigableSet { + private final NavigableSet forward; + + DescendingSet(NavigableSet forward) { + this.forward = forward; + } + + protected NavigableSet delegate() { + return this.forward; + } + + public E lower(E e) { + return this.forward.higher(e); + } + + public E floor(E e) { + return this.forward.ceiling(e); + } + + public E ceiling(E e) { + return this.forward.floor(e); + } + + public E higher(E e) { + return this.forward.lower(e); + } + + public E pollFirst() { + return this.forward.pollLast(); + } + + public E pollLast() { + return this.forward.pollFirst(); + } + + public NavigableSet descendingSet() { + return this.forward; + } + + public Iterator descendingIterator() { + return this.forward.iterator(); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return this.forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return this.forward.tailSet(toElement, inclusive).descendingSet(); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return this.forward.headSet(fromElement, inclusive).descendingSet(); + } + + public Comparator comparator() { + Comparator forwardComparator = this.forward.comparator(); + return forwardComparator == null ? Ordering.natural().reverse() : reverse(forwardComparator); + } + + private static Ordering reverse(Comparator forward) { + return Ordering.from(forward).reverse(); + } + + public E first() { + return this.forward.last(); + } + + public SortedSet headSet(E toElement) { + return this.standardHeadSet(toElement); + } + + public E last() { + return this.forward.first(); + } + + public SortedSet subSet(E fromElement, E toElement) { + return this.standardSubSet(fromElement, toElement); + } + + public SortedSet tailSet(E fromElement) { + return this.standardTailSet(fromElement); + } + + public Iterator iterator() { + return this.forward.descendingIterator(); + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + + public String toString() { + return this.standardToString(); + } + } + + @GwtIncompatible("NavigableSet") + static final class UnmodifiableNavigableSet extends ForwardingSortedSet implements NavigableSet, Serializable { + private final NavigableSet delegate; + private transient Sets.UnmodifiableNavigableSet descendingSet; + private static final long serialVersionUID = 0L; + + UnmodifiableNavigableSet(NavigableSet delegate) { + this.delegate = (NavigableSet)Preconditions.checkNotNull(delegate); + } + + protected SortedSet delegate() { + return Collections.unmodifiableSortedSet(this.delegate); + } + + public E lower(E e) { + return this.delegate.lower(e); + } + + public E floor(E e) { + return this.delegate.floor(e); + } + + public E ceiling(E e) { + return this.delegate.ceiling(e); + } + + public E higher(E e) { + return this.delegate.higher(e); + } + + public E pollFirst() { + throw new UnsupportedOperationException(); + } + + public E pollLast() { + throw new UnsupportedOperationException(); + } + + public NavigableSet descendingSet() { + Sets.UnmodifiableNavigableSet result = this.descendingSet; + if (result == null) { + result = this.descendingSet = new Sets.UnmodifiableNavigableSet(this.delegate.descendingSet()); + result.descendingSet = this; + } + + return result; + } + + public Iterator descendingIterator() { + return Iterators.unmodifiableIterator(this.delegate.descendingIterator()); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return Sets.unmodifiableNavigableSet(this.delegate.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return Sets.unmodifiableNavigableSet(this.delegate.headSet(toElement, inclusive)); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return Sets.unmodifiableNavigableSet(this.delegate.tailSet(fromElement, inclusive)); + } + } + + private static final class PowerSet extends AbstractSet> { + final ImmutableMap inputSet; + + PowerSet(Set input) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + int i = 0; + Iterator i$ = ((Set)Preconditions.checkNotNull(input)).iterator(); + + while(i$.hasNext()) { + E e = i$.next(); + builder.put(e, i++); + } + + this.inputSet = builder.build(); + Preconditions.checkArgument(this.inputSet.size() <= 30, "Too many elements to create power set: %s > 30", this.inputSet.size()); + } + + public int size() { + return 1 << this.inputSet.size(); + } + + public boolean isEmpty() { + return false; + } + + public Iterator> iterator() { + return new AbstractIndexedListIterator>(this.size()) { + protected Set get(int setBits) { + return new Sets.SubSet(PowerSet.this.inputSet, setBits); + } + }; + } + + public boolean contains(@Nullable Object obj) { + if (obj instanceof Set) { + Set set = (Set)obj; + return this.inputSet.keySet().containsAll(set); + } else { + return false; + } + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof Sets.PowerSet) { + Sets.PowerSet that = (Sets.PowerSet)obj; + return this.inputSet.equals(that.inputSet); + } else { + return super.equals(obj); + } + } + + public int hashCode() { + return this.inputSet.keySet().hashCode() << this.inputSet.size() - 1; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.inputSet)); + return (new StringBuilder(10 + var1.length())).append("powerSet(").append(var1).append(")").toString(); + } + } + + private static final class SubSet extends AbstractSet { + private final ImmutableMap inputSet; + private final int mask; + + SubSet(ImmutableMap inputSet, int mask) { + this.inputSet = inputSet; + this.mask = mask; + } + + public Iterator iterator() { + return new UnmodifiableIterator() { + final ImmutableList elements; + int remainingSetBits; + + { + this.elements = SubSet.this.inputSet.keySet().asList(); + this.remainingSetBits = SubSet.this.mask; + } + + public boolean hasNext() { + return this.remainingSetBits != 0; + } + + public E next() { + int index = Integer.numberOfTrailingZeros(this.remainingSetBits); + if (index == 32) { + throw new NoSuchElementException(); + } else { + this.remainingSetBits &= ~(1 << index); + return this.elements.get(index); + } + } + }; + } + + public int size() { + return Integer.bitCount(this.mask); + } + + public boolean contains(@Nullable Object o) { + Integer index = (Integer)this.inputSet.get(o); + return index != null && (this.mask & 1 << index) != 0; + } + } + + private static final class CartesianSet extends ForwardingCollection> implements Set> { + private final transient ImmutableList> axes; + private final transient CartesianList delegate; + + static Set> create(List> sets) { + ImmutableList.Builder> axesBuilder = new ImmutableList.Builder(sets.size()); + Iterator i$ = sets.iterator(); + + while(i$.hasNext()) { + Set set = (Set)i$.next(); + ImmutableSet copy = ImmutableSet.copyOf((Collection)set); + if (copy.isEmpty()) { + return ImmutableSet.of(); + } + + axesBuilder.add((Object)copy); + } + + final ImmutableList> axes = axesBuilder.build(); + ImmutableList> listAxes = new ImmutableList>() { + public int size() { + return axes.size(); + } + + public List get(int index) { + return ((ImmutableSet)axes.get(index)).asList(); + } + + boolean isPartialView() { + return true; + } + }; + return new Sets.CartesianSet(axes, new CartesianList(listAxes)); + } + + private CartesianSet(ImmutableList> axes, CartesianList delegate) { + this.axes = axes; + this.delegate = delegate; + } + + protected Collection> delegate() { + return this.delegate; + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Sets.CartesianSet) { + Sets.CartesianSet that = (Sets.CartesianSet)object; + return this.axes.equals(that.axes); + } else { + return super.equals(object); + } + } + + public int hashCode() { + int adjust = this.size() - 1; + + int hash; + for(hash = 0; hash < this.axes.size(); ++hash) { + adjust *= 31; + adjust = ~(~adjust); + } + + hash = 1; + + for(Iterator i$ = this.axes.iterator(); i$.hasNext(); hash = ~(~hash)) { + Set axis = (Set)i$.next(); + hash = 31 * hash + this.size() / axis.size() * axis.hashCode(); + } + + hash += adjust; + return ~(~hash); + } + } + + @GwtIncompatible("NavigableSet") + private static class FilteredNavigableSet extends Sets.FilteredSortedSet implements NavigableSet { + FilteredNavigableSet(NavigableSet unfiltered, Predicate predicate) { + super(unfiltered, predicate); + } + + NavigableSet unfiltered() { + return (NavigableSet)this.unfiltered; + } + + @Nullable + public E lower(E e) { + return Iterators.getNext(this.headSet(e, false).descendingIterator(), (Object)null); + } + + @Nullable + public E floor(E e) { + return Iterators.getNext(this.headSet(e, true).descendingIterator(), (Object)null); + } + + public E ceiling(E e) { + return Iterables.getFirst(this.tailSet(e, true), (Object)null); + } + + public E higher(E e) { + return Iterables.getFirst(this.tailSet(e, false), (Object)null); + } + + public E pollFirst() { + return Iterables.removeFirstMatching(this.unfiltered(), this.predicate); + } + + public E pollLast() { + return Iterables.removeFirstMatching(this.unfiltered().descendingSet(), this.predicate); + } + + public NavigableSet descendingSet() { + return Sets.filter(this.unfiltered().descendingSet(), this.predicate); + } + + public Iterator descendingIterator() { + return Iterators.filter(this.unfiltered().descendingIterator(), this.predicate); + } + + public E last() { + return this.descendingIterator().next(); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return Sets.filter(this.unfiltered().subSet(fromElement, fromInclusive, toElement, toInclusive), this.predicate); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return Sets.filter(this.unfiltered().headSet(toElement, inclusive), this.predicate); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return Sets.filter(this.unfiltered().tailSet(fromElement, inclusive), this.predicate); + } + } + + private static class FilteredSortedSet extends Sets.FilteredSet implements SortedSet { + FilteredSortedSet(SortedSet unfiltered, Predicate predicate) { + super(unfiltered, predicate); + } + + public Comparator comparator() { + return ((SortedSet)this.unfiltered).comparator(); + } + + public SortedSet subSet(E fromElement, E toElement) { + return new Sets.FilteredSortedSet(((SortedSet)this.unfiltered).subSet(fromElement, toElement), this.predicate); + } + + public SortedSet headSet(E toElement) { + return new Sets.FilteredSortedSet(((SortedSet)this.unfiltered).headSet(toElement), this.predicate); + } + + public SortedSet tailSet(E fromElement) { + return new Sets.FilteredSortedSet(((SortedSet)this.unfiltered).tailSet(fromElement), this.predicate); + } + + public E first() { + return this.iterator().next(); + } + + public E last() { + SortedSet sortedUnfiltered = (SortedSet)this.unfiltered; + + while(true) { + E element = sortedUnfiltered.last(); + if (this.predicate.apply(element)) { + return element; + } + + sortedUnfiltered = sortedUnfiltered.headSet(element); + } + } + } + + private static class FilteredSet extends Collections2.FilteredCollection implements Set { + FilteredSet(Set unfiltered, Predicate predicate) { + super(unfiltered, predicate); + } + + public boolean equals(@Nullable Object object) { + return Sets.equalsImpl(this, object); + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + } + + public abstract static class SetView extends AbstractSet { + private SetView() { + } + + public ImmutableSet immutableCopy() { + return ImmutableSet.copyOf((Collection)this); + } + + public > S copyInto(S set) { + set.addAll(this); + return set; + } + + // $FF: synthetic method + SetView(Object x0) { + this(); + } + } + + abstract static class ImprovedAbstractSet extends AbstractSet { + public boolean removeAll(Collection c) { + return Sets.removeAllImpl(this, (Collection)c); + } + + public boolean retainAll(Collection c) { + return super.retainAll((Collection)Preconditions.checkNotNull(c)); + } + } +} diff --git a/src/main/com/google/common/collect/SingletonImmutableBiMap.java b/src/main/com/google/common/collect/SingletonImmutableBiMap.java new file mode 100644 index 0000000..553eb4d --- /dev/null +++ b/src/main/com/google/common/collect/SingletonImmutableBiMap.java @@ -0,0 +1,64 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class SingletonImmutableBiMap extends ImmutableBiMap { + final transient K singleKey; + final transient V singleValue; + transient ImmutableBiMap inverse; + + SingletonImmutableBiMap(K singleKey, V singleValue) { + CollectPreconditions.checkEntryNotNull(singleKey, singleValue); + this.singleKey = singleKey; + this.singleValue = singleValue; + } + + private SingletonImmutableBiMap(K singleKey, V singleValue, ImmutableBiMap inverse) { + this.singleKey = singleKey; + this.singleValue = singleValue; + this.inverse = inverse; + } + + SingletonImmutableBiMap(Entry entry) { + this(entry.getKey(), entry.getValue()); + } + + public V get(@Nullable Object key) { + return this.singleKey.equals(key) ? this.singleValue : null; + } + + public int size() { + return 1; + } + + public boolean containsKey(@Nullable Object key) { + return this.singleKey.equals(key); + } + + public boolean containsValue(@Nullable Object value) { + return this.singleValue.equals(value); + } + + boolean isPartialView() { + return false; + } + + ImmutableSet> createEntrySet() { + return ImmutableSet.of(Maps.immutableEntry(this.singleKey, this.singleValue)); + } + + ImmutableSet createKeySet() { + return ImmutableSet.of(this.singleKey); + } + + public ImmutableBiMap inverse() { + ImmutableBiMap result = this.inverse; + return result == null ? (this.inverse = new SingletonImmutableBiMap(this.singleValue, this.singleKey, this)) : result; + } +} diff --git a/src/main/com/google/common/collect/SingletonImmutableList.java b/src/main/com/google/common/collect/SingletonImmutableList.java new file mode 100644 index 0000000..4a3f29c --- /dev/null +++ b/src/main/com/google/common/collect/SingletonImmutableList.java @@ -0,0 +1,85 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.List; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class SingletonImmutableList extends ImmutableList { + final transient E element; + + SingletonImmutableList(E element) { + this.element = Preconditions.checkNotNull(element); + } + + public E get(int index) { + Preconditions.checkElementIndex(index, 1); + return this.element; + } + + public int indexOf(@Nullable Object object) { + return this.element.equals(object) ? 0 : -1; + } + + public UnmodifiableIterator iterator() { + return Iterators.singletonIterator(this.element); + } + + public int lastIndexOf(@Nullable Object object) { + return this.indexOf(object); + } + + public int size() { + return 1; + } + + public ImmutableList subList(int fromIndex, int toIndex) { + Preconditions.checkPositionIndexes(fromIndex, toIndex, 1); + return (ImmutableList)(fromIndex == toIndex ? ImmutableList.of() : this); + } + + public ImmutableList reverse() { + return this; + } + + public boolean contains(@Nullable Object object) { + return this.element.equals(object); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (!(object instanceof List)) { + return false; + } else { + List that = (List)object; + return that.size() == 1 && this.element.equals(that.get(0)); + } + } + + public int hashCode() { + return 31 + this.element.hashCode(); + } + + public String toString() { + String elementToString = this.element.toString(); + return (new StringBuilder(elementToString.length() + 2)).append('[').append(elementToString).append(']').toString(); + } + + public boolean isEmpty() { + return false; + } + + boolean isPartialView() { + return false; + } + + int copyIntoArray(Object[] dst, int offset) { + dst[offset] = this.element; + return offset + 1; + } +} diff --git a/src/main/com/google/common/collect/SingletonImmutableSet.java b/src/main/com/google/common/collect/SingletonImmutableSet.java new file mode 100644 index 0000000..060d5f3 --- /dev/null +++ b/src/main/com/google/common/collect/SingletonImmutableSet.java @@ -0,0 +1,78 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +final class SingletonImmutableSet extends ImmutableSet { + final transient E element; + private transient int cachedHashCode; + + SingletonImmutableSet(E element) { + this.element = Preconditions.checkNotNull(element); + } + + SingletonImmutableSet(E element, int hashCode) { + this.element = element; + this.cachedHashCode = hashCode; + } + + public int size() { + return 1; + } + + public boolean isEmpty() { + return false; + } + + public boolean contains(Object target) { + return this.element.equals(target); + } + + public UnmodifiableIterator iterator() { + return Iterators.singletonIterator(this.element); + } + + boolean isPartialView() { + return false; + } + + int copyIntoArray(Object[] dst, int offset) { + dst[offset] = this.element; + return offset + 1; + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (!(object instanceof Set)) { + return false; + } else { + Set that = (Set)object; + return that.size() == 1 && this.element.equals(that.iterator().next()); + } + } + + public final int hashCode() { + int code = this.cachedHashCode; + if (code == 0) { + this.cachedHashCode = code = this.element.hashCode(); + } + + return code; + } + + boolean isHashCodeFast() { + return this.cachedHashCode != 0; + } + + public String toString() { + String elementToString = this.element.toString(); + return (new StringBuilder(elementToString.length() + 2)).append('[').append(elementToString).append(']').toString(); + } +} diff --git a/src/main/com/google/common/collect/SingletonImmutableTable.java b/src/main/com/google/common/collect/SingletonImmutableTable.java new file mode 100644 index 0000000..0774ef3 --- /dev/null +++ b/src/main/com/google/common/collect/SingletonImmutableTable.java @@ -0,0 +1,47 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Map; + +@GwtCompatible +class SingletonImmutableTable extends ImmutableTable { + final R singleRowKey; + final C singleColumnKey; + final V singleValue; + + SingletonImmutableTable(R rowKey, C columnKey, V value) { + this.singleRowKey = Preconditions.checkNotNull(rowKey); + this.singleColumnKey = Preconditions.checkNotNull(columnKey); + this.singleValue = Preconditions.checkNotNull(value); + } + + SingletonImmutableTable(Table.Cell cell) { + this(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + + public ImmutableMap column(C columnKey) { + Preconditions.checkNotNull(columnKey); + return this.containsColumn(columnKey) ? ImmutableMap.of(this.singleRowKey, this.singleValue) : ImmutableMap.of(); + } + + public ImmutableMap> columnMap() { + return ImmutableMap.of(this.singleColumnKey, ImmutableMap.of(this.singleRowKey, this.singleValue)); + } + + public ImmutableMap> rowMap() { + return ImmutableMap.of(this.singleRowKey, ImmutableMap.of(this.singleColumnKey, this.singleValue)); + } + + public int size() { + return 1; + } + + ImmutableSet> createCellSet() { + return ImmutableSet.of(cellOf(this.singleRowKey, this.singleColumnKey, this.singleValue)); + } + + ImmutableCollection createValues() { + return ImmutableSet.of(this.singleValue); + } +} diff --git a/src/main/com/google/common/collect/SortedIterable.java b/src/main/com/google/common/collect/SortedIterable.java new file mode 100644 index 0000000..060c9d1 --- /dev/null +++ b/src/main/com/google/common/collect/SortedIterable.java @@ -0,0 +1,12 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Comparator; +import java.util.Iterator; + +@GwtCompatible +interface SortedIterable extends Iterable { + Comparator comparator(); + + Iterator iterator(); +} diff --git a/src/main/com/google/common/collect/SortedIterables.java b/src/main/com/google/common/collect/SortedIterables.java new file mode 100644 index 0000000..9f20470 --- /dev/null +++ b/src/main/com/google/common/collect/SortedIterables.java @@ -0,0 +1,38 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Comparator; +import java.util.SortedSet; + +@GwtCompatible +final class SortedIterables { + private SortedIterables() { + } + + public static boolean hasSameComparator(Comparator comparator, Iterable elements) { + Preconditions.checkNotNull(comparator); + Preconditions.checkNotNull(elements); + Comparator comparator2; + if (elements instanceof SortedSet) { + comparator2 = comparator((SortedSet)elements); + } else { + if (!(elements instanceof SortedIterable)) { + return false; + } + + comparator2 = ((SortedIterable)elements).comparator(); + } + + return comparator.equals(comparator2); + } + + public static Comparator comparator(SortedSet sortedSet) { + Comparator result = sortedSet.comparator(); + if (result == null) { + result = Ordering.natural(); + } + + return (Comparator)result; + } +} diff --git a/src/main/com/google/common/collect/SortedLists.java b/src/main/com/google/common/collect/SortedLists.java new file mode 100644 index 0000000..6bb5558 --- /dev/null +++ b/src/main/com/google/common/collect/SortedLists.java @@ -0,0 +1,151 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; +import javax.annotation.Nullable; + +@GwtCompatible +@Beta +final class SortedLists { + private SortedLists() { + } + + public static int binarySearch(List list, E e, SortedLists.KeyPresentBehavior presentBehavior, SortedLists.KeyAbsentBehavior absentBehavior) { + Preconditions.checkNotNull(e); + return binarySearch(list, (Object)Preconditions.checkNotNull(e), (Comparator)Ordering.natural(), presentBehavior, absentBehavior); + } + + public static int binarySearch(List list, Function keyFunction, @Nullable K key, SortedLists.KeyPresentBehavior presentBehavior, SortedLists.KeyAbsentBehavior absentBehavior) { + return binarySearch(list, keyFunction, key, Ordering.natural(), presentBehavior, absentBehavior); + } + + public static int binarySearch(List list, Function keyFunction, @Nullable K key, Comparator keyComparator, SortedLists.KeyPresentBehavior presentBehavior, SortedLists.KeyAbsentBehavior absentBehavior) { + return binarySearch(Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior); + } + + public static int binarySearch(List list, @Nullable E key, Comparator comparator, SortedLists.KeyPresentBehavior presentBehavior, SortedLists.KeyAbsentBehavior absentBehavior) { + Preconditions.checkNotNull(comparator); + Preconditions.checkNotNull(list); + Preconditions.checkNotNull(presentBehavior); + Preconditions.checkNotNull(absentBehavior); + if (!(list instanceof RandomAccess)) { + list = Lists.newArrayList((Iterable)list); + } + + int lower = 0; + int upper = ((List)list).size() - 1; + + while(lower <= upper) { + int middle = lower + upper >>> 1; + int c = comparator.compare(key, ((List)list).get(middle)); + if (c < 0) { + upper = middle - 1; + } else { + if (c <= 0) { + return lower + presentBehavior.resultIndex(comparator, key, ((List)list).subList(lower, upper + 1), middle - lower); + } + + lower = middle + 1; + } + } + + return absentBehavior.resultIndex(lower); + } + + public static enum KeyAbsentBehavior { + NEXT_LOWER { + int resultIndex(int higherIndex) { + return higherIndex - 1; + } + }, + NEXT_HIGHER { + public int resultIndex(int higherIndex) { + return higherIndex; + } + }, + INVERTED_INSERTION_INDEX { + public int resultIndex(int higherIndex) { + return ~higherIndex; + } + }; + + private KeyAbsentBehavior() { + } + + abstract int resultIndex(int var1); + + // $FF: synthetic method + KeyAbsentBehavior(Object x2) { + this(); + } + } + + public static enum KeyPresentBehavior { + ANY_PRESENT { + int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + return foundIndex; + } + }, + LAST_PRESENT { + int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + int lower = foundIndex; + int upper = list.size() - 1; + + while(lower < upper) { + int middle = lower + upper + 1 >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c > 0) { + upper = middle - 1; + } else { + lower = middle; + } + } + + return lower; + } + }, + FIRST_PRESENT { + int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + int lower = 0; + int upper = foundIndex; + + while(lower < upper) { + int middle = lower + upper >>> 1; + int c = comparator.compare(list.get(middle), key); + if (c < 0) { + lower = middle + 1; + } else { + upper = middle; + } + } + + return lower; + } + }, + FIRST_AFTER { + public int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1; + } + }, + LAST_BEFORE { + public int resultIndex(Comparator comparator, E key, List list, int foundIndex) { + return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1; + } + }; + + private KeyPresentBehavior() { + } + + abstract int resultIndex(Comparator var1, E var2, List var3, int var4); + + // $FF: synthetic method + KeyPresentBehavior(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/SortedMapDifference.java b/src/main/com/google/common/collect/SortedMapDifference.java new file mode 100644 index 0000000..885d9fd --- /dev/null +++ b/src/main/com/google/common/collect/SortedMapDifference.java @@ -0,0 +1,15 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.SortedMap; + +@GwtCompatible +public interface SortedMapDifference extends MapDifference { + SortedMap entriesOnlyOnLeft(); + + SortedMap entriesOnlyOnRight(); + + SortedMap entriesInCommon(); + + SortedMap> entriesDiffering(); +} diff --git a/src/main/com/google/common/collect/SortedMultiset.java b/src/main/com/google/common/collect/SortedMultiset.java new file mode 100644 index 0000000..8c40079 --- /dev/null +++ b/src/main/com/google/common/collect/SortedMultiset.java @@ -0,0 +1,38 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.Set; + +@Beta +@GwtCompatible( + emulated = true +) +public interface SortedMultiset extends SortedMultisetBridge, SortedIterable { + Comparator comparator(); + + Multiset.Entry firstEntry(); + + Multiset.Entry lastEntry(); + + Multiset.Entry pollFirstEntry(); + + Multiset.Entry pollLastEntry(); + + NavigableSet elementSet(); + + Set> entrySet(); + + Iterator iterator(); + + SortedMultiset descendingMultiset(); + + SortedMultiset headMultiset(E var1, BoundType var2); + + SortedMultiset subMultiset(E var1, BoundType var2, E var3, BoundType var4); + + SortedMultiset tailMultiset(E var1, BoundType var2); +} diff --git a/src/main/com/google/common/collect/SortedMultisetBridge.java b/src/main/com/google/common/collect/SortedMultisetBridge.java new file mode 100644 index 0000000..607f949 --- /dev/null +++ b/src/main/com/google/common/collect/SortedMultisetBridge.java @@ -0,0 +1,7 @@ +package com.google.common.collect; + +import java.util.SortedSet; + +interface SortedMultisetBridge extends Multiset { + SortedSet elementSet(); +} diff --git a/src/main/com/google/common/collect/SortedMultisets.java b/src/main/com/google/common/collect/SortedMultisets.java new file mode 100644 index 0000000..db71a32 --- /dev/null +++ b/src/main/com/google/common/collect/SortedMultisets.java @@ -0,0 +1,117 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.SortedSet; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class SortedMultisets { + private SortedMultisets() { + } + + private static E getElementOrThrow(Multiset.Entry entry) { + if (entry == null) { + throw new NoSuchElementException(); + } else { + return entry.getElement(); + } + } + + private static E getElementOrNull(@Nullable Multiset.Entry entry) { + return entry == null ? null : entry.getElement(); + } + + @GwtIncompatible("Navigable") + static class NavigableElementSet extends SortedMultisets.ElementSet implements NavigableSet { + NavigableElementSet(SortedMultiset multiset) { + super(multiset); + } + + public E lower(E e) { + return SortedMultisets.getElementOrNull(this.multiset().headMultiset(e, BoundType.OPEN).lastEntry()); + } + + public E floor(E e) { + return SortedMultisets.getElementOrNull(this.multiset().headMultiset(e, BoundType.CLOSED).lastEntry()); + } + + public E ceiling(E e) { + return SortedMultisets.getElementOrNull(this.multiset().tailMultiset(e, BoundType.CLOSED).firstEntry()); + } + + public E higher(E e) { + return SortedMultisets.getElementOrNull(this.multiset().tailMultiset(e, BoundType.OPEN).firstEntry()); + } + + public NavigableSet descendingSet() { + return new SortedMultisets.NavigableElementSet(this.multiset().descendingMultiset()); + } + + public Iterator descendingIterator() { + return this.descendingSet().iterator(); + } + + public E pollFirst() { + return SortedMultisets.getElementOrNull(this.multiset().pollFirstEntry()); + } + + public E pollLast() { + return SortedMultisets.getElementOrNull(this.multiset().pollLastEntry()); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new SortedMultisets.NavigableElementSet(this.multiset().subMultiset(fromElement, BoundType.forBoolean(fromInclusive), toElement, BoundType.forBoolean(toInclusive))); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return new SortedMultisets.NavigableElementSet(this.multiset().headMultiset(toElement, BoundType.forBoolean(inclusive))); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return new SortedMultisets.NavigableElementSet(this.multiset().tailMultiset(fromElement, BoundType.forBoolean(inclusive))); + } + } + + static class ElementSet extends Multisets.ElementSet implements SortedSet { + private final SortedMultiset multiset; + + ElementSet(SortedMultiset multiset) { + this.multiset = multiset; + } + + final SortedMultiset multiset() { + return this.multiset; + } + + public Comparator comparator() { + return this.multiset().comparator(); + } + + public SortedSet subSet(E fromElement, E toElement) { + return this.multiset().subMultiset(fromElement, BoundType.CLOSED, toElement, BoundType.OPEN).elementSet(); + } + + public SortedSet headSet(E toElement) { + return this.multiset().headMultiset(toElement, BoundType.OPEN).elementSet(); + } + + public SortedSet tailSet(E fromElement) { + return this.multiset().tailMultiset(fromElement, BoundType.CLOSED).elementSet(); + } + + public E first() { + return SortedMultisets.getElementOrThrow(this.multiset().firstEntry()); + } + + public E last() { + return SortedMultisets.getElementOrThrow(this.multiset().lastEntry()); + } + } +} diff --git a/src/main/com/google/common/collect/SortedSetMultimap.java b/src/main/com/google/common/collect/SortedSetMultimap.java new file mode 100644 index 0000000..c8f4492 --- /dev/null +++ b/src/main/com/google/common/collect/SortedSetMultimap.java @@ -0,0 +1,21 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.SortedSet; +import javax.annotation.Nullable; + +@GwtCompatible +public interface SortedSetMultimap extends SetMultimap { + SortedSet get(@Nullable K var1); + + SortedSet removeAll(@Nullable Object var1); + + SortedSet replaceValues(K var1, Iterable var2); + + Map> asMap(); + + Comparator valueComparator(); +} diff --git a/src/main/com/google/common/collect/SparseImmutableTable.java b/src/main/com/google/common/collect/SparseImmutableTable.java new file mode 100644 index 0000000..9b8aebd --- /dev/null +++ b/src/main/com/google/common/collect/SparseImmutableTable.java @@ -0,0 +1,109 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.concurrent.Immutable; + +@GwtCompatible +@Immutable +final class SparseImmutableTable extends RegularImmutableTable { + private final ImmutableMap> rowMap; + private final ImmutableMap> columnMap; + private final int[] iterationOrderRow; + private final int[] iterationOrderColumn; + + SparseImmutableTable(ImmutableList> cellList, ImmutableSet rowSpace, ImmutableSet columnSpace) { + Map rowIndex = Maps.newHashMap(); + Map> rows = Maps.newLinkedHashMap(); + Iterator i$ = rowSpace.iterator(); + + while(i$.hasNext()) { + R row = i$.next(); + rowIndex.put(row, rows.size()); + rows.put(row, new LinkedHashMap()); + } + + Map> columns = Maps.newLinkedHashMap(); + Iterator i$ = columnSpace.iterator(); + + while(i$.hasNext()) { + C col = i$.next(); + columns.put(col, new LinkedHashMap()); + } + + int[] iterationOrderRow = new int[cellList.size()]; + int[] iterationOrderColumn = new int[cellList.size()]; + + for(int i = 0; i < cellList.size(); ++i) { + Table.Cell cell = (Table.Cell)cellList.get(i); + R rowKey = cell.getRowKey(); + C columnKey = cell.getColumnKey(); + V value = cell.getValue(); + iterationOrderRow[i] = (Integer)rowIndex.get(rowKey); + Map thisRow = (Map)rows.get(rowKey); + iterationOrderColumn[i] = thisRow.size(); + V oldValue = thisRow.put(columnKey, value); + if (oldValue != null) { + String var16 = String.valueOf(String.valueOf(rowKey)); + String var17 = String.valueOf(String.valueOf(columnKey)); + String var18 = String.valueOf(String.valueOf(value)); + String var19 = String.valueOf(String.valueOf(oldValue)); + throw new IllegalArgumentException((new StringBuilder(37 + var16.length() + var17.length() + var18.length() + var19.length())).append("Duplicate value for row=").append(var16).append(", column=").append(var17).append(": ").append(var18).append(", ").append(var19).toString()); + } + + ((Map)columns.get(columnKey)).put(rowKey, value); + } + + this.iterationOrderRow = iterationOrderRow; + this.iterationOrderColumn = iterationOrderColumn; + ImmutableMap.Builder> rowBuilder = ImmutableMap.builder(); + Iterator i$ = rows.entrySet().iterator(); + + while(i$.hasNext()) { + Entry> row = (Entry)i$.next(); + rowBuilder.put(row.getKey(), ImmutableMap.copyOf((Map)row.getValue())); + } + + this.rowMap = rowBuilder.build(); + ImmutableMap.Builder> columnBuilder = ImmutableMap.builder(); + Iterator i$ = columns.entrySet().iterator(); + + while(i$.hasNext()) { + Entry> col = (Entry)i$.next(); + columnBuilder.put(col.getKey(), ImmutableMap.copyOf((Map)col.getValue())); + } + + this.columnMap = columnBuilder.build(); + } + + public ImmutableMap> columnMap() { + return this.columnMap; + } + + public ImmutableMap> rowMap() { + return this.rowMap; + } + + public int size() { + return this.iterationOrderRow.length; + } + + Table.Cell getCell(int index) { + int rowIndex = this.iterationOrderRow[index]; + Entry> rowEntry = (Entry)this.rowMap.entrySet().asList().get(rowIndex); + ImmutableMap row = (ImmutableMap)rowEntry.getValue(); + int columnIndex = this.iterationOrderColumn[index]; + Entry colEntry = (Entry)row.entrySet().asList().get(columnIndex); + return cellOf(rowEntry.getKey(), colEntry.getKey(), colEntry.getValue()); + } + + V getValue(int index) { + int rowIndex = this.iterationOrderRow[index]; + ImmutableMap row = (ImmutableMap)this.rowMap.values().asList().get(rowIndex); + int columnIndex = this.iterationOrderColumn[index]; + return row.values().asList().get(columnIndex); + } +} diff --git a/src/main/com/google/common/collect/StandardRowSortedTable.java b/src/main/com/google/common/collect/StandardRowSortedTable.java new file mode 100644 index 0000000..0c8f1fc --- /dev/null +++ b/src/main/com/google/common/collect/StandardRowSortedTable.java @@ -0,0 +1,81 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import java.util.Comparator; +import java.util.Map; +import java.util.SortedMap; +import java.util.SortedSet; + +@GwtCompatible +class StandardRowSortedTable extends StandardTable implements RowSortedTable { + private static final long serialVersionUID = 0L; + + StandardRowSortedTable(SortedMap> backingMap, Supplier> factory) { + super(backingMap, factory); + } + + private SortedMap> sortedBackingMap() { + return (SortedMap)this.backingMap; + } + + public SortedSet rowKeySet() { + return (SortedSet)this.rowMap().keySet(); + } + + public SortedMap> rowMap() { + return (SortedMap)super.rowMap(); + } + + SortedMap> createRowMap() { + return new StandardRowSortedTable.RowSortedMap(); + } + + private class RowSortedMap extends StandardTable.RowMap implements SortedMap> { + private RowSortedMap() { + super(); + } + + public SortedSet keySet() { + return (SortedSet)super.keySet(); + } + + SortedSet createKeySet() { + return new Maps.SortedKeySet(this); + } + + public Comparator comparator() { + return StandardRowSortedTable.this.sortedBackingMap().comparator(); + } + + public R firstKey() { + return StandardRowSortedTable.this.sortedBackingMap().firstKey(); + } + + public R lastKey() { + return StandardRowSortedTable.this.sortedBackingMap().lastKey(); + } + + public SortedMap> headMap(R toKey) { + Preconditions.checkNotNull(toKey); + return (new StandardRowSortedTable(StandardRowSortedTable.this.sortedBackingMap().headMap(toKey), StandardRowSortedTable.this.factory)).rowMap(); + } + + public SortedMap> subMap(R fromKey, R toKey) { + Preconditions.checkNotNull(fromKey); + Preconditions.checkNotNull(toKey); + return (new StandardRowSortedTable(StandardRowSortedTable.this.sortedBackingMap().subMap(fromKey, toKey), StandardRowSortedTable.this.factory)).rowMap(); + } + + public SortedMap> tailMap(R fromKey) { + Preconditions.checkNotNull(fromKey); + return (new StandardRowSortedTable(StandardRowSortedTable.this.sortedBackingMap().tailMap(fromKey), StandardRowSortedTable.this.factory)).rowMap(); + } + + // $FF: synthetic method + RowSortedMap(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/StandardTable.java b/src/main/com/google/common/collect/StandardTable.java new file mode 100644 index 0000000..3efae1f --- /dev/null +++ b/src/main/com/google/common/collect/StandardTable.java @@ -0,0 +1,884 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.base.Supplier; +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible +class StandardTable extends AbstractTable implements Serializable { + @GwtTransient + final Map> backingMap; + @GwtTransient + final Supplier> factory; + private transient Set columnKeySet; + private transient Map> rowMap; + private transient StandardTable.ColumnMap columnMap; + private static final long serialVersionUID = 0L; + + StandardTable(Map> backingMap, Supplier> factory) { + this.backingMap = backingMap; + this.factory = factory; + } + + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); + } + + public boolean containsColumn(@Nullable Object columnKey) { + if (columnKey == null) { + return false; + } else { + Iterator i$ = this.backingMap.values().iterator(); + + Map map; + do { + if (!i$.hasNext()) { + return false; + } + + map = (Map)i$.next(); + } while(!Maps.safeContainsKey(map, columnKey)); + + return true; + } + } + + public boolean containsRow(@Nullable Object rowKey) { + return rowKey != null && Maps.safeContainsKey(this.backingMap, rowKey); + } + + public boolean containsValue(@Nullable Object value) { + return value != null && super.containsValue(value); + } + + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + return rowKey != null && columnKey != null ? super.get(rowKey, columnKey) : null; + } + + public boolean isEmpty() { + return this.backingMap.isEmpty(); + } + + public int size() { + int size = 0; + + Map map; + for(Iterator i$ = this.backingMap.values().iterator(); i$.hasNext(); size += map.size()) { + map = (Map)i$.next(); + } + + return size; + } + + public void clear() { + this.backingMap.clear(); + } + + private Map getOrCreate(R rowKey) { + Map map = (Map)this.backingMap.get(rowKey); + if (map == null) { + map = (Map)this.factory.get(); + this.backingMap.put(rowKey, map); + } + + return map; + } + + public V put(R rowKey, C columnKey, V value) { + Preconditions.checkNotNull(rowKey); + Preconditions.checkNotNull(columnKey); + Preconditions.checkNotNull(value); + return this.getOrCreate(rowKey).put(columnKey, value); + } + + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + if (rowKey != null && columnKey != null) { + Map map = (Map)Maps.safeGet(this.backingMap, rowKey); + if (map == null) { + return null; + } else { + V value = map.remove(columnKey); + if (map.isEmpty()) { + this.backingMap.remove(rowKey); + } + + return value; + } + } else { + return null; + } + } + + private Map removeColumn(Object column) { + Map output = new LinkedHashMap(); + Iterator iterator = this.backingMap.entrySet().iterator(); + + while(iterator.hasNext()) { + Entry> entry = (Entry)iterator.next(); + V value = ((Map)entry.getValue()).remove(column); + if (value != null) { + output.put(entry.getKey(), value); + if (((Map)entry.getValue()).isEmpty()) { + iterator.remove(); + } + } + } + + return output; + } + + private boolean containsMapping(Object rowKey, Object columnKey, Object value) { + return value != null && value.equals(this.get(rowKey, columnKey)); + } + + private boolean removeMapping(Object rowKey, Object columnKey, Object value) { + if (this.containsMapping(rowKey, columnKey, value)) { + this.remove(rowKey, columnKey); + return true; + } else { + return false; + } + } + + public Set> cellSet() { + return super.cellSet(); + } + + Iterator> cellIterator() { + return new StandardTable.CellIterator(); + } + + public Map row(R rowKey) { + return new StandardTable.Row(rowKey); + } + + public Map column(C columnKey) { + return new StandardTable.Column(columnKey); + } + + public Set rowKeySet() { + return this.rowMap().keySet(); + } + + public Set columnKeySet() { + Set result = this.columnKeySet; + return result == null ? (this.columnKeySet = new StandardTable.ColumnKeySet()) : result; + } + + Iterator createColumnKeyIterator() { + return new StandardTable.ColumnKeyIterator(); + } + + public Collection values() { + return super.values(); + } + + public Map> rowMap() { + Map> result = this.rowMap; + return result == null ? (this.rowMap = this.createRowMap()) : result; + } + + Map> createRowMap() { + return new StandardTable.RowMap(); + } + + public Map> columnMap() { + StandardTable.ColumnMap result = this.columnMap; + return result == null ? (this.columnMap = new StandardTable.ColumnMap()) : result; + } + + private class ColumnMap extends Maps.ImprovedAbstractMap> { + private ColumnMap() { + } + + public Map get(Object key) { + return StandardTable.this.containsColumn(key) ? StandardTable.this.column(key) : null; + } + + public boolean containsKey(Object key) { + return StandardTable.this.containsColumn(key); + } + + public Map remove(Object key) { + return StandardTable.this.containsColumn(key) ? StandardTable.this.removeColumn(key) : null; + } + + public Set>> createEntrySet() { + return new StandardTable.ColumnMap.ColumnMapEntrySet(); + } + + public Set keySet() { + return StandardTable.this.columnKeySet(); + } + + Collection> createValues() { + return new StandardTable.ColumnMap.ColumnMapValues(); + } + + // $FF: synthetic method + ColumnMap(Object x1) { + this(); + } + + private class ColumnMapValues extends Maps.Values> { + ColumnMapValues() { + super(ColumnMap.this); + } + + public boolean remove(Object obj) { + Iterator i$ = ColumnMap.this.entrySet().iterator(); + + Entry entry; + do { + if (!i$.hasNext()) { + return false; + } + + entry = (Entry)i$.next(); + } while(!((Map)entry.getValue()).equals(obj)); + + StandardTable.this.removeColumn(entry.getKey()); + return true; + } + + public boolean removeAll(Collection c) { + Preconditions.checkNotNull(c); + boolean changed = false; + Iterator i$ = Lists.newArrayList(StandardTable.this.columnKeySet().iterator()).iterator(); + + while(i$.hasNext()) { + C columnKey = i$.next(); + if (c.contains(StandardTable.this.column(columnKey))) { + StandardTable.this.removeColumn(columnKey); + changed = true; + } + } + + return changed; + } + + public boolean retainAll(Collection c) { + Preconditions.checkNotNull(c); + boolean changed = false; + Iterator i$ = Lists.newArrayList(StandardTable.this.columnKeySet().iterator()).iterator(); + + while(i$.hasNext()) { + C columnKey = i$.next(); + if (!c.contains(StandardTable.this.column(columnKey))) { + StandardTable.this.removeColumn(columnKey); + changed = true; + } + } + + return changed; + } + } + + class ColumnMapEntrySet extends StandardTable.TableSet>> { + ColumnMapEntrySet() { + super(null); + } + + public Iterator>> iterator() { + return Maps.asMapEntryIterator(StandardTable.this.columnKeySet(), new Function>() { + public Map apply(C columnKey) { + return StandardTable.this.column(columnKey); + } + }); + } + + public int size() { + return StandardTable.this.columnKeySet().size(); + } + + public boolean contains(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry)obj; + if (StandardTable.this.containsColumn(entry.getKey())) { + C columnKey = entry.getKey(); + return ColumnMap.this.get(columnKey).equals(entry.getValue()); + } + } + + return false; + } + + public boolean remove(Object obj) { + if (this.contains(obj)) { + Entry entry = (Entry)obj; + StandardTable.this.removeColumn(entry.getKey()); + return true; + } else { + return false; + } + } + + public boolean removeAll(Collection c) { + Preconditions.checkNotNull(c); + return Sets.removeAllImpl(this, (Iterator)c.iterator()); + } + + public boolean retainAll(Collection c) { + Preconditions.checkNotNull(c); + boolean changed = false; + Iterator i$ = Lists.newArrayList(StandardTable.this.columnKeySet().iterator()).iterator(); + + while(i$.hasNext()) { + C columnKey = i$.next(); + if (!c.contains(Maps.immutableEntry(columnKey, StandardTable.this.column(columnKey)))) { + StandardTable.this.removeColumn(columnKey); + changed = true; + } + } + + return changed; + } + } + } + + class RowMap extends Maps.ImprovedAbstractMap> { + public boolean containsKey(Object key) { + return StandardTable.this.containsRow(key); + } + + public Map get(Object key) { + return StandardTable.this.containsRow(key) ? StandardTable.this.row(key) : null; + } + + public Map remove(Object key) { + return key == null ? null : (Map)StandardTable.this.backingMap.remove(key); + } + + protected Set>> createEntrySet() { + return new StandardTable.RowMap.EntrySet(); + } + + class EntrySet extends StandardTable.TableSet>> { + EntrySet() { + super(null); + } + + public Iterator>> iterator() { + return Maps.asMapEntryIterator(StandardTable.this.backingMap.keySet(), new Function>() { + public Map apply(R rowKey) { + return StandardTable.this.row(rowKey); + } + }); + } + + public int size() { + return StandardTable.this.backingMap.size(); + } + + public boolean contains(Object obj) { + if (!(obj instanceof Entry)) { + return false; + } else { + Entry entry = (Entry)obj; + return entry.getKey() != null && entry.getValue() instanceof Map && Collections2.safeContains(StandardTable.this.backingMap.entrySet(), entry); + } + } + + public boolean remove(Object obj) { + if (!(obj instanceof Entry)) { + return false; + } else { + Entry entry = (Entry)obj; + return entry.getKey() != null && entry.getValue() instanceof Map && StandardTable.this.backingMap.entrySet().remove(entry); + } + } + } + } + + private class ColumnKeyIterator extends AbstractIterator { + final Map seen; + final Iterator> mapIterator; + Iterator> entryIterator; + + private ColumnKeyIterator() { + this.seen = (Map)StandardTable.this.factory.get(); + this.mapIterator = StandardTable.this.backingMap.values().iterator(); + this.entryIterator = Iterators.emptyIterator(); + } + + protected C computeNext() { + while(true) { + if (this.entryIterator.hasNext()) { + Entry entry = (Entry)this.entryIterator.next(); + if (!this.seen.containsKey(entry.getKey())) { + this.seen.put(entry.getKey(), entry.getValue()); + return entry.getKey(); + } + } else { + if (!this.mapIterator.hasNext()) { + return this.endOfData(); + } + + this.entryIterator = ((Map)this.mapIterator.next()).entrySet().iterator(); + } + } + } + + // $FF: synthetic method + ColumnKeyIterator(Object x1) { + this(); + } + } + + private class ColumnKeySet extends StandardTable.TableSet { + private ColumnKeySet() { + super(null); + } + + public Iterator iterator() { + return StandardTable.this.createColumnKeyIterator(); + } + + public int size() { + return Iterators.size(this.iterator()); + } + + public boolean remove(Object obj) { + if (obj == null) { + return false; + } else { + boolean changed = false; + Iterator iterator = StandardTable.this.backingMap.values().iterator(); + + while(iterator.hasNext()) { + Map map = (Map)iterator.next(); + if (map.keySet().remove(obj)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + + return changed; + } + } + + public boolean removeAll(Collection c) { + Preconditions.checkNotNull(c); + boolean changed = false; + Iterator iterator = StandardTable.this.backingMap.values().iterator(); + + while(iterator.hasNext()) { + Map map = (Map)iterator.next(); + if (Iterators.removeAll(map.keySet().iterator(), c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + + return changed; + } + + public boolean retainAll(Collection c) { + Preconditions.checkNotNull(c); + boolean changed = false; + Iterator iterator = StandardTable.this.backingMap.values().iterator(); + + while(iterator.hasNext()) { + Map map = (Map)iterator.next(); + if (map.keySet().retainAll(c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + + return changed; + } + + public boolean contains(Object obj) { + return StandardTable.this.containsColumn(obj); + } + + // $FF: synthetic method + ColumnKeySet(Object x1) { + this(); + } + } + + private class Column extends Maps.ImprovedAbstractMap { + final C columnKey; + + Column(C columnKey) { + this.columnKey = Preconditions.checkNotNull(columnKey); + } + + public V put(R key, V value) { + return StandardTable.this.put(key, this.columnKey, value); + } + + public V get(Object key) { + return StandardTable.this.get(key, this.columnKey); + } + + public boolean containsKey(Object key) { + return StandardTable.this.contains(key, this.columnKey); + } + + public V remove(Object key) { + return StandardTable.this.remove(key, this.columnKey); + } + + boolean removeFromColumnIf(Predicate> predicate) { + boolean changed = false; + Iterator iterator = StandardTable.this.backingMap.entrySet().iterator(); + + while(iterator.hasNext()) { + Entry> entry = (Entry)iterator.next(); + Map map = (Map)entry.getValue(); + V value = map.get(this.columnKey); + if (value != null && predicate.apply(Maps.immutableEntry(entry.getKey(), value))) { + map.remove(this.columnKey); + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + + return changed; + } + + Set> createEntrySet() { + return new StandardTable.Column.EntrySet(); + } + + Set createKeySet() { + return new StandardTable.Column.KeySet(); + } + + Collection createValues() { + return new StandardTable.Column.Values(); + } + + private class Values extends Maps.Values { + Values() { + super(Column.this); + } + + public boolean remove(Object obj) { + return obj != null && Column.this.removeFromColumnIf(Maps.valuePredicateOnEntries(Predicates.equalTo(obj))); + } + + public boolean removeAll(Collection c) { + return Column.this.removeFromColumnIf(Maps.valuePredicateOnEntries(Predicates.in(c))); + } + + public boolean retainAll(Collection c) { + return Column.this.removeFromColumnIf(Maps.valuePredicateOnEntries(Predicates.not(Predicates.in(c)))); + } + } + + private class KeySet extends Maps.KeySet { + KeySet() { + super(Column.this); + } + + public boolean contains(Object obj) { + return StandardTable.this.contains(obj, Column.this.columnKey); + } + + public boolean remove(Object obj) { + return StandardTable.this.remove(obj, Column.this.columnKey) != null; + } + + public boolean retainAll(Collection c) { + return Column.this.removeFromColumnIf(Maps.keyPredicateOnEntries(Predicates.not(Predicates.in(c)))); + } + } + + private class EntrySetIterator extends AbstractIterator> { + final Iterator>> iterator; + + private EntrySetIterator() { + this.iterator = StandardTable.this.backingMap.entrySet().iterator(); + } + + protected Entry computeNext() { + while(true) { + if (this.iterator.hasNext()) { + final Entry> entry = (Entry)this.iterator.next(); + if (!((Map)entry.getValue()).containsKey(Column.this.columnKey)) { + continue; + } + + return new AbstractMapEntry() { + public R getKey() { + return entry.getKey(); + } + + public V getValue() { + return ((Map)entry.getValue()).get(Column.this.columnKey); + } + + public V setValue(V value) { + return ((Map)entry.getValue()).put(Column.this.columnKey, Preconditions.checkNotNull(value)); + } + }; + } + + return (Entry)this.endOfData(); + } + } + + // $FF: synthetic method + EntrySetIterator(Object x1) { + this(); + } + } + + private class EntrySet extends Sets.ImprovedAbstractSet> { + private EntrySet() { + } + + public Iterator> iterator() { + return Column.this.new EntrySetIterator(); + } + + public int size() { + int size = 0; + Iterator i$ = StandardTable.this.backingMap.values().iterator(); + + while(i$.hasNext()) { + Map map = (Map)i$.next(); + if (map.containsKey(Column.this.columnKey)) { + ++size; + } + } + + return size; + } + + public boolean isEmpty() { + return !StandardTable.this.containsColumn(Column.this.columnKey); + } + + public void clear() { + Column.this.removeFromColumnIf(Predicates.alwaysTrue()); + } + + public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry)o; + return StandardTable.this.containsMapping(entry.getKey(), Column.this.columnKey, entry.getValue()); + } else { + return false; + } + } + + public boolean remove(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry)obj; + return StandardTable.this.removeMapping(entry.getKey(), Column.this.columnKey, entry.getValue()); + } else { + return false; + } + } + + public boolean retainAll(Collection c) { + return Column.this.removeFromColumnIf(Predicates.not(Predicates.in(c))); + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } + } + + class Row extends Maps.ImprovedAbstractMap { + final R rowKey; + Map backingRowMap; + + Row(R rowKey) { + this.rowKey = Preconditions.checkNotNull(rowKey); + } + + Map backingRowMap() { + return this.backingRowMap != null && (!this.backingRowMap.isEmpty() || !StandardTable.this.backingMap.containsKey(this.rowKey)) ? this.backingRowMap : (this.backingRowMap = this.computeBackingRowMap()); + } + + Map computeBackingRowMap() { + return (Map)StandardTable.this.backingMap.get(this.rowKey); + } + + void maintainEmptyInvariant() { + if (this.backingRowMap() != null && this.backingRowMap.isEmpty()) { + StandardTable.this.backingMap.remove(this.rowKey); + this.backingRowMap = null; + } + + } + + public boolean containsKey(Object key) { + Map backingRowMap = this.backingRowMap(); + return key != null && backingRowMap != null && Maps.safeContainsKey(backingRowMap, key); + } + + public V get(Object key) { + Map backingRowMap = this.backingRowMap(); + return key != null && backingRowMap != null ? Maps.safeGet(backingRowMap, key) : null; + } + + public V put(C key, V value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + return this.backingRowMap != null && !this.backingRowMap.isEmpty() ? this.backingRowMap.put(key, value) : StandardTable.this.put(this.rowKey, key, value); + } + + public V remove(Object key) { + Map backingRowMap = this.backingRowMap(); + if (backingRowMap == null) { + return null; + } else { + V result = Maps.safeRemove(backingRowMap, key); + this.maintainEmptyInvariant(); + return result; + } + } + + public void clear() { + Map backingRowMap = this.backingRowMap(); + if (backingRowMap != null) { + backingRowMap.clear(); + } + + this.maintainEmptyInvariant(); + } + + protected Set> createEntrySet() { + return new StandardTable.Row.RowEntrySet(); + } + + private final class RowEntrySet extends Maps.EntrySet { + private RowEntrySet() { + } + + Map map() { + return Row.this; + } + + public int size() { + Map map = Row.this.backingRowMap(); + return map == null ? 0 : map.size(); + } + + public Iterator> iterator() { + Map map = Row.this.backingRowMap(); + if (map == null) { + return Iterators.emptyModifiableIterator(); + } else { + final Iterator> iterator = map.entrySet().iterator(); + return new Iterator>() { + public boolean hasNext() { + return iterator.hasNext(); + } + + public Entry next() { + final Entry entry = (Entry)iterator.next(); + return new ForwardingMapEntry() { + protected Entry delegate() { + return entry; + } + + public V setValue(V value) { + return super.setValue(Preconditions.checkNotNull(value)); + } + + public boolean equals(Object object) { + return this.standardEquals(object); + } + }; + } + + public void remove() { + iterator.remove(); + Row.this.maintainEmptyInvariant(); + } + }; + } + } + + // $FF: synthetic method + RowEntrySet(Object x1) { + this(); + } + } + } + + private class CellIterator implements Iterator> { + final Iterator>> rowIterator; + Entry> rowEntry; + Iterator> columnIterator; + + private CellIterator() { + this.rowIterator = StandardTable.this.backingMap.entrySet().iterator(); + this.columnIterator = Iterators.emptyModifiableIterator(); + } + + public boolean hasNext() { + return this.rowIterator.hasNext() || this.columnIterator.hasNext(); + } + + public Table.Cell next() { + if (!this.columnIterator.hasNext()) { + this.rowEntry = (Entry)this.rowIterator.next(); + this.columnIterator = ((Map)this.rowEntry.getValue()).entrySet().iterator(); + } + + Entry columnEntry = (Entry)this.columnIterator.next(); + return Tables.immutableCell(this.rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); + } + + public void remove() { + this.columnIterator.remove(); + if (((Map)this.rowEntry.getValue()).isEmpty()) { + this.rowIterator.remove(); + } + + } + + // $FF: synthetic method + CellIterator(Object x1) { + this(); + } + } + + private abstract class TableSet extends Sets.ImprovedAbstractSet { + private TableSet() { + } + + public boolean isEmpty() { + return StandardTable.this.backingMap.isEmpty(); + } + + public void clear() { + StandardTable.this.backingMap.clear(); + } + + // $FF: synthetic method + TableSet(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/Synchronized.java b/src/main/com/google/common/collect/Synchronized.java new file mode 100644 index 0000000..1fee6f9 --- /dev/null +++ b/src/main/com/google/common/collect/Synchronized.java @@ -0,0 +1,1562 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Comparator; +import java.util.Deque; +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.Queue; +import java.util.RandomAccess; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +final class Synchronized { + private Synchronized() { + } + + private static Collection collection(Collection collection, @Nullable Object mutex) { + return new Synchronized.SynchronizedCollection(collection, mutex); + } + + @VisibleForTesting + static Set set(Set set, @Nullable Object mutex) { + return new Synchronized.SynchronizedSet(set, mutex); + } + + private static SortedSet sortedSet(SortedSet set, @Nullable Object mutex) { + return new Synchronized.SynchronizedSortedSet(set, mutex); + } + + private static List list(List list, @Nullable Object mutex) { + return (List)(list instanceof RandomAccess ? new Synchronized.SynchronizedRandomAccessList(list, mutex) : new Synchronized.SynchronizedList(list, mutex)); + } + + static Multiset multiset(Multiset multiset, @Nullable Object mutex) { + return (Multiset)(!(multiset instanceof Synchronized.SynchronizedMultiset) && !(multiset instanceof ImmutableMultiset) ? new Synchronized.SynchronizedMultiset(multiset, mutex) : multiset); + } + + static Multimap multimap(Multimap multimap, @Nullable Object mutex) { + return (Multimap)(!(multimap instanceof Synchronized.SynchronizedMultimap) && !(multimap instanceof ImmutableMultimap) ? new Synchronized.SynchronizedMultimap(multimap, mutex) : multimap); + } + + static ListMultimap listMultimap(ListMultimap multimap, @Nullable Object mutex) { + return (ListMultimap)(!(multimap instanceof Synchronized.SynchronizedListMultimap) && !(multimap instanceof ImmutableListMultimap) ? new Synchronized.SynchronizedListMultimap(multimap, mutex) : multimap); + } + + static SetMultimap setMultimap(SetMultimap multimap, @Nullable Object mutex) { + return (SetMultimap)(!(multimap instanceof Synchronized.SynchronizedSetMultimap) && !(multimap instanceof ImmutableSetMultimap) ? new Synchronized.SynchronizedSetMultimap(multimap, mutex) : multimap); + } + + static SortedSetMultimap sortedSetMultimap(SortedSetMultimap multimap, @Nullable Object mutex) { + return (SortedSetMultimap)(multimap instanceof Synchronized.SynchronizedSortedSetMultimap ? multimap : new Synchronized.SynchronizedSortedSetMultimap(multimap, mutex)); + } + + private static Collection typePreservingCollection(Collection collection, @Nullable Object mutex) { + if (collection instanceof SortedSet) { + return sortedSet((SortedSet)collection, mutex); + } else if (collection instanceof Set) { + return set((Set)collection, mutex); + } else { + return (Collection)(collection instanceof List ? list((List)collection, mutex) : collection(collection, mutex)); + } + } + + private static Set typePreservingSet(Set set, @Nullable Object mutex) { + return (Set)(set instanceof SortedSet ? sortedSet((SortedSet)set, mutex) : set(set, mutex)); + } + + @VisibleForTesting + static Map map(Map map, @Nullable Object mutex) { + return new Synchronized.SynchronizedMap(map, mutex); + } + + static SortedMap sortedMap(SortedMap sortedMap, @Nullable Object mutex) { + return new Synchronized.SynchronizedSortedMap(sortedMap, mutex); + } + + static BiMap biMap(BiMap bimap, @Nullable Object mutex) { + return (BiMap)(!(bimap instanceof Synchronized.SynchronizedBiMap) && !(bimap instanceof ImmutableBiMap) ? new Synchronized.SynchronizedBiMap(bimap, mutex, (BiMap)null) : bimap); + } + + @GwtIncompatible("NavigableSet") + static NavigableSet navigableSet(NavigableSet navigableSet, @Nullable Object mutex) { + return new Synchronized.SynchronizedNavigableSet(navigableSet, mutex); + } + + @GwtIncompatible("NavigableSet") + static NavigableSet navigableSet(NavigableSet navigableSet) { + return navigableSet(navigableSet, (Object)null); + } + + @GwtIncompatible("NavigableMap") + static NavigableMap navigableMap(NavigableMap navigableMap) { + return navigableMap(navigableMap, (Object)null); + } + + @GwtIncompatible("NavigableMap") + static NavigableMap navigableMap(NavigableMap navigableMap, @Nullable Object mutex) { + return new Synchronized.SynchronizedNavigableMap(navigableMap, mutex); + } + + @GwtIncompatible("works but is needed only for NavigableMap") + private static Entry nullableSynchronizedEntry(@Nullable Entry entry, @Nullable Object mutex) { + return entry == null ? null : new Synchronized.SynchronizedEntry(entry, mutex); + } + + static Queue queue(Queue queue, @Nullable Object mutex) { + return (Queue)(queue instanceof Synchronized.SynchronizedQueue ? queue : new Synchronized.SynchronizedQueue(queue, mutex)); + } + + @GwtIncompatible("Deque") + static Deque deque(Deque deque, @Nullable Object mutex) { + return new Synchronized.SynchronizedDeque(deque, mutex); + } + + @GwtIncompatible("Deque") + private static final class SynchronizedDeque extends Synchronized.SynchronizedQueue implements Deque { + private static final long serialVersionUID = 0L; + + SynchronizedDeque(Deque delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + Deque delegate() { + return (Deque)super.delegate(); + } + + public void addFirst(E e) { + synchronized(this.mutex) { + this.delegate().addFirst(e); + } + } + + public void addLast(E e) { + synchronized(this.mutex) { + this.delegate().addLast(e); + } + } + + public boolean offerFirst(E e) { + synchronized(this.mutex) { + return this.delegate().offerFirst(e); + } + } + + public boolean offerLast(E e) { + synchronized(this.mutex) { + return this.delegate().offerLast(e); + } + } + + public E removeFirst() { + synchronized(this.mutex) { + return this.delegate().removeFirst(); + } + } + + public E removeLast() { + synchronized(this.mutex) { + return this.delegate().removeLast(); + } + } + + public E pollFirst() { + synchronized(this.mutex) { + return this.delegate().pollFirst(); + } + } + + public E pollLast() { + synchronized(this.mutex) { + return this.delegate().pollLast(); + } + } + + public E getFirst() { + synchronized(this.mutex) { + return this.delegate().getFirst(); + } + } + + public E getLast() { + synchronized(this.mutex) { + return this.delegate().getLast(); + } + } + + public E peekFirst() { + synchronized(this.mutex) { + return this.delegate().peekFirst(); + } + } + + public E peekLast() { + synchronized(this.mutex) { + return this.delegate().peekLast(); + } + } + + public boolean removeFirstOccurrence(Object o) { + synchronized(this.mutex) { + return this.delegate().removeFirstOccurrence(o); + } + } + + public boolean removeLastOccurrence(Object o) { + synchronized(this.mutex) { + return this.delegate().removeLastOccurrence(o); + } + } + + public void push(E e) { + synchronized(this.mutex) { + this.delegate().push(e); + } + } + + public E pop() { + synchronized(this.mutex) { + return this.delegate().pop(); + } + } + + public Iterator descendingIterator() { + synchronized(this.mutex) { + return this.delegate().descendingIterator(); + } + } + } + + private static class SynchronizedQueue extends Synchronized.SynchronizedCollection implements Queue { + private static final long serialVersionUID = 0L; + + SynchronizedQueue(Queue delegate, @Nullable Object mutex) { + super(delegate, mutex, null); + } + + Queue delegate() { + return (Queue)super.delegate(); + } + + public E element() { + synchronized(this.mutex) { + return this.delegate().element(); + } + } + + public boolean offer(E e) { + synchronized(this.mutex) { + return this.delegate().offer(e); + } + } + + public E peek() { + synchronized(this.mutex) { + return this.delegate().peek(); + } + } + + public E poll() { + synchronized(this.mutex) { + return this.delegate().poll(); + } + } + + public E remove() { + synchronized(this.mutex) { + return this.delegate().remove(); + } + } + } + + @GwtIncompatible("works but is needed only for NavigableMap") + private static class SynchronizedEntry extends Synchronized.SynchronizedObject implements Entry { + private static final long serialVersionUID = 0L; + + SynchronizedEntry(Entry delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + Entry delegate() { + return (Entry)super.delegate(); + } + + public boolean equals(Object obj) { + synchronized(this.mutex) { + return this.delegate().equals(obj); + } + } + + public int hashCode() { + synchronized(this.mutex) { + return this.delegate().hashCode(); + } + } + + public K getKey() { + synchronized(this.mutex) { + return this.delegate().getKey(); + } + } + + public V getValue() { + synchronized(this.mutex) { + return this.delegate().getValue(); + } + } + + public V setValue(V value) { + synchronized(this.mutex) { + return this.delegate().setValue(value); + } + } + } + + @GwtIncompatible("NavigableMap") + @VisibleForTesting + static class SynchronizedNavigableMap extends Synchronized.SynchronizedSortedMap implements NavigableMap { + transient NavigableSet descendingKeySet; + transient NavigableMap descendingMap; + transient NavigableSet navigableKeySet; + private static final long serialVersionUID = 0L; + + SynchronizedNavigableMap(NavigableMap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + NavigableMap delegate() { + return (NavigableMap)super.delegate(); + } + + public Entry ceilingEntry(K key) { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().ceilingEntry(key), this.mutex); + } + } + + public K ceilingKey(K key) { + synchronized(this.mutex) { + return this.delegate().ceilingKey(key); + } + } + + public NavigableSet descendingKeySet() { + synchronized(this.mutex) { + return this.descendingKeySet == null ? (this.descendingKeySet = Synchronized.navigableSet(this.delegate().descendingKeySet(), this.mutex)) : this.descendingKeySet; + } + } + + public NavigableMap descendingMap() { + synchronized(this.mutex) { + return this.descendingMap == null ? (this.descendingMap = Synchronized.navigableMap(this.delegate().descendingMap(), this.mutex)) : this.descendingMap; + } + } + + public Entry firstEntry() { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().firstEntry(), this.mutex); + } + } + + public Entry floorEntry(K key) { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().floorEntry(key), this.mutex); + } + } + + public K floorKey(K key) { + synchronized(this.mutex) { + return this.delegate().floorKey(key); + } + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + synchronized(this.mutex) { + return Synchronized.navigableMap(this.delegate().headMap(toKey, inclusive), this.mutex); + } + } + + public Entry higherEntry(K key) { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().higherEntry(key), this.mutex); + } + } + + public K higherKey(K key) { + synchronized(this.mutex) { + return this.delegate().higherKey(key); + } + } + + public Entry lastEntry() { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().lastEntry(), this.mutex); + } + } + + public Entry lowerEntry(K key) { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().lowerEntry(key), this.mutex); + } + } + + public K lowerKey(K key) { + synchronized(this.mutex) { + return this.delegate().lowerKey(key); + } + } + + public Set keySet() { + return this.navigableKeySet(); + } + + public NavigableSet navigableKeySet() { + synchronized(this.mutex) { + return this.navigableKeySet == null ? (this.navigableKeySet = Synchronized.navigableSet(this.delegate().navigableKeySet(), this.mutex)) : this.navigableKeySet; + } + } + + public Entry pollFirstEntry() { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().pollFirstEntry(), this.mutex); + } + } + + public Entry pollLastEntry() { + synchronized(this.mutex) { + return Synchronized.nullableSynchronizedEntry(this.delegate().pollLastEntry(), this.mutex); + } + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + synchronized(this.mutex) { + return Synchronized.navigableMap(this.delegate().subMap(fromKey, fromInclusive, toKey, toInclusive), this.mutex); + } + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + synchronized(this.mutex) { + return Synchronized.navigableMap(this.delegate().tailMap(fromKey, inclusive), this.mutex); + } + } + + public SortedMap headMap(K toKey) { + return this.headMap(toKey, false); + } + + public SortedMap subMap(K fromKey, K toKey) { + return this.subMap(fromKey, true, toKey, false); + } + + public SortedMap tailMap(K fromKey) { + return this.tailMap(fromKey, true); + } + } + + @GwtIncompatible("NavigableSet") + @VisibleForTesting + static class SynchronizedNavigableSet extends Synchronized.SynchronizedSortedSet implements NavigableSet { + transient NavigableSet descendingSet; + private static final long serialVersionUID = 0L; + + SynchronizedNavigableSet(NavigableSet delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + NavigableSet delegate() { + return (NavigableSet)super.delegate(); + } + + public E ceiling(E e) { + synchronized(this.mutex) { + return this.delegate().ceiling(e); + } + } + + public Iterator descendingIterator() { + return this.delegate().descendingIterator(); + } + + public NavigableSet descendingSet() { + synchronized(this.mutex) { + if (this.descendingSet == null) { + NavigableSet dS = Synchronized.navigableSet(this.delegate().descendingSet(), this.mutex); + this.descendingSet = dS; + return dS; + } else { + return this.descendingSet; + } + } + } + + public E floor(E e) { + synchronized(this.mutex) { + return this.delegate().floor(e); + } + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + synchronized(this.mutex) { + return Synchronized.navigableSet(this.delegate().headSet(toElement, inclusive), this.mutex); + } + } + + public E higher(E e) { + synchronized(this.mutex) { + return this.delegate().higher(e); + } + } + + public E lower(E e) { + synchronized(this.mutex) { + return this.delegate().lower(e); + } + } + + public E pollFirst() { + synchronized(this.mutex) { + return this.delegate().pollFirst(); + } + } + + public E pollLast() { + synchronized(this.mutex) { + return this.delegate().pollLast(); + } + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + synchronized(this.mutex) { + return Synchronized.navigableSet(this.delegate().subSet(fromElement, fromInclusive, toElement, toInclusive), this.mutex); + } + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + synchronized(this.mutex) { + return Synchronized.navigableSet(this.delegate().tailSet(fromElement, inclusive), this.mutex); + } + } + + public SortedSet headSet(E toElement) { + return this.headSet(toElement, false); + } + + public SortedSet subSet(E fromElement, E toElement) { + return this.subSet(fromElement, true, toElement, false); + } + + public SortedSet tailSet(E fromElement) { + return this.tailSet(fromElement, true); + } + } + + private static class SynchronizedAsMapValues extends Synchronized.SynchronizedCollection> { + private static final long serialVersionUID = 0L; + + SynchronizedAsMapValues(Collection> delegate, @Nullable Object mutex) { + super(delegate, mutex, null); + } + + public Iterator> iterator() { + final Iterator> iterator = super.iterator(); + return new ForwardingIterator>() { + protected Iterator> delegate() { + return iterator; + } + + public Collection next() { + return Synchronized.typePreservingCollection((Collection)super.next(), SynchronizedAsMapValues.this.mutex); + } + }; + } + } + + private static class SynchronizedAsMap extends Synchronized.SynchronizedMap> { + transient Set>> asMapEntrySet; + transient Collection> asMapValues; + private static final long serialVersionUID = 0L; + + SynchronizedAsMap(Map> delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + public Collection get(Object key) { + synchronized(this.mutex) { + Collection collection = (Collection)super.get(key); + return collection == null ? null : Synchronized.typePreservingCollection(collection, this.mutex); + } + } + + public Set>> entrySet() { + synchronized(this.mutex) { + if (this.asMapEntrySet == null) { + this.asMapEntrySet = new Synchronized.SynchronizedAsMapEntries(this.delegate().entrySet(), this.mutex); + } + + return this.asMapEntrySet; + } + } + + public Collection> values() { + synchronized(this.mutex) { + if (this.asMapValues == null) { + this.asMapValues = new Synchronized.SynchronizedAsMapValues(this.delegate().values(), this.mutex); + } + + return this.asMapValues; + } + } + + public boolean containsValue(Object o) { + return this.values().contains(o); + } + } + + @VisibleForTesting + static class SynchronizedBiMap extends Synchronized.SynchronizedMap implements BiMap, Serializable { + private transient Set valueSet; + private transient BiMap inverse; + private static final long serialVersionUID = 0L; + + private SynchronizedBiMap(BiMap delegate, @Nullable Object mutex, @Nullable BiMap inverse) { + super(delegate, mutex); + this.inverse = inverse; + } + + BiMap delegate() { + return (BiMap)super.delegate(); + } + + public Set values() { + synchronized(this.mutex) { + if (this.valueSet == null) { + this.valueSet = Synchronized.set(this.delegate().values(), this.mutex); + } + + return this.valueSet; + } + } + + public V forcePut(K key, V value) { + synchronized(this.mutex) { + return this.delegate().forcePut(key, value); + } + } + + public BiMap inverse() { + synchronized(this.mutex) { + if (this.inverse == null) { + this.inverse = new Synchronized.SynchronizedBiMap(this.delegate().inverse(), this.mutex, this); + } + + return this.inverse; + } + } + + // $FF: synthetic method + SynchronizedBiMap(BiMap x0, Object x1, BiMap x2, Object x3) { + this(x0, x1, x2); + } + } + + static class SynchronizedSortedMap extends Synchronized.SynchronizedMap implements SortedMap { + private static final long serialVersionUID = 0L; + + SynchronizedSortedMap(SortedMap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + SortedMap delegate() { + return (SortedMap)super.delegate(); + } + + public Comparator comparator() { + synchronized(this.mutex) { + return this.delegate().comparator(); + } + } + + public K firstKey() { + synchronized(this.mutex) { + return this.delegate().firstKey(); + } + } + + public SortedMap headMap(K toKey) { + synchronized(this.mutex) { + return Synchronized.sortedMap(this.delegate().headMap(toKey), this.mutex); + } + } + + public K lastKey() { + synchronized(this.mutex) { + return this.delegate().lastKey(); + } + } + + public SortedMap subMap(K fromKey, K toKey) { + synchronized(this.mutex) { + return Synchronized.sortedMap(this.delegate().subMap(fromKey, toKey), this.mutex); + } + } + + public SortedMap tailMap(K fromKey) { + synchronized(this.mutex) { + return Synchronized.sortedMap(this.delegate().tailMap(fromKey), this.mutex); + } + } + } + + private static class SynchronizedMap extends Synchronized.SynchronizedObject implements Map { + transient Set keySet; + transient Collection values; + transient Set> entrySet; + private static final long serialVersionUID = 0L; + + SynchronizedMap(Map delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + Map delegate() { + return (Map)super.delegate(); + } + + public void clear() { + synchronized(this.mutex) { + this.delegate().clear(); + } + } + + public boolean containsKey(Object key) { + synchronized(this.mutex) { + return this.delegate().containsKey(key); + } + } + + public boolean containsValue(Object value) { + synchronized(this.mutex) { + return this.delegate().containsValue(value); + } + } + + public Set> entrySet() { + synchronized(this.mutex) { + if (this.entrySet == null) { + this.entrySet = Synchronized.set(this.delegate().entrySet(), this.mutex); + } + + return this.entrySet; + } + } + + public V get(Object key) { + synchronized(this.mutex) { + return this.delegate().get(key); + } + } + + public boolean isEmpty() { + synchronized(this.mutex) { + return this.delegate().isEmpty(); + } + } + + public Set keySet() { + synchronized(this.mutex) { + if (this.keySet == null) { + this.keySet = Synchronized.set(this.delegate().keySet(), this.mutex); + } + + return this.keySet; + } + } + + public V put(K key, V value) { + synchronized(this.mutex) { + return this.delegate().put(key, value); + } + } + + public void putAll(Map map) { + synchronized(this.mutex) { + this.delegate().putAll(map); + } + } + + public V remove(Object key) { + synchronized(this.mutex) { + return this.delegate().remove(key); + } + } + + public int size() { + synchronized(this.mutex) { + return this.delegate().size(); + } + } + + public Collection values() { + synchronized(this.mutex) { + if (this.values == null) { + this.values = Synchronized.collection(this.delegate().values(), this.mutex); + } + + return this.values; + } + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else { + synchronized(this.mutex) { + return this.delegate().equals(o); + } + } + } + + public int hashCode() { + synchronized(this.mutex) { + return this.delegate().hashCode(); + } + } + } + + private static class SynchronizedAsMapEntries extends Synchronized.SynchronizedSet>> { + private static final long serialVersionUID = 0L; + + SynchronizedAsMapEntries(Set>> delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + public Iterator>> iterator() { + final Iterator>> iterator = super.iterator(); + return new ForwardingIterator>>() { + protected Iterator>> delegate() { + return iterator; + } + + public Entry> next() { + final Entry> entry = (Entry)super.next(); + return new ForwardingMapEntry>() { + protected Entry> delegate() { + return entry; + } + + public Collection getValue() { + return Synchronized.typePreservingCollection((Collection)entry.getValue(), SynchronizedAsMapEntries.this.mutex); + } + }; + } + }; + } + + public Object[] toArray() { + synchronized(this.mutex) { + return ObjectArrays.toArrayImpl(this.delegate()); + } + } + + public T[] toArray(T[] array) { + synchronized(this.mutex) { + return ObjectArrays.toArrayImpl(this.delegate(), array); + } + } + + public boolean contains(Object o) { + synchronized(this.mutex) { + return Maps.containsEntryImpl(this.delegate(), o); + } + } + + public boolean containsAll(Collection c) { + synchronized(this.mutex) { + return Collections2.containsAllImpl(this.delegate(), c); + } + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else { + synchronized(this.mutex) { + return Sets.equalsImpl(this.delegate(), o); + } + } + } + + public boolean remove(Object o) { + synchronized(this.mutex) { + return Maps.removeEntryImpl(this.delegate(), o); + } + } + + public boolean removeAll(Collection c) { + synchronized(this.mutex) { + return Iterators.removeAll(this.delegate().iterator(), c); + } + } + + public boolean retainAll(Collection c) { + synchronized(this.mutex) { + return Iterators.retainAll(this.delegate().iterator(), c); + } + } + } + + private static class SynchronizedSortedSetMultimap extends Synchronized.SynchronizedSetMultimap implements SortedSetMultimap { + private static final long serialVersionUID = 0L; + + SynchronizedSortedSetMultimap(SortedSetMultimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + SortedSetMultimap delegate() { + return (SortedSetMultimap)super.delegate(); + } + + public SortedSet get(K key) { + synchronized(this.mutex) { + return Synchronized.sortedSet(this.delegate().get(key), this.mutex); + } + } + + public SortedSet removeAll(Object key) { + synchronized(this.mutex) { + return this.delegate().removeAll(key); + } + } + + public SortedSet replaceValues(K key, Iterable values) { + synchronized(this.mutex) { + return this.delegate().replaceValues(key, values); + } + } + + public Comparator valueComparator() { + synchronized(this.mutex) { + return this.delegate().valueComparator(); + } + } + } + + private static class SynchronizedSetMultimap extends Synchronized.SynchronizedMultimap implements SetMultimap { + transient Set> entrySet; + private static final long serialVersionUID = 0L; + + SynchronizedSetMultimap(SetMultimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + SetMultimap delegate() { + return (SetMultimap)super.delegate(); + } + + public Set get(K key) { + synchronized(this.mutex) { + return Synchronized.set(this.delegate().get(key), this.mutex); + } + } + + public Set removeAll(Object key) { + synchronized(this.mutex) { + return this.delegate().removeAll(key); + } + } + + public Set replaceValues(K key, Iterable values) { + synchronized(this.mutex) { + return this.delegate().replaceValues(key, values); + } + } + + public Set> entries() { + synchronized(this.mutex) { + if (this.entrySet == null) { + this.entrySet = Synchronized.set(this.delegate().entries(), this.mutex); + } + + return this.entrySet; + } + } + } + + private static class SynchronizedListMultimap extends Synchronized.SynchronizedMultimap implements ListMultimap { + private static final long serialVersionUID = 0L; + + SynchronizedListMultimap(ListMultimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + ListMultimap delegate() { + return (ListMultimap)super.delegate(); + } + + public List get(K key) { + synchronized(this.mutex) { + return Synchronized.list(this.delegate().get(key), this.mutex); + } + } + + public List removeAll(Object key) { + synchronized(this.mutex) { + return this.delegate().removeAll(key); + } + } + + public List replaceValues(K key, Iterable values) { + synchronized(this.mutex) { + return this.delegate().replaceValues(key, values); + } + } + } + + private static class SynchronizedMultimap extends Synchronized.SynchronizedObject implements Multimap { + transient Set keySet; + transient Collection valuesCollection; + transient Collection> entries; + transient Map> asMap; + transient Multiset keys; + private static final long serialVersionUID = 0L; + + Multimap delegate() { + return (Multimap)super.delegate(); + } + + SynchronizedMultimap(Multimap delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + public int size() { + synchronized(this.mutex) { + return this.delegate().size(); + } + } + + public boolean isEmpty() { + synchronized(this.mutex) { + return this.delegate().isEmpty(); + } + } + + public boolean containsKey(Object key) { + synchronized(this.mutex) { + return this.delegate().containsKey(key); + } + } + + public boolean containsValue(Object value) { + synchronized(this.mutex) { + return this.delegate().containsValue(value); + } + } + + public boolean containsEntry(Object key, Object value) { + synchronized(this.mutex) { + return this.delegate().containsEntry(key, value); + } + } + + public Collection get(K key) { + synchronized(this.mutex) { + return Synchronized.typePreservingCollection(this.delegate().get(key), this.mutex); + } + } + + public boolean put(K key, V value) { + synchronized(this.mutex) { + return this.delegate().put(key, value); + } + } + + public boolean putAll(K key, Iterable values) { + synchronized(this.mutex) { + return this.delegate().putAll(key, values); + } + } + + public boolean putAll(Multimap multimap) { + synchronized(this.mutex) { + return this.delegate().putAll(multimap); + } + } + + public Collection replaceValues(K key, Iterable values) { + synchronized(this.mutex) { + return this.delegate().replaceValues(key, values); + } + } + + public boolean remove(Object key, Object value) { + synchronized(this.mutex) { + return this.delegate().remove(key, value); + } + } + + public Collection removeAll(Object key) { + synchronized(this.mutex) { + return this.delegate().removeAll(key); + } + } + + public void clear() { + synchronized(this.mutex) { + this.delegate().clear(); + } + } + + public Set keySet() { + synchronized(this.mutex) { + if (this.keySet == null) { + this.keySet = Synchronized.typePreservingSet(this.delegate().keySet(), this.mutex); + } + + return this.keySet; + } + } + + public Collection values() { + synchronized(this.mutex) { + if (this.valuesCollection == null) { + this.valuesCollection = Synchronized.collection(this.delegate().values(), this.mutex); + } + + return this.valuesCollection; + } + } + + public Collection> entries() { + synchronized(this.mutex) { + if (this.entries == null) { + this.entries = Synchronized.typePreservingCollection(this.delegate().entries(), this.mutex); + } + + return this.entries; + } + } + + public Map> asMap() { + synchronized(this.mutex) { + if (this.asMap == null) { + this.asMap = new Synchronized.SynchronizedAsMap(this.delegate().asMap(), this.mutex); + } + + return this.asMap; + } + } + + public Multiset keys() { + synchronized(this.mutex) { + if (this.keys == null) { + this.keys = Synchronized.multiset(this.delegate().keys(), this.mutex); + } + + return this.keys; + } + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else { + synchronized(this.mutex) { + return this.delegate().equals(o); + } + } + } + + public int hashCode() { + synchronized(this.mutex) { + return this.delegate().hashCode(); + } + } + } + + private static class SynchronizedMultiset extends Synchronized.SynchronizedCollection implements Multiset { + transient Set elementSet; + transient Set> entrySet; + private static final long serialVersionUID = 0L; + + SynchronizedMultiset(Multiset delegate, @Nullable Object mutex) { + super(delegate, mutex, null); + } + + Multiset delegate() { + return (Multiset)super.delegate(); + } + + public int count(Object o) { + synchronized(this.mutex) { + return this.delegate().count(o); + } + } + + public int add(E e, int n) { + synchronized(this.mutex) { + return this.delegate().add(e, n); + } + } + + public int remove(Object o, int n) { + synchronized(this.mutex) { + return this.delegate().remove(o, n); + } + } + + public int setCount(E element, int count) { + synchronized(this.mutex) { + return this.delegate().setCount(element, count); + } + } + + public boolean setCount(E element, int oldCount, int newCount) { + synchronized(this.mutex) { + return this.delegate().setCount(element, oldCount, newCount); + } + } + + public Set elementSet() { + synchronized(this.mutex) { + if (this.elementSet == null) { + this.elementSet = Synchronized.typePreservingSet(this.delegate().elementSet(), this.mutex); + } + + return this.elementSet; + } + } + + public Set> entrySet() { + synchronized(this.mutex) { + if (this.entrySet == null) { + this.entrySet = Synchronized.typePreservingSet(this.delegate().entrySet(), this.mutex); + } + + return this.entrySet; + } + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else { + synchronized(this.mutex) { + return this.delegate().equals(o); + } + } + } + + public int hashCode() { + synchronized(this.mutex) { + return this.delegate().hashCode(); + } + } + } + + private static class SynchronizedRandomAccessList extends Synchronized.SynchronizedList implements RandomAccess { + private static final long serialVersionUID = 0L; + + SynchronizedRandomAccessList(List list, @Nullable Object mutex) { + super(list, mutex); + } + } + + private static class SynchronizedList extends Synchronized.SynchronizedCollection implements List { + private static final long serialVersionUID = 0L; + + SynchronizedList(List delegate, @Nullable Object mutex) { + super(delegate, mutex, null); + } + + List delegate() { + return (List)super.delegate(); + } + + public void add(int index, E element) { + synchronized(this.mutex) { + this.delegate().add(index, element); + } + } + + public boolean addAll(int index, Collection c) { + synchronized(this.mutex) { + return this.delegate().addAll(index, c); + } + } + + public E get(int index) { + synchronized(this.mutex) { + return this.delegate().get(index); + } + } + + public int indexOf(Object o) { + synchronized(this.mutex) { + return this.delegate().indexOf(o); + } + } + + public int lastIndexOf(Object o) { + synchronized(this.mutex) { + return this.delegate().lastIndexOf(o); + } + } + + public ListIterator listIterator() { + return this.delegate().listIterator(); + } + + public ListIterator listIterator(int index) { + return this.delegate().listIterator(index); + } + + public E remove(int index) { + synchronized(this.mutex) { + return this.delegate().remove(index); + } + } + + public E set(int index, E element) { + synchronized(this.mutex) { + return this.delegate().set(index, element); + } + } + + public List subList(int fromIndex, int toIndex) { + synchronized(this.mutex) { + return Synchronized.list(this.delegate().subList(fromIndex, toIndex), this.mutex); + } + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else { + synchronized(this.mutex) { + return this.delegate().equals(o); + } + } + } + + public int hashCode() { + synchronized(this.mutex) { + return this.delegate().hashCode(); + } + } + } + + static class SynchronizedSortedSet extends Synchronized.SynchronizedSet implements SortedSet { + private static final long serialVersionUID = 0L; + + SynchronizedSortedSet(SortedSet delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + SortedSet delegate() { + return (SortedSet)super.delegate(); + } + + public Comparator comparator() { + synchronized(this.mutex) { + return this.delegate().comparator(); + } + } + + public SortedSet subSet(E fromElement, E toElement) { + synchronized(this.mutex) { + return Synchronized.sortedSet(this.delegate().subSet(fromElement, toElement), this.mutex); + } + } + + public SortedSet headSet(E toElement) { + synchronized(this.mutex) { + return Synchronized.sortedSet(this.delegate().headSet(toElement), this.mutex); + } + } + + public SortedSet tailSet(E fromElement) { + synchronized(this.mutex) { + return Synchronized.sortedSet(this.delegate().tailSet(fromElement), this.mutex); + } + } + + public E first() { + synchronized(this.mutex) { + return this.delegate().first(); + } + } + + public E last() { + synchronized(this.mutex) { + return this.delegate().last(); + } + } + } + + static class SynchronizedSet extends Synchronized.SynchronizedCollection implements Set { + private static final long serialVersionUID = 0L; + + SynchronizedSet(Set delegate, @Nullable Object mutex) { + super(delegate, mutex, null); + } + + Set delegate() { + return (Set)super.delegate(); + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } else { + synchronized(this.mutex) { + return this.delegate().equals(o); + } + } + } + + public int hashCode() { + synchronized(this.mutex) { + return this.delegate().hashCode(); + } + } + } + + @VisibleForTesting + static class SynchronizedCollection extends Synchronized.SynchronizedObject implements Collection { + private static final long serialVersionUID = 0L; + + private SynchronizedCollection(Collection delegate, @Nullable Object mutex) { + super(delegate, mutex); + } + + Collection delegate() { + return (Collection)super.delegate(); + } + + public boolean add(E e) { + synchronized(this.mutex) { + return this.delegate().add(e); + } + } + + public boolean addAll(Collection c) { + synchronized(this.mutex) { + return this.delegate().addAll(c); + } + } + + public void clear() { + synchronized(this.mutex) { + this.delegate().clear(); + } + } + + public boolean contains(Object o) { + synchronized(this.mutex) { + return this.delegate().contains(o); + } + } + + public boolean containsAll(Collection c) { + synchronized(this.mutex) { + return this.delegate().containsAll(c); + } + } + + public boolean isEmpty() { + synchronized(this.mutex) { + return this.delegate().isEmpty(); + } + } + + public Iterator iterator() { + return this.delegate().iterator(); + } + + public boolean remove(Object o) { + synchronized(this.mutex) { + return this.delegate().remove(o); + } + } + + public boolean removeAll(Collection c) { + synchronized(this.mutex) { + return this.delegate().removeAll(c); + } + } + + public boolean retainAll(Collection c) { + synchronized(this.mutex) { + return this.delegate().retainAll(c); + } + } + + public int size() { + synchronized(this.mutex) { + return this.delegate().size(); + } + } + + public Object[] toArray() { + synchronized(this.mutex) { + return this.delegate().toArray(); + } + } + + public T[] toArray(T[] a) { + synchronized(this.mutex) { + return this.delegate().toArray(a); + } + } + + // $FF: synthetic method + SynchronizedCollection(Collection x0, Object x1, Object x2) { + this(x0, x1); + } + } + + static class SynchronizedObject implements Serializable { + final Object delegate; + final Object mutex; + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0L; + + SynchronizedObject(Object delegate, @Nullable Object mutex) { + this.delegate = Preconditions.checkNotNull(delegate); + this.mutex = mutex == null ? this : mutex; + } + + Object delegate() { + return this.delegate; + } + + public String toString() { + synchronized(this.mutex) { + return this.delegate.toString(); + } + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + synchronized(this.mutex) { + stream.defaultWriteObject(); + } + } + } +} diff --git a/src/main/com/google/common/collect/Table.java b/src/main/com/google/common/collect/Table.java new file mode 100644 index 0000000..236f003 --- /dev/null +++ b/src/main/com/google/common/collect/Table.java @@ -0,0 +1,64 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +@GwtCompatible +public interface Table { + boolean contains(@Nullable Object var1, @Nullable Object var2); + + boolean containsRow(@Nullable Object var1); + + boolean containsColumn(@Nullable Object var1); + + boolean containsValue(@Nullable Object var1); + + V get(@Nullable Object var1, @Nullable Object var2); + + boolean isEmpty(); + + int size(); + + boolean equals(@Nullable Object var1); + + int hashCode(); + + void clear(); + + V put(R var1, C var2, V var3); + + void putAll(Table var1); + + V remove(@Nullable Object var1, @Nullable Object var2); + + Map row(R var1); + + Map column(C var1); + + Set> cellSet(); + + Set rowKeySet(); + + Set columnKeySet(); + + Collection values(); + + Map> rowMap(); + + Map> columnMap(); + + public interface Cell { + R getRowKey(); + + C getColumnKey(); + + V getValue(); + + boolean equals(@Nullable Object var1); + + int hashCode(); + } +} diff --git a/src/main/com/google/common/collect/Tables.java b/src/main/com/google/common/collect/Tables.java new file mode 100644 index 0000000..b3261b2 --- /dev/null +++ b/src/main/com/google/common/collect/Tables.java @@ -0,0 +1,379 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import javax.annotation.Nullable; + +@GwtCompatible +public final class Tables { + private static final Function, ? extends Map> UNMODIFIABLE_WRAPPER = new Function, Map>() { + public Map apply(Map input) { + return Collections.unmodifiableMap(input); + } + }; + + private Tables() { + } + + public static Table.Cell immutableCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { + return new Tables.ImmutableCell(rowKey, columnKey, value); + } + + public static Table transpose(Table table) { + return (Table)(table instanceof Tables.TransposeTable ? ((Tables.TransposeTable)table).original : new Tables.TransposeTable(table)); + } + + @Beta + public static Table newCustomTable(Map> backingMap, Supplier> factory) { + Preconditions.checkArgument(backingMap.isEmpty()); + Preconditions.checkNotNull(factory); + return new StandardTable(backingMap, factory); + } + + @Beta + public static Table transformValues(Table fromTable, Function function) { + return new Tables.TransformedTable(fromTable, function); + } + + public static Table unmodifiableTable(Table table) { + return new Tables.UnmodifiableTable(table); + } + + @Beta + public static RowSortedTable unmodifiableRowSortedTable(RowSortedTable table) { + return new Tables.UnmodifiableRowSortedMap(table); + } + + private static Function, Map> unmodifiableWrapper() { + return UNMODIFIABLE_WRAPPER; + } + + static boolean equalsImpl(Table table, @Nullable Object obj) { + if (obj == table) { + return true; + } else if (obj instanceof Table) { + Table that = (Table)obj; + return table.cellSet().equals(that.cellSet()); + } else { + return false; + } + } + + static final class UnmodifiableRowSortedMap extends Tables.UnmodifiableTable implements RowSortedTable { + private static final long serialVersionUID = 0L; + + public UnmodifiableRowSortedMap(RowSortedTable delegate) { + super(delegate); + } + + protected RowSortedTable delegate() { + return (RowSortedTable)super.delegate(); + } + + public SortedMap> rowMap() { + Function, Map> wrapper = Tables.unmodifiableWrapper(); + return Collections.unmodifiableSortedMap(Maps.transformValues(this.delegate().rowMap(), wrapper)); + } + + public SortedSet rowKeySet() { + return Collections.unmodifiableSortedSet(this.delegate().rowKeySet()); + } + } + + private static class UnmodifiableTable extends ForwardingTable implements Serializable { + final Table delegate; + private static final long serialVersionUID = 0L; + + UnmodifiableTable(Table delegate) { + this.delegate = (Table)Preconditions.checkNotNull(delegate); + } + + protected Table delegate() { + return this.delegate; + } + + public Set> cellSet() { + return Collections.unmodifiableSet(super.cellSet()); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public Map column(@Nullable C columnKey) { + return Collections.unmodifiableMap(super.column(columnKey)); + } + + public Set columnKeySet() { + return Collections.unmodifiableSet(super.columnKeySet()); + } + + public Map> columnMap() { + Function, Map> wrapper = Tables.unmodifiableWrapper(); + return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); + } + + public V put(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { + throw new UnsupportedOperationException(); + } + + public void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + throw new UnsupportedOperationException(); + } + + public Map row(@Nullable R rowKey) { + return Collections.unmodifiableMap(super.row(rowKey)); + } + + public Set rowKeySet() { + return Collections.unmodifiableSet(super.rowKeySet()); + } + + public Map> rowMap() { + Function, Map> wrapper = Tables.unmodifiableWrapper(); + return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); + } + + public Collection values() { + return Collections.unmodifiableCollection(super.values()); + } + } + + private static class TransformedTable extends AbstractTable { + final Table fromTable; + final Function function; + + TransformedTable(Table fromTable, Function function) { + this.fromTable = (Table)Preconditions.checkNotNull(fromTable); + this.function = (Function)Preconditions.checkNotNull(function); + } + + public boolean contains(Object rowKey, Object columnKey) { + return this.fromTable.contains(rowKey, columnKey); + } + + public V2 get(Object rowKey, Object columnKey) { + return this.contains(rowKey, columnKey) ? this.function.apply(this.fromTable.get(rowKey, columnKey)) : null; + } + + public int size() { + return this.fromTable.size(); + } + + public void clear() { + this.fromTable.clear(); + } + + public V2 put(R rowKey, C columnKey, V2 value) { + throw new UnsupportedOperationException(); + } + + public void putAll(Table table) { + throw new UnsupportedOperationException(); + } + + public V2 remove(Object rowKey, Object columnKey) { + return this.contains(rowKey, columnKey) ? this.function.apply(this.fromTable.remove(rowKey, columnKey)) : null; + } + + public Map row(R rowKey) { + return Maps.transformValues(this.fromTable.row(rowKey), this.function); + } + + public Map column(C columnKey) { + return Maps.transformValues(this.fromTable.column(columnKey), this.function); + } + + Function, Table.Cell> cellFunction() { + return new Function, Table.Cell>() { + public Table.Cell apply(Table.Cell cell) { + return Tables.immutableCell(cell.getRowKey(), cell.getColumnKey(), TransformedTable.this.function.apply(cell.getValue())); + } + }; + } + + Iterator> cellIterator() { + return Iterators.transform(this.fromTable.cellSet().iterator(), this.cellFunction()); + } + + public Set rowKeySet() { + return this.fromTable.rowKeySet(); + } + + public Set columnKeySet() { + return this.fromTable.columnKeySet(); + } + + Collection createValues() { + return Collections2.transform(this.fromTable.values(), this.function); + } + + public Map> rowMap() { + Function, Map> rowFunction = new Function, Map>() { + public Map apply(Map row) { + return Maps.transformValues(row, TransformedTable.this.function); + } + }; + return Maps.transformValues(this.fromTable.rowMap(), rowFunction); + } + + public Map> columnMap() { + Function, Map> columnFunction = new Function, Map>() { + public Map apply(Map column) { + return Maps.transformValues(column, TransformedTable.this.function); + } + }; + return Maps.transformValues(this.fromTable.columnMap(), columnFunction); + } + } + + private static class TransposeTable extends AbstractTable { + final Table original; + private static final Function, Table.Cell> TRANSPOSE_CELL = new Function, Table.Cell>() { + public Table.Cell apply(Table.Cell cell) { + return Tables.immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); + } + }; + + TransposeTable(Table original) { + this.original = (Table)Preconditions.checkNotNull(original); + } + + public void clear() { + this.original.clear(); + } + + public Map column(R columnKey) { + return this.original.row(columnKey); + } + + public Set columnKeySet() { + return this.original.rowKeySet(); + } + + public Map> columnMap() { + return this.original.rowMap(); + } + + public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { + return this.original.contains(columnKey, rowKey); + } + + public boolean containsColumn(@Nullable Object columnKey) { + return this.original.containsRow(columnKey); + } + + public boolean containsRow(@Nullable Object rowKey) { + return this.original.containsColumn(rowKey); + } + + public boolean containsValue(@Nullable Object value) { + return this.original.containsValue(value); + } + + public V get(@Nullable Object rowKey, @Nullable Object columnKey) { + return this.original.get(columnKey, rowKey); + } + + public V put(C rowKey, R columnKey, V value) { + return this.original.put(columnKey, rowKey, value); + } + + public void putAll(Table table) { + this.original.putAll(Tables.transpose(table)); + } + + public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { + return this.original.remove(columnKey, rowKey); + } + + public Map row(C rowKey) { + return this.original.column(rowKey); + } + + public Set rowKeySet() { + return this.original.columnKeySet(); + } + + public Map> rowMap() { + return this.original.columnMap(); + } + + public int size() { + return this.original.size(); + } + + public Collection values() { + return this.original.values(); + } + + Iterator> cellIterator() { + return Iterators.transform(this.original.cellSet().iterator(), TRANSPOSE_CELL); + } + } + + abstract static class AbstractCell implements Table.Cell { + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof Table.Cell)) { + return false; + } else { + Table.Cell other = (Table.Cell)obj; + return Objects.equal(this.getRowKey(), other.getRowKey()) && Objects.equal(this.getColumnKey(), other.getColumnKey()) && Objects.equal(this.getValue(), other.getValue()); + } + } + + public int hashCode() { + return Objects.hashCode(this.getRowKey(), this.getColumnKey(), this.getValue()); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.getRowKey())); + String var2 = String.valueOf(String.valueOf(this.getColumnKey())); + String var3 = String.valueOf(String.valueOf(this.getValue())); + return (new StringBuilder(4 + var1.length() + var2.length() + var3.length())).append("(").append(var1).append(",").append(var2).append(")=").append(var3).toString(); + } + } + + static final class ImmutableCell extends Tables.AbstractCell implements Serializable { + private final R rowKey; + private final C columnKey; + private final V value; + private static final long serialVersionUID = 0L; + + ImmutableCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { + this.rowKey = rowKey; + this.columnKey = columnKey; + this.value = value; + } + + public R getRowKey() { + return this.rowKey; + } + + public C getColumnKey() { + return this.columnKey; + } + + public V getValue() { + return this.value; + } + } +} diff --git a/src/main/com/google/common/collect/TransformedIterator.java b/src/main/com/google/common/collect/TransformedIterator.java new file mode 100644 index 0000000..3fc9938 --- /dev/null +++ b/src/main/com/google/common/collect/TransformedIterator.java @@ -0,0 +1,28 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Iterator; + +@GwtCompatible +abstract class TransformedIterator implements Iterator { + final Iterator backingIterator; + + TransformedIterator(Iterator backingIterator) { + this.backingIterator = (Iterator)Preconditions.checkNotNull(backingIterator); + } + + abstract T transform(F var1); + + public final boolean hasNext() { + return this.backingIterator.hasNext(); + } + + public final T next() { + return this.transform(this.backingIterator.next()); + } + + public final void remove() { + this.backingIterator.remove(); + } +} diff --git a/src/main/com/google/common/collect/TransformedListIterator.java b/src/main/com/google/common/collect/TransformedListIterator.java new file mode 100644 index 0000000..7c6562f --- /dev/null +++ b/src/main/com/google/common/collect/TransformedListIterator.java @@ -0,0 +1,39 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.ListIterator; + +@GwtCompatible +abstract class TransformedListIterator extends TransformedIterator implements ListIterator { + TransformedListIterator(ListIterator backingIterator) { + super(backingIterator); + } + + private ListIterator backingIterator() { + return Iterators.cast(this.backingIterator); + } + + public final boolean hasPrevious() { + return this.backingIterator().hasPrevious(); + } + + public final T previous() { + return this.transform(this.backingIterator().previous()); + } + + public final int nextIndex() { + return this.backingIterator().nextIndex(); + } + + public final int previousIndex() { + return this.backingIterator().previousIndex(); + } + + public void set(T element) { + throw new UnsupportedOperationException(); + } + + public void add(T element) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/TreeBasedTable.java b/src/main/com/google/common/collect/TreeBasedTable.java new file mode 100644 index 0000000..24d0591 --- /dev/null +++ b/src/main/com/google/common/collect/TreeBasedTable.java @@ -0,0 +1,224 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import java.io.Serializable; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +@Beta +public class TreeBasedTable extends StandardRowSortedTable { + private final Comparator columnComparator; + private static final long serialVersionUID = 0L; + + public static TreeBasedTable create() { + return new TreeBasedTable(Ordering.natural(), Ordering.natural()); + } + + public static TreeBasedTable create(Comparator rowComparator, Comparator columnComparator) { + Preconditions.checkNotNull(rowComparator); + Preconditions.checkNotNull(columnComparator); + return new TreeBasedTable(rowComparator, columnComparator); + } + + public static TreeBasedTable create(TreeBasedTable table) { + TreeBasedTable result = new TreeBasedTable(table.rowComparator(), table.columnComparator()); + result.putAll(table); + return result; + } + + TreeBasedTable(Comparator rowComparator, Comparator columnComparator) { + super(new TreeMap(rowComparator), new TreeBasedTable.Factory(columnComparator)); + this.columnComparator = columnComparator; + } + + public Comparator rowComparator() { + return this.rowKeySet().comparator(); + } + + public Comparator columnComparator() { + return this.columnComparator; + } + + public SortedMap row(R rowKey) { + return new TreeBasedTable.TreeRow(rowKey); + } + + public SortedSet rowKeySet() { + return super.rowKeySet(); + } + + public SortedMap> rowMap() { + return super.rowMap(); + } + + Iterator createColumnKeyIterator() { + final Comparator comparator = this.columnComparator(); + final Iterator merged = Iterators.mergeSorted(Iterables.transform(this.backingMap.values(), new Function, Iterator>() { + public Iterator apply(Map input) { + return input.keySet().iterator(); + } + }), comparator); + return new AbstractIterator() { + C lastValue; + + protected C computeNext() { + Object next; + boolean duplicate; + do { + if (!merged.hasNext()) { + this.lastValue = null; + return this.endOfData(); + } + + next = merged.next(); + duplicate = this.lastValue != null && comparator.compare(next, this.lastValue) == 0; + } while(duplicate); + + this.lastValue = next; + return this.lastValue; + } + }; + } + + private class TreeRow extends StandardTable.Row implements SortedMap { + @Nullable + final C lowerBound; + @Nullable + final C upperBound; + transient SortedMap wholeRow; + + TreeRow(R rowKey) { + this(rowKey, (Object)null, (Object)null); + } + + TreeRow(R rowKey, @Nullable C lowerBound, @Nullable C upperBound) { + super(rowKey); + this.lowerBound = lowerBound; + this.upperBound = upperBound; + Preconditions.checkArgument(lowerBound == null || upperBound == null || this.compare(lowerBound, upperBound) <= 0); + } + + public SortedSet keySet() { + return new Maps.SortedKeySet(this); + } + + public Comparator comparator() { + return TreeBasedTable.this.columnComparator(); + } + + int compare(Object a, Object b) { + Comparator cmp = this.comparator(); + return cmp.compare(a, b); + } + + boolean rangeContains(@Nullable Object o) { + return o != null && (this.lowerBound == null || this.compare(this.lowerBound, o) <= 0) && (this.upperBound == null || this.compare(this.upperBound, o) > 0); + } + + public SortedMap subMap(C fromKey, C toKey) { + Preconditions.checkArgument(this.rangeContains(Preconditions.checkNotNull(fromKey)) && this.rangeContains(Preconditions.checkNotNull(toKey))); + return TreeBasedTable.this.new TreeRow(this.rowKey, fromKey, toKey); + } + + public SortedMap headMap(C toKey) { + Preconditions.checkArgument(this.rangeContains(Preconditions.checkNotNull(toKey))); + return TreeBasedTable.this.new TreeRow(this.rowKey, this.lowerBound, toKey); + } + + public SortedMap tailMap(C fromKey) { + Preconditions.checkArgument(this.rangeContains(Preconditions.checkNotNull(fromKey))); + return TreeBasedTable.this.new TreeRow(this.rowKey, fromKey, this.upperBound); + } + + public C firstKey() { + SortedMap backing = this.backingRowMap(); + if (backing == null) { + throw new NoSuchElementException(); + } else { + return this.backingRowMap().firstKey(); + } + } + + public C lastKey() { + SortedMap backing = this.backingRowMap(); + if (backing == null) { + throw new NoSuchElementException(); + } else { + return this.backingRowMap().lastKey(); + } + } + + SortedMap wholeRow() { + if (this.wholeRow == null || this.wholeRow.isEmpty() && TreeBasedTable.this.backingMap.containsKey(this.rowKey)) { + this.wholeRow = (SortedMap)TreeBasedTable.this.backingMap.get(this.rowKey); + } + + return this.wholeRow; + } + + SortedMap backingRowMap() { + return (SortedMap)super.backingRowMap(); + } + + SortedMap computeBackingRowMap() { + SortedMap map = this.wholeRow(); + if (map != null) { + if (this.lowerBound != null) { + map = map.tailMap(this.lowerBound); + } + + if (this.upperBound != null) { + map = map.headMap(this.upperBound); + } + + return map; + } else { + return null; + } + } + + void maintainEmptyInvariant() { + if (this.wholeRow() != null && this.wholeRow.isEmpty()) { + TreeBasedTable.this.backingMap.remove(this.rowKey); + this.wholeRow = null; + this.backingRowMap = null; + } + + } + + public boolean containsKey(Object key) { + return this.rangeContains(key) && super.containsKey(key); + } + + public V put(C key, V value) { + Preconditions.checkArgument(this.rangeContains(Preconditions.checkNotNull(key))); + return super.put(key, value); + } + } + + private static class Factory implements Supplier>, Serializable { + final Comparator comparator; + private static final long serialVersionUID = 0L; + + Factory(Comparator comparator) { + this.comparator = comparator; + } + + public TreeMap get() { + return new TreeMap(this.comparator); + } + } +} diff --git a/src/main/com/google/common/collect/TreeMultimap.java b/src/main/com/google/common/collect/TreeMultimap.java new file mode 100644 index 0000000..49d7c5b --- /dev/null +++ b/src/main/com/google/common/collect/TreeMultimap.java @@ -0,0 +1,127 @@ +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.io.IOException; +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.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true, + emulated = true +) +public class TreeMultimap extends AbstractSortedKeySortedSetMultimap { + private transient Comparator keyComparator; + private transient Comparator valueComparator; + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 0L; + + public static TreeMultimap create() { + return new TreeMultimap(Ordering.natural(), Ordering.natural()); + } + + public static TreeMultimap create(Comparator keyComparator, Comparator valueComparator) { + return new TreeMultimap((Comparator)Preconditions.checkNotNull(keyComparator), (Comparator)Preconditions.checkNotNull(valueComparator)); + } + + public static TreeMultimap create(Multimap multimap) { + return new TreeMultimap(Ordering.natural(), Ordering.natural(), multimap); + } + + TreeMultimap(Comparator keyComparator, Comparator valueComparator) { + super(new TreeMap(keyComparator)); + this.keyComparator = keyComparator; + this.valueComparator = valueComparator; + } + + private TreeMultimap(Comparator keyComparator, Comparator valueComparator, Multimap multimap) { + this(keyComparator, valueComparator); + this.putAll(multimap); + } + + SortedSet createCollection() { + return new TreeSet(this.valueComparator); + } + + Collection createCollection(@Nullable K key) { + if (key == null) { + this.keyComparator().compare(key, key); + } + + return super.createCollection(key); + } + + public Comparator keyComparator() { + return this.keyComparator; + } + + public Comparator valueComparator() { + return this.valueComparator; + } + + @GwtIncompatible("NavigableMap") + NavigableMap> backingMap() { + return (NavigableMap)super.backingMap(); + } + + @GwtIncompatible("NavigableSet") + public NavigableSet get(@Nullable K key) { + return (NavigableSet)super.get(key); + } + + @GwtIncompatible("NavigableSet") + Collection unmodifiableCollectionSubclass(Collection collection) { + return Sets.unmodifiableNavigableSet((NavigableSet)collection); + } + + @GwtIncompatible("NavigableSet") + Collection wrapCollection(K key, Collection collection) { + return new AbstractMapBasedMultimap.WrappedNavigableSet(key, (NavigableSet)collection, (AbstractMapBasedMultimap.WrappedCollection)null); + } + + @GwtIncompatible("NavigableSet") + public NavigableSet keySet() { + return (NavigableSet)super.keySet(); + } + + @GwtIncompatible("NavigableSet") + NavigableSet createKeySet() { + return new AbstractMapBasedMultimap.NavigableKeySet(this.backingMap()); + } + + @GwtIncompatible("NavigableMap") + public NavigableMap> asMap() { + return (NavigableMap)super.asMap(); + } + + @GwtIncompatible("NavigableMap") + NavigableMap> createAsMap() { + return new AbstractMapBasedMultimap.NavigableAsMap(this.backingMap()); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.keyComparator()); + stream.writeObject(this.valueComparator()); + Serialization.writeMultimap(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + this.keyComparator = (Comparator)Preconditions.checkNotNull((Comparator)stream.readObject()); + this.valueComparator = (Comparator)Preconditions.checkNotNull((Comparator)stream.readObject()); + this.setMap(new TreeMap(this.keyComparator)); + Serialization.populateMultimap(this, stream); + } +} diff --git a/src/main/com/google/common/collect/TreeMultiset.java b/src/main/com/google/common/collect/TreeMultiset.java new file mode 100644 index 0000000..9130236 --- /dev/null +++ b/src/main/com/google/common/collect/TreeMultiset.java @@ -0,0 +1,877 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.MoreObjects; +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.io.Serializable; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class TreeMultiset extends AbstractSortedMultiset implements Serializable { + private final transient TreeMultiset.Reference> rootReference; + private final transient GeneralRange range; + private final transient TreeMultiset.AvlNode header; + @GwtIncompatible("not needed in emulated source") + private static final long serialVersionUID = 1L; + + public static TreeMultiset create() { + return new TreeMultiset(Ordering.natural()); + } + + public static TreeMultiset create(@Nullable Comparator comparator) { + return comparator == null ? new TreeMultiset(Ordering.natural()) : new TreeMultiset(comparator); + } + + public static TreeMultiset create(Iterable elements) { + TreeMultiset multiset = create(); + Iterables.addAll(multiset, elements); + return multiset; + } + + TreeMultiset(TreeMultiset.Reference> rootReference, GeneralRange range, TreeMultiset.AvlNode endLink) { + super(range.comparator()); + this.rootReference = rootReference; + this.range = range; + this.header = endLink; + } + + TreeMultiset(Comparator comparator) { + super(comparator); + this.range = GeneralRange.all(comparator); + this.header = new TreeMultiset.AvlNode((Object)null, 1); + successor(this.header, this.header); + this.rootReference = new TreeMultiset.Reference(); + } + + private long aggregateForEntries(TreeMultiset.Aggregate aggr) { + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + long total = aggr.treeAggregate(root); + if (this.range.hasLowerBound()) { + total -= this.aggregateBelowRange(aggr, root); + } + + if (this.range.hasUpperBound()) { + total -= this.aggregateAboveRange(aggr, root); + } + + return total; + } + + private long aggregateBelowRange(TreeMultiset.Aggregate aggr, @Nullable TreeMultiset.AvlNode node) { + if (node == null) { + return 0L; + } else { + int cmp = this.comparator().compare(this.range.getLowerEndpoint(), node.elem); + if (cmp < 0) { + return this.aggregateBelowRange(aggr, node.left); + } else if (cmp == 0) { + switch(this.range.getLowerBoundType()) { + case OPEN: + return (long)aggr.nodeAggregate(node) + aggr.treeAggregate(node.left); + case CLOSED: + return aggr.treeAggregate(node.left); + default: + throw new AssertionError(); + } + } else { + return aggr.treeAggregate(node.left) + (long)aggr.nodeAggregate(node) + this.aggregateBelowRange(aggr, node.right); + } + } + } + + private long aggregateAboveRange(TreeMultiset.Aggregate aggr, @Nullable TreeMultiset.AvlNode node) { + if (node == null) { + return 0L; + } else { + int cmp = this.comparator().compare(this.range.getUpperEndpoint(), node.elem); + if (cmp > 0) { + return this.aggregateAboveRange(aggr, node.right); + } else if (cmp == 0) { + switch(this.range.getUpperBoundType()) { + case OPEN: + return (long)aggr.nodeAggregate(node) + aggr.treeAggregate(node.right); + case CLOSED: + return aggr.treeAggregate(node.right); + default: + throw new AssertionError(); + } + } else { + return aggr.treeAggregate(node.right) + (long)aggr.nodeAggregate(node) + this.aggregateAboveRange(aggr, node.left); + } + } + } + + public int size() { + return Ints.saturatedCast(this.aggregateForEntries(TreeMultiset.Aggregate.SIZE)); + } + + int distinctElements() { + return Ints.saturatedCast(this.aggregateForEntries(TreeMultiset.Aggregate.DISTINCT)); + } + + public int count(@Nullable Object element) { + try { + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + return this.range.contains(element) && root != null ? root.count(this.comparator(), element) : 0; + } catch (ClassCastException var4) { + return 0; + } catch (NullPointerException var5) { + return 0; + } + } + + public int add(@Nullable E element, int occurrences) { + CollectPreconditions.checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return this.count(element); + } else { + Preconditions.checkArgument(this.range.contains(element)); + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + if (root == null) { + this.comparator().compare(element, element); + TreeMultiset.AvlNode newRoot = new TreeMultiset.AvlNode(element, occurrences); + successor(this.header, newRoot, this.header); + this.rootReference.checkAndSet(root, newRoot); + return 0; + } else { + int[] result = new int[1]; + TreeMultiset.AvlNode newRoot = root.add(this.comparator(), element, occurrences, result); + this.rootReference.checkAndSet(root, newRoot); + return result[0]; + } + } + } + + public int remove(@Nullable Object element, int occurrences) { + CollectPreconditions.checkNonnegative(occurrences, "occurrences"); + if (occurrences == 0) { + return this.count(element); + } else { + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + int[] result = new int[1]; + + TreeMultiset.AvlNode newRoot; + try { + if (!this.range.contains(element) || root == null) { + return 0; + } + + newRoot = root.remove(this.comparator(), element, occurrences, result); + } catch (ClassCastException var7) { + return 0; + } catch (NullPointerException var8) { + return 0; + } + + this.rootReference.checkAndSet(root, newRoot); + return result[0]; + } + } + + public int setCount(@Nullable E element, int count) { + CollectPreconditions.checkNonnegative(count, "count"); + if (!this.range.contains(element)) { + Preconditions.checkArgument(count == 0); + return 0; + } else { + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + if (root == null) { + if (count > 0) { + this.add(element, count); + } + + return 0; + } else { + int[] result = new int[1]; + TreeMultiset.AvlNode newRoot = root.setCount(this.comparator(), element, count, result); + this.rootReference.checkAndSet(root, newRoot); + return result[0]; + } + } + } + + public boolean setCount(@Nullable E element, int oldCount, int newCount) { + CollectPreconditions.checkNonnegative(newCount, "newCount"); + CollectPreconditions.checkNonnegative(oldCount, "oldCount"); + Preconditions.checkArgument(this.range.contains(element)); + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + if (root == null) { + if (oldCount == 0) { + if (newCount > 0) { + this.add(element, newCount); + } + + return true; + } else { + return false; + } + } else { + int[] result = new int[1]; + TreeMultiset.AvlNode newRoot = root.setCount(this.comparator(), element, oldCount, newCount, result); + this.rootReference.checkAndSet(root, newRoot); + return result[0] == oldCount; + } + } + + private Multiset.Entry wrapEntry(final TreeMultiset.AvlNode baseEntry) { + return new Multisets.AbstractEntry() { + public E getElement() { + return baseEntry.getElement(); + } + + public int getCount() { + int result = baseEntry.getCount(); + return result == 0 ? TreeMultiset.this.count(this.getElement()) : result; + } + }; + } + + @Nullable + private TreeMultiset.AvlNode firstNode() { + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + if (root == null) { + return null; + } else { + TreeMultiset.AvlNode node; + if (this.range.hasLowerBound()) { + E endpoint = this.range.getLowerEndpoint(); + node = ((TreeMultiset.AvlNode)this.rootReference.get()).ceiling(this.comparator(), endpoint); + if (node == null) { + return null; + } + + if (this.range.getLowerBoundType() == BoundType.OPEN && this.comparator().compare(endpoint, node.getElement()) == 0) { + node = node.succ; + } + } else { + node = this.header.succ; + } + + return node != this.header && this.range.contains(node.getElement()) ? node : null; + } + } + + @Nullable + private TreeMultiset.AvlNode lastNode() { + TreeMultiset.AvlNode root = (TreeMultiset.AvlNode)this.rootReference.get(); + if (root == null) { + return null; + } else { + TreeMultiset.AvlNode node; + if (this.range.hasUpperBound()) { + E endpoint = this.range.getUpperEndpoint(); + node = ((TreeMultiset.AvlNode)this.rootReference.get()).floor(this.comparator(), endpoint); + if (node == null) { + return null; + } + + if (this.range.getUpperBoundType() == BoundType.OPEN && this.comparator().compare(endpoint, node.getElement()) == 0) { + node = node.pred; + } + } else { + node = this.header.pred; + } + + return node != this.header && this.range.contains(node.getElement()) ? node : null; + } + } + + Iterator> entryIterator() { + return new Iterator>() { + TreeMultiset.AvlNode current = TreeMultiset.this.firstNode(); + Multiset.Entry prevEntry; + + public boolean hasNext() { + if (this.current == null) { + return false; + } else if (TreeMultiset.this.range.tooHigh(this.current.getElement())) { + this.current = null; + return false; + } else { + return true; + } + } + + public Multiset.Entry next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + Multiset.Entry result = TreeMultiset.this.wrapEntry(this.current); + this.prevEntry = result; + if (this.current.succ == TreeMultiset.this.header) { + this.current = null; + } else { + this.current = this.current.succ; + } + + return result; + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.prevEntry != null); + TreeMultiset.this.setCount(this.prevEntry.getElement(), 0); + this.prevEntry = null; + } + }; + } + + Iterator> descendingEntryIterator() { + return new Iterator>() { + TreeMultiset.AvlNode current = TreeMultiset.this.lastNode(); + Multiset.Entry prevEntry = null; + + public boolean hasNext() { + if (this.current == null) { + return false; + } else if (TreeMultiset.this.range.tooLow(this.current.getElement())) { + this.current = null; + return false; + } else { + return true; + } + } + + public Multiset.Entry next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + Multiset.Entry result = TreeMultiset.this.wrapEntry(this.current); + this.prevEntry = result; + if (this.current.pred == TreeMultiset.this.header) { + this.current = null; + } else { + this.current = this.current.pred; + } + + return result; + } + } + + public void remove() { + CollectPreconditions.checkRemove(this.prevEntry != null); + TreeMultiset.this.setCount(this.prevEntry.getElement(), 0); + this.prevEntry = null; + } + }; + } + + public SortedMultiset headMultiset(@Nullable E upperBound, BoundType boundType) { + return new TreeMultiset(this.rootReference, this.range.intersect(GeneralRange.upTo(this.comparator(), upperBound, boundType)), this.header); + } + + public SortedMultiset tailMultiset(@Nullable E lowerBound, BoundType boundType) { + return new TreeMultiset(this.rootReference, this.range.intersect(GeneralRange.downTo(this.comparator(), lowerBound, boundType)), this.header); + } + + static int distinctElements(@Nullable TreeMultiset.AvlNode node) { + return node == null ? 0 : node.distinctElements; + } + + private static void successor(TreeMultiset.AvlNode a, TreeMultiset.AvlNode b) { + a.succ = b; + b.pred = a; + } + + private static void successor(TreeMultiset.AvlNode a, TreeMultiset.AvlNode b, TreeMultiset.AvlNode c) { + successor(a, b); + successor(b, c); + } + + @GwtIncompatible("java.io.ObjectOutputStream") + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(this.elementSet().comparator()); + Serialization.writeMultiset(this, stream); + } + + @GwtIncompatible("java.io.ObjectInputStream") + private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + Comparator comparator = (Comparator)stream.readObject(); + Serialization.getFieldSetter(AbstractSortedMultiset.class, "comparator").set(this, comparator); + Serialization.getFieldSetter(TreeMultiset.class, "range").set(this, GeneralRange.all(comparator)); + Serialization.getFieldSetter(TreeMultiset.class, "rootReference").set(this, new TreeMultiset.Reference()); + TreeMultiset.AvlNode header = new TreeMultiset.AvlNode((Object)null, 1); + Serialization.getFieldSetter(TreeMultiset.class, "header").set(this, header); + successor(header, header); + Serialization.populateMultiset(this, stream); + } + + private static final class AvlNode extends Multisets.AbstractEntry { + @Nullable + private final E elem; + private int elemCount; + private int distinctElements; + private long totalCount; + private int height; + private TreeMultiset.AvlNode left; + private TreeMultiset.AvlNode right; + private TreeMultiset.AvlNode pred; + private TreeMultiset.AvlNode succ; + + AvlNode(@Nullable E elem, int elemCount) { + Preconditions.checkArgument(elemCount > 0); + this.elem = elem; + this.elemCount = elemCount; + this.totalCount = (long)elemCount; + this.distinctElements = 1; + this.height = 1; + this.left = null; + this.right = null; + } + + public int count(Comparator comparator, E e) { + int cmp = comparator.compare(e, this.elem); + if (cmp < 0) { + return this.left == null ? 0 : this.left.count(comparator, e); + } else if (cmp > 0) { + return this.right == null ? 0 : this.right.count(comparator, e); + } else { + return this.elemCount; + } + } + + private TreeMultiset.AvlNode addRightChild(E e, int count) { + this.right = new TreeMultiset.AvlNode(e, count); + TreeMultiset.successor(this, this.right, this.succ); + this.height = Math.max(2, this.height); + ++this.distinctElements; + this.totalCount += (long)count; + return this; + } + + private TreeMultiset.AvlNode addLeftChild(E e, int count) { + this.left = new TreeMultiset.AvlNode(e, count); + TreeMultiset.successor(this.pred, this.left, this); + this.height = Math.max(2, this.height); + ++this.distinctElements; + this.totalCount += (long)count; + return this; + } + + TreeMultiset.AvlNode add(Comparator comparator, @Nullable E e, int count, int[] result) { + int cmp = comparator.compare(e, this.elem); + int initHeight; + TreeMultiset.AvlNode initRight; + if (cmp < 0) { + initRight = this.left; + if (initRight == null) { + result[0] = 0; + return this.addLeftChild(e, count); + } else { + initHeight = initRight.height; + this.left = initRight.add(comparator, e, count, result); + if (result[0] == 0) { + ++this.distinctElements; + } + + this.totalCount += (long)count; + return this.left.height == initHeight ? this : this.rebalance(); + } + } else if (cmp > 0) { + initRight = this.right; + if (initRight == null) { + result[0] = 0; + return this.addRightChild(e, count); + } else { + initHeight = initRight.height; + this.right = initRight.add(comparator, e, count, result); + if (result[0] == 0) { + ++this.distinctElements; + } + + this.totalCount += (long)count; + return this.right.height == initHeight ? this : this.rebalance(); + } + } else { + result[0] = this.elemCount; + long resultCount = (long)this.elemCount + (long)count; + Preconditions.checkArgument(resultCount <= 2147483647L); + this.elemCount += count; + this.totalCount += (long)count; + return this; + } + } + + TreeMultiset.AvlNode remove(Comparator comparator, @Nullable E e, int count, int[] result) { + int cmp = comparator.compare(e, this.elem); + TreeMultiset.AvlNode initRight; + if (cmp < 0) { + initRight = this.left; + if (initRight == null) { + result[0] = 0; + return this; + } else { + this.left = initRight.remove(comparator, e, count, result); + if (result[0] > 0) { + if (count >= result[0]) { + --this.distinctElements; + this.totalCount -= (long)result[0]; + } else { + this.totalCount -= (long)count; + } + } + + return result[0] == 0 ? this : this.rebalance(); + } + } else if (cmp > 0) { + initRight = this.right; + if (initRight == null) { + result[0] = 0; + return this; + } else { + this.right = initRight.remove(comparator, e, count, result); + if (result[0] > 0) { + if (count >= result[0]) { + --this.distinctElements; + this.totalCount -= (long)result[0]; + } else { + this.totalCount -= (long)count; + } + } + + return this.rebalance(); + } + } else { + result[0] = this.elemCount; + if (count >= this.elemCount) { + return this.deleteMe(); + } else { + this.elemCount -= count; + this.totalCount -= (long)count; + return this; + } + } + } + + TreeMultiset.AvlNode setCount(Comparator comparator, @Nullable E e, int count, int[] result) { + int cmp = comparator.compare(e, this.elem); + TreeMultiset.AvlNode initRight; + if (cmp < 0) { + initRight = this.left; + if (initRight == null) { + result[0] = 0; + return count > 0 ? this.addLeftChild(e, count) : this; + } else { + this.left = 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 += (long)(count - result[0]); + return this.rebalance(); + } + } else if (cmp > 0) { + initRight = this.right; + if (initRight == null) { + result[0] = 0; + return count > 0 ? this.addRightChild(e, count) : this; + } else { + 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 += (long)(count - result[0]); + return this.rebalance(); + } + } else { + result[0] = this.elemCount; + if (count == 0) { + return this.deleteMe(); + } else { + this.totalCount += (long)(count - this.elemCount); + this.elemCount = count; + return this; + } + } + } + + TreeMultiset.AvlNode setCount(Comparator comparator, @Nullable E e, int expectedCount, int newCount, int[] result) { + int cmp = comparator.compare(e, this.elem); + TreeMultiset.AvlNode initRight; + if (cmp < 0) { + initRight = this.left; + if (initRight == null) { + result[0] = 0; + return expectedCount == 0 && newCount > 0 ? this.addLeftChild(e, newCount) : this; + } else { + this.left = 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 += (long)(newCount - result[0]); + } + + return this.rebalance(); + } + } else if (cmp > 0) { + initRight = this.right; + if (initRight == null) { + result[0] = 0; + return expectedCount == 0 && newCount > 0 ? this.addRightChild(e, newCount) : this; + } else { + this.right = initRight.setCount(comparator, e, expectedCount, newCount, result); + if (result[0] == expectedCount) { + if (newCount == 0 && result[0] != 0) { + --this.distinctElements; + } else if (newCount > 0 && result[0] == 0) { + ++this.distinctElements; + } + + this.totalCount += (long)(newCount - result[0]); + } + + return this.rebalance(); + } + } else { + result[0] = this.elemCount; + if (expectedCount == this.elemCount) { + if (newCount == 0) { + return this.deleteMe(); + } + + this.totalCount += (long)(newCount - this.elemCount); + this.elemCount = newCount; + } + + return this; + } + } + + private TreeMultiset.AvlNode deleteMe() { + int oldElemCount = this.elemCount; + this.elemCount = 0; + TreeMultiset.successor(this.pred, this.succ); + if (this.left == null) { + return this.right; + } else if (this.right == null) { + return this.left; + } else { + TreeMultiset.AvlNode newTop; + if (this.left.height >= this.right.height) { + newTop = this.pred; + newTop.left = this.left.removeMax(newTop); + newTop.right = this.right; + newTop.distinctElements = this.distinctElements - 1; + newTop.totalCount = this.totalCount - (long)oldElemCount; + return newTop.rebalance(); + } else { + newTop = this.succ; + newTop.right = this.right.removeMin(newTop); + newTop.left = this.left; + newTop.distinctElements = this.distinctElements - 1; + newTop.totalCount = this.totalCount - (long)oldElemCount; + return newTop.rebalance(); + } + } + } + + private TreeMultiset.AvlNode removeMin(TreeMultiset.AvlNode node) { + if (this.left == null) { + return this.right; + } else { + this.left = this.left.removeMin(node); + --this.distinctElements; + this.totalCount -= (long)node.elemCount; + return this.rebalance(); + } + } + + private TreeMultiset.AvlNode removeMax(TreeMultiset.AvlNode node) { + if (this.right == null) { + return this.left; + } else { + this.right = this.right.removeMax(node); + --this.distinctElements; + this.totalCount -= (long)node.elemCount; + return this.rebalance(); + } + } + + private void recomputeMultiset() { + this.distinctElements = 1 + TreeMultiset.distinctElements(this.left) + TreeMultiset.distinctElements(this.right); + this.totalCount = (long)this.elemCount + totalCount(this.left) + totalCount(this.right); + } + + private void recomputeHeight() { + this.height = 1 + Math.max(height(this.left), height(this.right)); + } + + private void recompute() { + this.recomputeMultiset(); + this.recomputeHeight(); + } + + private TreeMultiset.AvlNode rebalance() { + switch(this.balanceFactor()) { + case -2: + if (this.right.balanceFactor() > 0) { + this.right = this.right.rotateRight(); + } + + return this.rotateLeft(); + case 2: + if (this.left.balanceFactor() < 0) { + this.left = this.left.rotateLeft(); + } + + return this.rotateRight(); + default: + this.recomputeHeight(); + return this; + } + } + + private int balanceFactor() { + return height(this.left) - height(this.right); + } + + private TreeMultiset.AvlNode rotateLeft() { + Preconditions.checkState(this.right != null); + TreeMultiset.AvlNode newTop = this.right; + this.right = newTop.left; + newTop.left = this; + newTop.totalCount = this.totalCount; + newTop.distinctElements = this.distinctElements; + this.recompute(); + newTop.recomputeHeight(); + return newTop; + } + + private TreeMultiset.AvlNode rotateRight() { + Preconditions.checkState(this.left != null); + TreeMultiset.AvlNode newTop = this.left; + this.left = newTop.right; + newTop.right = this; + newTop.totalCount = this.totalCount; + newTop.distinctElements = this.distinctElements; + this.recompute(); + newTop.recomputeHeight(); + return newTop; + } + + private static long totalCount(@Nullable TreeMultiset.AvlNode node) { + return node == null ? 0L : node.totalCount; + } + + private static int height(@Nullable TreeMultiset.AvlNode node) { + return node == null ? 0 : node.height; + } + + @Nullable + private TreeMultiset.AvlNode ceiling(Comparator comparator, E e) { + int cmp = comparator.compare(e, this.elem); + if (cmp < 0) { + return this.left == null ? this : (TreeMultiset.AvlNode)MoreObjects.firstNonNull(this.left.ceiling(comparator, e), this); + } else if (cmp == 0) { + return this; + } else { + return this.right == null ? null : this.right.ceiling(comparator, e); + } + } + + @Nullable + private TreeMultiset.AvlNode floor(Comparator comparator, E e) { + int cmp = comparator.compare(e, this.elem); + if (cmp > 0) { + return this.right == null ? this : (TreeMultiset.AvlNode)MoreObjects.firstNonNull(this.right.floor(comparator, e), this); + } else if (cmp == 0) { + return this; + } else { + return this.left == null ? null : this.left.floor(comparator, e); + } + } + + public E getElement() { + return this.elem; + } + + public int getCount() { + return this.elemCount; + } + + public String toString() { + return Multisets.immutableEntry(this.getElement(), this.getCount()).toString(); + } + } + + private static final class Reference { + @Nullable + private T value; + + private Reference() { + } + + @Nullable + public T get() { + return this.value; + } + + public void checkAndSet(@Nullable T expected, T newValue) { + if (this.value != expected) { + throw new ConcurrentModificationException(); + } else { + this.value = newValue; + } + } + + // $FF: synthetic method + Reference(Object x0) { + this(); + } + } + + private static enum Aggregate { + SIZE { + int nodeAggregate(TreeMultiset.AvlNode node) { + return node.elemCount; + } + + long treeAggregate(@Nullable TreeMultiset.AvlNode root) { + return root == null ? 0L : root.totalCount; + } + }, + DISTINCT { + int nodeAggregate(TreeMultiset.AvlNode node) { + return 1; + } + + long treeAggregate(@Nullable TreeMultiset.AvlNode root) { + return root == null ? 0L : (long)root.distinctElements; + } + }; + + private Aggregate() { + } + + abstract int nodeAggregate(TreeMultiset.AvlNode var1); + + abstract long treeAggregate(@Nullable TreeMultiset.AvlNode var1); + + // $FF: synthetic method + Aggregate(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/TreeRangeMap.java b/src/main/com/google/common/collect/TreeRangeMap.java new file mode 100644 index 0000000..8368eb8 --- /dev/null +++ b/src/main/com/google/common/collect/TreeRangeMap.java @@ -0,0 +1,496 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +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.NavigableMap; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +@GwtIncompatible("NavigableMap") +public final class TreeRangeMap implements RangeMap { + private final NavigableMap, TreeRangeMap.RangeMapEntry> entriesByLowerBound = Maps.newTreeMap(); + private static final RangeMap EMPTY_SUB_RANGE_MAP = new RangeMap() { + @Nullable + public Object get(Comparable key) { + return null; + } + + @Nullable + public Entry getEntry(Comparable key) { + return null; + } + + public Range span() { + throw new NoSuchElementException(); + } + + public void put(Range range, Object value) { + Preconditions.checkNotNull(range); + String var3 = String.valueOf(String.valueOf(range)); + throw new IllegalArgumentException((new StringBuilder(46 + var3.length())).append("Cannot insert range ").append(var3).append(" into an empty subRangeMap").toString()); + } + + public void putAll(RangeMap rangeMap) { + if (!rangeMap.asMapOfRanges().isEmpty()) { + throw new IllegalArgumentException("Cannot putAll(nonEmptyRangeMap) into an empty subRangeMap"); + } + } + + public void clear() { + } + + public void remove(Range range) { + Preconditions.checkNotNull(range); + } + + public Map asMapOfRanges() { + return Collections.emptyMap(); + } + + public RangeMap subRangeMap(Range range) { + Preconditions.checkNotNull(range); + return this; + } + }; + + public static TreeRangeMap create() { + return new TreeRangeMap(); + } + + private TreeRangeMap() { + } + + @Nullable + public V get(K key) { + Entry, V> entry = this.getEntry(key); + return entry == null ? null : entry.getValue(); + } + + @Nullable + public Entry, V> getEntry(K key) { + Entry, TreeRangeMap.RangeMapEntry> mapEntry = this.entriesByLowerBound.floorEntry(Cut.belowValue(key)); + return mapEntry != null && ((TreeRangeMap.RangeMapEntry)mapEntry.getValue()).contains(key) ? (Entry)mapEntry.getValue() : null; + } + + public void put(Range range, V value) { + if (!range.isEmpty()) { + Preconditions.checkNotNull(value); + this.remove(range); + this.entriesByLowerBound.put(range.lowerBound, new TreeRangeMap.RangeMapEntry(range, value)); + } + + } + + public void putAll(RangeMap rangeMap) { + Iterator i$ = rangeMap.asMapOfRanges().entrySet().iterator(); + + while(i$.hasNext()) { + Entry, V> entry = (Entry)i$.next(); + this.put((Range)entry.getKey(), entry.getValue()); + } + + } + + public void clear() { + this.entriesByLowerBound.clear(); + } + + public Range span() { + Entry, TreeRangeMap.RangeMapEntry> firstEntry = this.entriesByLowerBound.firstEntry(); + Entry, TreeRangeMap.RangeMapEntry> lastEntry = this.entriesByLowerBound.lastEntry(); + if (firstEntry == null) { + throw new NoSuchElementException(); + } else { + return Range.create(((TreeRangeMap.RangeMapEntry)firstEntry.getValue()).getKey().lowerBound, ((TreeRangeMap.RangeMapEntry)lastEntry.getValue()).getKey().upperBound); + } + } + + private void putRangeMapEntry(Cut lowerBound, Cut upperBound, V value) { + this.entriesByLowerBound.put(lowerBound, new TreeRangeMap.RangeMapEntry(lowerBound, upperBound, value)); + } + + public void remove(Range rangeToRemove) { + if (!rangeToRemove.isEmpty()) { + Entry, TreeRangeMap.RangeMapEntry> mapEntryBelowToTruncate = this.entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound); + if (mapEntryBelowToTruncate != null) { + TreeRangeMap.RangeMapEntry rangeMapEntry = (TreeRangeMap.RangeMapEntry)mapEntryBelowToTruncate.getValue(); + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) { + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { + this.putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), ((TreeRangeMap.RangeMapEntry)mapEntryBelowToTruncate.getValue()).getValue()); + } + + this.putRangeMapEntry(rangeMapEntry.getLowerBound(), rangeToRemove.lowerBound, ((TreeRangeMap.RangeMapEntry)mapEntryBelowToTruncate.getValue()).getValue()); + } + } + + Entry, TreeRangeMap.RangeMapEntry> mapEntryAboveToTruncate = this.entriesByLowerBound.lowerEntry(rangeToRemove.upperBound); + if (mapEntryAboveToTruncate != null) { + TreeRangeMap.RangeMapEntry rangeMapEntry = (TreeRangeMap.RangeMapEntry)mapEntryAboveToTruncate.getValue(); + if (rangeMapEntry.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) { + this.putRangeMapEntry(rangeToRemove.upperBound, rangeMapEntry.getUpperBound(), ((TreeRangeMap.RangeMapEntry)mapEntryAboveToTruncate.getValue()).getValue()); + this.entriesByLowerBound.remove(rangeToRemove.lowerBound); + } + } + + this.entriesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); + } + } + + public Map, V> asMapOfRanges() { + return new TreeRangeMap.AsMapOfRanges(); + } + + public RangeMap subRangeMap(Range subRange) { + return (RangeMap)(subRange.equals(Range.all()) ? this : new TreeRangeMap.SubRangeMap(subRange)); + } + + private RangeMap emptySubRangeMap() { + return EMPTY_SUB_RANGE_MAP; + } + + public boolean equals(@Nullable Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap)o; + return this.asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } else { + return false; + } + } + + public int hashCode() { + return this.asMapOfRanges().hashCode(); + } + + public String toString() { + return this.entriesByLowerBound.values().toString(); + } + + private class SubRangeMap implements RangeMap { + private final Range subRange; + + SubRangeMap(Range subRange) { + this.subRange = subRange; + } + + @Nullable + public V get(K key) { + return this.subRange.contains(key) ? TreeRangeMap.this.get(key) : null; + } + + @Nullable + public Entry, V> getEntry(K key) { + if (this.subRange.contains(key)) { + Entry, V> entry = TreeRangeMap.this.getEntry(key); + if (entry != null) { + return Maps.immutableEntry(((Range)entry.getKey()).intersection(this.subRange), entry.getValue()); + } + } + + return null; + } + + public Range span() { + Entry, TreeRangeMap.RangeMapEntry> lowerEntry = TreeRangeMap.this.entriesByLowerBound.floorEntry(this.subRange.lowerBound); + Cut lowerBound; + if (lowerEntry != null && ((TreeRangeMap.RangeMapEntry)lowerEntry.getValue()).getUpperBound().compareTo(this.subRange.lowerBound) > 0) { + lowerBound = this.subRange.lowerBound; + } else { + lowerBound = (Cut)TreeRangeMap.this.entriesByLowerBound.ceilingKey(this.subRange.lowerBound); + if (lowerBound == null || lowerBound.compareTo(this.subRange.upperBound) >= 0) { + throw new NoSuchElementException(); + } + } + + Entry, TreeRangeMap.RangeMapEntry> upperEntry = TreeRangeMap.this.entriesByLowerBound.lowerEntry(this.subRange.upperBound); + if (upperEntry == null) { + throw new NoSuchElementException(); + } else { + Cut upperBound; + if (((TreeRangeMap.RangeMapEntry)upperEntry.getValue()).getUpperBound().compareTo(this.subRange.upperBound) >= 0) { + upperBound = this.subRange.upperBound; + } else { + upperBound = ((TreeRangeMap.RangeMapEntry)upperEntry.getValue()).getUpperBound(); + } + + return Range.create(lowerBound, upperBound); + } + } + + public void put(Range range, V value) { + Preconditions.checkArgument(this.subRange.encloses(range), "Cannot put range %s into a subRangeMap(%s)", range, this.subRange); + TreeRangeMap.this.put(range, value); + } + + public void putAll(RangeMap rangeMap) { + if (!rangeMap.asMapOfRanges().isEmpty()) { + Range span = rangeMap.span(); + Preconditions.checkArgument(this.subRange.encloses(span), "Cannot putAll rangeMap with span %s into a subRangeMap(%s)", span, this.subRange); + TreeRangeMap.this.putAll(rangeMap); + } + } + + public void clear() { + TreeRangeMap.this.remove(this.subRange); + } + + public void remove(Range range) { + if (range.isConnected(this.subRange)) { + TreeRangeMap.this.remove(range.intersection(this.subRange)); + } + + } + + public RangeMap subRangeMap(Range range) { + return !range.isConnected(this.subRange) ? TreeRangeMap.this.emptySubRangeMap() : TreeRangeMap.this.subRangeMap(range.intersection(this.subRange)); + } + + public Map, V> asMapOfRanges() { + return new TreeRangeMap.SubRangeMap.SubRangeMapAsMap(); + } + + public boolean equals(@Nullable Object o) { + if (o instanceof RangeMap) { + RangeMap rangeMap = (RangeMap)o; + return this.asMapOfRanges().equals(rangeMap.asMapOfRanges()); + } else { + return false; + } + } + + public int hashCode() { + return this.asMapOfRanges().hashCode(); + } + + public String toString() { + return this.asMapOfRanges().toString(); + } + + class SubRangeMapAsMap extends AbstractMap, V> { + public boolean containsKey(Object key) { + return this.get(key) != null; + } + + public V get(Object key) { + try { + if (key instanceof Range) { + Range r = (Range)key; + if (!SubRangeMap.this.subRange.encloses(r) || r.isEmpty()) { + return null; + } + + TreeRangeMap.RangeMapEntry candidate = null; + if (r.lowerBound.compareTo(SubRangeMap.this.subRange.lowerBound) == 0) { + Entry, TreeRangeMap.RangeMapEntry> entry = TreeRangeMap.this.entriesByLowerBound.floorEntry(r.lowerBound); + if (entry != null) { + candidate = (TreeRangeMap.RangeMapEntry)entry.getValue(); + } + } else { + candidate = (TreeRangeMap.RangeMapEntry)TreeRangeMap.this.entriesByLowerBound.get(r.lowerBound); + } + + if (candidate != null && candidate.getKey().isConnected(SubRangeMap.this.subRange) && candidate.getKey().intersection(SubRangeMap.this.subRange).equals(r)) { + return candidate.getValue(); + } + } + + return null; + } catch (ClassCastException var5) { + return null; + } + } + + public V remove(Object key) { + V value = this.get(key); + if (value != null) { + Range range = (Range)key; + TreeRangeMap.this.remove(range); + return value; + } else { + return null; + } + } + + public void clear() { + SubRangeMap.this.clear(); + } + + private boolean removeEntryIf(Predicate, V>> predicate) { + List> toRemove = Lists.newArrayList(); + Iterator i$ = this.entrySet().iterator(); + + while(i$.hasNext()) { + Entry, V> entry = (Entry)i$.next(); + if (predicate.apply(entry)) { + toRemove.add(entry.getKey()); + } + } + + i$ = toRemove.iterator(); + + while(i$.hasNext()) { + Range range = (Range)i$.next(); + TreeRangeMap.this.remove(range); + } + + return !toRemove.isEmpty(); + } + + public Set> keySet() { + return new Maps.KeySet, V>(this) { + public boolean remove(@Nullable Object o) { + return SubRangeMapAsMap.this.remove(o) != null; + } + + public boolean retainAll(Collection c) { + return SubRangeMapAsMap.this.removeEntryIf(Predicates.compose(Predicates.not(Predicates.in(c)), Maps.keyFunction())); + } + }; + } + + public Set, V>> entrySet() { + return new Maps.EntrySet, V>() { + Map, V> map() { + return SubRangeMapAsMap.this; + } + + public Iterator, V>> iterator() { + if (SubRangeMap.this.subRange.isEmpty()) { + return Iterators.emptyIterator(); + } else { + Cut cutToStart = (Cut)MoreObjects.firstNonNull(TreeRangeMap.this.entriesByLowerBound.floorKey(SubRangeMap.this.subRange.lowerBound), SubRangeMap.this.subRange.lowerBound); + final Iterator> backingItr = TreeRangeMap.this.entriesByLowerBound.tailMap(cutToStart, true).values().iterator(); + return new AbstractIterator, V>>() { + protected Entry, V> computeNext() { + while(true) { + if (backingItr.hasNext()) { + TreeRangeMap.RangeMapEntry entry = (TreeRangeMap.RangeMapEntry)backingItr.next(); + if (entry.getLowerBound().compareTo(SubRangeMap.this.subRange.upperBound) < 0) { + if (entry.getUpperBound().compareTo(SubRangeMap.this.subRange.lowerBound) <= 0) { + continue; + } + + return Maps.immutableEntry(entry.getKey().intersection(SubRangeMap.this.subRange), entry.getValue()); + } + } + + return (Entry)this.endOfData(); + } + } + }; + } + } + + public boolean retainAll(Collection c) { + return SubRangeMapAsMap.this.removeEntryIf(Predicates.not(Predicates.in(c))); + } + + public int size() { + return Iterators.size(this.iterator()); + } + + public boolean isEmpty() { + return !this.iterator().hasNext(); + } + }; + } + + public Collection values() { + return new Maps.Values, V>(this) { + public boolean removeAll(Collection c) { + return SubRangeMapAsMap.this.removeEntryIf(Predicates.compose(Predicates.in(c), Maps.valueFunction())); + } + + public boolean retainAll(Collection c) { + return SubRangeMapAsMap.this.removeEntryIf(Predicates.compose(Predicates.not(Predicates.in(c)), Maps.valueFunction())); + } + }; + } + } + } + + private final class AsMapOfRanges extends AbstractMap, V> { + private AsMapOfRanges() { + } + + public boolean containsKey(@Nullable Object key) { + return this.get(key) != null; + } + + public V get(@Nullable Object key) { + if (key instanceof Range) { + Range range = (Range)key; + TreeRangeMap.RangeMapEntry rangeMapEntry = (TreeRangeMap.RangeMapEntry)TreeRangeMap.this.entriesByLowerBound.get(range.lowerBound); + if (rangeMapEntry != null && rangeMapEntry.getKey().equals(range)) { + return rangeMapEntry.getValue(); + } + } + + return null; + } + + public Set, V>> entrySet() { + return new AbstractSet, V>>() { + public Iterator, V>> iterator() { + return TreeRangeMap.this.entriesByLowerBound.values().iterator(); + } + + public int size() { + return TreeRangeMap.this.entriesByLowerBound.size(); + } + }; + } + + // $FF: synthetic method + AsMapOfRanges(Object x1) { + this(); + } + } + + private static final class RangeMapEntry extends AbstractMapEntry, V> { + private final Range range; + private final V value; + + RangeMapEntry(Cut lowerBound, Cut upperBound, V value) { + this(Range.create(lowerBound, upperBound), value); + } + + RangeMapEntry(Range range, V value) { + this.range = range; + this.value = value; + } + + public Range getKey() { + return this.range; + } + + public V getValue() { + return this.value; + } + + public boolean contains(K value) { + return this.range.contains(value); + } + + Cut getLowerBound() { + return this.range.lowerBound; + } + + Cut getUpperBound() { + return this.range.upperBound; + } + } +} diff --git a/src/main/com/google/common/collect/TreeRangeSet.java b/src/main/com/google/common/collect/TreeRangeSet.java new file mode 100644 index 0000000..b9b710c --- /dev/null +++ b/src/main/com/google/common/collect/TreeRangeSet.java @@ -0,0 +1,650 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.NavigableMap; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeMap; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +@GwtIncompatible("uses NavigableMap") +public class TreeRangeSet> extends AbstractRangeSet { + @VisibleForTesting + final NavigableMap, Range> rangesByLowerBound; + private transient Set> asRanges; + private transient RangeSet complement; + + public static > TreeRangeSet create() { + return new TreeRangeSet(new TreeMap()); + } + + public static > TreeRangeSet create(RangeSet rangeSet) { + TreeRangeSet result = create(); + result.addAll(rangeSet); + return result; + } + + private TreeRangeSet(NavigableMap, Range> rangesByLowerCut) { + this.rangesByLowerBound = rangesByLowerCut; + } + + public Set> asRanges() { + Set> result = this.asRanges; + return result == null ? (this.asRanges = new TreeRangeSet.AsRanges()) : result; + } + + @Nullable + public Range rangeContaining(C value) { + Preconditions.checkNotNull(value); + Entry, Range> floorEntry = this.rangesByLowerBound.floorEntry(Cut.belowValue(value)); + return floorEntry != null && ((Range)floorEntry.getValue()).contains(value) ? (Range)floorEntry.getValue() : null; + } + + public boolean encloses(Range range) { + Preconditions.checkNotNull(range); + Entry, Range> floorEntry = this.rangesByLowerBound.floorEntry(range.lowerBound); + return floorEntry != null && ((Range)floorEntry.getValue()).encloses(range); + } + + @Nullable + private Range rangeEnclosing(Range range) { + Preconditions.checkNotNull(range); + Entry, Range> floorEntry = this.rangesByLowerBound.floorEntry(range.lowerBound); + return floorEntry != null && ((Range)floorEntry.getValue()).encloses(range) ? (Range)floorEntry.getValue() : null; + } + + public Range span() { + Entry, Range> firstEntry = this.rangesByLowerBound.firstEntry(); + Entry, Range> lastEntry = this.rangesByLowerBound.lastEntry(); + if (firstEntry == null) { + throw new NoSuchElementException(); + } else { + return Range.create(((Range)firstEntry.getValue()).lowerBound, ((Range)lastEntry.getValue()).upperBound); + } + } + + public void add(Range rangeToAdd) { + Preconditions.checkNotNull(rangeToAdd); + if (!rangeToAdd.isEmpty()) { + Cut lbToAdd = rangeToAdd.lowerBound; + Cut ubToAdd = rangeToAdd.upperBound; + Entry, Range> entryBelowLB = this.rangesByLowerBound.lowerEntry(lbToAdd); + if (entryBelowLB != null) { + Range rangeBelowLB = (Range)entryBelowLB.getValue(); + if (rangeBelowLB.upperBound.compareTo(lbToAdd) >= 0) { + if (rangeBelowLB.upperBound.compareTo(ubToAdd) >= 0) { + ubToAdd = rangeBelowLB.upperBound; + } + + lbToAdd = rangeBelowLB.lowerBound; + } + } + + Entry, Range> entryBelowUB = this.rangesByLowerBound.floorEntry(ubToAdd); + if (entryBelowUB != null) { + Range rangeBelowUB = (Range)entryBelowUB.getValue(); + if (rangeBelowUB.upperBound.compareTo(ubToAdd) >= 0) { + ubToAdd = rangeBelowUB.upperBound; + } + } + + this.rangesByLowerBound.subMap(lbToAdd, ubToAdd).clear(); + this.replaceRangeWithSameLowerBound(Range.create(lbToAdd, ubToAdd)); + } + } + + public void remove(Range rangeToRemove) { + Preconditions.checkNotNull(rangeToRemove); + if (!rangeToRemove.isEmpty()) { + Entry, Range> entryBelowLB = this.rangesByLowerBound.lowerEntry(rangeToRemove.lowerBound); + if (entryBelowLB != null) { + Range rangeBelowLB = (Range)entryBelowLB.getValue(); + if (rangeBelowLB.upperBound.compareTo(rangeToRemove.lowerBound) >= 0) { + if (rangeToRemove.hasUpperBound() && rangeBelowLB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + this.replaceRangeWithSameLowerBound(Range.create(rangeToRemove.upperBound, rangeBelowLB.upperBound)); + } + + this.replaceRangeWithSameLowerBound(Range.create(rangeBelowLB.lowerBound, rangeToRemove.lowerBound)); + } + } + + Entry, Range> entryBelowUB = this.rangesByLowerBound.floorEntry(rangeToRemove.upperBound); + if (entryBelowUB != null) { + Range rangeBelowUB = (Range)entryBelowUB.getValue(); + if (rangeToRemove.hasUpperBound() && rangeBelowUB.upperBound.compareTo(rangeToRemove.upperBound) >= 0) { + this.replaceRangeWithSameLowerBound(Range.create(rangeToRemove.upperBound, rangeBelowUB.upperBound)); + } + } + + this.rangesByLowerBound.subMap(rangeToRemove.lowerBound, rangeToRemove.upperBound).clear(); + } + } + + private void replaceRangeWithSameLowerBound(Range range) { + if (range.isEmpty()) { + this.rangesByLowerBound.remove(range.lowerBound); + } else { + this.rangesByLowerBound.put(range.lowerBound, range); + } + + } + + public RangeSet complement() { + RangeSet result = this.complement; + return result == null ? (this.complement = new TreeRangeSet.Complement()) : result; + } + + public RangeSet subRangeSet(Range view) { + return (RangeSet)(view.equals(Range.all()) ? this : new TreeRangeSet.SubRangeSet(view)); + } + + // $FF: synthetic method + TreeRangeSet(NavigableMap x0, Object x1) { + this(x0); + } + + private final class SubRangeSet extends TreeRangeSet { + private final Range restriction; + + SubRangeSet(Range restriction) { + super(new TreeRangeSet.SubRangeSetRangesByLowerBound(Range.all(), restriction, TreeRangeSet.this.rangesByLowerBound), null); + this.restriction = restriction; + } + + public boolean encloses(Range range) { + if (!this.restriction.isEmpty() && this.restriction.encloses(range)) { + Range enclosing = TreeRangeSet.this.rangeEnclosing(range); + return enclosing != null && !enclosing.intersection(this.restriction).isEmpty(); + } else { + return false; + } + } + + @Nullable + public Range rangeContaining(C value) { + if (!this.restriction.contains(value)) { + return null; + } else { + Range result = TreeRangeSet.this.rangeContaining(value); + return result == null ? null : result.intersection(this.restriction); + } + } + + public void add(Range rangeToAdd) { + Preconditions.checkArgument(this.restriction.encloses(rangeToAdd), "Cannot add range %s to subRangeSet(%s)", rangeToAdd, this.restriction); + super.add(rangeToAdd); + } + + public void remove(Range rangeToRemove) { + if (rangeToRemove.isConnected(this.restriction)) { + TreeRangeSet.this.remove(rangeToRemove.intersection(this.restriction)); + } + + } + + public boolean contains(C value) { + return this.restriction.contains(value) && TreeRangeSet.this.contains(value); + } + + public void clear() { + TreeRangeSet.this.remove(this.restriction); + } + + public RangeSet subRangeSet(Range view) { + if (view.encloses(this.restriction)) { + return this; + } else { + return (RangeSet)(view.isConnected(this.restriction) ? new TreeRangeSet.SubRangeSet(this.restriction.intersection(view)) : ImmutableRangeSet.of()); + } + } + } + + private static final class SubRangeSetRangesByLowerBound> extends AbstractNavigableMap, Range> { + private final Range> lowerBoundWindow; + private final Range restriction; + private final NavigableMap, Range> rangesByLowerBound; + private final NavigableMap, Range> rangesByUpperBound; + + private SubRangeSetRangesByLowerBound(Range> lowerBoundWindow, Range restriction, NavigableMap, Range> rangesByLowerBound) { + this.lowerBoundWindow = (Range)Preconditions.checkNotNull(lowerBoundWindow); + this.restriction = (Range)Preconditions.checkNotNull(restriction); + this.rangesByLowerBound = (NavigableMap)Preconditions.checkNotNull(rangesByLowerBound); + this.rangesByUpperBound = new TreeRangeSet.RangesByUpperBound(rangesByLowerBound); + } + + private NavigableMap, Range> subMap(Range> window) { + return (NavigableMap)(!window.isConnected(this.lowerBoundWindow) ? ImmutableSortedMap.of() : new TreeRangeSet.SubRangeSetRangesByLowerBound(this.lowerBoundWindow.intersection(window), this.restriction, this.rangesByLowerBound)); + } + + public NavigableMap, Range> subMap(Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { + return this.subMap(Range.range(fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); + } + + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return this.subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return this.subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + public Comparator> comparator() { + return Ordering.natural(); + } + + public boolean containsKey(@Nullable Object key) { + return this.get(key) != null; + } + + @Nullable + public Range get(@Nullable Object key) { + if (key instanceof Cut) { + try { + Cut cut = (Cut)key; + if (!this.lowerBoundWindow.contains(cut) || cut.compareTo(this.restriction.lowerBound) < 0 || cut.compareTo(this.restriction.upperBound) >= 0) { + return null; + } + + Range candidate; + if (cut.equals(this.restriction.lowerBound)) { + candidate = (Range)Maps.valueOrNull(this.rangesByLowerBound.floorEntry(cut)); + if (candidate != null && candidate.upperBound.compareTo(this.restriction.lowerBound) > 0) { + return candidate.intersection(this.restriction); + } + } else { + candidate = (Range)this.rangesByLowerBound.get(cut); + if (candidate != null) { + return candidate.intersection(this.restriction); + } + } + } catch (ClassCastException var4) { + return null; + } + } + + return null; + } + + Iterator, Range>> entryIterator() { + if (this.restriction.isEmpty()) { + return Iterators.emptyIterator(); + } else if (this.lowerBoundWindow.upperBound.isLessThan(this.restriction.lowerBound)) { + return Iterators.emptyIterator(); + } else { + final Iterator completeRangeItr; + if (this.lowerBoundWindow.lowerBound.isLessThan(this.restriction.lowerBound)) { + completeRangeItr = this.rangesByUpperBound.tailMap(this.restriction.lowerBound, false).values().iterator(); + } else { + completeRangeItr = this.rangesByLowerBound.tailMap(this.lowerBoundWindow.lowerBound.endpoint(), this.lowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values().iterator(); + } + + final Cut> upperBoundOnLowerBounds = (Cut)Ordering.natural().min(this.lowerBoundWindow.upperBound, Cut.belowValue(this.restriction.upperBound)); + return new AbstractIterator, Range>>() { + protected Entry, Range> computeNext() { + if (!completeRangeItr.hasNext()) { + return (Entry)this.endOfData(); + } else { + Range nextRange = (Range)completeRangeItr.next(); + if (upperBoundOnLowerBounds.isLessThan(nextRange.lowerBound)) { + return (Entry)this.endOfData(); + } else { + nextRange = nextRange.intersection(SubRangeSetRangesByLowerBound.this.restriction); + return Maps.immutableEntry(nextRange.lowerBound, nextRange); + } + } + } + }; + } + } + + Iterator, Range>> descendingEntryIterator() { + if (this.restriction.isEmpty()) { + return Iterators.emptyIterator(); + } else { + Cut> upperBoundOnLowerBounds = (Cut)Ordering.natural().min(this.lowerBoundWindow.upperBound, Cut.belowValue(this.restriction.upperBound)); + final Iterator> completeRangeItr = this.rangesByLowerBound.headMap(upperBoundOnLowerBounds.endpoint(), upperBoundOnLowerBounds.typeAsUpperBound() == BoundType.CLOSED).descendingMap().values().iterator(); + return new AbstractIterator, Range>>() { + protected Entry, Range> computeNext() { + if (!completeRangeItr.hasNext()) { + return (Entry)this.endOfData(); + } else { + Range nextRange = (Range)completeRangeItr.next(); + if (SubRangeSetRangesByLowerBound.this.restriction.lowerBound.compareTo(nextRange.upperBound) >= 0) { + return (Entry)this.endOfData(); + } else { + nextRange = nextRange.intersection(SubRangeSetRangesByLowerBound.this.restriction); + return SubRangeSetRangesByLowerBound.this.lowerBoundWindow.contains(nextRange.lowerBound) ? Maps.immutableEntry(nextRange.lowerBound, nextRange) : (Entry)this.endOfData(); + } + } + } + }; + } + } + + public int size() { + return Iterators.size(this.entryIterator()); + } + + // $FF: synthetic method + SubRangeSetRangesByLowerBound(Range x0, Range x1, NavigableMap x2, Object x3) { + this(x0, x1, x2); + } + } + + private final class Complement extends TreeRangeSet { + Complement() { + super(new TreeRangeSet.ComplementRangesByLowerBound(TreeRangeSet.this.rangesByLowerBound), null); + } + + public void add(Range rangeToAdd) { + TreeRangeSet.this.remove(rangeToAdd); + } + + public void remove(Range rangeToRemove) { + TreeRangeSet.this.add(rangeToRemove); + } + + public boolean contains(C value) { + return !TreeRangeSet.this.contains(value); + } + + public RangeSet complement() { + return TreeRangeSet.this; + } + } + + private static final class ComplementRangesByLowerBound> extends AbstractNavigableMap, Range> { + private final NavigableMap, Range> positiveRangesByLowerBound; + private final NavigableMap, Range> positiveRangesByUpperBound; + private final Range> complementLowerBoundWindow; + + ComplementRangesByLowerBound(NavigableMap, Range> positiveRangesByLowerBound) { + this(positiveRangesByLowerBound, Range.all()); + } + + private ComplementRangesByLowerBound(NavigableMap, Range> positiveRangesByLowerBound, Range> window) { + this.positiveRangesByLowerBound = positiveRangesByLowerBound; + this.positiveRangesByUpperBound = new TreeRangeSet.RangesByUpperBound(positiveRangesByLowerBound); + this.complementLowerBoundWindow = window; + } + + private NavigableMap, Range> subMap(Range> subWindow) { + if (!this.complementLowerBoundWindow.isConnected(subWindow)) { + return ImmutableSortedMap.of(); + } else { + subWindow = subWindow.intersection(this.complementLowerBoundWindow); + return new TreeRangeSet.ComplementRangesByLowerBound(this.positiveRangesByLowerBound, subWindow); + } + } + + public NavigableMap, Range> subMap(Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { + return this.subMap(Range.range(fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); + } + + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return this.subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return this.subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + public Comparator> comparator() { + return Ordering.natural(); + } + + Iterator, Range>> entryIterator() { + Collection positiveRanges; + if (this.complementLowerBoundWindow.hasLowerBound()) { + positiveRanges = this.positiveRangesByUpperBound.tailMap(this.complementLowerBoundWindow.lowerEndpoint(), this.complementLowerBoundWindow.lowerBoundType() == BoundType.CLOSED).values(); + } else { + positiveRanges = this.positiveRangesByUpperBound.values(); + } + + final PeekingIterator> positiveItr = Iterators.peekingIterator(positiveRanges.iterator()); + final Cut firstComplementRangeLowerBound; + if (this.complementLowerBoundWindow.contains(Cut.belowAll()) && (!positiveItr.hasNext() || ((Range)positiveItr.peek()).lowerBound != Cut.belowAll())) { + firstComplementRangeLowerBound = Cut.belowAll(); + } else { + if (!positiveItr.hasNext()) { + return Iterators.emptyIterator(); + } + + firstComplementRangeLowerBound = ((Range)positiveItr.next()).upperBound; + } + + return new AbstractIterator, Range>>() { + Cut nextComplementRangeLowerBound = firstComplementRangeLowerBound; + + protected Entry, Range> computeNext() { + if (!ComplementRangesByLowerBound.this.complementLowerBoundWindow.upperBound.isLessThan(this.nextComplementRangeLowerBound) && this.nextComplementRangeLowerBound != Cut.aboveAll()) { + Range negativeRange; + if (positiveItr.hasNext()) { + Range positiveRange = (Range)positiveItr.next(); + negativeRange = Range.create(this.nextComplementRangeLowerBound, positiveRange.lowerBound); + this.nextComplementRangeLowerBound = positiveRange.upperBound; + } else { + negativeRange = Range.create(this.nextComplementRangeLowerBound, Cut.aboveAll()); + this.nextComplementRangeLowerBound = Cut.aboveAll(); + } + + return Maps.immutableEntry(negativeRange.lowerBound, negativeRange); + } else { + return (Entry)this.endOfData(); + } + } + }; + } + + Iterator, Range>> descendingEntryIterator() { + Cut startingPoint = this.complementLowerBoundWindow.hasUpperBound() ? (Cut)this.complementLowerBoundWindow.upperEndpoint() : Cut.aboveAll(); + boolean inclusive = this.complementLowerBoundWindow.hasUpperBound() && this.complementLowerBoundWindow.upperBoundType() == BoundType.CLOSED; + final PeekingIterator> positiveItr = Iterators.peekingIterator(this.positiveRangesByUpperBound.headMap(startingPoint, inclusive).descendingMap().values().iterator()); + Cut cut; + if (positiveItr.hasNext()) { + cut = ((Range)positiveItr.peek()).upperBound == Cut.aboveAll() ? ((Range)positiveItr.next()).lowerBound : (Cut)this.positiveRangesByLowerBound.higherKey(((Range)positiveItr.peek()).upperBound); + } else { + if (!this.complementLowerBoundWindow.contains(Cut.belowAll()) || this.positiveRangesByLowerBound.containsKey(Cut.belowAll())) { + return Iterators.emptyIterator(); + } + + cut = (Cut)this.positiveRangesByLowerBound.higherKey(Cut.belowAll()); + } + + final Cut firstComplementRangeUpperBound = (Cut)MoreObjects.firstNonNull(cut, Cut.aboveAll()); + return new AbstractIterator, Range>>() { + Cut nextComplementRangeUpperBound = firstComplementRangeUpperBound; + + protected Entry, Range> computeNext() { + if (this.nextComplementRangeUpperBound == Cut.belowAll()) { + return (Entry)this.endOfData(); + } else { + Range negativeRange; + if (positiveItr.hasNext()) { + negativeRange = (Range)positiveItr.next(); + Range negativeRangex = Range.create(negativeRange.upperBound, this.nextComplementRangeUpperBound); + this.nextComplementRangeUpperBound = negativeRange.lowerBound; + if (ComplementRangesByLowerBound.this.complementLowerBoundWindow.lowerBound.isLessThan(negativeRangex.lowerBound)) { + return Maps.immutableEntry(negativeRangex.lowerBound, negativeRangex); + } + } else if (ComplementRangesByLowerBound.this.complementLowerBoundWindow.lowerBound.isLessThan(Cut.belowAll())) { + negativeRange = Range.create(Cut.belowAll(), this.nextComplementRangeUpperBound); + this.nextComplementRangeUpperBound = Cut.belowAll(); + return Maps.immutableEntry(Cut.belowAll(), negativeRange); + } + + return (Entry)this.endOfData(); + } + } + }; + } + + public int size() { + return Iterators.size(this.entryIterator()); + } + + @Nullable + public Range get(Object key) { + if (key instanceof Cut) { + try { + Cut cut = (Cut)key; + Entry, Range> firstEntry = this.tailMap(cut, true).firstEntry(); + if (firstEntry != null && ((Cut)firstEntry.getKey()).equals(cut)) { + return (Range)firstEntry.getValue(); + } + } catch (ClassCastException var4) { + return null; + } + } + + return null; + } + + public boolean containsKey(Object key) { + return this.get(key) != null; + } + } + + @VisibleForTesting + static final class RangesByUpperBound> extends AbstractNavigableMap, Range> { + private final NavigableMap, Range> rangesByLowerBound; + private final Range> upperBoundWindow; + + RangesByUpperBound(NavigableMap, Range> rangesByLowerBound) { + this.rangesByLowerBound = rangesByLowerBound; + this.upperBoundWindow = Range.all(); + } + + private RangesByUpperBound(NavigableMap, Range> rangesByLowerBound, Range> upperBoundWindow) { + this.rangesByLowerBound = rangesByLowerBound; + this.upperBoundWindow = upperBoundWindow; + } + + private NavigableMap, Range> subMap(Range> window) { + return (NavigableMap)(window.isConnected(this.upperBoundWindow) ? new TreeRangeSet.RangesByUpperBound(this.rangesByLowerBound, window.intersection(this.upperBoundWindow)) : ImmutableSortedMap.of()); + } + + public NavigableMap, Range> subMap(Cut fromKey, boolean fromInclusive, Cut toKey, boolean toInclusive) { + return this.subMap(Range.range(fromKey, BoundType.forBoolean(fromInclusive), toKey, BoundType.forBoolean(toInclusive))); + } + + public NavigableMap, Range> headMap(Cut toKey, boolean inclusive) { + return this.subMap(Range.upTo(toKey, BoundType.forBoolean(inclusive))); + } + + public NavigableMap, Range> tailMap(Cut fromKey, boolean inclusive) { + return this.subMap(Range.downTo(fromKey, BoundType.forBoolean(inclusive))); + } + + public Comparator> comparator() { + return Ordering.natural(); + } + + public boolean containsKey(@Nullable Object key) { + return this.get(key) != null; + } + + public Range get(@Nullable Object key) { + if (key instanceof Cut) { + try { + Cut cut = (Cut)key; + if (!this.upperBoundWindow.contains(cut)) { + return null; + } + + Entry, Range> candidate = this.rangesByLowerBound.lowerEntry(cut); + if (candidate != null && ((Range)candidate.getValue()).upperBound.equals(cut)) { + return (Range)candidate.getValue(); + } + } catch (ClassCastException var4) { + return null; + } + } + + return null; + } + + Iterator, Range>> entryIterator() { + final Iterator backingItr; + if (!this.upperBoundWindow.hasLowerBound()) { + backingItr = this.rangesByLowerBound.values().iterator(); + } else { + Entry, Range> lowerEntry = this.rangesByLowerBound.lowerEntry(this.upperBoundWindow.lowerEndpoint()); + if (lowerEntry == null) { + backingItr = this.rangesByLowerBound.values().iterator(); + } else if (this.upperBoundWindow.lowerBound.isLessThan(((Range)lowerEntry.getValue()).upperBound)) { + backingItr = this.rangesByLowerBound.tailMap(lowerEntry.getKey(), true).values().iterator(); + } else { + backingItr = this.rangesByLowerBound.tailMap(this.upperBoundWindow.lowerEndpoint(), true).values().iterator(); + } + } + + return new AbstractIterator, Range>>() { + protected Entry, Range> computeNext() { + if (!backingItr.hasNext()) { + return (Entry)this.endOfData(); + } else { + Range range = (Range)backingItr.next(); + return RangesByUpperBound.this.upperBoundWindow.upperBound.isLessThan(range.upperBound) ? (Entry)this.endOfData() : Maps.immutableEntry(range.upperBound, range); + } + } + }; + } + + Iterator, Range>> descendingEntryIterator() { + Collection candidates; + if (this.upperBoundWindow.hasUpperBound()) { + candidates = this.rangesByLowerBound.headMap(this.upperBoundWindow.upperEndpoint(), false).descendingMap().values(); + } else { + candidates = this.rangesByLowerBound.descendingMap().values(); + } + + final PeekingIterator> backingItr = Iterators.peekingIterator(candidates.iterator()); + if (backingItr.hasNext() && this.upperBoundWindow.upperBound.isLessThan(((Range)backingItr.peek()).upperBound)) { + backingItr.next(); + } + + return new AbstractIterator, Range>>() { + protected Entry, Range> computeNext() { + if (!backingItr.hasNext()) { + return (Entry)this.endOfData(); + } else { + Range range = (Range)backingItr.next(); + return RangesByUpperBound.this.upperBoundWindow.lowerBound.isLessThan(range.upperBound) ? Maps.immutableEntry(range.upperBound, range) : (Entry)this.endOfData(); + } + } + }; + } + + public int size() { + return this.upperBoundWindow.equals(Range.all()) ? this.rangesByLowerBound.size() : Iterators.size(this.entryIterator()); + } + + public boolean isEmpty() { + return this.upperBoundWindow.equals(Range.all()) ? this.rangesByLowerBound.isEmpty() : !this.entryIterator().hasNext(); + } + } + + final class AsRanges extends ForwardingCollection> implements Set> { + protected Collection> delegate() { + return TreeRangeSet.this.rangesByLowerBound.values(); + } + + public int hashCode() { + return Sets.hashCodeImpl(this); + } + + public boolean equals(@Nullable Object o) { + return Sets.equalsImpl(this, o); + } + } +} diff --git a/src/main/com/google/common/collect/TreeTraverser.java b/src/main/com/google/common/collect/TreeTraverser.java new file mode 100644 index 0000000..8829201 --- /dev/null +++ b/src/main/com/google/common/collect/TreeTraverser.java @@ -0,0 +1,141 @@ +package com.google.common.collect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.Queue; + +@Beta +@GwtCompatible( + emulated = true +) +public abstract class TreeTraverser { + public abstract Iterable children(T var1); + + public final FluentIterable preOrderTraversal(final T root) { + Preconditions.checkNotNull(root); + return new FluentIterable() { + public UnmodifiableIterator iterator() { + return TreeTraverser.this.preOrderIterator(root); + } + }; + } + + UnmodifiableIterator preOrderIterator(T root) { + return new TreeTraverser.PreOrderIterator(root); + } + + public final FluentIterable postOrderTraversal(final T root) { + Preconditions.checkNotNull(root); + return new FluentIterable() { + public UnmodifiableIterator iterator() { + return TreeTraverser.this.postOrderIterator(root); + } + }; + } + + UnmodifiableIterator postOrderIterator(T root) { + return new TreeTraverser.PostOrderIterator(root); + } + + public final FluentIterable breadthFirstTraversal(final T root) { + Preconditions.checkNotNull(root); + return new FluentIterable() { + public UnmodifiableIterator iterator() { + return TreeTraverser.this.new BreadthFirstIterator(root); + } + }; + } + + private final class BreadthFirstIterator extends UnmodifiableIterator implements PeekingIterator { + private final Queue queue = new ArrayDeque(); + + BreadthFirstIterator(T root) { + this.queue.add(root); + } + + public boolean hasNext() { + return !this.queue.isEmpty(); + } + + public T peek() { + return this.queue.element(); + } + + public T next() { + T result = this.queue.remove(); + Iterables.addAll(this.queue, TreeTraverser.this.children(result)); + return result; + } + } + + private final class PostOrderIterator extends AbstractIterator { + private final ArrayDeque> stack = new ArrayDeque(); + + PostOrderIterator(T root) { + this.stack.addLast(this.expand(root)); + } + + protected T computeNext() { + while(true) { + if (!this.stack.isEmpty()) { + TreeTraverser.PostOrderNode top = (TreeTraverser.PostOrderNode)this.stack.getLast(); + if (top.childIterator.hasNext()) { + T child = top.childIterator.next(); + this.stack.addLast(this.expand(child)); + continue; + } + + this.stack.removeLast(); + return top.root; + } + + return this.endOfData(); + } + } + + private TreeTraverser.PostOrderNode expand(T t) { + return new TreeTraverser.PostOrderNode(t, TreeTraverser.this.children(t).iterator()); + } + } + + private static final class PostOrderNode { + final T root; + final Iterator childIterator; + + PostOrderNode(T root, Iterator childIterator) { + this.root = Preconditions.checkNotNull(root); + this.childIterator = (Iterator)Preconditions.checkNotNull(childIterator); + } + } + + private final class PreOrderIterator extends UnmodifiableIterator { + private final Deque> stack = new ArrayDeque(); + + PreOrderIterator(T root) { + this.stack.addLast(Iterators.singletonIterator(Preconditions.checkNotNull(root))); + } + + public boolean hasNext() { + return !this.stack.isEmpty(); + } + + public T next() { + Iterator itr = (Iterator)this.stack.getLast(); + T result = Preconditions.checkNotNull(itr.next()); + if (!itr.hasNext()) { + this.stack.removeLast(); + } + + Iterator childItr = TreeTraverser.this.children(result).iterator(); + if (childItr.hasNext()) { + this.stack.addLast(childItr); + } + + return result; + } + } +} diff --git a/src/main/com/google/common/collect/UnmodifiableIterator.java b/src/main/com/google/common/collect/UnmodifiableIterator.java new file mode 100644 index 0000000..0568331 --- /dev/null +++ b/src/main/com/google/common/collect/UnmodifiableIterator.java @@ -0,0 +1,16 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; + +@GwtCompatible +public abstract class UnmodifiableIterator implements Iterator { + protected UnmodifiableIterator() { + } + + /** @deprecated */ + @Deprecated + public final void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/UnmodifiableListIterator.java b/src/main/com/google/common/collect/UnmodifiableListIterator.java new file mode 100644 index 0000000..af6971a --- /dev/null +++ b/src/main/com/google/common/collect/UnmodifiableListIterator.java @@ -0,0 +1,22 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.ListIterator; + +@GwtCompatible +public abstract class UnmodifiableListIterator extends UnmodifiableIterator implements ListIterator { + protected UnmodifiableListIterator() { + } + + /** @deprecated */ + @Deprecated + public final void add(E e) { + throw new UnsupportedOperationException(); + } + + /** @deprecated */ + @Deprecated + public final void set(E e) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/common/collect/UnmodifiableSortedMultiset.java b/src/main/com/google/common/collect/UnmodifiableSortedMultiset.java new file mode 100644 index 0000000..fb56b89 --- /dev/null +++ b/src/main/com/google/common/collect/UnmodifiableSortedMultiset.java @@ -0,0 +1,72 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Comparator; +import java.util.NavigableSet; + +@GwtCompatible( + emulated = true +) +final class UnmodifiableSortedMultiset extends Multisets.UnmodifiableMultiset implements SortedMultiset { + private transient UnmodifiableSortedMultiset descendingMultiset; + private static final long serialVersionUID = 0L; + + UnmodifiableSortedMultiset(SortedMultiset delegate) { + super(delegate); + } + + protected SortedMultiset delegate() { + return (SortedMultiset)super.delegate(); + } + + public Comparator comparator() { + return this.delegate().comparator(); + } + + NavigableSet createElementSet() { + return Sets.unmodifiableNavigableSet(this.delegate().elementSet()); + } + + public NavigableSet elementSet() { + return (NavigableSet)super.elementSet(); + } + + public SortedMultiset descendingMultiset() { + UnmodifiableSortedMultiset result = this.descendingMultiset; + if (result == null) { + result = new UnmodifiableSortedMultiset(this.delegate().descendingMultiset()); + result.descendingMultiset = this; + return this.descendingMultiset = result; + } else { + return result; + } + } + + public Multiset.Entry firstEntry() { + return this.delegate().firstEntry(); + } + + public Multiset.Entry lastEntry() { + return this.delegate().lastEntry(); + } + + public Multiset.Entry pollFirstEntry() { + throw new UnsupportedOperationException(); + } + + public Multiset.Entry pollLastEntry() { + throw new UnsupportedOperationException(); + } + + public SortedMultiset headMultiset(E upperBound, BoundType boundType) { + return Multisets.unmodifiableSortedMultiset(this.delegate().headMultiset(upperBound, boundType)); + } + + public SortedMultiset subMultiset(E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { + return Multisets.unmodifiableSortedMultiset(this.delegate().subMultiset(lowerBound, lowerBoundType, upperBound, upperBoundType)); + } + + public SortedMultiset tailMultiset(E lowerBound, BoundType boundType) { + return Multisets.unmodifiableSortedMultiset(this.delegate().tailMultiset(lowerBound, boundType)); + } +} diff --git a/src/main/com/google/common/collect/UsingToStringOrdering.java b/src/main/com/google/common/collect/UsingToStringOrdering.java new file mode 100644 index 0000000..b8e2611 --- /dev/null +++ b/src/main/com/google/common/collect/UsingToStringOrdering.java @@ -0,0 +1,27 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.io.Serializable; + +@GwtCompatible( + serializable = true +) +final class UsingToStringOrdering extends Ordering implements Serializable { + static final UsingToStringOrdering INSTANCE = new UsingToStringOrdering(); + private static final long serialVersionUID = 0L; + + public int compare(Object left, Object right) { + return left.toString().compareTo(right.toString()); + } + + private Object readResolve() { + return INSTANCE; + } + + public String toString() { + return "Ordering.usingToString()"; + } + + private UsingToStringOrdering() { + } +} diff --git a/src/main/com/google/common/collect/WellBehavedMap.java b/src/main/com/google/common/collect/WellBehavedMap.java new file mode 100644 index 0000000..8c09060 --- /dev/null +++ b/src/main/com/google/common/collect/WellBehavedMap.java @@ -0,0 +1,64 @@ +package com.google.common.collect; + +import com.google.common.annotations.GwtCompatible; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +@GwtCompatible +final class WellBehavedMap extends ForwardingMap { + private final Map delegate; + private Set> entrySet; + + private WellBehavedMap(Map delegate) { + this.delegate = delegate; + } + + static WellBehavedMap wrap(Map delegate) { + return new WellBehavedMap(delegate); + } + + protected Map delegate() { + return this.delegate; + } + + public Set> entrySet() { + Set> es = this.entrySet; + return es != null ? es : (this.entrySet = new WellBehavedMap.EntrySet()); + } + + private final class EntrySet extends Maps.EntrySet { + private EntrySet() { + } + + Map map() { + return WellBehavedMap.this; + } + + public Iterator> iterator() { + return new TransformedIterator>(WellBehavedMap.this.keySet().iterator()) { + Entry transform(final K key) { + return new AbstractMapEntry() { + public K getKey() { + return key; + } + + public V getValue() { + return WellBehavedMap.this.get(key); + } + + public V setValue(V value) { + return WellBehavedMap.this.put(key, value); + } + }; + } + }; + } + + // $FF: synthetic method + EntrySet(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/collect/package-info.java b/src/main/com/google/common/collect/package-info.java new file mode 100644 index 0000000..64cb80a --- /dev/null +++ b/src/main/com/google/common/collect/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.collect; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/escape/ArrayBasedCharEscaper.java b/src/main/com/google/common/escape/ArrayBasedCharEscaper.java new file mode 100644 index 0000000..7f6de83 --- /dev/null +++ b/src/main/com/google/common/escape/ArrayBasedCharEscaper.java @@ -0,0 +1,58 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Map; + +@Beta +@GwtCompatible +public abstract class ArrayBasedCharEscaper extends CharEscaper { + private final char[][] replacements; + private final int replacementsLength; + private final char safeMin; + private final char safeMax; + + protected ArrayBasedCharEscaper(Map replacementMap, char safeMin, char safeMax) { + this(ArrayBasedEscaperMap.create(replacementMap), safeMin, safeMax); + } + + protected ArrayBasedCharEscaper(ArrayBasedEscaperMap escaperMap, char safeMin, char safeMax) { + Preconditions.checkNotNull(escaperMap); + this.replacements = escaperMap.getReplacementArray(); + this.replacementsLength = this.replacements.length; + if (safeMax < safeMin) { + safeMax = 0; + safeMin = '\uffff'; + } + + this.safeMin = safeMin; + this.safeMax = safeMax; + } + + public final String escape(String s) { + Preconditions.checkNotNull(s); + + for(int i = 0; i < s.length(); ++i) { + char c = s.charAt(i); + if (c < this.replacementsLength && this.replacements[c] != null || c > this.safeMax || c < this.safeMin) { + return this.escapeSlow(s, i); + } + } + + return s; + } + + protected final char[] escape(char c) { + if (c < this.replacementsLength) { + char[] chars = this.replacements[c]; + if (chars != null) { + return chars; + } + } + + return c >= this.safeMin && c <= this.safeMax ? null : this.escapeUnsafe(c); + } + + protected abstract char[] escapeUnsafe(char var1); +} diff --git a/src/main/com/google/common/escape/ArrayBasedEscaperMap.java b/src/main/com/google/common/escape/ArrayBasedEscaperMap.java new file mode 100644 index 0000000..71603f6 --- /dev/null +++ b/src/main/com/google/common/escape/ArrayBasedEscaperMap.java @@ -0,0 +1,46 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +@Beta +@GwtCompatible +public final class ArrayBasedEscaperMap { + private final char[][] replacementArray; + private static final char[][] EMPTY_REPLACEMENT_ARRAY = new char[0][0]; + + public static ArrayBasedEscaperMap create(Map replacements) { + return new ArrayBasedEscaperMap(createReplacementArray(replacements)); + } + + private ArrayBasedEscaperMap(char[][] replacementArray) { + this.replacementArray = replacementArray; + } + + char[][] getReplacementArray() { + return this.replacementArray; + } + + @VisibleForTesting + static char[][] createReplacementArray(Map map) { + Preconditions.checkNotNull(map); + if (map.isEmpty()) { + return EMPTY_REPLACEMENT_ARRAY; + } else { + char max = (Character)Collections.max(map.keySet()); + char[][] replacements = new char[max + 1][]; + + char c; + for(Iterator i$ = map.keySet().iterator(); i$.hasNext(); replacements[c] = ((String)map.get(c)).toCharArray()) { + c = (Character)i$.next(); + } + + return replacements; + } + } +} diff --git a/src/main/com/google/common/escape/ArrayBasedUnicodeEscaper.java b/src/main/com/google/common/escape/ArrayBasedUnicodeEscaper.java new file mode 100644 index 0000000..d340fdc --- /dev/null +++ b/src/main/com/google/common/escape/ArrayBasedUnicodeEscaper.java @@ -0,0 +1,83 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Map; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public abstract class ArrayBasedUnicodeEscaper extends UnicodeEscaper { + private final char[][] replacements; + private final int replacementsLength; + private final int safeMin; + private final int safeMax; + private final char safeMinChar; + private final char safeMaxChar; + + protected ArrayBasedUnicodeEscaper(Map replacementMap, int safeMin, int safeMax, @Nullable String unsafeReplacement) { + this(ArrayBasedEscaperMap.create(replacementMap), safeMin, safeMax, unsafeReplacement); + } + + protected ArrayBasedUnicodeEscaper(ArrayBasedEscaperMap escaperMap, int safeMin, int safeMax, @Nullable String unsafeReplacement) { + Preconditions.checkNotNull(escaperMap); + this.replacements = escaperMap.getReplacementArray(); + this.replacementsLength = this.replacements.length; + if (safeMax < safeMin) { + safeMax = -1; + safeMin = Integer.MAX_VALUE; + } + + this.safeMin = safeMin; + this.safeMax = safeMax; + if (safeMin >= 55296) { + this.safeMinChar = '\uffff'; + this.safeMaxChar = 0; + } else { + this.safeMinChar = (char)safeMin; + this.safeMaxChar = (char)Math.min(safeMax, 55295); + } + + } + + public final String escape(String s) { + Preconditions.checkNotNull(s); + + for(int i = 0; i < s.length(); ++i) { + char c = s.charAt(i); + if (c < this.replacementsLength && this.replacements[c] != null || c > this.safeMaxChar || c < this.safeMinChar) { + return this.escapeSlow(s, i); + } + } + + return s; + } + + protected final int nextEscapeIndex(CharSequence csq, int index, int end) { + while(true) { + if (index < end) { + char c = csq.charAt(index); + if ((c >= this.replacementsLength || this.replacements[c] == null) && c <= this.safeMaxChar && c >= this.safeMinChar) { + ++index; + continue; + } + } + + return index; + } + } + + protected final char[] escape(int cp) { + if (cp < this.replacementsLength) { + char[] chars = this.replacements[cp]; + if (chars != null) { + return chars; + } + } + + return cp >= this.safeMin && cp <= this.safeMax ? null : this.escapeUnsafe(cp); + } + + protected abstract char[] escapeUnsafe(int var1); +} diff --git a/src/main/com/google/common/escape/CharEscaper.java b/src/main/com/google/common/escape/CharEscaper.java new file mode 100644 index 0000000..26bfd95 --- /dev/null +++ b/src/main/com/google/common/escape/CharEscaper.java @@ -0,0 +1,85 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; + +@Beta +@GwtCompatible +public abstract class CharEscaper extends Escaper { + private static final int DEST_PAD_MULTIPLIER = 2; + + protected CharEscaper() { + } + + public String escape(String string) { + Preconditions.checkNotNull(string); + int length = string.length(); + + for(int index = 0; index < length; ++index) { + if (this.escape(string.charAt(index)) != null) { + return this.escapeSlow(string, index); + } + } + + return string; + } + + protected final String escapeSlow(String s, int index) { + int slen = s.length(); + char[] dest = Platform.charBufferFromThreadLocal(); + int destSize = dest.length; + int destIndex = 0; + + int lastEscape; + int rlen; + for(lastEscape = 0; index < slen; ++index) { + char[] r = this.escape(s.charAt(index)); + if (r != null) { + rlen = r.length; + int charsSkipped = index - lastEscape; + int sizeNeeded = destIndex + charsSkipped + rlen; + if (destSize < sizeNeeded) { + destSize = sizeNeeded + 2 * (slen - index); + dest = growBuffer(dest, destIndex, destSize); + } + + if (charsSkipped > 0) { + s.getChars(lastEscape, index, dest, destIndex); + destIndex += charsSkipped; + } + + if (rlen > 0) { + System.arraycopy(r, 0, dest, destIndex, rlen); + destIndex += rlen; + } + + lastEscape = index + 1; + } + } + + int charsLeft = slen - lastEscape; + if (charsLeft > 0) { + rlen = destIndex + charsLeft; + if (destSize < rlen) { + dest = growBuffer(dest, destIndex, rlen); + } + + s.getChars(lastEscape, slen, dest, destIndex); + destIndex = rlen; + } + + return new String(dest, 0, destIndex); + } + + protected abstract char[] escape(char var1); + + private static char[] growBuffer(char[] dest, int index, int size) { + char[] copy = new char[size]; + if (index > 0) { + System.arraycopy(dest, 0, copy, 0, index); + } + + return copy; + } +} diff --git a/src/main/com/google/common/escape/CharEscaperBuilder.java b/src/main/com/google/common/escape/CharEscaperBuilder.java new file mode 100644 index 0000000..b7c1cfd --- /dev/null +++ b/src/main/com/google/common/escape/CharEscaperBuilder.java @@ -0,0 +1,80 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +@Beta +@GwtCompatible +public final class CharEscaperBuilder { + private final Map map = new HashMap(); + private int max = -1; + + public CharEscaperBuilder addEscape(char c, String r) { + this.map.put(c, Preconditions.checkNotNull(r)); + if (c > this.max) { + this.max = c; + } + + return this; + } + + public CharEscaperBuilder addEscapes(char[] cs, String r) { + Preconditions.checkNotNull(r); + char[] arr$ = cs; + int len$ = cs.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char c = arr$[i$]; + this.addEscape(c, r); + } + + return this; + } + + public char[][] toArray() { + char[][] result = new char[this.max + 1][]; + + Entry entry; + for(Iterator i$ = this.map.entrySet().iterator(); i$.hasNext(); result[(Character)entry.getKey()] = ((String)entry.getValue()).toCharArray()) { + entry = (Entry)i$.next(); + } + + return result; + } + + public Escaper toEscaper() { + return new CharEscaperBuilder.CharArrayDecorator(this.toArray()); + } + + private static class CharArrayDecorator extends CharEscaper { + private final char[][] replacements; + private final int replaceLength; + + CharArrayDecorator(char[][] replacements) { + this.replacements = replacements; + this.replaceLength = replacements.length; + } + + public String escape(String s) { + int slen = s.length(); + + for(int index = 0; index < slen; ++index) { + char c = s.charAt(index); + if (c < this.replacements.length && this.replacements[c] != null) { + return this.escapeSlow(s, index); + } + } + + return s; + } + + protected char[] escape(char c) { + return c < this.replaceLength ? this.replacements[c] : null; + } + } +} diff --git a/src/main/com/google/common/escape/Escaper.java b/src/main/com/google/common/escape/Escaper.java new file mode 100644 index 0000000..5d042fb --- /dev/null +++ b/src/main/com/google/common/escape/Escaper.java @@ -0,0 +1,24 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; + +@Beta +@GwtCompatible +public abstract class Escaper { + private final Function asFunction = new Function() { + public String apply(String from) { + return Escaper.this.escape(from); + } + }; + + protected Escaper() { + } + + public abstract String escape(String var1); + + public final Function asFunction() { + return this.asFunction; + } +} diff --git a/src/main/com/google/common/escape/Escapers.java b/src/main/com/google/common/escape/Escapers.java new file mode 100644 index 0000000..2f8587a --- /dev/null +++ b/src/main/com/google/common/escape/Escapers.java @@ -0,0 +1,159 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public final class Escapers { + private static final Escaper NULL_ESCAPER = new CharEscaper() { + public String escape(String string) { + return (String)Preconditions.checkNotNull(string); + } + + protected char[] escape(char c) { + return null; + } + }; + + private Escapers() { + } + + public static Escaper nullEscaper() { + return NULL_ESCAPER; + } + + public static Escapers.Builder builder() { + return new Escapers.Builder(); + } + + static UnicodeEscaper asUnicodeEscaper(Escaper escaper) { + Preconditions.checkNotNull(escaper); + if (escaper instanceof UnicodeEscaper) { + return (UnicodeEscaper)escaper; + } else if (escaper instanceof CharEscaper) { + return wrap((CharEscaper)escaper); + } else { + IllegalArgumentException var10000 = new IllegalArgumentException; + String var10003 = String.valueOf(escaper.getClass().getName()); + String var10002; + if (var10003.length() != 0) { + var10002 = "Cannot create a UnicodeEscaper from: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Cannot create a UnicodeEscaper from: "); + } + + var10000.(var10002); + throw var10000; + } + } + + public static String computeReplacement(CharEscaper escaper, char c) { + return stringOrNull(escaper.escape(c)); + } + + public static String computeReplacement(UnicodeEscaper escaper, int cp) { + return stringOrNull(escaper.escape(cp)); + } + + private static String stringOrNull(char[] in) { + return in == null ? null : new String(in); + } + + private static UnicodeEscaper wrap(final CharEscaper escaper) { + return new UnicodeEscaper() { + protected char[] escape(int cp) { + if (cp < 65536) { + return escaper.escape((char)cp); + } else { + char[] surrogateChars = new char[2]; + Character.toChars(cp, surrogateChars, 0); + char[] hiChars = escaper.escape(surrogateChars[0]); + char[] loChars = escaper.escape(surrogateChars[1]); + if (hiChars == null && loChars == null) { + return null; + } else { + int hiCount = hiChars != null ? hiChars.length : 1; + int loCount = loChars != null ? loChars.length : 1; + char[] output = new char[hiCount + loCount]; + int n; + if (hiChars != null) { + for(n = 0; n < hiChars.length; ++n) { + output[n] = hiChars[n]; + } + } else { + output[0] = surrogateChars[0]; + } + + if (loChars != null) { + for(n = 0; n < loChars.length; ++n) { + output[hiCount + n] = loChars[n]; + } + } else { + output[hiCount] = surrogateChars[1]; + } + + return output; + } + } + } + }; + } + + @Beta + public static final class Builder { + private final Map replacementMap; + private char safeMin; + private char safeMax; + private String unsafeReplacement; + + private Builder() { + this.replacementMap = new HashMap(); + this.safeMin = 0; + this.safeMax = '\uffff'; + this.unsafeReplacement = null; + } + + public Escapers.Builder setSafeRange(char safeMin, char safeMax) { + this.safeMin = safeMin; + this.safeMax = safeMax; + return this; + } + + public Escapers.Builder setUnsafeReplacement(@Nullable String unsafeReplacement) { + this.unsafeReplacement = unsafeReplacement; + return this; + } + + public Escapers.Builder addEscape(char c, String replacement) { + Preconditions.checkNotNull(replacement); + this.replacementMap.put(c, replacement); + return this; + } + + public Escaper build() { + return new ArrayBasedCharEscaper(this.replacementMap, this.safeMin, this.safeMax) { + private final char[] replacementChars; + + { + this.replacementChars = Builder.this.unsafeReplacement != null ? Builder.this.unsafeReplacement.toCharArray() : null; + } + + protected char[] escapeUnsafe(char c) { + return this.replacementChars; + } + }; + } + + // $FF: synthetic method + Builder(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/escape/Platform.java b/src/main/com/google/common/escape/Platform.java new file mode 100644 index 0000000..51dd215 --- /dev/null +++ b/src/main/com/google/common/escape/Platform.java @@ -0,0 +1,21 @@ +package com.google.common.escape; + +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible( + emulated = true +) +final class Platform { + private static final ThreadLocal DEST_TL = new ThreadLocal() { + protected char[] initialValue() { + return new char[1024]; + } + }; + + private Platform() { + } + + static char[] charBufferFromThreadLocal() { + return (char[])DEST_TL.get(); + } +} diff --git a/src/main/com/google/common/escape/UnicodeEscaper.java b/src/main/com/google/common/escape/UnicodeEscaper.java new file mode 100644 index 0000000..307f1df --- /dev/null +++ b/src/main/com/google/common/escape/UnicodeEscaper.java @@ -0,0 +1,127 @@ +package com.google.common.escape; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; + +@Beta +@GwtCompatible +public abstract class UnicodeEscaper extends Escaper { + private static final int DEST_PAD = 32; + + protected UnicodeEscaper() { + } + + protected abstract char[] escape(int var1); + + protected int nextEscapeIndex(CharSequence csq, int start, int end) { + int index; + int cp; + for(index = start; index < end; index += Character.isSupplementaryCodePoint(cp) ? 2 : 1) { + cp = codePointAt(csq, index, end); + if (cp < 0 || this.escape(cp) != null) { + break; + } + } + + return index; + } + + public String escape(String string) { + Preconditions.checkNotNull(string); + int end = string.length(); + int index = this.nextEscapeIndex(string, 0, end); + return index == end ? string : this.escapeSlow(string, index); + } + + protected final String escapeSlow(String s, int index) { + int end = s.length(); + char[] dest = Platform.charBufferFromThreadLocal(); + int destIndex = 0; + + int unescapedChunkStart; + int charsSkipped; + int nextIndex; + for(unescapedChunkStart = 0; index < end; index = this.nextEscapeIndex(s, nextIndex, end)) { + charsSkipped = codePointAt(s, index, end); + if (charsSkipped < 0) { + throw new IllegalArgumentException("Trailing high surrogate at end of input"); + } + + char[] escaped = this.escape(charsSkipped); + nextIndex = index + (Character.isSupplementaryCodePoint(charsSkipped) ? 2 : 1); + if (escaped != null) { + int charsSkipped = index - unescapedChunkStart; + int sizeNeeded = destIndex + charsSkipped + escaped.length; + if (dest.length < sizeNeeded) { + int destLength = sizeNeeded + (end - index) + 32; + dest = growBuffer(dest, destIndex, destLength); + } + + if (charsSkipped > 0) { + s.getChars(unescapedChunkStart, index, dest, destIndex); + destIndex += charsSkipped; + } + + if (escaped.length > 0) { + System.arraycopy(escaped, 0, dest, destIndex, escaped.length); + destIndex += escaped.length; + } + + unescapedChunkStart = nextIndex; + } + } + + charsSkipped = end - unescapedChunkStart; + if (charsSkipped > 0) { + int endIndex = destIndex + charsSkipped; + if (dest.length < endIndex) { + dest = growBuffer(dest, destIndex, endIndex); + } + + s.getChars(unescapedChunkStart, end, dest, destIndex); + destIndex = endIndex; + } + + return new String(dest, 0, destIndex); + } + + protected static int codePointAt(CharSequence seq, int index, int end) { + Preconditions.checkNotNull(seq); + if (index < end) { + char c1 = seq.charAt(index++); + if (c1 >= '\ud800' && c1 <= '\udfff') { + if (c1 <= '\udbff') { + if (index == end) { + return -c1; + } else { + char c2 = seq.charAt(index); + if (Character.isLowSurrogate(c2)) { + return Character.toCodePoint(c1, c2); + } else { + String var8 = String.valueOf(String.valueOf(seq)); + throw new IllegalArgumentException((new StringBuilder(89 + var8.length())).append("Expected low surrogate but got char '").append(c2).append("' with value ").append(c2).append(" at index ").append(index).append(" in '").append(var8).append("'").toString()); + } + } + } else { + int var6 = index - 1; + String var7 = String.valueOf(String.valueOf(seq)); + throw new IllegalArgumentException((new StringBuilder(88 + var7.length())).append("Unexpected low surrogate character '").append(c1).append("' with value ").append(c1).append(" at index ").append(var6).append(" in '").append(var7).append("'").toString()); + } + } else { + return c1; + } + } else { + throw new IndexOutOfBoundsException("Index exceeds specified range"); + } + } + + private static char[] growBuffer(char[] dest, int index, int size) { + char[] copy = new char[size]; + if (index > 0) { + System.arraycopy(dest, 0, copy, 0, index); + } + + return copy; + } +} diff --git a/src/main/com/google/common/escape/package-info.java b/src/main/com/google/common/escape/package-info.java new file mode 100644 index 0000000..2ebf952 --- /dev/null +++ b/src/main/com/google/common/escape/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.escape; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/eventbus/AllowConcurrentEvents.java b/src/main/com/google/common/eventbus/AllowConcurrentEvents.java new file mode 100644 index 0000000..640a5ac --- /dev/null +++ b/src/main/com/google/common/eventbus/AllowConcurrentEvents.java @@ -0,0 +1,13 @@ +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; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +@Beta +public @interface AllowConcurrentEvents { +} diff --git a/src/main/com/google/common/eventbus/AnnotatedSubscriberFinder.java b/src/main/com/google/common/eventbus/AnnotatedSubscriberFinder.java new file mode 100644 index 0000000..1eed716 --- /dev/null +++ b/src/main/com/google/common/eventbus/AnnotatedSubscriberFinder.java @@ -0,0 +1,121 @@ +package com.google.common.eventbus; + +import com.google.common.base.Objects; +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.Maps; +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.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +class AnnotatedSubscriberFinder implements SubscriberFindingStrategy { + private static final LoadingCache, ImmutableList> subscriberMethodsCache = CacheBuilder.newBuilder().weakKeys().build(new CacheLoader, ImmutableList>() { + public ImmutableList load(Class concreteClass) throws Exception { + return AnnotatedSubscriberFinder.getAnnotatedMethodsInternal(concreteClass); + } + }); + + public Multimap, EventSubscriber> findAllSubscribers(Object listener) { + Multimap, EventSubscriber> methodsInListener = HashMultimap.create(); + Class clazz = listener.getClass(); + Iterator i$ = getAnnotatedMethods(clazz).iterator(); + + while(i$.hasNext()) { + Method method = (Method)i$.next(); + Class[] parameterTypes = method.getParameterTypes(); + Class eventType = parameterTypes[0]; + EventSubscriber subscriber = makeSubscriber(listener, method); + methodsInListener.put(eventType, subscriber); + } + + return methodsInListener; + } + + private static ImmutableList getAnnotatedMethods(Class clazz) { + try { + return (ImmutableList)subscriberMethodsCache.getUnchecked(clazz); + } catch (UncheckedExecutionException var2) { + throw Throwables.propagate(var2.getCause()); + } + } + + private static ImmutableList getAnnotatedMethodsInternal(Class clazz) { + Set> supers = TypeToken.of(clazz).getTypes().rawTypes(); + Map identifiers = Maps.newHashMap(); + Iterator i$ = supers.iterator(); + + while(i$.hasNext()) { + Class superClazz = (Class)i$.next(); + Method[] arr$ = superClazz.getMethods(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Method superClazzMethod = arr$[i$]; + if (superClazzMethod.isAnnotationPresent(Subscribe.class) && !superClazzMethod.isBridge()) { + Class[] parameterTypes = superClazzMethod.getParameterTypes(); + if (parameterTypes.length != 1) { + String var12 = String.valueOf(String.valueOf(superClazzMethod)); + int var11 = parameterTypes.length; + throw new IllegalArgumentException((new StringBuilder(128 + var12.length())).append("Method ").append(var12).append(" has @Subscribe annotation, but requires ").append(var11).append(" arguments. Event subscriber methods must require a single argument.").toString()); + } + + AnnotatedSubscriberFinder.MethodIdentifier ident = new AnnotatedSubscriberFinder.MethodIdentifier(superClazzMethod); + if (!identifiers.containsKey(ident)) { + identifiers.put(ident, superClazzMethod); + } + } + } + } + + return ImmutableList.copyOf(identifiers.values()); + } + + private static EventSubscriber makeSubscriber(Object listener, Method method) { + Object wrapper; + if (methodIsDeclaredThreadSafe(method)) { + wrapper = new EventSubscriber(listener, method); + } else { + wrapper = new SynchronizedEventSubscriber(listener, method); + } + + return (EventSubscriber)wrapper; + } + + private static boolean methodIsDeclaredThreadSafe(Method method) { + return method.getAnnotation(AllowConcurrentEvents.class) != null; + } + + private static final class MethodIdentifier { + private final String name; + private final List> parameterTypes; + + MethodIdentifier(Method method) { + this.name = method.getName(); + this.parameterTypes = Arrays.asList(method.getParameterTypes()); + } + + public int hashCode() { + return Objects.hashCode(this.name, this.parameterTypes); + } + + public boolean equals(@Nullable Object o) { + if (!(o instanceof AnnotatedSubscriberFinder.MethodIdentifier)) { + return false; + } else { + AnnotatedSubscriberFinder.MethodIdentifier ident = (AnnotatedSubscriberFinder.MethodIdentifier)o; + return this.name.equals(ident.name) && this.parameterTypes.equals(ident.parameterTypes); + } + } + } +} diff --git a/src/main/com/google/common/eventbus/AsyncEventBus.java b/src/main/com/google/common/eventbus/AsyncEventBus.java new file mode 100644 index 0000000..1ac45c5 --- /dev/null +++ b/src/main/com/google/common/eventbus/AsyncEventBus.java @@ -0,0 +1,52 @@ +package com.google.common.eventbus; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; + +@Beta +public class AsyncEventBus extends EventBus { + private final Executor executor; + private final ConcurrentLinkedQueue eventsToDispatch = new ConcurrentLinkedQueue(); + + public AsyncEventBus(String identifier, Executor executor) { + super(identifier); + this.executor = (Executor)Preconditions.checkNotNull(executor); + } + + public AsyncEventBus(Executor executor, SubscriberExceptionHandler subscriberExceptionHandler) { + super(subscriberExceptionHandler); + this.executor = (Executor)Preconditions.checkNotNull(executor); + } + + public AsyncEventBus(Executor executor) { + super("default"); + this.executor = (Executor)Preconditions.checkNotNull(executor); + } + + void enqueueEvent(Object event, EventSubscriber subscriber) { + this.eventsToDispatch.offer(new EventBus.EventWithSubscriber(event, subscriber)); + } + + protected void dispatchQueuedEvents() { + while(true) { + EventBus.EventWithSubscriber eventWithSubscriber = (EventBus.EventWithSubscriber)this.eventsToDispatch.poll(); + if (eventWithSubscriber == null) { + return; + } + + this.dispatch(eventWithSubscriber.event, eventWithSubscriber.subscriber); + } + } + + void dispatch(final Object event, final EventSubscriber subscriber) { + Preconditions.checkNotNull(event); + Preconditions.checkNotNull(subscriber); + this.executor.execute(new Runnable() { + public void run() { + AsyncEventBus.super.dispatch(event, subscriber); + } + }); + } +} diff --git a/src/main/com/google/common/eventbus/DeadEvent.java b/src/main/com/google/common/eventbus/DeadEvent.java new file mode 100644 index 0000000..976cdd2 --- /dev/null +++ b/src/main/com/google/common/eventbus/DeadEvent.java @@ -0,0 +1,23 @@ +package com.google.common.eventbus; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +@Beta +public class DeadEvent { + private final Object source; + private final Object event; + + public DeadEvent(Object source, Object event) { + this.source = Preconditions.checkNotNull(source); + this.event = Preconditions.checkNotNull(event); + } + + public Object getSource() { + return this.source; + } + + public Object getEvent() { + return this.event; + } +} diff --git a/src/main/com/google/common/eventbus/EventBus.java b/src/main/com/google/common/eventbus/EventBus.java new file mode 100644 index 0000000..b13ac5d --- /dev/null +++ b/src/main/com/google/common/eventbus/EventBus.java @@ -0,0 +1,209 @@ +package com.google.common.eventbus; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +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.Multimap; +import com.google.common.collect.SetMultimap; +import com.google.common.reflect.TypeToken; +import com.google.common.util.concurrent.UncheckedExecutionException; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Beta +public class EventBus { + private static final LoadingCache, Set>> flattenHierarchyCache = CacheBuilder.newBuilder().weakKeys().build(new CacheLoader, Set>>() { + public Set> load(Class concreteClass) { + return TypeToken.of(concreteClass).getTypes().rawTypes(); + } + }); + private final SetMultimap, EventSubscriber> subscribersByType; + private final ReadWriteLock subscribersByTypeLock; + private final SubscriberFindingStrategy finder; + private final ThreadLocal> eventsToDispatch; + private final ThreadLocal isDispatching; + private SubscriberExceptionHandler subscriberExceptionHandler; + + public EventBus() { + this("default"); + } + + public EventBus(String identifier) { + this((SubscriberExceptionHandler)(new EventBus.LoggingSubscriberExceptionHandler(identifier))); + } + + public EventBus(SubscriberExceptionHandler subscriberExceptionHandler) { + this.subscribersByType = HashMultimap.create(); + this.subscribersByTypeLock = new ReentrantReadWriteLock(); + this.finder = new AnnotatedSubscriberFinder(); + this.eventsToDispatch = new ThreadLocal>() { + protected Queue initialValue() { + return new LinkedList(); + } + }; + this.isDispatching = new ThreadLocal() { + protected Boolean initialValue() { + return false; + } + }; + this.subscriberExceptionHandler = (SubscriberExceptionHandler)Preconditions.checkNotNull(subscriberExceptionHandler); + } + + public void register(Object object) { + Multimap, EventSubscriber> methodsInListener = this.finder.findAllSubscribers(object); + this.subscribersByTypeLock.writeLock().lock(); + + try { + this.subscribersByType.putAll(methodsInListener); + } finally { + this.subscribersByTypeLock.writeLock().unlock(); + } + + } + + public void unregister(Object object) { + Multimap, EventSubscriber> methodsInListener = this.finder.findAllSubscribers(object); + Iterator i$ = methodsInListener.asMap().entrySet().iterator(); + + while(i$.hasNext()) { + Entry, Collection> entry = (Entry)i$.next(); + Class eventType = (Class)entry.getKey(); + Collection eventMethodsInListener = (Collection)entry.getValue(); + this.subscribersByTypeLock.writeLock().lock(); + + try { + Set currentSubscribers = this.subscribersByType.get(eventType); + if (!currentSubscribers.containsAll(eventMethodsInListener)) { + String var8 = String.valueOf(String.valueOf(object)); + throw new IllegalArgumentException((new StringBuilder(65 + var8.length())).append("missing event subscriber for an annotated method. Is ").append(var8).append(" registered?").toString()); + } + + currentSubscribers.removeAll(eventMethodsInListener); + } finally { + this.subscribersByTypeLock.writeLock().unlock(); + } + } + + } + + public void post(Object event) { + Set> dispatchTypes = this.flattenHierarchy(event.getClass()); + boolean dispatched = false; + Iterator i$ = dispatchTypes.iterator(); + + while(i$.hasNext()) { + Class eventType = (Class)i$.next(); + this.subscribersByTypeLock.readLock().lock(); + + try { + Set wrappers = this.subscribersByType.get(eventType); + if (!wrappers.isEmpty()) { + dispatched = true; + Iterator i$ = wrappers.iterator(); + + while(i$.hasNext()) { + EventSubscriber wrapper = (EventSubscriber)i$.next(); + this.enqueueEvent(event, wrapper); + } + } + } finally { + this.subscribersByTypeLock.readLock().unlock(); + } + } + + if (!dispatched && !(event instanceof DeadEvent)) { + this.post(new DeadEvent(this, event)); + } + + this.dispatchQueuedEvents(); + } + + void enqueueEvent(Object event, EventSubscriber subscriber) { + ((Queue)this.eventsToDispatch.get()).offer(new EventBus.EventWithSubscriber(event, subscriber)); + } + + void dispatchQueuedEvents() { + if (!(Boolean)this.isDispatching.get()) { + this.isDispatching.set(true); + + try { + Queue events = (Queue)this.eventsToDispatch.get(); + + EventBus.EventWithSubscriber eventWithSubscriber; + while((eventWithSubscriber = (EventBus.EventWithSubscriber)events.poll()) != null) { + this.dispatch(eventWithSubscriber.event, eventWithSubscriber.subscriber); + } + } finally { + this.isDispatching.remove(); + this.eventsToDispatch.remove(); + } + + } + } + + void dispatch(Object event, EventSubscriber wrapper) { + try { + wrapper.handleEvent(event); + } catch (InvocationTargetException var6) { + InvocationTargetException e = var6; + + try { + this.subscriberExceptionHandler.handleException(e.getCause(), new SubscriberExceptionContext(this, event, wrapper.getSubscriber(), wrapper.getMethod())); + } catch (Throwable var5) { + Logger.getLogger(EventBus.class.getName()).log(Level.SEVERE, String.format("Exception %s thrown while handling exception: %s", var5, var6.getCause()), var5); + } + } + + } + + @VisibleForTesting + Set> flattenHierarchy(Class concreteClass) { + try { + return (Set)flattenHierarchyCache.getUnchecked(concreteClass); + } catch (UncheckedExecutionException var3) { + throw Throwables.propagate(var3.getCause()); + } + } + + static class EventWithSubscriber { + final Object event; + final EventSubscriber subscriber; + + public EventWithSubscriber(Object event, EventSubscriber subscriber) { + this.event = Preconditions.checkNotNull(event); + this.subscriber = (EventSubscriber)Preconditions.checkNotNull(subscriber); + } + } + + private static final class LoggingSubscriberExceptionHandler implements SubscriberExceptionHandler { + private final Logger logger; + + public LoggingSubscriberExceptionHandler(String identifier) { + String var2 = String.valueOf(String.valueOf(EventBus.class.getName())); + String var3 = String.valueOf(String.valueOf((String)Preconditions.checkNotNull(identifier))); + this.logger = Logger.getLogger((new StringBuilder(1 + var2.length() + var3.length())).append(var2).append(".").append(var3).toString()); + } + + public void handleException(Throwable exception, SubscriberExceptionContext context) { + Logger var10000 = this.logger; + Level var10001 = Level.SEVERE; + String var3 = String.valueOf(String.valueOf(context.getSubscriber())); + String var4 = String.valueOf(String.valueOf(context.getSubscriberMethod())); + var10000.log(var10001, (new StringBuilder(30 + var3.length() + var4.length())).append("Could not dispatch event: ").append(var3).append(" to ").append(var4).toString(), exception.getCause()); + } + } +} diff --git a/src/main/com/google/common/eventbus/EventSubscriber.java b/src/main/com/google/common/eventbus/EventSubscriber.java new file mode 100644 index 0000000..aea7ba7 --- /dev/null +++ b/src/main/com/google/common/eventbus/EventSubscriber.java @@ -0,0 +1,67 @@ +package com.google.common.eventbus; + +import com.google.common.base.Preconditions; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import javax.annotation.Nullable; + +class EventSubscriber { + private final Object target; + private final Method method; + + EventSubscriber(Object target, Method method) { + Preconditions.checkNotNull(target, "EventSubscriber target cannot be null."); + Preconditions.checkNotNull(method, "EventSubscriber method cannot be null."); + this.target = target; + this.method = method; + method.setAccessible(true); + } + + public void handleEvent(Object event) throws InvocationTargetException { + Preconditions.checkNotNull(event); + + String var3; + try { + this.method.invoke(this.target, event); + } catch (IllegalArgumentException var4) { + var3 = String.valueOf(String.valueOf(event)); + throw new Error((new StringBuilder(33 + var3.length())).append("Method rejected target/argument: ").append(var3).toString(), var4); + } catch (IllegalAccessException var5) { + var3 = String.valueOf(String.valueOf(event)); + throw new Error((new StringBuilder(28 + var3.length())).append("Method became inaccessible: ").append(var3).toString(), var5); + } catch (InvocationTargetException var6) { + if (var6.getCause() instanceof Error) { + throw (Error)var6.getCause(); + } else { + throw var6; + } + } + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.method)); + return (new StringBuilder(10 + var1.length())).append("[wrapper ").append(var1).append("]").toString(); + } + + public int hashCode() { + int PRIME = true; + return (31 + this.method.hashCode()) * 31 + System.identityHashCode(this.target); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof EventSubscriber)) { + return false; + } else { + EventSubscriber that = (EventSubscriber)obj; + return this.target == that.target && this.method.equals(that.method); + } + } + + public Object getSubscriber() { + return this.target; + } + + public Method getMethod() { + return this.method; + } +} diff --git a/src/main/com/google/common/eventbus/Subscribe.java b/src/main/com/google/common/eventbus/Subscribe.java new file mode 100644 index 0000000..371bca8 --- /dev/null +++ b/src/main/com/google/common/eventbus/Subscribe.java @@ -0,0 +1,13 @@ +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; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +@Beta +public @interface Subscribe { +} diff --git a/src/main/com/google/common/eventbus/SubscriberExceptionContext.java b/src/main/com/google/common/eventbus/SubscriberExceptionContext.java new file mode 100644 index 0000000..f8368ef --- /dev/null +++ b/src/main/com/google/common/eventbus/SubscriberExceptionContext.java @@ -0,0 +1,34 @@ +package com.google.common.eventbus; + +import com.google.common.base.Preconditions; +import java.lang.reflect.Method; + +public class SubscriberExceptionContext { + private final EventBus eventBus; + private final Object event; + private final Object subscriber; + private final Method subscriberMethod; + + SubscriberExceptionContext(EventBus eventBus, Object event, Object subscriber, Method subscriberMethod) { + this.eventBus = (EventBus)Preconditions.checkNotNull(eventBus); + this.event = Preconditions.checkNotNull(event); + this.subscriber = Preconditions.checkNotNull(subscriber); + this.subscriberMethod = (Method)Preconditions.checkNotNull(subscriberMethod); + } + + public EventBus getEventBus() { + return this.eventBus; + } + + public Object getEvent() { + return this.event; + } + + public Object getSubscriber() { + return this.subscriber; + } + + public Method getSubscriberMethod() { + return this.subscriberMethod; + } +} diff --git a/src/main/com/google/common/eventbus/SubscriberExceptionHandler.java b/src/main/com/google/common/eventbus/SubscriberExceptionHandler.java new file mode 100644 index 0000000..d588c6c --- /dev/null +++ b/src/main/com/google/common/eventbus/SubscriberExceptionHandler.java @@ -0,0 +1,5 @@ +package com.google.common.eventbus; + +public interface SubscriberExceptionHandler { + void handleException(Throwable var1, SubscriberExceptionContext var2); +} diff --git a/src/main/com/google/common/eventbus/SubscriberFindingStrategy.java b/src/main/com/google/common/eventbus/SubscriberFindingStrategy.java new file mode 100644 index 0000000..3c7b300 --- /dev/null +++ b/src/main/com/google/common/eventbus/SubscriberFindingStrategy.java @@ -0,0 +1,7 @@ +package com.google.common.eventbus; + +import com.google.common.collect.Multimap; + +interface SubscriberFindingStrategy { + Multimap, EventSubscriber> findAllSubscribers(Object var1); +} diff --git a/src/main/com/google/common/eventbus/SynchronizedEventSubscriber.java b/src/main/com/google/common/eventbus/SynchronizedEventSubscriber.java new file mode 100644 index 0000000..c203fa3 --- /dev/null +++ b/src/main/com/google/common/eventbus/SynchronizedEventSubscriber.java @@ -0,0 +1,16 @@ +package com.google.common.eventbus; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +final class SynchronizedEventSubscriber extends EventSubscriber { + public SynchronizedEventSubscriber(Object target, Method method) { + super(target, method); + } + + public void handleEvent(Object event) throws InvocationTargetException { + synchronized(this) { + super.handleEvent(event); + } + } +} diff --git a/src/main/com/google/common/hash/AbstractByteHasher.java b/src/main/com/google/common/hash/AbstractByteHasher.java new file mode 100644 index 0000000..52f2891 --- /dev/null +++ b/src/main/com/google/common/hash/AbstractByteHasher.java @@ -0,0 +1,78 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +abstract class AbstractByteHasher extends AbstractHasher { + private final ByteBuffer scratch; + + AbstractByteHasher() { + this.scratch = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); + } + + protected abstract void update(byte var1); + + protected void update(byte[] b) { + this.update(b, 0, b.length); + } + + protected void update(byte[] b, int off, int len) { + for(int i = off; i < off + len; ++i) { + this.update(b[i]); + } + + } + + public Hasher putByte(byte b) { + this.update(b); + return this; + } + + public Hasher putBytes(byte[] bytes) { + Preconditions.checkNotNull(bytes); + this.update(bytes); + return this; + } + + public Hasher putBytes(byte[] bytes, int off, int len) { + Preconditions.checkPositionIndexes(off, off + len, bytes.length); + this.update(bytes, off, len); + return this; + } + + private Hasher update(int bytes) { + try { + this.update(this.scratch.array(), 0, bytes); + } finally { + this.scratch.clear(); + } + + return this; + } + + public Hasher putShort(short s) { + this.scratch.putShort(s); + return this.update((int)2); + } + + public Hasher putInt(int i) { + this.scratch.putInt(i); + return this.update((int)4); + } + + public Hasher putLong(long l) { + this.scratch.putLong(l); + return this.update((int)8); + } + + public Hasher putChar(char c) { + this.scratch.putChar(c); + return this.update((int)2); + } + + public Hasher putObject(T instance, Funnel funnel) { + funnel.funnel(instance, this); + return this; + } +} diff --git a/src/main/com/google/common/hash/AbstractCompositeHashFunction.java b/src/main/com/google/common/hash/AbstractCompositeHashFunction.java new file mode 100644 index 0000000..a951e02 --- /dev/null +++ b/src/main/com/google/common/hash/AbstractCompositeHashFunction.java @@ -0,0 +1,193 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import java.nio.charset.Charset; + +abstract class AbstractCompositeHashFunction extends AbstractStreamingHashFunction { + final HashFunction[] functions; + private static final long serialVersionUID = 0L; + + AbstractCompositeHashFunction(HashFunction... functions) { + HashFunction[] arr$ = functions; + int len$ = functions.length; + + for(int i$ = 0; i$ < len$; ++i$) { + HashFunction function = arr$[i$]; + Preconditions.checkNotNull(function); + } + + this.functions = functions; + } + + abstract HashCode makeHash(Hasher[] var1); + + public Hasher newHasher() { + final Hasher[] hashers = new Hasher[this.functions.length]; + + for(int i = 0; i < hashers.length; ++i) { + hashers[i] = this.functions[i].newHasher(); + } + + return new Hasher() { + public Hasher putByte(byte b) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putByte(b); + } + + return this; + } + + public Hasher putBytes(byte[] bytes) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putBytes(bytes); + } + + return this; + } + + public Hasher putBytes(byte[] bytes, int off, int len) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putBytes(bytes, off, len); + } + + return this; + } + + public Hasher putShort(short s) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putShort(s); + } + + return this; + } + + public Hasher putInt(int i) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putInt(i); + } + + return this; + } + + public Hasher putLong(long l) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putLong(l); + } + + return this; + } + + public Hasher putFloat(float f) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putFloat(f); + } + + return this; + } + + public Hasher putDouble(double d) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putDouble(d); + } + + return this; + } + + public Hasher putBoolean(boolean b) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putBoolean(b); + } + + return this; + } + + public Hasher putChar(char c) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putChar(c); + } + + return this; + } + + public Hasher putUnencodedChars(CharSequence chars) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putUnencodedChars(chars); + } + + return this; + } + + public Hasher putString(CharSequence chars, Charset charset) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putString(chars, charset); + } + + return this; + } + + public Hasher putObject(T instance, Funnel funnel) { + Hasher[] arr$ = hashers; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + hasher.putObject(instance, funnel); + } + + return this; + } + + public HashCode hash() { + return AbstractCompositeHashFunction.this.makeHash(hashers); + } + }; + } +} diff --git a/src/main/com/google/common/hash/AbstractHasher.java b/src/main/com/google/common/hash/AbstractHasher.java new file mode 100644 index 0000000..8c67045 --- /dev/null +++ b/src/main/com/google/common/hash/AbstractHasher.java @@ -0,0 +1,31 @@ +package com.google.common.hash; + +import java.nio.charset.Charset; + +abstract class AbstractHasher implements Hasher { + public final Hasher putBoolean(boolean b) { + return this.putByte((byte)(b ? 1 : 0)); + } + + public final Hasher putDouble(double d) { + return this.putLong(Double.doubleToRawLongBits(d)); + } + + public final Hasher putFloat(float f) { + return this.putInt(Float.floatToRawIntBits(f)); + } + + public Hasher putUnencodedChars(CharSequence charSequence) { + int i = 0; + + for(int len = charSequence.length(); i < len; ++i) { + this.putChar(charSequence.charAt(i)); + } + + return this; + } + + public Hasher putString(CharSequence charSequence, Charset charset) { + return this.putBytes(charSequence.toString().getBytes(charset)); + } +} diff --git a/src/main/com/google/common/hash/AbstractNonStreamingHashFunction.java b/src/main/com/google/common/hash/AbstractNonStreamingHashFunction.java new file mode 100644 index 0000000..79060eb --- /dev/null +++ b/src/main/com/google/common/hash/AbstractNonStreamingHashFunction.java @@ -0,0 +1,127 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +abstract class AbstractNonStreamingHashFunction implements HashFunction { + public Hasher newHasher() { + return new AbstractNonStreamingHashFunction.BufferingHasher(32); + } + + public Hasher newHasher(int expectedInputSize) { + Preconditions.checkArgument(expectedInputSize >= 0); + return new AbstractNonStreamingHashFunction.BufferingHasher(expectedInputSize); + } + + public HashCode hashObject(T instance, Funnel funnel) { + return this.newHasher().putObject(instance, funnel).hash(); + } + + public HashCode hashUnencodedChars(CharSequence input) { + int len = input.length(); + Hasher hasher = this.newHasher(len * 2); + + for(int i = 0; i < len; ++i) { + hasher.putChar(input.charAt(i)); + } + + return hasher.hash(); + } + + public HashCode hashString(CharSequence input, Charset charset) { + return this.hashBytes(input.toString().getBytes(charset)); + } + + public HashCode hashInt(int input) { + return this.newHasher(4).putInt(input).hash(); + } + + public HashCode hashLong(long input) { + return this.newHasher(8).putLong(input).hash(); + } + + public HashCode hashBytes(byte[] input) { + return this.hashBytes(input, 0, input.length); + } + + private static final class ExposedByteArrayOutputStream extends ByteArrayOutputStream { + ExposedByteArrayOutputStream(int expectedInputSize) { + super(expectedInputSize); + } + + byte[] byteArray() { + return this.buf; + } + + int length() { + return this.count; + } + } + + private final class BufferingHasher extends AbstractHasher { + final AbstractNonStreamingHashFunction.ExposedByteArrayOutputStream stream; + static final int BOTTOM_BYTE = 255; + + BufferingHasher(int expectedInputSize) { + this.stream = new AbstractNonStreamingHashFunction.ExposedByteArrayOutputStream(expectedInputSize); + } + + public Hasher putByte(byte b) { + this.stream.write(b); + return this; + } + + public Hasher putBytes(byte[] bytes) { + try { + this.stream.write(bytes); + return this; + } catch (IOException var3) { + throw new RuntimeException(var3); + } + } + + public Hasher putBytes(byte[] bytes, int off, int len) { + this.stream.write(bytes, off, len); + return this; + } + + public Hasher putShort(short s) { + this.stream.write(s & 255); + this.stream.write(s >>> 8 & 255); + return this; + } + + public Hasher putInt(int i) { + this.stream.write(i & 255); + this.stream.write(i >>> 8 & 255); + this.stream.write(i >>> 16 & 255); + this.stream.write(i >>> 24 & 255); + return this; + } + + public Hasher putLong(long l) { + for(int i = 0; i < 64; i += 8) { + this.stream.write((byte)((int)(l >>> i & 255L))); + } + + return this; + } + + public Hasher putChar(char c) { + this.stream.write(c & 255); + this.stream.write(c >>> 8 & 255); + return this; + } + + public Hasher putObject(T instance, Funnel funnel) { + funnel.funnel(instance, this); + return this; + } + + public HashCode hash() { + return AbstractNonStreamingHashFunction.this.hashBytes(this.stream.byteArray(), 0, this.stream.length()); + } + } +} diff --git a/src/main/com/google/common/hash/AbstractStreamingHashFunction.java b/src/main/com/google/common/hash/AbstractStreamingHashFunction.java new file mode 100644 index 0000000..7532194 --- /dev/null +++ b/src/main/com/google/common/hash/AbstractStreamingHashFunction.java @@ -0,0 +1,176 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +abstract class AbstractStreamingHashFunction implements HashFunction { + public HashCode hashObject(T instance, Funnel funnel) { + return this.newHasher().putObject(instance, funnel).hash(); + } + + public HashCode hashUnencodedChars(CharSequence input) { + return this.newHasher().putUnencodedChars(input).hash(); + } + + public HashCode hashString(CharSequence input, Charset charset) { + return this.newHasher().putString(input, charset).hash(); + } + + public HashCode hashInt(int input) { + return this.newHasher().putInt(input).hash(); + } + + public HashCode hashLong(long input) { + return this.newHasher().putLong(input).hash(); + } + + public HashCode hashBytes(byte[] input) { + return this.newHasher().putBytes(input).hash(); + } + + public HashCode hashBytes(byte[] input, int off, int len) { + return this.newHasher().putBytes(input, off, len).hash(); + } + + public Hasher newHasher(int expectedInputSize) { + Preconditions.checkArgument(expectedInputSize >= 0); + return this.newHasher(); + } + + protected abstract static class AbstractStreamingHasher extends AbstractHasher { + private final ByteBuffer buffer; + private final int bufferSize; + private final int chunkSize; + + protected AbstractStreamingHasher(int chunkSize) { + this(chunkSize, chunkSize); + } + + protected AbstractStreamingHasher(int chunkSize, int bufferSize) { + Preconditions.checkArgument(bufferSize % chunkSize == 0); + this.buffer = ByteBuffer.allocate(bufferSize + 7).order(ByteOrder.LITTLE_ENDIAN); + this.bufferSize = bufferSize; + this.chunkSize = chunkSize; + } + + protected abstract void process(ByteBuffer var1); + + protected void processRemaining(ByteBuffer bb) { + bb.position(bb.limit()); + bb.limit(this.chunkSize + 7); + + while(bb.position() < this.chunkSize) { + bb.putLong(0L); + } + + bb.limit(this.chunkSize); + bb.flip(); + this.process(bb); + } + + public final Hasher putBytes(byte[] bytes) { + return this.putBytes(bytes, 0, bytes.length); + } + + public final Hasher putBytes(byte[] bytes, int off, int len) { + return this.putBytes(ByteBuffer.wrap(bytes, off, len).order(ByteOrder.LITTLE_ENDIAN)); + } + + private Hasher putBytes(ByteBuffer readBuffer) { + if (readBuffer.remaining() <= this.buffer.remaining()) { + this.buffer.put(readBuffer); + this.munchIfFull(); + return this; + } else { + int bytesToCopy = this.bufferSize - this.buffer.position(); + + for(int i = 0; i < bytesToCopy; ++i) { + this.buffer.put(readBuffer.get()); + } + + this.munch(); + + while(readBuffer.remaining() >= this.chunkSize) { + this.process(readBuffer); + } + + this.buffer.put(readBuffer); + return this; + } + } + + public final Hasher putUnencodedChars(CharSequence charSequence) { + for(int i = 0; i < charSequence.length(); ++i) { + this.putChar(charSequence.charAt(i)); + } + + return this; + } + + public final Hasher putByte(byte b) { + this.buffer.put(b); + this.munchIfFull(); + return this; + } + + public final Hasher putShort(short s) { + this.buffer.putShort(s); + this.munchIfFull(); + return this; + } + + public final Hasher putChar(char c) { + this.buffer.putChar(c); + this.munchIfFull(); + return this; + } + + public final Hasher putInt(int i) { + this.buffer.putInt(i); + this.munchIfFull(); + return this; + } + + public final Hasher putLong(long l) { + this.buffer.putLong(l); + this.munchIfFull(); + return this; + } + + public final Hasher putObject(T instance, Funnel funnel) { + funnel.funnel(instance, this); + return this; + } + + public final HashCode hash() { + this.munch(); + this.buffer.flip(); + if (this.buffer.remaining() > 0) { + this.processRemaining(this.buffer); + } + + return this.makeHash(); + } + + abstract HashCode makeHash(); + + private void munchIfFull() { + if (this.buffer.remaining() < 8) { + this.munch(); + } + + } + + private void munch() { + this.buffer.flip(); + + while(this.buffer.remaining() >= this.chunkSize) { + this.process(this.buffer); + } + + this.buffer.compact(); + } + } +} diff --git a/src/main/com/google/common/hash/BloomFilter.java b/src/main/com/google/common/hash/BloomFilter.java new file mode 100644 index 0000000..6821562 --- /dev/null +++ b/src/main/com/google/common/hash/BloomFilter.java @@ -0,0 +1,217 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.primitives.SignedBytes; +import com.google.common.primitives.UnsignedBytes; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import javax.annotation.Nullable; + +@Beta +public final class BloomFilter implements Predicate, Serializable { + private final BloomFilterStrategies.BitArray bits; + private final int numHashFunctions; + private final Funnel funnel; + private final BloomFilter.Strategy strategy; + private static final BloomFilter.Strategy DEFAULT_STRATEGY; + + private BloomFilter(BloomFilterStrategies.BitArray bits, int numHashFunctions, Funnel funnel, BloomFilter.Strategy strategy) { + Preconditions.checkArgument(numHashFunctions > 0, "numHashFunctions (%s) must be > 0", numHashFunctions); + Preconditions.checkArgument(numHashFunctions <= 255, "numHashFunctions (%s) must be <= 255", numHashFunctions); + this.bits = (BloomFilterStrategies.BitArray)Preconditions.checkNotNull(bits); + this.numHashFunctions = numHashFunctions; + this.funnel = (Funnel)Preconditions.checkNotNull(funnel); + this.strategy = (BloomFilter.Strategy)Preconditions.checkNotNull(strategy); + } + + public BloomFilter copy() { + return new BloomFilter(this.bits.copy(), this.numHashFunctions, this.funnel, this.strategy); + } + + public boolean mightContain(T object) { + return this.strategy.mightContain(object, this.funnel, this.numHashFunctions, this.bits); + } + + /** @deprecated */ + @Deprecated + public boolean apply(T input) { + return this.mightContain(input); + } + + public boolean put(T object) { + return this.strategy.put(object, this.funnel, this.numHashFunctions, this.bits); + } + + public double expectedFpp() { + return Math.pow((double)this.bits.bitCount() / (double)this.bitSize(), (double)this.numHashFunctions); + } + + @VisibleForTesting + long bitSize() { + return this.bits.bitSize(); + } + + public boolean isCompatible(BloomFilter that) { + Preconditions.checkNotNull(that); + return this != that && this.numHashFunctions == that.numHashFunctions && this.bitSize() == that.bitSize() && this.strategy.equals(that.strategy) && this.funnel.equals(that.funnel); + } + + public void putAll(BloomFilter that) { + Preconditions.checkNotNull(that); + Preconditions.checkArgument(this != that, "Cannot combine a BloomFilter with itself."); + Preconditions.checkArgument(this.numHashFunctions == that.numHashFunctions, "BloomFilters must have the same number of hash functions (%s != %s)", this.numHashFunctions, that.numHashFunctions); + Preconditions.checkArgument(this.bitSize() == that.bitSize(), "BloomFilters must have the same size underlying bit arrays (%s != %s)", this.bitSize(), that.bitSize()); + Preconditions.checkArgument(this.strategy.equals(that.strategy), "BloomFilters must have equal strategies (%s != %s)", this.strategy, that.strategy); + Preconditions.checkArgument(this.funnel.equals(that.funnel), "BloomFilters must have equal funnels (%s != %s)", this.funnel, that.funnel); + this.bits.putAll(that.bits); + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (!(object instanceof BloomFilter)) { + return false; + } else { + BloomFilter that = (BloomFilter)object; + return this.numHashFunctions == that.numHashFunctions && this.funnel.equals(that.funnel) && this.bits.equals(that.bits) && this.strategy.equals(that.strategy); + } + } + + public int hashCode() { + return Objects.hashCode(this.numHashFunctions, this.funnel, this.strategy, this.bits); + } + + public static BloomFilter create(Funnel funnel, int expectedInsertions, double fpp) { + return create(funnel, expectedInsertions, fpp, DEFAULT_STRATEGY); + } + + @VisibleForTesting + static BloomFilter create(Funnel funnel, int expectedInsertions, double fpp, BloomFilter.Strategy strategy) { + Preconditions.checkNotNull(funnel); + Preconditions.checkArgument(expectedInsertions >= 0, "Expected insertions (%s) must be >= 0", expectedInsertions); + Preconditions.checkArgument(fpp > 0.0D, "False positive probability (%s) must be > 0.0", fpp); + Preconditions.checkArgument(fpp < 1.0D, "False positive probability (%s) must be < 1.0", fpp); + Preconditions.checkNotNull(strategy); + if (expectedInsertions == 0) { + expectedInsertions = 1; + } + + long numBits = optimalNumOfBits((long)expectedInsertions, fpp); + int numHashFunctions = optimalNumOfHashFunctions((long)expectedInsertions, numBits); + + try { + return new BloomFilter(new BloomFilterStrategies.BitArray(numBits), numHashFunctions, funnel, strategy); + } catch (IllegalArgumentException var11) { + throw new IllegalArgumentException((new StringBuilder(57)).append("Could not create BloomFilter of ").append(numBits).append(" bits").toString(), var11); + } + } + + public static BloomFilter create(Funnel funnel, int expectedInsertions) { + return create(funnel, expectedInsertions, 0.03D); + } + + @VisibleForTesting + static int optimalNumOfHashFunctions(long n, long m) { + return Math.max(1, (int)Math.round((double)m / (double)n * Math.log(2.0D))); + } + + @VisibleForTesting + static long optimalNumOfBits(long n, double p) { + if (p == 0.0D) { + p = Double.MIN_VALUE; + } + + return (long)((double)(-n) * Math.log(p) / (Math.log(2.0D) * Math.log(2.0D))); + } + + private Object writeReplace() { + return new BloomFilter.SerialForm(this); + } + + public void writeTo(OutputStream out) throws IOException { + DataOutputStream dout = new DataOutputStream(out); + dout.writeByte(SignedBytes.checkedCast((long)this.strategy.ordinal())); + dout.writeByte(UnsignedBytes.checkedCast((long)this.numHashFunctions)); + dout.writeInt(this.bits.data.length); + long[] arr$ = this.bits.data; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + long value = arr$[i$]; + dout.writeLong(value); + } + + } + + public static BloomFilter readFrom(InputStream in, Funnel funnel) throws IOException { + Preconditions.checkNotNull(in, "InputStream"); + Preconditions.checkNotNull(funnel, "Funnel"); + int strategyOrdinal = -1; + int numHashFunctions = -1; + byte dataLength = -1; + + try { + DataInputStream din = new DataInputStream(in); + strategyOrdinal = din.readByte(); + int numHashFunctions = UnsignedBytes.toInt(din.readByte()); + int dataLength = din.readInt(); + BloomFilter.Strategy strategy = BloomFilterStrategies.values()[strategyOrdinal]; + long[] data = new long[dataLength]; + + for(int i = 0; i < data.length; ++i) { + data[i] = din.readLong(); + } + + return new BloomFilter(new BloomFilterStrategies.BitArray(data), numHashFunctions, funnel, strategy); + } catch (RuntimeException var11) { + String var7 = String.valueOf(String.valueOf("Unable to deserialize BloomFilter from InputStream. strategyOrdinal: ")); + IOException ioException = new IOException((new StringBuilder(65 + var7.length())).append(var7).append(strategyOrdinal).append(" numHashFunctions: ").append(numHashFunctions).append(" dataLength: ").append(dataLength).toString()); + ioException.initCause(var11); + throw ioException; + } + } + + // $FF: synthetic method + BloomFilter(BloomFilterStrategies.BitArray x0, int x1, Funnel x2, BloomFilter.Strategy x3, Object x4) { + this(x0, x1, x2, x3); + } + + static { + DEFAULT_STRATEGY = BloomFilterStrategies.MURMUR128_MITZ_64; + } + + private static class SerialForm implements Serializable { + final long[] data; + final int numHashFunctions; + final Funnel funnel; + final BloomFilter.Strategy strategy; + private static final long serialVersionUID = 1L; + + SerialForm(BloomFilter bf) { + this.data = bf.bits.data; + this.numHashFunctions = bf.numHashFunctions; + this.funnel = bf.funnel; + this.strategy = bf.strategy; + } + + Object readResolve() { + return new BloomFilter(new BloomFilterStrategies.BitArray(this.data), this.numHashFunctions, this.funnel, this.strategy); + } + } + + interface Strategy extends Serializable { + boolean put(T var1, Funnel var2, int var3, BloomFilterStrategies.BitArray var4); + + boolean mightContain(T var1, Funnel var2, int var3, BloomFilterStrategies.BitArray var4); + + int ordinal(); + } +} diff --git a/src/main/com/google/common/hash/BloomFilterStrategies.java b/src/main/com/google/common/hash/BloomFilterStrategies.java new file mode 100644 index 0000000..025e762 --- /dev/null +++ b/src/main/com/google/common/hash/BloomFilterStrategies.java @@ -0,0 +1,178 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import com.google.common.math.LongMath; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import java.math.RoundingMode; +import java.util.Arrays; + +enum BloomFilterStrategies implements BloomFilter.Strategy { + MURMUR128_MITZ_32 { + public boolean put(T object, Funnel funnel, int numHashFunctions, BloomFilterStrategies.BitArray bits) { + long bitSize = bits.bitSize(); + long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); + int hash1 = (int)hash64; + int hash2 = (int)(hash64 >>> 32); + boolean bitsChanged = false; + + for(int i = 1; i <= numHashFunctions; ++i) { + int combinedHash = hash1 + i * hash2; + if (combinedHash < 0) { + combinedHash = ~combinedHash; + } + + bitsChanged |= bits.set((long)combinedHash % bitSize); + } + + return bitsChanged; + } + + public boolean mightContain(T object, Funnel funnel, int numHashFunctions, BloomFilterStrategies.BitArray bits) { + long bitSize = bits.bitSize(); + long hash64 = Hashing.murmur3_128().hashObject(object, funnel).asLong(); + int hash1 = (int)hash64; + int hash2 = (int)(hash64 >>> 32); + + for(int i = 1; i <= numHashFunctions; ++i) { + int combinedHash = hash1 + i * hash2; + if (combinedHash < 0) { + combinedHash = ~combinedHash; + } + + if (!bits.get((long)combinedHash % bitSize)) { + return false; + } + } + + return true; + } + }, + MURMUR128_MITZ_64 { + public boolean put(T object, Funnel funnel, int numHashFunctions, BloomFilterStrategies.BitArray bits) { + long bitSize = bits.bitSize(); + byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal(); + long hash1 = this.lowerEight(bytes); + long hash2 = this.upperEight(bytes); + boolean bitsChanged = false; + long combinedHash = hash1; + + for(int i = 0; i < numHashFunctions; ++i) { + bitsChanged |= bits.set((combinedHash & Long.MAX_VALUE) % bitSize); + combinedHash += hash2; + } + + return bitsChanged; + } + + public boolean mightContain(T object, Funnel funnel, int numHashFunctions, BloomFilterStrategies.BitArray bits) { + long bitSize = bits.bitSize(); + byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal(); + long hash1 = this.lowerEight(bytes); + long hash2 = this.upperEight(bytes); + long combinedHash = hash1; + + for(int i = 0; i < numHashFunctions; ++i) { + if (!bits.get((combinedHash & Long.MAX_VALUE) % bitSize)) { + return false; + } + + combinedHash += hash2; + } + + return true; + } + + private long lowerEight(byte[] bytes) { + return Longs.fromBytes(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]); + } + + private long upperEight(byte[] bytes) { + return Longs.fromBytes(bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8]); + } + }; + + private BloomFilterStrategies() { + } + + // $FF: synthetic method + BloomFilterStrategies(Object x2) { + this(); + } + + static final class BitArray { + final long[] data; + long bitCount; + + BitArray(long bits) { + this(new long[Ints.checkedCast(LongMath.divide(bits, 64L, RoundingMode.CEILING))]); + } + + BitArray(long[] data) { + Preconditions.checkArgument(data.length > 0, "data length is zero!"); + this.data = data; + long bitCount = 0L; + long[] arr$ = data; + int len$ = data.length; + + for(int i$ = 0; i$ < len$; ++i$) { + long value = arr$[i$]; + bitCount += (long)Long.bitCount(value); + } + + this.bitCount = bitCount; + } + + boolean set(long index) { + if (!this.get(index)) { + long[] var10000 = this.data; + var10000[(int)(index >>> 6)] |= 1L << (int)index; + ++this.bitCount; + return true; + } else { + return false; + } + } + + boolean get(long index) { + return (this.data[(int)(index >>> 6)] & 1L << (int)index) != 0L; + } + + long bitSize() { + return (long)this.data.length * 64L; + } + + long bitCount() { + return this.bitCount; + } + + BloomFilterStrategies.BitArray copy() { + return new BloomFilterStrategies.BitArray((long[])this.data.clone()); + } + + void putAll(BloomFilterStrategies.BitArray array) { + Preconditions.checkArgument(this.data.length == array.data.length, "BitArrays must be of equal length (%s != %s)", this.data.length, array.data.length); + this.bitCount = 0L; + + for(int i = 0; i < this.data.length; ++i) { + long[] var10000 = this.data; + var10000[i] |= array.data[i]; + this.bitCount += (long)Long.bitCount(this.data[i]); + } + + } + + public boolean equals(Object o) { + if (o instanceof BloomFilterStrategies.BitArray) { + BloomFilterStrategies.BitArray bitArray = (BloomFilterStrategies.BitArray)o; + return Arrays.equals(this.data, bitArray.data); + } else { + return false; + } + } + + public int hashCode() { + return Arrays.hashCode(this.data); + } + } +} diff --git a/src/main/com/google/common/hash/ChecksumHashFunction.java b/src/main/com/google/common/hash/ChecksumHashFunction.java new file mode 100644 index 0000000..abc132f --- /dev/null +++ b/src/main/com/google/common/hash/ChecksumHashFunction.java @@ -0,0 +1,58 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import java.io.Serializable; +import java.util.zip.Checksum; + +final class ChecksumHashFunction extends AbstractStreamingHashFunction implements Serializable { + private final Supplier checksumSupplier; + private final int bits; + private final String toString; + private static final long serialVersionUID = 0L; + + ChecksumHashFunction(Supplier checksumSupplier, int bits, String toString) { + this.checksumSupplier = (Supplier)Preconditions.checkNotNull(checksumSupplier); + Preconditions.checkArgument(bits == 32 || bits == 64, "bits (%s) must be either 32 or 64", bits); + this.bits = bits; + this.toString = (String)Preconditions.checkNotNull(toString); + } + + public int bits() { + return this.bits; + } + + public Hasher newHasher() { + return new ChecksumHashFunction.ChecksumHasher((Checksum)this.checksumSupplier.get()); + } + + public String toString() { + return this.toString; + } + + private final class ChecksumHasher extends AbstractByteHasher { + private final Checksum checksum; + + private ChecksumHasher(Checksum checksum) { + this.checksum = (Checksum)Preconditions.checkNotNull(checksum); + } + + protected void update(byte b) { + this.checksum.update(b); + } + + protected void update(byte[] bytes, int off, int len) { + this.checksum.update(bytes, off, len); + } + + public HashCode hash() { + long value = this.checksum.getValue(); + return ChecksumHashFunction.this.bits == 32 ? HashCode.fromInt((int)value) : HashCode.fromLong(value); + } + + // $FF: synthetic method + ChecksumHasher(Checksum x1, Object x2) { + this(x1); + } + } +} diff --git a/src/main/com/google/common/hash/Crc32cHashFunction.java b/src/main/com/google/common/hash/Crc32cHashFunction.java new file mode 100644 index 0000000..5e68d18 --- /dev/null +++ b/src/main/com/google/common/hash/Crc32cHashFunction.java @@ -0,0 +1,29 @@ +package com.google.common.hash; + +final class Crc32cHashFunction extends AbstractStreamingHashFunction { + public int bits() { + return 32; + } + + public Hasher newHasher() { + return new Crc32cHashFunction.Crc32cHasher(); + } + + public String toString() { + return "Hashing.crc32c()"; + } + + static final class Crc32cHasher extends AbstractByteHasher { + static final int[] CRC_TABLE = new int[]{0, -227835133, -516198153, 324072436, -946170081, 904991772, 648144872, -724933397, -1965467441, 2024987596, 1809983544, -1719030981, 1296289744, -1087877933, -1401372889, 1578318884, 274646895, -499825556, -244992104, 51262619, -675000208, 632279923, 922689671, -996891772, -1702387808, 1760304291, 2075979607, -1982370732, 1562183871, -1351185476, -1138329528, 1313733451, 549293790, -757723683, -1048117719, 871202090, -416867903, 357341890, 102525238, -193467851, -1436232175, 1477399826, 1264559846, -1187764763, 1845379342, -1617575411, -1933233671, 2125378298, 820201905, -1031222606, -774358714, 598981189, -143008082, 85089709, 373468761, -467063462, -1170599554, 1213305469, 1526817161, -1452612982, 2107672161, -1882520222, -1667500394, 1861252501, 1098587580, -1290756417, -1606390453, 1378610760, -2032039261, 1955203488, 1742404180, -1783531177, -878557837, 969524848, 714683780, -655182201, 205050476, -28094097, -318528869, 526918040, 1361435347, -1555146288, -1340167644, 1114974503, -1765847604, 1691668175, 2005155131, -2047885768, -604208612, 697762079, 986182379, -928222744, 476452099, -301099520, -44210700, 255256311, 1640403810, -1817374623, -2130844779, 1922457750, -1503918979, 1412925310, 1197962378, -1257441399, -350237779, 427051182, 170179418, -129025959, 746937522, -554770511, -843174843, 1070968646, 1905808397, -2081171698, -1868356358, 1657317369, -1241332974, 1147748369, 1463399397, -1521340186, -79622974, 153784257, 444234805, -401473738, 1021025245, -827320098, -572462294, 797665321, -2097792136, 1889384571, 1674398607, -1851340660, 1164749927, -1224265884, -1537745776, 1446797203, 137323447, -96149324, -384560320, 461344835, -810158936, 1037989803, 781091935, -588970148, -1834419177, 1623424788, 1939049696, -2114449437, 1429367560, -1487280117, -1274471425, 1180866812, 410100952, -367384613, -112536529, 186734380, -538233913, 763408580, 1053836080, -860110797, -1572096602, 1344288421, 1131464017, -1323612590, 1708204729, -1749376582, -2065018290, 1988219213, 680717673, -621187478, -911630946, 1002577565, -284657034, 493091189, 238226049, -61306494, -1307217207, 1082061258, 1395524158, -1589280451, 1972364758, -2015074603, -1800104671, 1725896226, 952904198, -894981883, -638100751, 731699698, -11092711, 222117402, 510512622, -335130899, -1014159676, 837199303, 582374963, -790768336, 68661723, -159632680, -450051796, 390545967, 1230274059, -1153434360, -1469116676, 1510247935, -1899042540, 2091215383, 1878366691, -1650582816, -741088853, 565732008, 854102364, -1065151905, 340358836, -433916489, -177076669, 119113024, 1493875044, -1419691417, -1204696685, 1247431312, -1634718085, 1828433272, 2141937292, -1916740209, -483350502, 291187481, 34330861, -262120466, 615137029, -691946490, -980332558, 939183345, 1776939221, -1685949482, -1999470558, 2058945313, -1368168502, 1545135305, 1330124605, -1121741762, -210866315, 17165430, 307568514, -532767615, 888469610, -962626711, -707819363, 665062302, 2042050490, -1948470087, -1735637171, 1793573966, -1104306011, 1279665062, 1595330642, -1384295599}; + private int crc = 0; + + public void update(byte b) { + this.crc = ~this.crc; + this.crc = ~(this.crc >>> 8 ^ CRC_TABLE[(this.crc ^ b) & 255]); + } + + public HashCode hash() { + return HashCode.fromInt(this.crc); + } + } +} diff --git a/src/main/com/google/common/hash/Funnel.java b/src/main/com/google/common/hash/Funnel.java new file mode 100644 index 0000000..02483de --- /dev/null +++ b/src/main/com/google/common/hash/Funnel.java @@ -0,0 +1,9 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import java.io.Serializable; + +@Beta +public interface Funnel extends Serializable { + void funnel(T var1, PrimitiveSink var2); +} diff --git a/src/main/com/google/common/hash/Funnels.java b/src/main/com/google/common/hash/Funnels.java new file mode 100644 index 0000000..b61a169 --- /dev/null +++ b/src/main/com/google/common/hash/Funnels.java @@ -0,0 +1,199 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.OutputStream; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.util.Iterator; +import javax.annotation.Nullable; + +@Beta +public final class Funnels { + private Funnels() { + } + + public static Funnel byteArrayFunnel() { + return Funnels.ByteArrayFunnel.INSTANCE; + } + + public static Funnel unencodedCharsFunnel() { + return Funnels.UnencodedCharsFunnel.INSTANCE; + } + + public static Funnel stringFunnel(Charset charset) { + return new Funnels.StringCharsetFunnel(charset); + } + + public static Funnel integerFunnel() { + return Funnels.IntegerFunnel.INSTANCE; + } + + public static Funnel> sequentialFunnel(Funnel elementFunnel) { + return new Funnels.SequentialFunnel(elementFunnel); + } + + public static Funnel longFunnel() { + return Funnels.LongFunnel.INSTANCE; + } + + public static OutputStream asOutputStream(PrimitiveSink sink) { + return new Funnels.SinkAsStream(sink); + } + + private static class SinkAsStream extends OutputStream { + final PrimitiveSink sink; + + SinkAsStream(PrimitiveSink sink) { + this.sink = (PrimitiveSink)Preconditions.checkNotNull(sink); + } + + public void write(int b) { + this.sink.putByte((byte)b); + } + + public void write(byte[] bytes) { + this.sink.putBytes(bytes); + } + + public void write(byte[] bytes, int off, int len) { + this.sink.putBytes(bytes, off, len); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.sink)); + return (new StringBuilder(24 + var1.length())).append("Funnels.asOutputStream(").append(var1).append(")").toString(); + } + } + + private static enum LongFunnel implements Funnel { + INSTANCE; + + public void funnel(Long from, PrimitiveSink into) { + into.putLong(from); + } + + public String toString() { + return "Funnels.longFunnel()"; + } + } + + private static class SequentialFunnel implements Funnel>, Serializable { + private final Funnel elementFunnel; + + SequentialFunnel(Funnel elementFunnel) { + this.elementFunnel = (Funnel)Preconditions.checkNotNull(elementFunnel); + } + + public void funnel(Iterable from, PrimitiveSink into) { + Iterator i$ = from.iterator(); + + while(i$.hasNext()) { + E e = i$.next(); + this.elementFunnel.funnel(e, into); + } + + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.elementFunnel)); + return (new StringBuilder(26 + var1.length())).append("Funnels.sequentialFunnel(").append(var1).append(")").toString(); + } + + public boolean equals(@Nullable Object o) { + if (o instanceof Funnels.SequentialFunnel) { + Funnels.SequentialFunnel funnel = (Funnels.SequentialFunnel)o; + return this.elementFunnel.equals(funnel.elementFunnel); + } else { + return false; + } + } + + public int hashCode() { + return Funnels.SequentialFunnel.class.hashCode() ^ this.elementFunnel.hashCode(); + } + } + + private static enum IntegerFunnel implements Funnel { + INSTANCE; + + public void funnel(Integer from, PrimitiveSink into) { + into.putInt(from); + } + + public String toString() { + return "Funnels.integerFunnel()"; + } + } + + private static class StringCharsetFunnel implements Funnel, Serializable { + private final Charset charset; + + StringCharsetFunnel(Charset charset) { + this.charset = (Charset)Preconditions.checkNotNull(charset); + } + + public void funnel(CharSequence from, PrimitiveSink into) { + into.putString(from, this.charset); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.charset.name())); + return (new StringBuilder(22 + var1.length())).append("Funnels.stringFunnel(").append(var1).append(")").toString(); + } + + public boolean equals(@Nullable Object o) { + if (o instanceof Funnels.StringCharsetFunnel) { + Funnels.StringCharsetFunnel funnel = (Funnels.StringCharsetFunnel)o; + return this.charset.equals(funnel.charset); + } else { + return false; + } + } + + public int hashCode() { + return Funnels.StringCharsetFunnel.class.hashCode() ^ this.charset.hashCode(); + } + + Object writeReplace() { + return new Funnels.StringCharsetFunnel.SerializedForm(this.charset); + } + + private static class SerializedForm implements Serializable { + private final String charsetCanonicalName; + private static final long serialVersionUID = 0L; + + SerializedForm(Charset charset) { + this.charsetCanonicalName = charset.name(); + } + + private Object readResolve() { + return Funnels.stringFunnel(Charset.forName(this.charsetCanonicalName)); + } + } + } + + private static enum UnencodedCharsFunnel implements Funnel { + INSTANCE; + + public void funnel(CharSequence from, PrimitiveSink into) { + into.putUnencodedChars(from); + } + + public String toString() { + return "Funnels.unencodedCharsFunnel()"; + } + } + + private static enum ByteArrayFunnel implements Funnel { + INSTANCE; + + public void funnel(byte[] from, PrimitiveSink into) { + into.putBytes(from); + } + + public String toString() { + return "Funnels.byteArrayFunnel()"; + } + } +} diff --git a/src/main/com/google/common/hash/HashCode.java b/src/main/com/google/common/hash/HashCode.java new file mode 100644 index 0000000..22fd82b --- /dev/null +++ b/src/main/com/google/common/hash/HashCode.java @@ -0,0 +1,250 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; +import com.google.common.primitives.UnsignedInts; +import java.io.Serializable; +import java.security.MessageDigest; +import javax.annotation.Nullable; + +@Beta +public abstract class HashCode { + private static final char[] hexDigits = "0123456789abcdef".toCharArray(); + + HashCode() { + } + + public abstract int bits(); + + public abstract int asInt(); + + public abstract long asLong(); + + public abstract long padToLong(); + + public abstract byte[] asBytes(); + + public int writeBytesTo(byte[] dest, int offset, int maxLength) { + maxLength = Ints.min(maxLength, this.bits() / 8); + Preconditions.checkPositionIndexes(offset, offset + maxLength, dest.length); + this.writeBytesToImpl(dest, offset, maxLength); + return maxLength; + } + + abstract void writeBytesToImpl(byte[] var1, int var2, int var3); + + byte[] getBytesInternal() { + return this.asBytes(); + } + + abstract boolean equalsSameBits(HashCode var1); + + public static HashCode fromInt(int hash) { + return new HashCode.IntHashCode(hash); + } + + public static HashCode fromLong(long hash) { + return new HashCode.LongHashCode(hash); + } + + public static HashCode fromBytes(byte[] bytes) { + Preconditions.checkArgument(bytes.length >= 1, "A HashCode must contain at least 1 byte."); + return fromBytesNoCopy((byte[])bytes.clone()); + } + + static HashCode fromBytesNoCopy(byte[] bytes) { + return new HashCode.BytesHashCode(bytes); + } + + public static HashCode fromString(String string) { + Preconditions.checkArgument(string.length() >= 2, "input string (%s) must have at least 2 characters", string); + Preconditions.checkArgument(string.length() % 2 == 0, "input string (%s) must have an even number of characters", string); + byte[] bytes = new byte[string.length() / 2]; + + for(int i = 0; i < string.length(); i += 2) { + int ch1 = decode(string.charAt(i)) << 4; + int ch2 = decode(string.charAt(i + 1)); + bytes[i / 2] = (byte)(ch1 + ch2); + } + + return fromBytesNoCopy(bytes); + } + + private static int decode(char ch) { + if (ch >= '0' && ch <= '9') { + return ch - 48; + } else if (ch >= 'a' && ch <= 'f') { + return ch - 97 + 10; + } else { + throw new IllegalArgumentException((new StringBuilder(32)).append("Illegal hexadecimal character: ").append(ch).toString()); + } + } + + public final boolean equals(@Nullable Object object) { + if (!(object instanceof HashCode)) { + return false; + } else { + HashCode that = (HashCode)object; + return this.bits() == that.bits() && this.equalsSameBits(that); + } + } + + public final int hashCode() { + if (this.bits() >= 32) { + return this.asInt(); + } else { + byte[] bytes = this.asBytes(); + int val = bytes[0] & 255; + + for(int i = 1; i < bytes.length; ++i) { + val |= (bytes[i] & 255) << i * 8; + } + + return val; + } + } + + public final String toString() { + byte[] bytes = this.asBytes(); + StringBuilder sb = new StringBuilder(2 * bytes.length); + byte[] arr$ = bytes; + int len$ = bytes.length; + + for(int i$ = 0; i$ < len$; ++i$) { + byte b = arr$[i$]; + sb.append(hexDigits[b >> 4 & 15]).append(hexDigits[b & 15]); + } + + return sb.toString(); + } + + private static final class BytesHashCode extends HashCode implements Serializable { + final byte[] bytes; + private static final long serialVersionUID = 0L; + + BytesHashCode(byte[] bytes) { + this.bytes = (byte[])Preconditions.checkNotNull(bytes); + } + + public int bits() { + return this.bytes.length * 8; + } + + public byte[] asBytes() { + return (byte[])this.bytes.clone(); + } + + public int asInt() { + Preconditions.checkState(this.bytes.length >= 4, "HashCode#asInt() requires >= 4 bytes (it only has %s bytes).", this.bytes.length); + return this.bytes[0] & 255 | (this.bytes[1] & 255) << 8 | (this.bytes[2] & 255) << 16 | (this.bytes[3] & 255) << 24; + } + + public long asLong() { + Preconditions.checkState(this.bytes.length >= 8, "HashCode#asLong() requires >= 8 bytes (it only has %s bytes).", this.bytes.length); + return this.padToLong(); + } + + public long padToLong() { + long retVal = (long)(this.bytes[0] & 255); + + for(int i = 1; i < Math.min(this.bytes.length, 8); ++i) { + retVal |= ((long)this.bytes[i] & 255L) << i * 8; + } + + return retVal; + } + + void writeBytesToImpl(byte[] dest, int offset, int maxLength) { + System.arraycopy(this.bytes, 0, dest, offset, maxLength); + } + + byte[] getBytesInternal() { + return this.bytes; + } + + boolean equalsSameBits(HashCode that) { + return MessageDigest.isEqual(this.bytes, that.getBytesInternal()); + } + } + + private static final class LongHashCode extends HashCode implements Serializable { + final long hash; + private static final long serialVersionUID = 0L; + + LongHashCode(long hash) { + this.hash = hash; + } + + public int bits() { + return 64; + } + + public byte[] asBytes() { + return new byte[]{(byte)((int)this.hash), (byte)((int)(this.hash >> 8)), (byte)((int)(this.hash >> 16)), (byte)((int)(this.hash >> 24)), (byte)((int)(this.hash >> 32)), (byte)((int)(this.hash >> 40)), (byte)((int)(this.hash >> 48)), (byte)((int)(this.hash >> 56))}; + } + + public int asInt() { + return (int)this.hash; + } + + public long asLong() { + return this.hash; + } + + public long padToLong() { + return this.hash; + } + + void writeBytesToImpl(byte[] dest, int offset, int maxLength) { + for(int i = 0; i < maxLength; ++i) { + dest[offset + i] = (byte)((int)(this.hash >> i * 8)); + } + + } + + boolean equalsSameBits(HashCode that) { + return this.hash == that.asLong(); + } + } + + private static final class IntHashCode extends HashCode implements Serializable { + final int hash; + private static final long serialVersionUID = 0L; + + IntHashCode(int hash) { + this.hash = hash; + } + + public int bits() { + return 32; + } + + public byte[] asBytes() { + return new byte[]{(byte)this.hash, (byte)(this.hash >> 8), (byte)(this.hash >> 16), (byte)(this.hash >> 24)}; + } + + public int asInt() { + return this.hash; + } + + public long asLong() { + throw new IllegalStateException("this HashCode only has 32 bits; cannot create a long"); + } + + public long padToLong() { + return UnsignedInts.toLong(this.hash); + } + + void writeBytesToImpl(byte[] dest, int offset, int maxLength) { + for(int i = 0; i < maxLength; ++i) { + dest[offset + i] = (byte)(this.hash >> i * 8); + } + + } + + boolean equalsSameBits(HashCode that) { + return this.hash == that.asInt(); + } + } +} diff --git a/src/main/com/google/common/hash/HashFunction.java b/src/main/com/google/common/hash/HashFunction.java new file mode 100644 index 0000000..f71f4ff --- /dev/null +++ b/src/main/com/google/common/hash/HashFunction.java @@ -0,0 +1,27 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import java.nio.charset.Charset; + +@Beta +public interface HashFunction { + Hasher newHasher(); + + Hasher newHasher(int var1); + + HashCode hashInt(int var1); + + HashCode hashLong(long var1); + + HashCode hashBytes(byte[] var1); + + HashCode hashBytes(byte[] var1, int var2, int var3); + + HashCode hashUnencodedChars(CharSequence var1); + + HashCode hashString(CharSequence var1, Charset var2); + + HashCode hashObject(T var1, Funnel var2); + + int bits(); +} diff --git a/src/main/com/google/common/hash/Hasher.java b/src/main/com/google/common/hash/Hasher.java new file mode 100644 index 0000000..9f4a930 --- /dev/null +++ b/src/main/com/google/common/hash/Hasher.java @@ -0,0 +1,35 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import java.nio.charset.Charset; + +@Beta +public interface Hasher extends PrimitiveSink { + Hasher putByte(byte var1); + + Hasher putBytes(byte[] var1); + + Hasher putBytes(byte[] var1, int var2, int var3); + + Hasher putShort(short var1); + + Hasher putInt(int var1); + + Hasher putLong(long var1); + + Hasher putFloat(float var1); + + Hasher putDouble(double var1); + + Hasher putBoolean(boolean var1); + + Hasher putChar(char var1); + + Hasher putUnencodedChars(CharSequence var1); + + Hasher putString(CharSequence var1, Charset var2); + + Hasher putObject(T var1, Funnel var2); + + HashCode hash(); +} diff --git a/src/main/com/google/common/hash/Hashing.java b/src/main/com/google/common/hash/Hashing.java new file mode 100644 index 0000000..3ecfad4 --- /dev/null +++ b/src/main/com/google/common/hash/Hashing.java @@ -0,0 +1,326 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import java.util.Iterator; +import java.util.zip.Adler32; +import java.util.zip.CRC32; +import java.util.zip.Checksum; +import javax.annotation.Nullable; + +@Beta +public final class Hashing { + private static final int GOOD_FAST_HASH_SEED = (int)System.currentTimeMillis(); + + public static HashFunction goodFastHash(int minimumBits) { + int bits = checkPositiveAndMakeMultipleOf32(minimumBits); + if (bits == 32) { + return Hashing.Murmur3_32Holder.GOOD_FAST_HASH_FUNCTION_32; + } else if (bits <= 128) { + return Hashing.Murmur3_128Holder.GOOD_FAST_HASH_FUNCTION_128; + } else { + int hashFunctionsNeeded = (bits + 127) / 128; + HashFunction[] hashFunctions = new HashFunction[hashFunctionsNeeded]; + hashFunctions[0] = Hashing.Murmur3_128Holder.GOOD_FAST_HASH_FUNCTION_128; + int seed = GOOD_FAST_HASH_SEED; + + for(int i = 1; i < hashFunctionsNeeded; ++i) { + seed += 1500450271; + hashFunctions[i] = murmur3_128(seed); + } + + return new Hashing.ConcatenatedHashFunction(hashFunctions); + } + } + + public static HashFunction murmur3_32(int seed) { + return new Murmur3_32HashFunction(seed); + } + + public static HashFunction murmur3_32() { + return Hashing.Murmur3_32Holder.MURMUR3_32; + } + + public static HashFunction murmur3_128(int seed) { + return new Murmur3_128HashFunction(seed); + } + + public static HashFunction murmur3_128() { + return Hashing.Murmur3_128Holder.MURMUR3_128; + } + + public static HashFunction sipHash24() { + return Hashing.SipHash24Holder.SIP_HASH_24; + } + + public static HashFunction sipHash24(long k0, long k1) { + return new SipHashFunction(2, 4, k0, k1); + } + + public static HashFunction md5() { + return Hashing.Md5Holder.MD5; + } + + public static HashFunction sha1() { + return Hashing.Sha1Holder.SHA_1; + } + + public static HashFunction sha256() { + return Hashing.Sha256Holder.SHA_256; + } + + public static HashFunction sha512() { + return Hashing.Sha512Holder.SHA_512; + } + + public static HashFunction crc32c() { + return Hashing.Crc32cHolder.CRC_32_C; + } + + public static HashFunction crc32() { + return Hashing.Crc32Holder.CRC_32; + } + + public static HashFunction adler32() { + return Hashing.Adler32Holder.ADLER_32; + } + + private static HashFunction checksumHashFunction(Hashing.ChecksumType type, String toString) { + return new ChecksumHashFunction(type, type.bits, toString); + } + + public static int consistentHash(HashCode hashCode, int buckets) { + return consistentHash(hashCode.padToLong(), buckets); + } + + public static int consistentHash(long input, int buckets) { + Preconditions.checkArgument(buckets > 0, "buckets must be positive: %s", buckets); + Hashing.LinearCongruentialGenerator generator = new Hashing.LinearCongruentialGenerator(input); + int candidate = 0; + + while(true) { + int next = (int)((double)(candidate + 1) / generator.nextDouble()); + if (next < 0 || next >= buckets) { + return candidate; + } + + candidate = next; + } + } + + public static HashCode combineOrdered(Iterable hashCodes) { + Iterator iterator = hashCodes.iterator(); + Preconditions.checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); + int bits = ((HashCode)iterator.next()).bits(); + byte[] resultBytes = new byte[bits / 8]; + Iterator i$ = hashCodes.iterator(); + + while(i$.hasNext()) { + HashCode hashCode = (HashCode)i$.next(); + byte[] nextBytes = hashCode.asBytes(); + Preconditions.checkArgument(nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); + + for(int i = 0; i < nextBytes.length; ++i) { + resultBytes[i] = (byte)(resultBytes[i] * 37 ^ nextBytes[i]); + } + } + + return HashCode.fromBytesNoCopy(resultBytes); + } + + public static HashCode combineUnordered(Iterable hashCodes) { + Iterator iterator = hashCodes.iterator(); + Preconditions.checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); + byte[] resultBytes = new byte[((HashCode)iterator.next()).bits() / 8]; + Iterator i$ = hashCodes.iterator(); + + while(i$.hasNext()) { + HashCode hashCode = (HashCode)i$.next(); + byte[] nextBytes = hashCode.asBytes(); + Preconditions.checkArgument(nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); + + for(int i = 0; i < nextBytes.length; ++i) { + resultBytes[i] += nextBytes[i]; + } + } + + return HashCode.fromBytesNoCopy(resultBytes); + } + + static int checkPositiveAndMakeMultipleOf32(int bits) { + Preconditions.checkArgument(bits > 0, "Number of bits must be positive"); + return bits + 31 & -32; + } + + private Hashing() { + } + + private static final class LinearCongruentialGenerator { + private long state; + + public LinearCongruentialGenerator(long seed) { + this.state = seed; + } + + public double nextDouble() { + this.state = 2862933555777941757L * this.state + 1L; + return (double)((int)(this.state >>> 33) + 1) / 2.147483648E9D; + } + } + + @VisibleForTesting + static final class ConcatenatedHashFunction extends AbstractCompositeHashFunction { + private final int bits; + + ConcatenatedHashFunction(HashFunction... functions) { + super(functions); + int bitSum = 0; + HashFunction[] arr$ = functions; + int len$ = functions.length; + + for(int i$ = 0; i$ < len$; ++i$) { + HashFunction function = arr$[i$]; + bitSum += function.bits(); + } + + this.bits = bitSum; + } + + HashCode makeHash(Hasher[] hashers) { + byte[] bytes = new byte[this.bits / 8]; + int i = 0; + Hasher[] arr$ = hashers; + int len$ = hashers.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Hasher hasher = arr$[i$]; + HashCode newHash = hasher.hash(); + i += newHash.writeBytesTo(bytes, i, newHash.bits() / 8); + } + + return HashCode.fromBytesNoCopy(bytes); + } + + public int bits() { + return this.bits; + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Hashing.ConcatenatedHashFunction) { + Hashing.ConcatenatedHashFunction other = (Hashing.ConcatenatedHashFunction)object; + if (this.bits == other.bits && this.functions.length == other.functions.length) { + for(int i = 0; i < this.functions.length; ++i) { + if (!this.functions[i].equals(other.functions[i])) { + return false; + } + } + + return true; + } else { + return false; + } + } else { + return false; + } + } + + public int hashCode() { + int hash = this.bits; + HashFunction[] arr$ = this.functions; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + HashFunction function = arr$[i$]; + hash ^= function.hashCode(); + } + + return hash; + } + } + + static enum ChecksumType implements Supplier { + CRC_32(32) { + public Checksum get() { + return new CRC32(); + } + }, + ADLER_32(32) { + public Checksum get() { + return new Adler32(); + } + }; + + private final int bits; + + private ChecksumType(int bits) { + this.bits = bits; + } + + public abstract Checksum get(); + + // $FF: synthetic method + ChecksumType(int x2, Object x3) { + this(x2); + } + } + + private static class Adler32Holder { + static final HashFunction ADLER_32; + + static { + ADLER_32 = Hashing.checksumHashFunction(Hashing.ChecksumType.ADLER_32, "Hashing.adler32()"); + } + } + + private static class Crc32Holder { + static final HashFunction CRC_32; + + static { + CRC_32 = Hashing.checksumHashFunction(Hashing.ChecksumType.CRC_32, "Hashing.crc32()"); + } + } + + private static final class Crc32cHolder { + static final HashFunction CRC_32_C = new Crc32cHashFunction(); + } + + private static class Sha512Holder { + static final HashFunction SHA_512 = new MessageDigestHashFunction("SHA-512", "Hashing.sha512()"); + } + + private static class Sha256Holder { + static final HashFunction SHA_256 = new MessageDigestHashFunction("SHA-256", "Hashing.sha256()"); + } + + private static class Sha1Holder { + static final HashFunction SHA_1 = new MessageDigestHashFunction("SHA-1", "Hashing.sha1()"); + } + + private static class Md5Holder { + static final HashFunction MD5 = new MessageDigestHashFunction("MD5", "Hashing.md5()"); + } + + private static class SipHash24Holder { + static final HashFunction SIP_HASH_24 = new SipHashFunction(2, 4, 506097522914230528L, 1084818905618843912L); + } + + private static class Murmur3_128Holder { + static final HashFunction MURMUR3_128 = new Murmur3_128HashFunction(0); + static final HashFunction GOOD_FAST_HASH_FUNCTION_128; + + static { + GOOD_FAST_HASH_FUNCTION_128 = Hashing.murmur3_128(Hashing.GOOD_FAST_HASH_SEED); + } + } + + private static class Murmur3_32Holder { + static final HashFunction MURMUR3_32 = new Murmur3_32HashFunction(0); + static final HashFunction GOOD_FAST_HASH_FUNCTION_32; + + static { + GOOD_FAST_HASH_FUNCTION_32 = Hashing.murmur3_32(Hashing.GOOD_FAST_HASH_SEED); + } + } +} diff --git a/src/main/com/google/common/hash/HashingInputStream.java b/src/main/com/google/common/hash/HashingInputStream.java new file mode 100644 index 0000000..df9d0b0 --- /dev/null +++ b/src/main/com/google/common/hash/HashingInputStream.java @@ -0,0 +1,50 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +@Beta +public final class HashingInputStream extends FilterInputStream { + private final Hasher hasher; + + public HashingInputStream(HashFunction hashFunction, InputStream in) { + super((InputStream)Preconditions.checkNotNull(in)); + this.hasher = (Hasher)Preconditions.checkNotNull(hashFunction.newHasher()); + } + + public int read() throws IOException { + int b = this.in.read(); + if (b != -1) { + this.hasher.putByte((byte)b); + } + + return b; + } + + public int read(byte[] bytes, int off, int len) throws IOException { + int numOfBytesRead = this.in.read(bytes, off, len); + if (numOfBytesRead != -1) { + this.hasher.putBytes(bytes, off, numOfBytesRead); + } + + return numOfBytesRead; + } + + public boolean markSupported() { + return false; + } + + public void mark(int readlimit) { + } + + public void reset() throws IOException { + throw new IOException("reset not supported"); + } + + public HashCode hash() { + return this.hasher.hash(); + } +} diff --git a/src/main/com/google/common/hash/HashingOutputStream.java b/src/main/com/google/common/hash/HashingOutputStream.java new file mode 100644 index 0000000..cee3210 --- /dev/null +++ b/src/main/com/google/common/hash/HashingOutputStream.java @@ -0,0 +1,35 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +@Beta +public final class HashingOutputStream extends FilterOutputStream { + private final Hasher hasher; + + public HashingOutputStream(HashFunction hashFunction, OutputStream out) { + super((OutputStream)Preconditions.checkNotNull(out)); + this.hasher = (Hasher)Preconditions.checkNotNull(hashFunction.newHasher()); + } + + public void write(int b) throws IOException { + this.hasher.putByte((byte)b); + this.out.write(b); + } + + public void write(byte[] bytes, int off, int len) throws IOException { + this.hasher.putBytes(bytes, off, len); + this.out.write(bytes, off, len); + } + + public HashCode hash() { + return this.hasher.hash(); + } + + public void close() throws IOException { + this.out.close(); + } +} diff --git a/src/main/com/google/common/hash/MessageDigestHashFunction.java b/src/main/com/google/common/hash/MessageDigestHashFunction.java new file mode 100644 index 0000000..6a017f0 --- /dev/null +++ b/src/main/com/google/common/hash/MessageDigestHashFunction.java @@ -0,0 +1,133 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +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 = this.prototype.getDigestLength(); + this.toString = (String)Preconditions.checkNotNull(toString); + this.supportsClone = this.supportsClone(); + } + + MessageDigestHashFunction(String algorithmName, int bytes, String toString) { + this.toString = (String)Preconditions.checkNotNull(toString); + this.prototype = getMessageDigest(algorithmName); + int maxLength = this.prototype.getDigestLength(); + Preconditions.checkArgument(bytes >= 4 && bytes <= maxLength, "bytes (%s) must be >= 4 and < %s", bytes, maxLength); + this.bytes = bytes; + this.supportsClone = this.supportsClone(); + } + + private boolean supportsClone() { + try { + this.prototype.clone(); + return true; + } catch (CloneNotSupportedException var2) { + return false; + } + } + + public int bits() { + return this.bytes * 8; + } + + public String toString() { + return this.toString; + } + + private static MessageDigest getMessageDigest(String algorithmName) { + try { + return MessageDigest.getInstance(algorithmName); + } catch (NoSuchAlgorithmException var2) { + throw new AssertionError(var2); + } + } + + public Hasher newHasher() { + if (this.supportsClone) { + try { + return new MessageDigestHashFunction.MessageDigestHasher((MessageDigest)this.prototype.clone(), this.bytes); + } catch (CloneNotSupportedException var2) { + } + } + + return new MessageDigestHashFunction.MessageDigestHasher(getMessageDigest(this.prototype.getAlgorithm()), this.bytes); + } + + Object writeReplace() { + return new MessageDigestHashFunction.SerializedForm(this.prototype.getAlgorithm(), this.bytes, this.toString); + } + + private static final class MessageDigestHasher extends AbstractByteHasher { + private final MessageDigest digest; + private final int bytes; + private boolean done; + + private MessageDigestHasher(MessageDigest digest, int bytes) { + this.digest = digest; + this.bytes = bytes; + } + + protected void update(byte b) { + this.checkNotDone(); + this.digest.update(b); + } + + protected void update(byte[] b) { + this.checkNotDone(); + this.digest.update(b); + } + + protected void update(byte[] b, int off, int len) { + this.checkNotDone(); + this.digest.update(b, off, len); + } + + private void checkNotDone() { + Preconditions.checkState(!this.done, "Cannot re-use a Hasher after calling hash() on it"); + } + + public HashCode hash() { + this.checkNotDone(); + this.done = true; + return this.bytes == this.digest.getDigestLength() ? HashCode.fromBytesNoCopy(this.digest.digest()) : HashCode.fromBytesNoCopy(Arrays.copyOf(this.digest.digest(), this.bytes)); + } + + // $FF: synthetic method + MessageDigestHasher(MessageDigest x0, int x1, Object x2) { + this(x0, x1); + } + } + + private static final class SerializedForm implements Serializable { + private final String algorithmName; + private final int bytes; + private final String toString; + private static final long serialVersionUID = 0L; + + private SerializedForm(String algorithmName, int bytes, String toString) { + this.algorithmName = algorithmName; + this.bytes = bytes; + this.toString = toString; + } + + private Object readResolve() { + return new MessageDigestHashFunction(this.algorithmName, this.bytes, this.toString); + } + + // $FF: synthetic method + SerializedForm(String x0, int x1, String x2, Object x3) { + this(x0, x1, x2); + } + } +} diff --git a/src/main/com/google/common/hash/Murmur3_128HashFunction.java b/src/main/com/google/common/hash/Murmur3_128HashFunction.java new file mode 100644 index 0000000..724adee --- /dev/null +++ b/src/main/com/google/common/hash/Murmur3_128HashFunction.java @@ -0,0 +1,156 @@ +package com.google.common.hash; + +import com.google.common.primitives.UnsignedBytes; +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import javax.annotation.Nullable; + +final class Murmur3_128HashFunction extends AbstractStreamingHashFunction implements Serializable { + private final int seed; + private static final long serialVersionUID = 0L; + + Murmur3_128HashFunction(int seed) { + this.seed = seed; + } + + public int bits() { + return 128; + } + + public Hasher newHasher() { + return new Murmur3_128HashFunction.Murmur3_128Hasher(this.seed); + } + + public String toString() { + int var1 = this.seed; + return (new StringBuilder(32)).append("Hashing.murmur3_128(").append(var1).append(")").toString(); + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Murmur3_128HashFunction) { + Murmur3_128HashFunction other = (Murmur3_128HashFunction)object; + return this.seed == other.seed; + } else { + return false; + } + } + + public int hashCode() { + return this.getClass().hashCode() ^ this.seed; + } + + private static final class Murmur3_128Hasher extends AbstractStreamingHashFunction.AbstractStreamingHasher { + private static final int CHUNK_SIZE = 16; + private static final long C1 = -8663945395140668459L; + private static final long C2 = 5545529020109919103L; + private long h1; + private long h2; + private int length; + + Murmur3_128Hasher(int seed) { + super(16); + this.h1 = (long)seed; + this.h2 = (long)seed; + this.length = 0; + } + + protected void process(ByteBuffer bb) { + long k1 = bb.getLong(); + long k2 = bb.getLong(); + this.bmix64(k1, k2); + this.length += 16; + } + + private void bmix64(long k1, long k2) { + this.h1 ^= mixK1(k1); + this.h1 = Long.rotateLeft(this.h1, 27); + this.h1 += this.h2; + this.h1 = this.h1 * 5L + 1390208809L; + this.h2 ^= mixK2(k2); + this.h2 = Long.rotateLeft(this.h2, 31); + this.h2 += this.h1; + this.h2 = this.h2 * 5L + 944331445L; + } + + protected void processRemaining(ByteBuffer bb) { + long k1 = 0L; + long k2 = 0L; + this.length += bb.remaining(); + switch(bb.remaining()) { + case 7: + k1 ^= (long)UnsignedBytes.toInt(bb.get(6)) << 48; + case 6: + k1 ^= (long)UnsignedBytes.toInt(bb.get(5)) << 40; + case 5: + k1 ^= (long)UnsignedBytes.toInt(bb.get(4)) << 32; + case 4: + k1 ^= (long)UnsignedBytes.toInt(bb.get(3)) << 24; + case 3: + k1 ^= (long)UnsignedBytes.toInt(bb.get(2)) << 16; + case 2: + k1 ^= (long)UnsignedBytes.toInt(bb.get(1)) << 8; + case 1: + k1 ^= (long)UnsignedBytes.toInt(bb.get(0)); + break; + case 15: + k2 ^= (long)UnsignedBytes.toInt(bb.get(14)) << 48; + case 14: + k2 ^= (long)UnsignedBytes.toInt(bb.get(13)) << 40; + case 13: + k2 ^= (long)UnsignedBytes.toInt(bb.get(12)) << 32; + case 12: + k2 ^= (long)UnsignedBytes.toInt(bb.get(11)) << 24; + case 11: + k2 ^= (long)UnsignedBytes.toInt(bb.get(10)) << 16; + case 10: + k2 ^= (long)UnsignedBytes.toInt(bb.get(9)) << 8; + case 9: + k2 ^= (long)UnsignedBytes.toInt(bb.get(8)); + case 8: + k1 ^= bb.getLong(); + break; + default: + throw new AssertionError("Should never get here."); + } + + this.h1 ^= mixK1(k1); + this.h2 ^= mixK2(k2); + } + + public HashCode makeHash() { + this.h1 ^= (long)this.length; + this.h2 ^= (long)this.length; + this.h1 += this.h2; + this.h2 += this.h1; + this.h1 = fmix64(this.h1); + this.h2 = fmix64(this.h2); + this.h1 += this.h2; + this.h2 += this.h1; + return HashCode.fromBytesNoCopy(ByteBuffer.wrap(new byte[16]).order(ByteOrder.LITTLE_ENDIAN).putLong(this.h1).putLong(this.h2).array()); + } + + private static long fmix64(long k) { + k ^= k >>> 33; + k *= -49064778989728563L; + k ^= k >>> 33; + k *= -4265267296055464877L; + k ^= k >>> 33; + return k; + } + + private static long mixK1(long k1) { + k1 *= -8663945395140668459L; + k1 = Long.rotateLeft(k1, 31); + k1 *= 5545529020109919103L; + return k1; + } + + private static long mixK2(long k2) { + k2 *= 5545529020109919103L; + k2 = Long.rotateLeft(k2, 33); + k2 *= -8663945395140668459L; + return k2; + } + } +} diff --git a/src/main/com/google/common/hash/Murmur3_32HashFunction.java b/src/main/com/google/common/hash/Murmur3_32HashFunction.java new file mode 100644 index 0000000..5c8629b --- /dev/null +++ b/src/main/com/google/common/hash/Murmur3_32HashFunction.java @@ -0,0 +1,135 @@ +package com.google.common.hash; + +import com.google.common.primitives.UnsignedBytes; +import java.io.Serializable; +import java.nio.ByteBuffer; +import javax.annotation.Nullable; + +final class Murmur3_32HashFunction extends AbstractStreamingHashFunction implements Serializable { + private static final int C1 = -862048943; + private static final int C2 = 461845907; + private final int seed; + private static final long serialVersionUID = 0L; + + Murmur3_32HashFunction(int seed) { + this.seed = seed; + } + + public int bits() { + return 32; + } + + public Hasher newHasher() { + return new Murmur3_32HashFunction.Murmur3_32Hasher(this.seed); + } + + public String toString() { + int var1 = this.seed; + return (new StringBuilder(31)).append("Hashing.murmur3_32(").append(var1).append(")").toString(); + } + + public boolean equals(@Nullable Object object) { + if (object instanceof Murmur3_32HashFunction) { + Murmur3_32HashFunction other = (Murmur3_32HashFunction)object; + return this.seed == other.seed; + } else { + return false; + } + } + + public int hashCode() { + return this.getClass().hashCode() ^ this.seed; + } + + public HashCode hashInt(int input) { + int k1 = mixK1(input); + int h1 = mixH1(this.seed, k1); + return fmix(h1, 4); + } + + public HashCode hashLong(long input) { + int low = (int)input; + int high = (int)(input >>> 32); + int k1 = mixK1(low); + int h1 = mixH1(this.seed, k1); + k1 = mixK1(high); + h1 = mixH1(h1, k1); + return fmix(h1, 8); + } + + public HashCode hashUnencodedChars(CharSequence input) { + int h1 = this.seed; + + int k1; + for(k1 = 1; k1 < input.length(); k1 += 2) { + int k1 = input.charAt(k1 - 1) | input.charAt(k1) << 16; + k1 = mixK1(k1); + h1 = mixH1(h1, k1); + } + + if ((input.length() & 1) == 1) { + int k1 = input.charAt(input.length() - 1); + k1 = mixK1(k1); + h1 ^= k1; + } + + return fmix(h1, 2 * input.length()); + } + + private static int mixK1(int k1) { + k1 *= -862048943; + k1 = Integer.rotateLeft(k1, 15); + k1 *= 461845907; + return k1; + } + + private static int mixH1(int h1, int k1) { + h1 ^= k1; + h1 = Integer.rotateLeft(h1, 13); + h1 = h1 * 5 + -430675100; + return h1; + } + + private static HashCode fmix(int h1, int length) { + h1 ^= length; + h1 ^= h1 >>> 16; + h1 *= -2048144789; + h1 ^= h1 >>> 13; + h1 *= -1028477387; + h1 ^= h1 >>> 16; + return HashCode.fromInt(h1); + } + + private static final class Murmur3_32Hasher extends AbstractStreamingHashFunction.AbstractStreamingHasher { + private static final int CHUNK_SIZE = 4; + private int h1; + private int length; + + Murmur3_32Hasher(int seed) { + super(4); + this.h1 = seed; + this.length = 0; + } + + protected void process(ByteBuffer bb) { + int k1 = Murmur3_32HashFunction.mixK1(bb.getInt()); + this.h1 = Murmur3_32HashFunction.mixH1(this.h1, k1); + this.length += 4; + } + + protected void processRemaining(ByteBuffer bb) { + this.length += bb.remaining(); + int k1 = 0; + + for(int i = 0; bb.hasRemaining(); i += 8) { + k1 ^= UnsignedBytes.toInt(bb.get()) << i; + } + + this.h1 ^= Murmur3_32HashFunction.mixK1(k1); + } + + public HashCode makeHash() { + return Murmur3_32HashFunction.fmix(this.h1, this.length); + } + } +} diff --git a/src/main/com/google/common/hash/PrimitiveSink.java b/src/main/com/google/common/hash/PrimitiveSink.java new file mode 100644 index 0000000..a8b3c11 --- /dev/null +++ b/src/main/com/google/common/hash/PrimitiveSink.java @@ -0,0 +1,31 @@ +package com.google.common.hash; + +import com.google.common.annotations.Beta; +import java.nio.charset.Charset; + +@Beta +public interface PrimitiveSink { + PrimitiveSink putByte(byte var1); + + PrimitiveSink putBytes(byte[] var1); + + PrimitiveSink putBytes(byte[] var1, int var2, int var3); + + PrimitiveSink putShort(short var1); + + PrimitiveSink putInt(int var1); + + PrimitiveSink putLong(long var1); + + PrimitiveSink putFloat(float var1); + + PrimitiveSink putDouble(double var1); + + PrimitiveSink putBoolean(boolean var1); + + PrimitiveSink putChar(char var1); + + PrimitiveSink putUnencodedChars(CharSequence var1); + + PrimitiveSink putString(CharSequence var1, Charset var2); +} diff --git a/src/main/com/google/common/hash/SipHashFunction.java b/src/main/com/google/common/hash/SipHashFunction.java new file mode 100644 index 0000000..a7453a8 --- /dev/null +++ b/src/main/com/google/common/hash/SipHashFunction.java @@ -0,0 +1,122 @@ +package com.google.common.hash; + +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.nio.ByteBuffer; +import javax.annotation.Nullable; + +final class SipHashFunction extends AbstractStreamingHashFunction implements Serializable { + private final int c; + private final int d; + private final long k0; + private final long k1; + private static final long serialVersionUID = 0L; + + SipHashFunction(int c, int d, long k0, long k1) { + Preconditions.checkArgument(c > 0, "The number of SipRound iterations (c=%s) during Compression must be positive.", c); + Preconditions.checkArgument(d > 0, "The number of SipRound iterations (d=%s) during Finalization must be positive.", d); + this.c = c; + this.d = d; + this.k0 = k0; + this.k1 = k1; + } + + public int bits() { + return 64; + } + + public Hasher newHasher() { + return new SipHashFunction.SipHasher(this.c, this.d, this.k0, this.k1); + } + + public String toString() { + int var1 = this.c; + int var2 = this.d; + long var3 = this.k0; + long var5 = this.k1; + return (new StringBuilder(81)).append("Hashing.sipHash").append(var1).append(var2).append("(").append(var3).append(", ").append(var5).append(")").toString(); + } + + public boolean equals(@Nullable Object object) { + if (!(object instanceof SipHashFunction)) { + return false; + } else { + SipHashFunction other = (SipHashFunction)object; + return this.c == other.c && this.d == other.d && this.k0 == other.k0 && this.k1 == other.k1; + } + } + + public int hashCode() { + return (int)((long)(this.getClass().hashCode() ^ this.c ^ this.d) ^ this.k0 ^ this.k1); + } + + private static final class SipHasher extends AbstractStreamingHashFunction.AbstractStreamingHasher { + private static final int CHUNK_SIZE = 8; + private final int c; + private final int d; + private long v0 = 8317987319222330741L; + private long v1 = 7237128888997146477L; + private long v2 = 7816392313619706465L; + private long v3 = 8387220255154660723L; + private long b = 0L; + private long finalM = 0L; + + SipHasher(int c, int d, long k0, long k1) { + super(8); + this.c = c; + this.d = d; + this.v0 ^= k0; + this.v1 ^= k1; + this.v2 ^= k0; + this.v3 ^= k1; + } + + protected void process(ByteBuffer buffer) { + this.b += 8L; + this.processM(buffer.getLong()); + } + + protected void processRemaining(ByteBuffer buffer) { + this.b += (long)buffer.remaining(); + + for(int i = 0; buffer.hasRemaining(); i += 8) { + this.finalM ^= ((long)buffer.get() & 255L) << i; + } + + } + + public HashCode makeHash() { + this.finalM ^= this.b << 56; + this.processM(this.finalM); + this.v2 ^= 255L; + this.sipRound(this.d); + return HashCode.fromLong(this.v0 ^ this.v1 ^ this.v2 ^ this.v3); + } + + private void processM(long m) { + this.v3 ^= m; + this.sipRound(this.c); + this.v0 ^= m; + } + + private void sipRound(int iterations) { + for(int i = 0; i < iterations; ++i) { + this.v0 += this.v1; + this.v2 += this.v3; + this.v1 = Long.rotateLeft(this.v1, 13); + this.v3 = Long.rotateLeft(this.v3, 16); + this.v1 ^= this.v0; + this.v3 ^= this.v2; + this.v0 = Long.rotateLeft(this.v0, 32); + this.v2 += this.v1; + this.v0 += this.v3; + this.v1 = Long.rotateLeft(this.v1, 17); + this.v3 = Long.rotateLeft(this.v3, 21); + this.v1 ^= this.v2; + this.v3 ^= this.v0; + this.v2 = Long.rotateLeft(this.v2, 32); + } + + } + } +} diff --git a/src/main/com/google/common/hash/package-info.java b/src/main/com/google/common/hash/package-info.java new file mode 100644 index 0000000..66333a3 --- /dev/null +++ b/src/main/com/google/common/hash/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.hash; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/html/HtmlEscapers.java b/src/main/com/google/common/html/HtmlEscapers.java new file mode 100644 index 0000000..f01f031 --- /dev/null +++ b/src/main/com/google/common/html/HtmlEscapers.java @@ -0,0 +1,19 @@ +package com.google.common.html; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.escape.Escaper; +import com.google.common.escape.Escapers; + +@Beta +@GwtCompatible +public final class HtmlEscapers { + private static final Escaper HTML_ESCAPER = Escapers.builder().addEscape('"', """).addEscape('\'', "'").addEscape('&', "&").addEscape('<', "<").addEscape('>', ">").build(); + + public static Escaper htmlEscaper() { + return HTML_ESCAPER; + } + + private HtmlEscapers() { + } +} diff --git a/src/main/com/google/common/html/package-info.java b/src/main/com/google/common/html/package-info.java new file mode 100644 index 0000000..eb251ab --- /dev/null +++ b/src/main/com/google/common/html/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.html; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/io/AppendableWriter.java b/src/main/com/google/common/io/AppendableWriter.java new file mode 100644 index 0000000..de10c18 --- /dev/null +++ b/src/main/com/google/common/io/AppendableWriter.java @@ -0,0 +1,77 @@ +package com.google.common.io; + +import com.google.common.base.Preconditions; +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.Writer; +import javax.annotation.Nullable; + +class AppendableWriter extends Writer { + private final Appendable target; + private boolean closed; + + AppendableWriter(Appendable target) { + this.target = (Appendable)Preconditions.checkNotNull(target); + } + + public void write(char[] cbuf, int off, int len) throws IOException { + this.checkNotClosed(); + this.target.append(new String(cbuf, off, len)); + } + + public void flush() throws IOException { + this.checkNotClosed(); + if (this.target instanceof Flushable) { + ((Flushable)this.target).flush(); + } + + } + + public void close() throws IOException { + this.closed = true; + if (this.target instanceof Closeable) { + ((Closeable)this.target).close(); + } + + } + + public void write(int c) throws IOException { + this.checkNotClosed(); + this.target.append((char)c); + } + + public void write(@Nullable String str) throws IOException { + this.checkNotClosed(); + this.target.append(str); + } + + public void write(@Nullable String str, int off, int len) throws IOException { + this.checkNotClosed(); + this.target.append(str, off, off + len); + } + + public Writer append(char c) throws IOException { + this.checkNotClosed(); + this.target.append(c); + return this; + } + + public Writer append(@Nullable CharSequence charSeq) throws IOException { + this.checkNotClosed(); + this.target.append(charSeq); + return this; + } + + public Writer append(@Nullable CharSequence charSeq, int start, int end) throws IOException { + this.checkNotClosed(); + this.target.append(charSeq, start, end); + return this; + } + + private void checkNotClosed() throws IOException { + if (this.closed) { + throw new IOException("Cannot write to a closed writer."); + } + } +} diff --git a/src/main/com/google/common/io/BaseEncoding.java b/src/main/com/google/common/io/BaseEncoding.java new file mode 100644 index 0000000..0d861a9 --- /dev/null +++ b/src/main/com/google/common/io/BaseEncoding.java @@ -0,0 +1,589 @@ +package com.google.common.io; + +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.base.Preconditions; +import com.google.common.math.IntMath; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.math.RoundingMode; +import java.util.Arrays; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible( + emulated = true +) +public abstract class BaseEncoding { + private static final BaseEncoding BASE64 = new BaseEncoding.StandardBaseEncoding("base64()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", '='); + private static final BaseEncoding BASE64_URL = new BaseEncoding.StandardBaseEncoding("base64Url()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", '='); + private static final BaseEncoding BASE32 = new BaseEncoding.StandardBaseEncoding("base32()", "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", '='); + private static final BaseEncoding BASE32_HEX = new BaseEncoding.StandardBaseEncoding("base32Hex()", "0123456789ABCDEFGHIJKLMNOPQRSTUV", '='); + private static final BaseEncoding BASE16 = new BaseEncoding.StandardBaseEncoding("base16()", "0123456789ABCDEF", (Character)null); + + BaseEncoding() { + } + + public String encode(byte[] bytes) { + return this.encode((byte[])Preconditions.checkNotNull(bytes), 0, bytes.length); + } + + public final String encode(byte[] bytes, int off, int len) { + Preconditions.checkNotNull(bytes); + Preconditions.checkPositionIndexes(off, off + len, bytes.length); + GwtWorkarounds.CharOutput result = GwtWorkarounds.stringBuilderOutput(this.maxEncodedSize(len)); + GwtWorkarounds.ByteOutput byteOutput = this.encodingStream(result); + + try { + for(int i = 0; i < len; ++i) { + byteOutput.write(bytes[off + i]); + } + + byteOutput.close(); + return result.toString(); + } catch (IOException var7) { + throw new AssertionError("impossible"); + } + } + + @GwtIncompatible("Writer,OutputStream") + public final OutputStream encodingStream(Writer writer) { + return GwtWorkarounds.asOutputStream(this.encodingStream(GwtWorkarounds.asCharOutput(writer))); + } + + @GwtIncompatible("ByteSink,CharSink") + public final ByteSink encodingSink(final CharSink encodedSink) { + Preconditions.checkNotNull(encodedSink); + return new ByteSink() { + public OutputStream openStream() throws IOException { + return BaseEncoding.this.encodingStream(encodedSink.openStream()); + } + }; + } + + 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; + } + } + + public final byte[] decode(CharSequence chars) { + try { + return this.decodeChecked(chars); + } catch (BaseEncoding.DecodingException var3) { + throw new IllegalArgumentException(var3); + } + } + + final byte[] decodeChecked(CharSequence chars) throws BaseEncoding.DecodingException { + CharSequence chars = this.padding().trimTrailingFrom(chars); + GwtWorkarounds.ByteInput decodedInput = this.decodingStream(GwtWorkarounds.asCharInput((CharSequence)chars)); + byte[] tmp = new byte[this.maxDecodedSize(chars.length())]; + int index = 0; + + try { + for(int i = decodedInput.read(); i != -1; i = decodedInput.read()) { + tmp[index++] = (byte)i; + } + } catch (BaseEncoding.DecodingException var6) { + throw var6; + } catch (IOException var7) { + throw new AssertionError(var7); + } + + return extract(tmp, index); + } + + @GwtIncompatible("Reader,InputStream") + public final InputStream decodingStream(Reader reader) { + return GwtWorkarounds.asInputStream(this.decodingStream(GwtWorkarounds.asCharInput(reader))); + } + + @GwtIncompatible("ByteSource,CharSource") + public final ByteSource decodingSource(final CharSource encodedSource) { + Preconditions.checkNotNull(encodedSource); + return new ByteSource() { + public InputStream openStream() throws IOException { + return BaseEncoding.this.decodingStream(encodedSource.openStream()); + } + }; + } + + abstract int maxEncodedSize(int var1); + + abstract GwtWorkarounds.ByteOutput encodingStream(GwtWorkarounds.CharOutput var1); + + abstract int maxDecodedSize(int var1); + + abstract GwtWorkarounds.ByteInput decodingStream(GwtWorkarounds.CharInput var1); + + abstract CharMatcher padding(); + + @CheckReturnValue + public abstract BaseEncoding omitPadding(); + + @CheckReturnValue + public abstract BaseEncoding withPadChar(char var1); + + @CheckReturnValue + public abstract BaseEncoding withSeparator(String var1, int var2); + + @CheckReturnValue + public abstract BaseEncoding upperCase(); + + @CheckReturnValue + public abstract BaseEncoding lowerCase(); + + public static BaseEncoding base64() { + return BASE64; + } + + public static BaseEncoding base64Url() { + return BASE64_URL; + } + + public static BaseEncoding base32() { + return BASE32; + } + + public static BaseEncoding base32Hex() { + return BASE32_HEX; + } + + public static BaseEncoding base16() { + return BASE16; + } + + static GwtWorkarounds.CharInput ignoringInput(final GwtWorkarounds.CharInput delegate, final CharMatcher toIgnore) { + Preconditions.checkNotNull(delegate); + Preconditions.checkNotNull(toIgnore); + return new GwtWorkarounds.CharInput() { + public int read() throws IOException { + int readChar; + do { + readChar = delegate.read(); + } while(readChar != -1 && toIgnore.matches((char)readChar)); + + return readChar; + } + + public void close() throws IOException { + delegate.close(); + } + }; + } + + static GwtWorkarounds.CharOutput separatingOutput(final GwtWorkarounds.CharOutput delegate, final String separator, final int afterEveryChars) { + Preconditions.checkNotNull(delegate); + Preconditions.checkNotNull(separator); + Preconditions.checkArgument(afterEveryChars > 0); + return new GwtWorkarounds.CharOutput() { + int charsUntilSeparator = afterEveryChars; + + public void write(char c) throws IOException { + if (this.charsUntilSeparator == 0) { + for(int i = 0; i < separator.length(); ++i) { + delegate.write(separator.charAt(i)); + } + + this.charsUntilSeparator = afterEveryChars; + } + + delegate.write(c); + --this.charsUntilSeparator; + } + + public void flush() throws IOException { + delegate.flush(); + } + + 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 = (BaseEncoding)Preconditions.checkNotNull(delegate); + this.separator = (String)Preconditions.checkNotNull(separator); + this.afterEveryChars = afterEveryChars; + Preconditions.checkArgument(afterEveryChars > 0, "Cannot add a separator after every %s chars", afterEveryChars); + this.separatorChars = CharMatcher.anyOf(separator).precomputed(); + } + + CharMatcher padding() { + return this.delegate.padding(); + } + + int maxEncodedSize(int bytes) { + int unseparatedSize = this.delegate.maxEncodedSize(bytes); + return unseparatedSize + this.separator.length() * IntMath.divide(Math.max(0, unseparatedSize - 1), this.afterEveryChars, RoundingMode.FLOOR); + } + + GwtWorkarounds.ByteOutput encodingStream(GwtWorkarounds.CharOutput output) { + return this.delegate.encodingStream(separatingOutput(output, this.separator, this.afterEveryChars)); + } + + int maxDecodedSize(int chars) { + return this.delegate.maxDecodedSize(chars); + } + + GwtWorkarounds.ByteInput decodingStream(GwtWorkarounds.CharInput input) { + return this.delegate.decodingStream(ignoringInput(input, this.separatorChars)); + } + + public BaseEncoding omitPadding() { + return this.delegate.omitPadding().withSeparator(this.separator, this.afterEveryChars); + } + + public BaseEncoding withPadChar(char padChar) { + return this.delegate.withPadChar(padChar).withSeparator(this.separator, this.afterEveryChars); + } + + public BaseEncoding withSeparator(String separator, int afterEveryChars) { + throw new UnsupportedOperationException("Already have a separator"); + } + + public BaseEncoding upperCase() { + return this.delegate.upperCase().withSeparator(this.separator, this.afterEveryChars); + } + + public BaseEncoding lowerCase() { + return this.delegate.lowerCase().withSeparator(this.separator, this.afterEveryChars); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.delegate.toString())); + String var2 = String.valueOf(String.valueOf(this.separator)); + int var3 = this.afterEveryChars; + return (new StringBuilder(31 + var1.length() + var2.length())).append(var1).append(".withSeparator(\"").append(var2).append("\", ").append(var3).append(")").toString(); + } + } + + static final class StandardBaseEncoding extends BaseEncoding { + private final BaseEncoding.Alphabet alphabet; + @Nullable + private final Character paddingChar; + private transient BaseEncoding upperCase; + private transient BaseEncoding lowerCase; + + StandardBaseEncoding(String name, String alphabetChars, @Nullable Character paddingChar) { + this(new BaseEncoding.Alphabet(name, alphabetChars.toCharArray()), paddingChar); + } + + StandardBaseEncoding(BaseEncoding.Alphabet alphabet, @Nullable Character paddingChar) { + this.alphabet = (BaseEncoding.Alphabet)Preconditions.checkNotNull(alphabet); + Preconditions.checkArgument(paddingChar == null || !alphabet.matches(paddingChar), "Padding character %s was already in alphabet", paddingChar); + this.paddingChar = paddingChar; + } + + CharMatcher padding() { + return this.paddingChar == null ? CharMatcher.NONE : CharMatcher.is(this.paddingChar); + } + + int maxEncodedSize(int bytes) { + return this.alphabet.charsPerChunk * IntMath.divide(bytes, this.alphabet.bytesPerChunk, RoundingMode.CEILING); + } + + GwtWorkarounds.ByteOutput encodingStream(final GwtWorkarounds.CharOutput out) { + Preconditions.checkNotNull(out); + return new GwtWorkarounds.ByteOutput() { + int bitBuffer = 0; + int bitBufferLength = 0; + int writtenChars = 0; + + public void write(byte b) throws IOException { + this.bitBuffer <<= 8; + this.bitBuffer |= b & 255; + + for(this.bitBufferLength += 8; this.bitBufferLength >= StandardBaseEncoding.this.alphabet.bitsPerChar; this.bitBufferLength -= StandardBaseEncoding.this.alphabet.bitsPerChar) { + int charIndex = this.bitBuffer >> this.bitBufferLength - StandardBaseEncoding.this.alphabet.bitsPerChar & StandardBaseEncoding.this.alphabet.mask; + out.write(StandardBaseEncoding.this.alphabet.encode(charIndex)); + ++this.writtenChars; + } + + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + if (this.bitBufferLength > 0) { + int charIndex = this.bitBuffer << StandardBaseEncoding.this.alphabet.bitsPerChar - this.bitBufferLength & StandardBaseEncoding.this.alphabet.mask; + out.write(StandardBaseEncoding.this.alphabet.encode(charIndex)); + ++this.writtenChars; + if (StandardBaseEncoding.this.paddingChar != null) { + while(this.writtenChars % StandardBaseEncoding.this.alphabet.charsPerChunk != 0) { + out.write(StandardBaseEncoding.this.paddingChar); + ++this.writtenChars; + } + } + } + + out.close(); + } + }; + } + + int maxDecodedSize(int chars) { + return (int)(((long)this.alphabet.bitsPerChar * (long)chars + 7L) / 8L); + } + + GwtWorkarounds.ByteInput decodingStream(final GwtWorkarounds.CharInput reader) { + Preconditions.checkNotNull(reader); + return new GwtWorkarounds.ByteInput() { + int bitBuffer = 0; + int bitBufferLength = 0; + int readChars = 0; + boolean hitPadding = false; + final CharMatcher paddingMatcher = StandardBaseEncoding.this.padding(); + + public int read() throws IOException { + while(true) { + int readChar = reader.read(); + if (readChar == -1) { + if (!this.hitPadding && !StandardBaseEncoding.this.alphabet.isValidPaddingStartPosition(this.readChars)) { + int var5 = this.readChars; + throw new BaseEncoding.DecodingException((new StringBuilder(32)).append("Invalid input length ").append(var5).toString()); + } + + return -1; + } + + ++this.readChars; + char ch = (char)readChar; + if (!this.paddingMatcher.matches(ch)) { + if (this.hitPadding) { + int var4 = this.readChars; + throw new BaseEncoding.DecodingException((new StringBuilder(61)).append("Expected padding character but found '").append(ch).append("' at index ").append(var4).toString()); + } + + this.bitBuffer <<= StandardBaseEncoding.this.alphabet.bitsPerChar; + this.bitBuffer |= StandardBaseEncoding.this.alphabet.decode(ch); + this.bitBufferLength += StandardBaseEncoding.this.alphabet.bitsPerChar; + if (this.bitBufferLength >= 8) { + this.bitBufferLength -= 8; + return this.bitBuffer >> this.bitBufferLength & 255; + } + } else { + if (!this.hitPadding && (this.readChars == 1 || !StandardBaseEncoding.this.alphabet.isValidPaddingStartPosition(this.readChars - 1))) { + int var3 = this.readChars; + throw new BaseEncoding.DecodingException((new StringBuilder(41)).append("Padding cannot start at index ").append(var3).toString()); + } + + this.hitPadding = true; + } + } + } + + public void close() throws IOException { + reader.close(); + } + }; + } + + public BaseEncoding omitPadding() { + return this.paddingChar == null ? this : new BaseEncoding.StandardBaseEncoding(this.alphabet, (Character)null); + } + + public BaseEncoding withPadChar(char padChar) { + return 8 % this.alphabet.bitsPerChar != 0 && (this.paddingChar == null || this.paddingChar != padChar) ? new BaseEncoding.StandardBaseEncoding(this.alphabet, padChar) : this; + } + + public BaseEncoding withSeparator(String separator, int afterEveryChars) { + Preconditions.checkNotNull(separator); + Preconditions.checkArgument(this.padding().or(this.alphabet).matchesNoneOf(separator), "Separator cannot contain alphabet or padding characters"); + return new BaseEncoding.SeparatedBaseEncoding(this, separator, afterEveryChars); + } + + public BaseEncoding upperCase() { + BaseEncoding result = this.upperCase; + if (result == null) { + BaseEncoding.Alphabet upper = this.alphabet.upperCase(); + result = this.upperCase = upper == this.alphabet ? this : new BaseEncoding.StandardBaseEncoding(upper, this.paddingChar); + } + + return result; + } + + public BaseEncoding lowerCase() { + BaseEncoding result = this.lowerCase; + if (result == null) { + BaseEncoding.Alphabet lower = this.alphabet.lowerCase(); + result = this.lowerCase = lower == this.alphabet ? this : new BaseEncoding.StandardBaseEncoding(lower, this.paddingChar); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder("BaseEncoding."); + builder.append(this.alphabet.toString()); + if (8 % this.alphabet.bitsPerChar != 0) { + if (this.paddingChar == null) { + builder.append(".omitPadding()"); + } else { + builder.append(".withPadChar(").append(this.paddingChar).append(')'); + } + } + + return builder.toString(); + } + } + + private static final class Alphabet extends CharMatcher { + private final String name; + 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 = (String)Preconditions.checkNotNull(name); + this.chars = (char[])Preconditions.checkNotNull(chars); + + try { + this.bitsPerChar = IntMath.log2(chars.length, RoundingMode.UNNECESSARY); + } catch (ArithmeticException var7) { + int var4 = chars.length; + throw new IllegalArgumentException((new StringBuilder(35)).append("Illegal alphabet length ").append(var4).toString(), var7); + } + + int gcd = Math.min(8, Integer.lowestOneBit(this.bitsPerChar)); + this.charsPerChunk = 8 / gcd; + this.bytesPerChunk = this.bitsPerChar / gcd; + this.mask = chars.length - 1; + byte[] decodabet = new byte[128]; + Arrays.fill(decodabet, (byte)-1); + + for(int i = 0; i < chars.length; ++i) { + char c = chars[i]; + Preconditions.checkArgument(CharMatcher.ASCII.matches(c), "Non-ASCII character: %s", c); + Preconditions.checkArgument(decodabet[c] == -1, "Duplicate character: %s", c); + decodabet[c] = (byte)i; + } + + this.decodabet = decodabet; + boolean[] validPadding = new boolean[this.charsPerChunk]; + + for(int i = 0; i < this.bytesPerChunk; ++i) { + validPadding[IntMath.divide(i * 8, this.bitsPerChar, RoundingMode.CEILING)] = true; + } + + this.validPadding = validPadding; + } + + char encode(int bits) { + return this.chars[bits]; + } + + boolean isValidPaddingStartPosition(int index) { + return this.validPadding[index % this.charsPerChunk]; + } + + int decode(char ch) throws IOException { + if (ch <= 127 && this.decodabet[ch] != -1) { + return this.decodabet[ch]; + } else { + throw new BaseEncoding.DecodingException((new StringBuilder(25)).append("Unrecognized character: ").append(ch).toString()); + } + } + + private boolean hasLowerCase() { + char[] arr$ = this.chars; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char c = arr$[i$]; + if (Ascii.isLowerCase(c)) { + return true; + } + } + + return false; + } + + private boolean hasUpperCase() { + char[] arr$ = this.chars; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char c = arr$[i$]; + if (Ascii.isUpperCase(c)) { + return true; + } + } + + return false; + } + + BaseEncoding.Alphabet upperCase() { + if (!this.hasLowerCase()) { + return this; + } else { + Preconditions.checkState(!this.hasUpperCase(), "Cannot call upperCase() on a mixed-case alphabet"); + char[] upperCased = new char[this.chars.length]; + + for(int i = 0; i < this.chars.length; ++i) { + upperCased[i] = Ascii.toUpperCase(this.chars[i]); + } + + return new BaseEncoding.Alphabet(String.valueOf(this.name).concat(".upperCase()"), upperCased); + } + } + + BaseEncoding.Alphabet lowerCase() { + if (!this.hasUpperCase()) { + return this; + } else { + Preconditions.checkState(!this.hasLowerCase(), "Cannot call lowerCase() on a mixed-case alphabet"); + char[] lowerCased = new char[this.chars.length]; + + for(int i = 0; i < this.chars.length; ++i) { + lowerCased[i] = Ascii.toLowerCase(this.chars[i]); + } + + return new BaseEncoding.Alphabet(String.valueOf(this.name).concat(".lowerCase()"), lowerCased); + } + } + + public boolean matches(char c) { + return CharMatcher.ASCII.matches(c) && this.decodabet[c] != -1; + } + + public String toString() { + return this.name; + } + } + + public static final class DecodingException extends IOException { + DecodingException(String message) { + super(message); + } + + DecodingException(Throwable cause) { + super(cause); + } + } +} diff --git a/src/main/com/google/common/io/ByteArrayDataInput.java b/src/main/com/google/common/io/ByteArrayDataInput.java new file mode 100644 index 0000000..3f81c21 --- /dev/null +++ b/src/main/com/google/common/io/ByteArrayDataInput.java @@ -0,0 +1,35 @@ +package com.google.common.io; + +import java.io.DataInput; + +public interface ByteArrayDataInput extends DataInput { + void readFully(byte[] var1); + + void readFully(byte[] var1, int var2, int var3); + + int skipBytes(int var1); + + boolean readBoolean(); + + byte readByte(); + + int readUnsignedByte(); + + short readShort(); + + int readUnsignedShort(); + + char readChar(); + + int readInt(); + + long readLong(); + + float readFloat(); + + double readDouble(); + + String readLine(); + + String readUTF(); +} diff --git a/src/main/com/google/common/io/ByteArrayDataOutput.java b/src/main/com/google/common/io/ByteArrayDataOutput.java new file mode 100644 index 0000000..9d165ae --- /dev/null +++ b/src/main/com/google/common/io/ByteArrayDataOutput.java @@ -0,0 +1,37 @@ +package com.google.common.io; + +import java.io.DataOutput; + +public interface ByteArrayDataOutput extends DataOutput { + void write(int var1); + + void write(byte[] var1); + + void write(byte[] var1, int var2, int var3); + + void writeBoolean(boolean var1); + + void writeByte(int var1); + + void writeShort(int var1); + + void writeChar(int var1); + + void writeInt(int var1); + + void writeLong(long var1); + + void writeFloat(float var1); + + void writeDouble(double var1); + + void writeChars(String var1); + + void writeUTF(String var1); + + /** @deprecated */ + @Deprecated + void writeBytes(String var1); + + byte[] toByteArray(); +} diff --git a/src/main/com/google/common/io/ByteProcessor.java b/src/main/com/google/common/io/ByteProcessor.java new file mode 100644 index 0000000..9c08a7a --- /dev/null +++ b/src/main/com/google/common/io/ByteProcessor.java @@ -0,0 +1,11 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import java.io.IOException; + +@Beta +public interface ByteProcessor { + boolean processBytes(byte[] var1, int var2, int var3) throws IOException; + + T getResult(); +} diff --git a/src/main/com/google/common/io/ByteSink.java b/src/main/com/google/common/io/ByteSink.java new file mode 100644 index 0000000..f94df64 --- /dev/null +++ b/src/main/com/google/common/io/ByteSink.java @@ -0,0 +1,84 @@ +package com.google.common.io; + +import com.google.common.base.Preconditions; +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; + +public abstract class ByteSink { + protected ByteSink() { + } + + public CharSink asCharSink(Charset charset) { + return new ByteSink.AsCharSink(charset); + } + + public abstract OutputStream openStream() throws IOException; + + public OutputStream openBufferedStream() throws IOException { + OutputStream out = this.openStream(); + return out instanceof BufferedOutputStream ? (BufferedOutputStream)out : new BufferedOutputStream(out); + } + + public void write(byte[] bytes) throws IOException { + Preconditions.checkNotNull(bytes); + Closer closer = Closer.create(); + + try { + OutputStream out = (OutputStream)closer.register(this.openStream()); + out.write(bytes); + out.flush(); + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + } + + public long writeFrom(InputStream input) throws IOException { + Preconditions.checkNotNull(input); + Closer closer = Closer.create(); + + long var6; + try { + OutputStream out = (OutputStream)closer.register(this.openStream()); + long written = ByteStreams.copy(input, out); + out.flush(); + var6 = written; + } catch (Throwable var11) { + throw closer.rethrow(var11); + } finally { + closer.close(); + } + + return var6; + } + + private final class AsCharSink extends CharSink { + private final Charset charset; + + private AsCharSink(Charset charset) { + this.charset = (Charset)Preconditions.checkNotNull(charset); + } + + public Writer openStream() throws IOException { + return new OutputStreamWriter(ByteSink.this.openStream(), this.charset); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(ByteSink.this.toString())); + String var2 = String.valueOf(String.valueOf(this.charset)); + return (new StringBuilder(13 + var1.length() + var2.length())).append(var1).append(".asCharSink(").append(var2).append(")").toString(); + } + + // $FF: synthetic method + AsCharSink(Charset x1, Object x2) { + this(x1); + } + } +} diff --git a/src/main/com/google/common/io/ByteSource.java b/src/main/com/google/common/io/ByteSource.java new file mode 100644 index 0000000..545932d --- /dev/null +++ b/src/main/com/google/common/io/ByteSource.java @@ -0,0 +1,441 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Ascii; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +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.ByteArrayInputStream; +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; +import java.util.Iterator; + +public abstract class ByteSource { + private static final int BUF_SIZE = 4096; + private static final byte[] countBuffer = new byte[4096]; + + protected ByteSource() { + } + + public CharSource asCharSource(Charset charset) { + return new ByteSource.AsCharSource(charset); + } + + public abstract InputStream openStream() throws IOException; + + public InputStream openBufferedStream() throws IOException { + InputStream in = this.openStream(); + return in instanceof BufferedInputStream ? (BufferedInputStream)in : new BufferedInputStream(in); + } + + public ByteSource slice(long offset, long length) { + return new ByteSource.SlicedByteSource(offset, length); + } + + public boolean isEmpty() throws IOException { + Closer closer = Closer.create(); + + boolean var3; + try { + InputStream in = (InputStream)closer.register(this.openStream()); + var3 = in.read() == -1; + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + return var3; + } + + public long size() throws IOException { + Closer closer = Closer.create(); + + InputStream in; + long var3; + try { + in = (InputStream)closer.register(this.openStream()); + var3 = this.countBySkipping(in); + return var3; + } catch (IOException var17) { + } finally { + closer.close(); + } + + closer = Closer.create(); + + try { + in = (InputStream)closer.register(this.openStream()); + var3 = this.countByReading(in); + } catch (Throwable var15) { + throw closer.rethrow(var15); + } finally { + closer.close(); + } + + return var3; + } + + private long countBySkipping(InputStream in) throws IOException { + long count = 0L; + + while(true) { + while(true) { + long skipped = in.skip((long)Math.min(in.available(), Integer.MAX_VALUE)); + if (skipped <= 0L) { + if (in.read() == -1) { + return count; + } + + if (count == 0L && in.available() == 0) { + throw new IOException(); + } + + ++count; + } else { + count += skipped; + } + } + } + } + + private long countByReading(InputStream in) throws IOException { + long count; + long read; + for(count = 0L; (read = (long)in.read(countBuffer)) != -1L; count += read) { + } + + return count; + } + + public long copyTo(OutputStream output) throws IOException { + Preconditions.checkNotNull(output); + Closer closer = Closer.create(); + + long var4; + try { + InputStream in = (InputStream)closer.register(this.openStream()); + var4 = ByteStreams.copy(in, output); + } catch (Throwable var9) { + throw closer.rethrow(var9); + } finally { + closer.close(); + } + + return var4; + } + + public long copyTo(ByteSink sink) throws IOException { + Preconditions.checkNotNull(sink); + Closer closer = Closer.create(); + + long var5; + try { + InputStream in = (InputStream)closer.register(this.openStream()); + OutputStream out = (OutputStream)closer.register(sink.openStream()); + var5 = ByteStreams.copy(in, out); + } catch (Throwable var10) { + throw closer.rethrow(var10); + } finally { + closer.close(); + } + + return var5; + } + + public byte[] read() throws IOException { + Closer closer = Closer.create(); + + byte[] var3; + try { + InputStream in = (InputStream)closer.register(this.openStream()); + var3 = ByteStreams.toByteArray(in); + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + return var3; + } + + @Beta + public T read(ByteProcessor processor) throws IOException { + Preconditions.checkNotNull(processor); + Closer closer = Closer.create(); + + Object var4; + try { + InputStream in = (InputStream)closer.register(this.openStream()); + var4 = ByteStreams.readBytes(in, processor); + } catch (Throwable var8) { + throw closer.rethrow(var8); + } finally { + closer.close(); + } + + return var4; + } + + public HashCode hash(HashFunction hashFunction) throws IOException { + Hasher hasher = hashFunction.newHasher(); + this.copyTo(Funnels.asOutputStream(hasher)); + return hasher.hash(); + } + + public boolean contentEquals(ByteSource other) throws IOException { + Preconditions.checkNotNull(other); + byte[] buf1 = new byte[4096]; + byte[] buf2 = new byte[4096]; + Closer closer = Closer.create(); + + try { + InputStream in1 = (InputStream)closer.register(this.openStream()); + InputStream in2 = (InputStream)closer.register(other.openStream()); + + int read1; + boolean var9; + do { + read1 = ByteStreams.read(in1, buf1, 0, 4096); + int read2 = ByteStreams.read(in2, buf2, 0, 4096); + if (read1 != read2 || !Arrays.equals(buf1, buf2)) { + var9 = false; + return var9; + } + } while(read1 == 4096); + + var9 = true; + return var9; + } catch (Throwable var13) { + throw closer.rethrow(var13); + } finally { + closer.close(); + } + } + + public static ByteSource concat(Iterable sources) { + return new ByteSource.ConcatenatedByteSource(sources); + } + + public static ByteSource concat(Iterator sources) { + return concat((Iterable)ImmutableList.copyOf(sources)); + } + + public static ByteSource concat(ByteSource... sources) { + return concat((Iterable)ImmutableList.copyOf((Object[])sources)); + } + + public static ByteSource wrap(byte[] b) { + return new ByteSource.ByteArrayByteSource(b); + } + + public static ByteSource empty() { + return ByteSource.EmptyByteSource.INSTANCE; + } + + private static final class ConcatenatedByteSource extends ByteSource { + private final Iterable sources; + + ConcatenatedByteSource(Iterable sources) { + this.sources = (Iterable)Preconditions.checkNotNull(sources); + } + + public InputStream openStream() throws IOException { + return new MultiInputStream(this.sources.iterator()); + } + + public boolean isEmpty() throws IOException { + Iterator i$ = this.sources.iterator(); + + ByteSource source; + do { + if (!i$.hasNext()) { + return true; + } + + source = (ByteSource)i$.next(); + } while(source.isEmpty()); + + return false; + } + + public long size() throws IOException { + long result = 0L; + + ByteSource source; + for(Iterator i$ = this.sources.iterator(); i$.hasNext(); result += source.size()) { + source = (ByteSource)i$.next(); + } + + return result; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.sources)); + return (new StringBuilder(19 + var1.length())).append("ByteSource.concat(").append(var1).append(")").toString(); + } + } + + private static final class EmptyByteSource extends ByteSource.ByteArrayByteSource { + private static final ByteSource.EmptyByteSource INSTANCE = new ByteSource.EmptyByteSource(); + + private EmptyByteSource() { + super(new byte[0]); + } + + public CharSource asCharSource(Charset charset) { + Preconditions.checkNotNull(charset); + return CharSource.empty(); + } + + public byte[] read() { + return this.bytes; + } + + public String toString() { + return "ByteSource.empty()"; + } + } + + private static class ByteArrayByteSource extends ByteSource { + protected final byte[] bytes; + + protected ByteArrayByteSource(byte[] bytes) { + this.bytes = (byte[])Preconditions.checkNotNull(bytes); + } + + public InputStream openStream() { + return new ByteArrayInputStream(this.bytes); + } + + public InputStream openBufferedStream() throws IOException { + return this.openStream(); + } + + public boolean isEmpty() { + return this.bytes.length == 0; + } + + public long size() { + return (long)this.bytes.length; + } + + public byte[] read() { + return (byte[])this.bytes.clone(); + } + + public long copyTo(OutputStream output) throws IOException { + output.write(this.bytes); + return (long)this.bytes.length; + } + + public T read(ByteProcessor processor) throws IOException { + processor.processBytes(this.bytes, 0, this.bytes.length); + return processor.getResult(); + } + + public HashCode hash(HashFunction hashFunction) throws IOException { + return hashFunction.hashBytes(this.bytes); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(Ascii.truncate(BaseEncoding.base16().encode(this.bytes), 30, "..."))); + return (new StringBuilder(17 + var1.length())).append("ByteSource.wrap(").append(var1).append(")").toString(); + } + } + + private final class SlicedByteSource extends ByteSource { + private final long offset; + private final long length; + + private SlicedByteSource(long offset, long length) { + Preconditions.checkArgument(offset >= 0L, "offset (%s) may not be negative", offset); + Preconditions.checkArgument(length >= 0L, "length (%s) may not be negative", length); + this.offset = offset; + this.length = length; + } + + public InputStream openStream() throws IOException { + return this.sliceStream(ByteSource.this.openStream()); + } + + public InputStream openBufferedStream() throws IOException { + return this.sliceStream(ByteSource.this.openBufferedStream()); + } + + private InputStream sliceStream(InputStream in) throws IOException { + if (this.offset > 0L) { + try { + ByteStreams.skipFully(in, this.offset); + } catch (Throwable var8) { + Throwable e = var8; + Closer closer = Closer.create(); + closer.register(in); + + try { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + } + + return ByteStreams.limit(in, this.length); + } + + public ByteSource slice(long offset, long length) { + Preconditions.checkArgument(offset >= 0L, "offset (%s) may not be negative", offset); + Preconditions.checkArgument(length >= 0L, "length (%s) may not be negative", length); + long maxLength = this.length - offset; + return ByteSource.this.slice(this.offset + offset, Math.min(length, maxLength)); + } + + public boolean isEmpty() throws IOException { + return this.length == 0L || super.isEmpty(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(ByteSource.this.toString())); + long var2 = this.offset; + long var4 = this.length; + return (new StringBuilder(50 + var1.length())).append(var1).append(".slice(").append(var2).append(", ").append(var4).append(")").toString(); + } + + // $FF: synthetic method + SlicedByteSource(long x1, long x2, Object x3) { + this(x1, x2); + } + } + + private final class AsCharSource extends CharSource { + private final Charset charset; + + private AsCharSource(Charset charset) { + this.charset = (Charset)Preconditions.checkNotNull(charset); + } + + public Reader openStream() throws IOException { + return new InputStreamReader(ByteSource.this.openStream(), this.charset); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(ByteSource.this.toString())); + String var2 = String.valueOf(String.valueOf(this.charset)); + return (new StringBuilder(15 + var1.length() + var2.length())).append(var1).append(".asCharSource(").append(var2).append(")").toString(); + } + + // $FF: synthetic method + AsCharSource(Charset x1, Object x2) { + this(x1); + } + } +} diff --git a/src/main/com/google/common/io/ByteStreams.java b/src/main/com/google/common/io/ByteStreams.java new file mode 100644 index 0000000..1ad6ae9 --- /dev/null +++ b/src/main/com/google/common/io/ByteStreams.java @@ -0,0 +1,544 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +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.util.Arrays; + +@Beta +public final class ByteStreams { + private static final int BUF_SIZE = 4096; + private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() { + public void write(int b) { + } + + public void write(byte[] b) { + Preconditions.checkNotNull(b); + } + + public void write(byte[] b, int off, int len) { + Preconditions.checkNotNull(b); + } + + public String toString() { + return "ByteStreams.nullOutputStream()"; + } + }; + + private ByteStreams() { + } + + public static long copy(InputStream from, OutputStream to) throws IOException { + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + byte[] buf = new byte[4096]; + long total = 0L; + + while(true) { + int r = from.read(buf); + if (r == -1) { + return total; + } + + to.write(buf, 0, r); + total += (long)r; + } + } + + public static long copy(ReadableByteChannel from, WritableByteChannel to) throws IOException { + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + ByteBuffer buf = ByteBuffer.allocate(4096); + long total = 0L; + + while(from.read(buf) != -1) { + buf.flip(); + + while(buf.hasRemaining()) { + total += (long)to.write(buf); + } + + buf.clear(); + } + + return total; + } + + public static byte[] toByteArray(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copy((InputStream)in, (OutputStream)out); + return out.toByteArray(); + } + + static byte[] toByteArray(InputStream in, int expectedSize) throws IOException { + byte[] bytes = new byte[expectedSize]; + + int b; + int read; + for(int remaining = expectedSize; remaining > 0; remaining -= read) { + b = expectedSize - remaining; + read = in.read(bytes, b, remaining); + if (read == -1) { + return Arrays.copyOf(bytes, b); + } + } + + b = in.read(); + if (b == -1) { + return bytes; + } else { + ByteStreams.FastByteArrayOutputStream out = new ByteStreams.FastByteArrayOutputStream(); + out.write(b); + copy((InputStream)in, (OutputStream)out); + byte[] result = new byte[bytes.length + out.size()]; + System.arraycopy(bytes, 0, result, 0, bytes.length); + out.writeTo(result, bytes.length); + return result; + } + } + + public static ByteArrayDataInput newDataInput(byte[] bytes) { + return newDataInput(new ByteArrayInputStream(bytes)); + } + + public static ByteArrayDataInput newDataInput(byte[] bytes, int start) { + Preconditions.checkPositionIndex(start, bytes.length); + return newDataInput(new ByteArrayInputStream(bytes, start, bytes.length - start)); + } + + public static ByteArrayDataInput newDataInput(ByteArrayInputStream byteArrayInputStream) { + return new ByteStreams.ByteArrayDataInputStream((ByteArrayInputStream)Preconditions.checkNotNull(byteArrayInputStream)); + } + + public static ByteArrayDataOutput newDataOutput() { + return newDataOutput(new ByteArrayOutputStream()); + } + + public static ByteArrayDataOutput newDataOutput(int size) { + Preconditions.checkArgument(size >= 0, "Invalid size: %s", size); + return newDataOutput(new ByteArrayOutputStream(size)); + } + + public static ByteArrayDataOutput newDataOutput(ByteArrayOutputStream byteArrayOutputSteam) { + return new ByteStreams.ByteArrayDataOutputStream((ByteArrayOutputStream)Preconditions.checkNotNull(byteArrayOutputSteam)); + } + + public static OutputStream nullOutputStream() { + return NULL_OUTPUT_STREAM; + } + + public static InputStream limit(InputStream in, long limit) { + return new ByteStreams.LimitedInputStream(in, limit); + } + + public static void readFully(InputStream in, byte[] b) throws IOException { + readFully(in, b, 0, b.length); + } + + 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((new StringBuilder(81)).append("reached end of stream after reading ").append(read).append(" bytes; ").append(len).append(" bytes expected").toString()); + } + } + + public static void skipFully(InputStream in, long n) throws IOException { + long toSkip = n; + + while(n > 0L) { + long amt = in.skip(n); + if (amt == 0L) { + if (in.read() == -1) { + long skipped = toSkip - n; + throw new EOFException((new StringBuilder(100)).append("reached end of stream after skipping ").append(skipped).append(" bytes; ").append(toSkip).append(" bytes expected").toString()); + } + + --n; + } else { + n -= amt; + } + } + + } + + public static T readBytes(InputStream input, ByteProcessor processor) throws IOException { + Preconditions.checkNotNull(input); + Preconditions.checkNotNull(processor); + byte[] buf = new byte[4096]; + + int read; + do { + read = input.read(buf); + } while(read != -1 && processor.processBytes(buf, 0, read)); + + return processor.getResult(); + } + + public static int read(InputStream in, byte[] b, int off, int len) throws IOException { + Preconditions.checkNotNull(in); + Preconditions.checkNotNull(b); + if (len < 0) { + throw new IndexOutOfBoundsException("len is negative"); + } else { + int total; + int result; + for(total = 0; total < len; total += result) { + result = in.read(b, off + total, len - total); + if (result == -1) { + break; + } + } + + return total; + } + } + + private static final class LimitedInputStream extends FilterInputStream { + private long left; + private long mark = -1L; + + LimitedInputStream(InputStream in, long limit) { + super(in); + Preconditions.checkNotNull(in); + Preconditions.checkArgument(limit >= 0L, "limit must be non-negative"); + this.left = limit; + } + + public int available() throws IOException { + return (int)Math.min((long)this.in.available(), this.left); + } + + public synchronized void mark(int readLimit) { + this.in.mark(readLimit); + this.mark = this.left; + } + + public int read() throws IOException { + if (this.left == 0L) { + return -1; + } else { + int result = this.in.read(); + if (result != -1) { + --this.left; + } + + return result; + } + } + + public int read(byte[] b, int off, int len) throws IOException { + if (this.left == 0L) { + return -1; + } else { + len = (int)Math.min((long)len, this.left); + int result = this.in.read(b, off, len); + if (result != -1) { + this.left -= (long)result; + } + + return result; + } + } + + public synchronized void reset() throws IOException { + if (!this.in.markSupported()) { + throw new IOException("Mark not supported"); + } else if (this.mark == -1L) { + throw new IOException("Mark not set"); + } else { + this.in.reset(); + this.left = this.mark; + } + } + + public long skip(long n) throws IOException { + n = Math.min(n, this.left); + long skipped = this.in.skip(n); + this.left -= skipped; + return skipped; + } + } + + private static class ByteArrayDataOutputStream implements ByteArrayDataOutput { + final DataOutput output; + final ByteArrayOutputStream byteArrayOutputSteam; + + ByteArrayDataOutputStream(ByteArrayOutputStream byteArrayOutputSteam) { + this.byteArrayOutputSteam = byteArrayOutputSteam; + this.output = new DataOutputStream(byteArrayOutputSteam); + } + + public void write(int b) { + try { + this.output.write(b); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void write(byte[] b) { + try { + this.output.write(b); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void write(byte[] b, int off, int len) { + try { + this.output.write(b, off, len); + } catch (IOException var5) { + throw new AssertionError(var5); + } + } + + public void writeBoolean(boolean v) { + try { + this.output.writeBoolean(v); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeByte(int v) { + try { + this.output.writeByte(v); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeBytes(String s) { + try { + this.output.writeBytes(s); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeChar(int v) { + try { + this.output.writeChar(v); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeChars(String s) { + try { + this.output.writeChars(s); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeDouble(double v) { + try { + this.output.writeDouble(v); + } catch (IOException var4) { + throw new AssertionError(var4); + } + } + + public void writeFloat(float v) { + try { + this.output.writeFloat(v); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeInt(int v) { + try { + this.output.writeInt(v); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeLong(long v) { + try { + this.output.writeLong(v); + } catch (IOException var4) { + throw new AssertionError(var4); + } + } + + public void writeShort(int v) { + try { + this.output.writeShort(v); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public void writeUTF(String s) { + try { + this.output.writeUTF(s); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public byte[] toByteArray() { + return this.byteArrayOutputSteam.toByteArray(); + } + } + + private static class ByteArrayDataInputStream implements ByteArrayDataInput { + final DataInput input; + + ByteArrayDataInputStream(ByteArrayInputStream byteArrayInputStream) { + this.input = new DataInputStream(byteArrayInputStream); + } + + public void readFully(byte[] b) { + try { + this.input.readFully(b); + } catch (IOException var3) { + throw new IllegalStateException(var3); + } + } + + public void readFully(byte[] b, int off, int len) { + try { + this.input.readFully(b, off, len); + } catch (IOException var5) { + throw new IllegalStateException(var5); + } + } + + public int skipBytes(int n) { + try { + return this.input.skipBytes(n); + } catch (IOException var3) { + throw new IllegalStateException(var3); + } + } + + public boolean readBoolean() { + try { + return this.input.readBoolean(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public byte readByte() { + try { + return this.input.readByte(); + } catch (EOFException var2) { + throw new IllegalStateException(var2); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } + + public int readUnsignedByte() { + try { + return this.input.readUnsignedByte(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public short readShort() { + try { + return this.input.readShort(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public int readUnsignedShort() { + try { + return this.input.readUnsignedShort(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public char readChar() { + try { + return this.input.readChar(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public int readInt() { + try { + return this.input.readInt(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public long readLong() { + try { + return this.input.readLong(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public float readFloat() { + try { + return this.input.readFloat(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public double readDouble() { + try { + return this.input.readDouble(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public String readLine() { + try { + return this.input.readLine(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + + public String readUTF() { + try { + return this.input.readUTF(); + } catch (IOException var2) { + throw new IllegalStateException(var2); + } + } + } + + private static final class FastByteArrayOutputStream extends ByteArrayOutputStream { + private FastByteArrayOutputStream() { + } + + void writeTo(byte[] b, int off) { + System.arraycopy(this.buf, 0, b, off, this.count); + } + + // $FF: synthetic method + FastByteArrayOutputStream(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/io/CharSequenceReader.java b/src/main/com/google/common/io/CharSequenceReader.java new file mode 100644 index 0000000..1bf7d0a --- /dev/null +++ b/src/main/com/google/common/io/CharSequenceReader.java @@ -0,0 +1,99 @@ +package com.google.common.io; + +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.Reader; +import java.nio.CharBuffer; + +final class CharSequenceReader extends Reader { + private CharSequence seq; + private int pos; + private int mark; + + public CharSequenceReader(CharSequence seq) { + this.seq = (CharSequence)Preconditions.checkNotNull(seq); + } + + private void checkOpen() throws IOException { + if (this.seq == null) { + throw new IOException("reader closed"); + } + } + + private boolean hasRemaining() { + return this.remaining() > 0; + } + + private int remaining() { + return this.seq.length() - this.pos; + } + + public synchronized int read(CharBuffer target) throws IOException { + Preconditions.checkNotNull(target); + this.checkOpen(); + if (!this.hasRemaining()) { + return -1; + } else { + int charsToRead = Math.min(target.remaining(), this.remaining()); + + for(int i = 0; i < charsToRead; ++i) { + target.put(this.seq.charAt(this.pos++)); + } + + return charsToRead; + } + } + + public synchronized int read() throws IOException { + this.checkOpen(); + return this.hasRemaining() ? this.seq.charAt(this.pos++) : -1; + } + + public synchronized int read(char[] cbuf, int off, int len) throws IOException { + Preconditions.checkPositionIndexes(off, off + len, cbuf.length); + this.checkOpen(); + if (!this.hasRemaining()) { + return -1; + } else { + int charsToRead = Math.min(len, this.remaining()); + + for(int i = 0; i < charsToRead; ++i) { + cbuf[off + i] = this.seq.charAt(this.pos++); + } + + return charsToRead; + } + } + + public synchronized long skip(long n) throws IOException { + Preconditions.checkArgument(n >= 0L, "n (%s) may not be negative", n); + this.checkOpen(); + int charsToSkip = (int)Math.min((long)this.remaining(), n); + this.pos += charsToSkip; + return (long)charsToSkip; + } + + public synchronized boolean ready() throws IOException { + this.checkOpen(); + return true; + } + + public boolean markSupported() { + return true; + } + + public synchronized void mark(int readAheadLimit) throws IOException { + Preconditions.checkArgument(readAheadLimit >= 0, "readAheadLimit (%s) may not be negative", readAheadLimit); + this.checkOpen(); + this.mark = this.pos; + } + + public synchronized void reset() throws IOException { + this.checkOpen(); + this.pos = this.mark; + } + + public synchronized void close() throws IOException { + this.seq = null; + } +} diff --git a/src/main/com/google/common/io/CharSink.java b/src/main/com/google/common/io/CharSink.java new file mode 100644 index 0000000..aa48611 --- /dev/null +++ b/src/main/com/google/common/io/CharSink.java @@ -0,0 +1,80 @@ +package com.google.common.io; + +import com.google.common.base.Preconditions; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Iterator; + +public abstract class CharSink { + protected CharSink() { + } + + public abstract Writer openStream() throws IOException; + + public Writer openBufferedStream() throws IOException { + Writer writer = this.openStream(); + return writer instanceof BufferedWriter ? (BufferedWriter)writer : new BufferedWriter(writer); + } + + public void write(CharSequence charSequence) throws IOException { + Preconditions.checkNotNull(charSequence); + Closer closer = Closer.create(); + + try { + Writer out = (Writer)closer.register(this.openStream()); + out.append(charSequence); + out.flush(); + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + } + + public void writeLines(Iterable lines) throws IOException { + this.writeLines(lines, System.getProperty("line.separator")); + } + + public void writeLines(Iterable lines, String lineSeparator) throws IOException { + Preconditions.checkNotNull(lines); + Preconditions.checkNotNull(lineSeparator); + Closer closer = Closer.create(); + + try { + Writer out = (Writer)closer.register(this.openBufferedStream()); + Iterator i$ = lines.iterator(); + + while(i$.hasNext()) { + CharSequence line = (CharSequence)i$.next(); + out.append(line).append(lineSeparator); + } + + out.flush(); + } catch (Throwable var10) { + throw closer.rethrow(var10); + } finally { + closer.close(); + } + } + + public long writeFrom(Readable readable) throws IOException { + Preconditions.checkNotNull(readable); + Closer closer = Closer.create(); + + long var6; + try { + Writer out = (Writer)closer.register(this.openStream()); + long written = CharStreams.copy(readable, out); + out.flush(); + var6 = written; + } catch (Throwable var11) { + throw closer.rethrow(var11); + } finally { + closer.close(); + } + + return var6; + } +} diff --git a/src/main/com/google/common/io/CharSource.java b/src/main/com/google/common/io/CharSource.java new file mode 100644 index 0000000..1aa0e5e --- /dev/null +++ b/src/main/com/google/common/io/CharSource.java @@ -0,0 +1,290 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Ascii; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.collect.AbstractIterator; +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.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +public abstract class CharSource { + protected CharSource() { + } + + public abstract Reader openStream() throws IOException; + + public BufferedReader openBufferedStream() throws IOException { + Reader reader = this.openStream(); + return reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader); + } + + public long copyTo(Appendable appendable) throws IOException { + Preconditions.checkNotNull(appendable); + Closer closer = Closer.create(); + + long var4; + try { + Reader reader = (Reader)closer.register(this.openStream()); + var4 = CharStreams.copy(reader, appendable); + } catch (Throwable var9) { + throw closer.rethrow(var9); + } finally { + closer.close(); + } + + return var4; + } + + public long copyTo(CharSink sink) throws IOException { + Preconditions.checkNotNull(sink); + Closer closer = Closer.create(); + + long var5; + try { + Reader reader = (Reader)closer.register(this.openStream()); + Writer writer = (Writer)closer.register(sink.openStream()); + var5 = CharStreams.copy(reader, writer); + } catch (Throwable var10) { + throw closer.rethrow(var10); + } finally { + closer.close(); + } + + return var5; + } + + public String read() throws IOException { + Closer closer = Closer.create(); + + String var3; + try { + Reader reader = (Reader)closer.register(this.openStream()); + var3 = CharStreams.toString(reader); + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + return var3; + } + + @Nullable + public String readFirstLine() throws IOException { + Closer closer = Closer.create(); + + String var3; + try { + BufferedReader reader = (BufferedReader)closer.register(this.openBufferedStream()); + var3 = reader.readLine(); + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + return var3; + } + + public ImmutableList readLines() throws IOException { + Closer closer = Closer.create(); + + try { + BufferedReader reader = (BufferedReader)closer.register(this.openBufferedStream()); + ArrayList result = Lists.newArrayList(); + + String line; + while((line = reader.readLine()) != null) { + result.add(line); + } + + ImmutableList var5 = ImmutableList.copyOf((Collection)result); + return var5; + } catch (Throwable var9) { + throw closer.rethrow(var9); + } finally { + closer.close(); + } + } + + @Beta + public T readLines(LineProcessor processor) throws IOException { + Preconditions.checkNotNull(processor); + Closer closer = Closer.create(); + + Object var4; + try { + Reader reader = (Reader)closer.register(this.openStream()); + var4 = CharStreams.readLines(reader, processor); + } catch (Throwable var8) { + throw closer.rethrow(var8); + } finally { + closer.close(); + } + + return var4; + } + + public boolean isEmpty() throws IOException { + Closer closer = Closer.create(); + + boolean var3; + try { + Reader reader = (Reader)closer.register(this.openStream()); + var3 = reader.read() == -1; + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + return var3; + } + + public static CharSource concat(Iterable sources) { + return new CharSource.ConcatenatedCharSource(sources); + } + + public static CharSource concat(Iterator sources) { + return concat((Iterable)ImmutableList.copyOf(sources)); + } + + public static CharSource concat(CharSource... sources) { + return concat((Iterable)ImmutableList.copyOf((Object[])sources)); + } + + public static CharSource wrap(CharSequence charSequence) { + return new CharSource.CharSequenceCharSource(charSequence); + } + + public static CharSource empty() { + return CharSource.EmptyCharSource.INSTANCE; + } + + private static final class ConcatenatedCharSource extends CharSource { + private final Iterable sources; + + ConcatenatedCharSource(Iterable sources) { + this.sources = (Iterable)Preconditions.checkNotNull(sources); + } + + public Reader openStream() throws IOException { + return new MultiReader(this.sources.iterator()); + } + + public boolean isEmpty() throws IOException { + Iterator i$ = this.sources.iterator(); + + CharSource source; + do { + if (!i$.hasNext()) { + return true; + } + + source = (CharSource)i$.next(); + } while(source.isEmpty()); + + return false; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.sources)); + return (new StringBuilder(19 + var1.length())).append("CharSource.concat(").append(var1).append(")").toString(); + } + } + + private static final class EmptyCharSource extends CharSource.CharSequenceCharSource { + private static final CharSource.EmptyCharSource INSTANCE = new CharSource.EmptyCharSource(); + + private EmptyCharSource() { + super(""); + } + + public String toString() { + return "CharSource.empty()"; + } + } + + private static class CharSequenceCharSource extends CharSource { + private static final Splitter LINE_SPLITTER = Splitter.on(Pattern.compile("\r\n|\n|\r")); + private final CharSequence seq; + + protected CharSequenceCharSource(CharSequence seq) { + this.seq = (CharSequence)Preconditions.checkNotNull(seq); + } + + public Reader openStream() { + return new CharSequenceReader(this.seq); + } + + public String read() { + return this.seq.toString(); + } + + public boolean isEmpty() { + return this.seq.length() == 0; + } + + private Iterable lines() { + return new Iterable() { + public Iterator iterator() { + return new AbstractIterator() { + Iterator lines; + + { + this.lines = CharSource.CharSequenceCharSource.LINE_SPLITTER.split(CharSequenceCharSource.this.seq).iterator(); + } + + protected String computeNext() { + if (this.lines.hasNext()) { + String next = (String)this.lines.next(); + if (this.lines.hasNext() || !next.isEmpty()) { + return next; + } + } + + return (String)this.endOfData(); + } + }; + } + }; + } + + public String readFirstLine() { + Iterator lines = this.lines().iterator(); + return lines.hasNext() ? (String)lines.next() : null; + } + + public ImmutableList readLines() { + return ImmutableList.copyOf(this.lines()); + } + + public T readLines(LineProcessor processor) throws IOException { + Iterator i$ = this.lines().iterator(); + + while(i$.hasNext()) { + String line = (String)i$.next(); + if (!processor.processLine(line)) { + break; + } + } + + return processor.getResult(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(Ascii.truncate(this.seq, 30, "..."))); + return (new StringBuilder(17 + var1.length())).append("CharSource.wrap(").append(var1).append(")").toString(); + } + } +} diff --git a/src/main/com/google/common/io/CharStreams.java b/src/main/com/google/common/io/CharStreams.java new file mode 100644 index 0000000..b8cdb39 --- /dev/null +++ b/src/main/com/google/common/io/CharStreams.java @@ -0,0 +1,163 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.CharBuffer; +import java.util.ArrayList; +import java.util.List; + +@Beta +public final class CharStreams { + private static final int BUF_SIZE = 2048; + + private CharStreams() { + } + + public static long copy(Readable from, Appendable to) throws IOException { + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + CharBuffer buf = CharBuffer.allocate(2048); + long total = 0L; + + while(from.read(buf) != -1) { + buf.flip(); + to.append(buf); + total += (long)buf.remaining(); + buf.clear(); + } + + return total; + } + + public static String toString(Readable r) throws IOException { + return toStringBuilder(r).toString(); + } + + private static StringBuilder toStringBuilder(Readable r) throws IOException { + StringBuilder sb = new StringBuilder(); + copy(r, sb); + return sb; + } + + public static List readLines(Readable r) throws IOException { + List result = new ArrayList(); + LineReader lineReader = new LineReader(r); + + String line; + while((line = lineReader.readLine()) != null) { + result.add(line); + } + + return result; + } + + public static T readLines(Readable readable, LineProcessor processor) throws IOException { + Preconditions.checkNotNull(readable); + Preconditions.checkNotNull(processor); + LineReader lineReader = new LineReader(readable); + + String line; + while((line = lineReader.readLine()) != null && processor.processLine(line)) { + } + + return processor.getResult(); + } + + public static void skipFully(Reader reader, long n) throws IOException { + Preconditions.checkNotNull(reader); + + while(n > 0L) { + long amt = reader.skip(n); + if (amt == 0L) { + if (reader.read() == -1) { + throw new EOFException(); + } + + --n; + } else { + n -= amt; + } + } + + } + + public static Writer nullWriter() { + return CharStreams.NullWriter.INSTANCE; + } + + public static Writer asWriter(Appendable target) { + return (Writer)(target instanceof Writer ? (Writer)target : new AppendableWriter(target)); + } + + static Reader asReader(final Readable readable) { + Preconditions.checkNotNull(readable); + return readable instanceof Reader ? (Reader)readable : new Reader() { + public int read(char[] cbuf, int off, int len) throws IOException { + return this.read(CharBuffer.wrap(cbuf, off, len)); + } + + public int read(CharBuffer target) throws IOException { + return readable.read(target); + } + + public void close() throws IOException { + if (readable instanceof Closeable) { + ((Closeable)readable).close(); + } + + } + }; + } + + private static final class NullWriter extends Writer { + private static final CharStreams.NullWriter INSTANCE = new CharStreams.NullWriter(); + + public void write(int c) { + } + + public void write(char[] cbuf) { + Preconditions.checkNotNull(cbuf); + } + + public void write(char[] cbuf, int off, int len) { + Preconditions.checkPositionIndexes(off, off + len, cbuf.length); + } + + public void write(String str) { + Preconditions.checkNotNull(str); + } + + public void write(String str, int off, int len) { + Preconditions.checkPositionIndexes(off, off + len, str.length()); + } + + public Writer append(CharSequence csq) { + Preconditions.checkNotNull(csq); + return this; + } + + public Writer append(CharSequence csq, int start, int end) { + Preconditions.checkPositionIndexes(start, end, csq.length()); + return this; + } + + public Writer append(char c) { + return this; + } + + public void flush() { + } + + public void close() { + } + + public String toString() { + return "CharStreams.nullWriter()"; + } + } +} diff --git a/src/main/com/google/common/io/Closeables.java b/src/main/com/google/common/io/Closeables.java new file mode 100644 index 0000000..05ad315 --- /dev/null +++ b/src/main/com/google/common/io/Closeables.java @@ -0,0 +1,51 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +@Beta +public final class Closeables { + @VisibleForTesting + static final Logger logger = Logger.getLogger(Closeables.class.getName()); + + private Closeables() { + } + + public static void close(@Nullable Closeable closeable, boolean swallowIOException) throws IOException { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException var3) { + if (!swallowIOException) { + throw var3; + } + + logger.log(Level.WARNING, "IOException thrown while closing Closeable.", var3); + } + + } + } + + public static void closeQuietly(@Nullable InputStream inputStream) { + try { + close(inputStream, true); + } catch (IOException var2) { + throw new AssertionError(var2); + } + } + + public static void closeQuietly(@Nullable Reader reader) { + try { + close(reader, true); + } catch (IOException var2) { + throw new AssertionError(var2); + } + } +} diff --git a/src/main/com/google/common/io/Closer.java b/src/main/com/google/common/io/Closer.java new file mode 100644 index 0000000..6512ff2 --- /dev/null +++ b/src/main/com/google/common/io/Closer.java @@ -0,0 +1,136 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +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; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +@Beta +public final class Closer implements Closeable { + private static final Closer.Suppressor SUPPRESSOR; + @VisibleForTesting + final Closer.Suppressor suppressor; + private final Deque stack = new ArrayDeque(4); + private Throwable thrown; + + public static Closer create() { + return new Closer(SUPPRESSOR); + } + + @VisibleForTesting + Closer(Closer.Suppressor suppressor) { + this.suppressor = (Closer.Suppressor)Preconditions.checkNotNull(suppressor); + } + + public C register(@Nullable C closeable) { + if (closeable != null) { + this.stack.addFirst(closeable); + } + + return closeable; + } + + public RuntimeException rethrow(Throwable e) throws IOException { + Preconditions.checkNotNull(e); + this.thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + throw new RuntimeException(e); + } + + public RuntimeException rethrow(Throwable e, Class declaredType) throws IOException, X { + Preconditions.checkNotNull(e); + this.thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType); + throw new RuntimeException(e); + } + + public RuntimeException rethrow(Throwable e, Class declaredType1, Class declaredType2) throws IOException, X1, X2 { + Preconditions.checkNotNull(e); + this.thrown = e; + Throwables.propagateIfPossible(e, IOException.class); + Throwables.propagateIfPossible(e, declaredType1, declaredType2); + throw new RuntimeException(e); + } + + public void close() throws IOException { + Throwable throwable = this.thrown; + + while(!this.stack.isEmpty()) { + Closeable closeable = (Closeable)this.stack.removeFirst(); + + try { + closeable.close(); + } catch (Throwable var4) { + if (throwable == null) { + throwable = var4; + } else { + this.suppressor.suppress(closeable, throwable, var4); + } + } + } + + if (this.thrown == null && throwable != null) { + Throwables.propagateIfPossible(throwable, IOException.class); + throw new AssertionError(throwable); + } + } + + static { + SUPPRESSOR = (Closer.Suppressor)(Closer.SuppressingSuppressor.isAvailable() ? Closer.SuppressingSuppressor.INSTANCE : Closer.LoggingSuppressor.INSTANCE); + } + + @VisibleForTesting + static final class SuppressingSuppressor implements Closer.Suppressor { + static final Closer.SuppressingSuppressor INSTANCE = new Closer.SuppressingSuppressor(); + static final Method addSuppressed = getAddSuppressed(); + + static boolean isAvailable() { + return addSuppressed != null; + } + + private static Method getAddSuppressed() { + try { + return Throwable.class.getMethod("addSuppressed", Throwable.class); + } catch (Throwable var1) { + return null; + } + } + + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + if (thrown != suppressed) { + try { + addSuppressed.invoke(thrown, suppressed); + } catch (Throwable var5) { + Closer.LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed); + } + + } + } + } + + @VisibleForTesting + static final class LoggingSuppressor implements Closer.Suppressor { + static final Closer.LoggingSuppressor INSTANCE = new Closer.LoggingSuppressor(); + + public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) { + Logger var10000 = Closeables.logger; + Level var10001 = Level.WARNING; + String var4 = String.valueOf(String.valueOf(closeable)); + var10000.log(var10001, (new StringBuilder(42 + var4.length())).append("Suppressing exception thrown when closing ").append(var4).toString(), suppressed); + } + } + + @VisibleForTesting + interface Suppressor { + void suppress(Closeable var1, Throwable var2, Throwable var3); + } +} diff --git a/src/main/com/google/common/io/CountingInputStream.java b/src/main/com/google/common/io/CountingInputStream.java new file mode 100644 index 0000000..d64d361 --- /dev/null +++ b/src/main/com/google/common/io/CountingInputStream.java @@ -0,0 +1,61 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import javax.annotation.Nullable; + +@Beta +public final class CountingInputStream extends FilterInputStream { + private long count; + private long mark = -1L; + + public CountingInputStream(@Nullable InputStream in) { + super(in); + } + + public long getCount() { + return this.count; + } + + public int read() throws IOException { + int result = this.in.read(); + if (result != -1) { + ++this.count; + } + + return result; + } + + public int read(byte[] b, int off, int len) throws IOException { + int result = this.in.read(b, off, len); + if (result != -1) { + this.count += (long)result; + } + + return result; + } + + public long skip(long n) throws IOException { + long result = this.in.skip(n); + this.count += result; + return result; + } + + public synchronized void mark(int readlimit) { + this.in.mark(readlimit); + this.mark = this.count; + } + + public synchronized void reset() throws IOException { + if (!this.in.markSupported()) { + throw new IOException("Mark not supported"); + } else if (this.mark == -1L) { + throw new IOException("Mark not set"); + } else { + this.in.reset(); + this.count = this.mark; + } + } +} diff --git a/src/main/com/google/common/io/CountingOutputStream.java b/src/main/com/google/common/io/CountingOutputStream.java new file mode 100644 index 0000000..8edf0d9 --- /dev/null +++ b/src/main/com/google/common/io/CountingOutputStream.java @@ -0,0 +1,34 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.annotation.Nullable; + +@Beta +public final class CountingOutputStream extends FilterOutputStream { + private long count; + + public CountingOutputStream(@Nullable OutputStream out) { + super(out); + } + + public long getCount() { + return this.count; + } + + public void write(byte[] b, int off, int len) throws IOException { + this.out.write(b, off, len); + this.count += (long)len; + } + + public void write(int b) throws IOException { + this.out.write(b); + ++this.count; + } + + public void close() throws IOException { + this.out.close(); + } +} diff --git a/src/main/com/google/common/io/FileBackedOutputStream.java b/src/main/com/google/common/io/FileBackedOutputStream.java new file mode 100644 index 0000000..d381412 --- /dev/null +++ b/src/main/com/google/common/io/FileBackedOutputStream.java @@ -0,0 +1,172 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +@Beta +public final class FileBackedOutputStream extends OutputStream { + private final int fileThreshold; + private final boolean resetOnFinalize; + private final ByteSource source; + private OutputStream out; + private FileBackedOutputStream.MemoryOutput memory; + private File file; + + @VisibleForTesting + synchronized File getFile() { + return this.file; + } + + public FileBackedOutputStream(int fileThreshold) { + this(fileThreshold, false); + } + + public FileBackedOutputStream(int fileThreshold, boolean resetOnFinalize) { + this.fileThreshold = fileThreshold; + this.resetOnFinalize = resetOnFinalize; + this.memory = new FileBackedOutputStream.MemoryOutput(); + this.out = this.memory; + if (resetOnFinalize) { + this.source = new ByteSource() { + public InputStream openStream() throws IOException { + return FileBackedOutputStream.this.openInputStream(); + } + + protected void finalize() { + try { + FileBackedOutputStream.this.reset(); + } catch (Throwable var2) { + var2.printStackTrace(System.err); + } + + } + }; + } else { + this.source = new ByteSource() { + public InputStream openStream() throws IOException { + return FileBackedOutputStream.this.openInputStream(); + } + }; + } + + } + + public ByteSource asByteSource() { + return this.source; + } + + private synchronized InputStream openInputStream() throws IOException { + return (InputStream)(this.file != null ? new FileInputStream(this.file) : new ByteArrayInputStream(this.memory.getBuffer(), 0, this.memory.getCount())); + } + + public synchronized void reset() throws IOException { + boolean var7 = false; + + try { + var7 = true; + this.close(); + var7 = false; + } finally { + if (var7) { + if (this.memory == null) { + this.memory = new FileBackedOutputStream.MemoryOutput(); + } else { + this.memory.reset(); + } + + this.out = this.memory; + if (this.file != null) { + File deleteMe = this.file; + this.file = null; + if (!deleteMe.delete()) { + String var5 = String.valueOf(String.valueOf(deleteMe)); + throw new IOException((new StringBuilder(18 + var5.length())).append("Could not delete: ").append(var5).toString()); + } + } + + } + } + + if (this.memory == null) { + this.memory = new FileBackedOutputStream.MemoryOutput(); + } else { + this.memory.reset(); + } + + this.out = this.memory; + if (this.file != null) { + File deleteMe = this.file; + this.file = null; + if (!deleteMe.delete()) { + String var2 = String.valueOf(String.valueOf(deleteMe)); + throw new IOException((new StringBuilder(18 + var2.length())).append("Could not delete: ").append(var2).toString()); + } + } + + } + + public synchronized void write(int b) throws IOException { + this.update(1); + this.out.write(b); + } + + public synchronized void write(byte[] b) throws IOException { + this.write(b, 0, b.length); + } + + public synchronized void write(byte[] b, int off, int len) throws IOException { + this.update(len); + this.out.write(b, off, len); + } + + public synchronized void close() throws IOException { + this.out.close(); + } + + public synchronized void flush() throws IOException { + this.out.flush(); + } + + private void update(int len) throws IOException { + if (this.file == null && this.memory.getCount() + len > this.fileThreshold) { + File temp = File.createTempFile("FileBackedOutputStream", (String)null); + if (this.resetOnFinalize) { + temp.deleteOnExit(); + } + + FileOutputStream transfer = new FileOutputStream(temp); + transfer.write(this.memory.getBuffer(), 0, this.memory.getCount()); + transfer.flush(); + this.out = transfer; + this.file = temp; + this.memory = null; + } + + } + + private static class MemoryOutput extends ByteArrayOutputStream { + private MemoryOutput() { + } + + byte[] getBuffer() { + return this.buf; + } + + int getCount() { + return this.count; + } + + // $FF: synthetic method + MemoryOutput(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/io/FileWriteMode.java b/src/main/com/google/common/io/FileWriteMode.java new file mode 100644 index 0000000..3969f7b --- /dev/null +++ b/src/main/com/google/common/io/FileWriteMode.java @@ -0,0 +1,5 @@ +package com.google.common.io; + +public enum FileWriteMode { + APPEND; +} diff --git a/src/main/com/google/common/io/Files.java b/src/main/com/google/common/io/Files.java new file mode 100644 index 0000000..7440148 --- /dev/null +++ b/src/main/com/google/common/io/Files.java @@ -0,0 +1,474 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.TreeTraverser; +import com.google.common.hash.HashCode; +import com.google.common.hash.HashFunction; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.RandomAccessFile; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +@Beta +public final class Files { + private static final int TEMP_DIR_ATTEMPTS = 10000; + private static final TreeTraverser FILE_TREE_TRAVERSER = new TreeTraverser() { + public Iterable children(File file) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + return Collections.unmodifiableList(Arrays.asList(files)); + } + } + + return Collections.emptyList(); + } + + public String toString() { + return "Files.fileTreeTraverser()"; + } + }; + + private Files() { + } + + public static BufferedReader newReader(File file, Charset charset) throws FileNotFoundException { + Preconditions.checkNotNull(file); + Preconditions.checkNotNull(charset); + return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); + } + + public static BufferedWriter newWriter(File file, Charset charset) throws FileNotFoundException { + Preconditions.checkNotNull(file); + Preconditions.checkNotNull(charset); + return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)); + } + + public static ByteSource asByteSource(File file) { + return new Files.FileByteSource(file); + } + + static byte[] readFile(InputStream in, long expectedSize) throws IOException { + if (expectedSize > 2147483647L) { + throw new OutOfMemoryError((new StringBuilder(68)).append("file is too large to fit in a byte array: ").append(expectedSize).append(" bytes").toString()); + } else { + return expectedSize == 0L ? ByteStreams.toByteArray(in) : ByteStreams.toByteArray(in, (int)expectedSize); + } + } + + public static ByteSink asByteSink(File file, FileWriteMode... modes) { + return new Files.FileByteSink(file, modes); + } + + public static CharSource asCharSource(File file, Charset charset) { + return asByteSource(file).asCharSource(charset); + } + + public static CharSink asCharSink(File file, Charset charset, FileWriteMode... modes) { + return asByteSink(file, modes).asCharSink(charset); + } + + private static FileWriteMode[] modes(boolean append) { + return append ? new FileWriteMode[]{FileWriteMode.APPEND} : new FileWriteMode[0]; + } + + public static byte[] toByteArray(File file) throws IOException { + return asByteSource(file).read(); + } + + public static String toString(File file, Charset charset) throws IOException { + return asCharSource(file, charset).read(); + } + + public static void write(byte[] from, File to) throws IOException { + asByteSink(to).write(from); + } + + public static void copy(File from, OutputStream to) throws IOException { + asByteSource(from).copyTo(to); + } + + public static void copy(File from, File to) throws IOException { + Preconditions.checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); + asByteSource(from).copyTo(asByteSink(to)); + } + + public static void write(CharSequence from, File to, Charset charset) throws IOException { + asCharSink(to, charset).write(from); + } + + public static void append(CharSequence from, File to, Charset charset) throws IOException { + write(from, to, charset, true); + } + + private static void write(CharSequence from, File to, Charset charset, boolean append) throws IOException { + asCharSink(to, charset, modes(append)).write(from); + } + + public static void copy(File from, Charset charset, Appendable to) throws IOException { + asCharSource(from, charset).copyTo(to); + } + + public static boolean equal(File file1, File file2) throws IOException { + Preconditions.checkNotNull(file1); + Preconditions.checkNotNull(file2); + if (file1 != file2 && !file1.equals(file2)) { + long len1 = file1.length(); + long len2 = file2.length(); + return len1 != 0L && len2 != 0L && len1 != len2 ? false : asByteSource(file1).contentEquals(asByteSource(file2)); + } else { + return true; + } + } + + public static File createTempDir() { + File baseDir = new File(System.getProperty("java.io.tmpdir")); + long var2 = System.currentTimeMillis(); + String baseName = (new StringBuilder(21)).append(var2).append("-").toString(); + + String var6; + for(int counter = 0; counter < 10000; ++counter) { + var6 = String.valueOf(String.valueOf(baseName)); + File tempDir = new File(baseDir, (new StringBuilder(11 + var6.length())).append(var6).append(counter).toString()); + if (tempDir.mkdir()) { + return tempDir; + } + } + + String var8 = String.valueOf(String.valueOf("Failed to create directory within 10000 attempts (tried ")); + String var9 = String.valueOf(String.valueOf(baseName)); + var6 = String.valueOf(String.valueOf(baseName)); + short var7 = 9999; + throw new IllegalStateException((new StringBuilder(17 + var8.length() + var9.length() + var6.length())).append(var8).append(var9).append("0 to ").append(var6).append(var7).append(")").toString()); + } + + public static void touch(File file) throws IOException { + Preconditions.checkNotNull(file); + if (!file.createNewFile() && !file.setLastModified(System.currentTimeMillis())) { + String var1 = String.valueOf(String.valueOf(file)); + throw new IOException((new StringBuilder(38 + var1.length())).append("Unable to update modification time of ").append(var1).toString()); + } + } + + public static void createParentDirs(File file) throws IOException { + Preconditions.checkNotNull(file); + File parent = file.getCanonicalFile().getParentFile(); + if (parent != null) { + parent.mkdirs(); + if (!parent.isDirectory()) { + String var2 = String.valueOf(String.valueOf(file)); + throw new IOException((new StringBuilder(39 + var2.length())).append("Unable to create parent directories of ").append(var2).toString()); + } + } + } + + public static void move(File from, File to) throws IOException { + Preconditions.checkNotNull(from); + Preconditions.checkNotNull(to); + Preconditions.checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); + if (!from.renameTo(to)) { + copy(from, to); + if (!from.delete()) { + String var2; + if (!to.delete()) { + var2 = String.valueOf(String.valueOf(to)); + throw new IOException((new StringBuilder(17 + var2.length())).append("Unable to delete ").append(var2).toString()); + } + + var2 = String.valueOf(String.valueOf(from)); + throw new IOException((new StringBuilder(17 + var2.length())).append("Unable to delete ").append(var2).toString()); + } + } + + } + + public static String readFirstLine(File file, Charset charset) throws IOException { + return asCharSource(file, charset).readFirstLine(); + } + + public static List readLines(File file, Charset charset) throws IOException { + return (List)readLines(file, charset, new LineProcessor>() { + final List result = Lists.newArrayList(); + + public boolean processLine(String line) { + this.result.add(line); + return true; + } + + public List getResult() { + return this.result; + } + }); + } + + public static T readLines(File file, Charset charset, LineProcessor callback) throws IOException { + return asCharSource(file, charset).readLines(callback); + } + + public static T readBytes(File file, ByteProcessor processor) throws IOException { + return asByteSource(file).read(processor); + } + + public static HashCode hash(File file, HashFunction hashFunction) throws IOException { + return asByteSource(file).hash(hashFunction); + } + + public static MappedByteBuffer map(File file) throws IOException { + Preconditions.checkNotNull(file); + return map(file, MapMode.READ_ONLY); + } + + public static MappedByteBuffer map(File file, MapMode mode) throws IOException { + Preconditions.checkNotNull(file); + Preconditions.checkNotNull(mode); + if (!file.exists()) { + throw new FileNotFoundException(file.toString()); + } else { + return map(file, mode, file.length()); + } + } + + public static MappedByteBuffer map(File file, MapMode mode, long size) throws FileNotFoundException, IOException { + Preconditions.checkNotNull(file); + Preconditions.checkNotNull(mode); + Closer closer = Closer.create(); + + MappedByteBuffer var6; + try { + RandomAccessFile raf = (RandomAccessFile)closer.register(new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw")); + var6 = map(raf, mode, size); + } catch (Throwable var10) { + throw closer.rethrow(var10); + } finally { + closer.close(); + } + + return var6; + } + + private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode, long size) throws IOException { + Closer closer = Closer.create(); + + MappedByteBuffer var6; + try { + FileChannel channel = (FileChannel)closer.register(raf.getChannel()); + var6 = channel.map(mode, 0L, size); + } catch (Throwable var10) { + throw closer.rethrow(var10); + } finally { + closer.close(); + } + + return var6; + } + + public static String simplifyPath(String pathname) { + Preconditions.checkNotNull(pathname); + if (pathname.length() == 0) { + return "."; + } else { + Iterable components = Splitter.on('/').omitEmptyStrings().split(pathname); + List path = new ArrayList(); + Iterator i$ = components.iterator(); + + while(true) { + while(true) { + while(true) { + String component; + do { + if (!i$.hasNext()) { + String result = Joiner.on('/').join((Iterable)path); + if (pathname.charAt(0) == '/') { + String var10001 = String.valueOf(result); + String var10000; + if (var10001.length() != 0) { + var10000 = "/".concat(var10001); + } else { + String var10002 = new String; + var10000 = var10002; + var10002.("/"); + } + + result = var10000; + } + + while(result.startsWith("/../")) { + result = result.substring(3); + } + + if (result.equals("/..")) { + result = "/"; + } else if ("".equals(result)) { + result = "."; + } + + return result; + } + + component = (String)i$.next(); + } while(component.equals(".")); + + if (component.equals("..")) { + if (path.size() > 0 && !((String)path.get(path.size() - 1)).equals("..")) { + path.remove(path.size() - 1); + } else { + path.add(".."); + } + } else { + path.add(component); + } + } + } + } + } + } + + public static String getFileExtension(String fullName) { + Preconditions.checkNotNull(fullName); + String fileName = (new File(fullName)).getName(); + int dotIndex = fileName.lastIndexOf(46); + return dotIndex == -1 ? "" : fileName.substring(dotIndex + 1); + } + + public static String getNameWithoutExtension(String file) { + Preconditions.checkNotNull(file); + String fileName = (new File(file)).getName(); + int dotIndex = fileName.lastIndexOf(46); + return dotIndex == -1 ? fileName : fileName.substring(0, dotIndex); + } + + public static TreeTraverser fileTreeTraverser() { + return FILE_TREE_TRAVERSER; + } + + public static Predicate isDirectory() { + return Files.FilePredicate.IS_DIRECTORY; + } + + public static Predicate isFile() { + return Files.FilePredicate.IS_FILE; + } + + private static enum FilePredicate implements Predicate { + IS_DIRECTORY { + public boolean apply(File file) { + return file.isDirectory(); + } + + public String toString() { + return "Files.isDirectory()"; + } + }, + IS_FILE { + public boolean apply(File file) { + return file.isFile(); + } + + public String toString() { + return "Files.isFile()"; + } + }; + + private FilePredicate() { + } + + // $FF: synthetic method + FilePredicate(Object x2) { + this(); + } + } + + private static final class FileByteSink extends ByteSink { + private final File file; + private final ImmutableSet modes; + + private FileByteSink(File file, FileWriteMode... modes) { + this.file = (File)Preconditions.checkNotNull(file); + this.modes = ImmutableSet.copyOf((Object[])modes); + } + + public FileOutputStream openStream() throws IOException { + return new FileOutputStream(this.file, this.modes.contains(FileWriteMode.APPEND)); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.file)); + String var2 = String.valueOf(String.valueOf(this.modes)); + return (new StringBuilder(20 + var1.length() + var2.length())).append("Files.asByteSink(").append(var1).append(", ").append(var2).append(")").toString(); + } + + // $FF: synthetic method + FileByteSink(File x0, FileWriteMode[] x1, Object x2) { + this(x0, x1); + } + } + + private static final class FileByteSource extends ByteSource { + private final File file; + + private FileByteSource(File file) { + this.file = (File)Preconditions.checkNotNull(file); + } + + public FileInputStream openStream() throws IOException { + return new FileInputStream(this.file); + } + + public long size() throws IOException { + if (!this.file.isFile()) { + throw new FileNotFoundException(this.file.toString()); + } else { + return this.file.length(); + } + } + + public byte[] read() throws IOException { + Closer closer = Closer.create(); + + byte[] var3; + try { + FileInputStream in = (FileInputStream)closer.register(this.openStream()); + var3 = Files.readFile(in, in.getChannel().size()); + } catch (Throwable var7) { + throw closer.rethrow(var7); + } finally { + closer.close(); + } + + return var3; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.file)); + return (new StringBuilder(20 + var1.length())).append("Files.asByteSource(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + FileByteSource(File x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/io/Flushables.java b/src/main/com/google/common/io/Flushables.java new file mode 100644 index 0000000..86db38b --- /dev/null +++ b/src/main/com/google/common/io/Flushables.java @@ -0,0 +1,37 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import java.io.Flushable; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Beta +public final class Flushables { + private static final Logger logger = Logger.getLogger(Flushables.class.getName()); + + private Flushables() { + } + + public static void flush(Flushable flushable, boolean swallowIOException) throws IOException { + try { + flushable.flush(); + } catch (IOException var3) { + if (!swallowIOException) { + throw var3; + } + + logger.log(Level.WARNING, "IOException thrown while flushing Flushable.", var3); + } + + } + + public static void flushQuietly(Flushable flushable) { + try { + flush(flushable, true); + } catch (IOException var2) { + logger.log(Level.SEVERE, "IOException should not have been thrown.", var2); + } + + } +} diff --git a/src/main/com/google/common/io/GwtWorkarounds.java b/src/main/com/google/common/io/GwtWorkarounds.java new file mode 100644 index 0000000..7da34a3 --- /dev/null +++ b/src/main/com/google/common/io/GwtWorkarounds.java @@ -0,0 +1,170 @@ +package com.google.common.io; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; + +@GwtCompatible( + emulated = true +) +final class GwtWorkarounds { + private GwtWorkarounds() { + } + + @GwtIncompatible("Reader") + static GwtWorkarounds.CharInput asCharInput(final Reader reader) { + Preconditions.checkNotNull(reader); + return new GwtWorkarounds.CharInput() { + public int read() throws IOException { + return reader.read(); + } + + public void close() throws IOException { + reader.close(); + } + }; + } + + static GwtWorkarounds.CharInput asCharInput(final CharSequence chars) { + Preconditions.checkNotNull(chars); + return new GwtWorkarounds.CharInput() { + int index = 0; + + public int read() { + return this.index < chars.length() ? chars.charAt(this.index++) : -1; + } + + public void close() { + this.index = chars.length(); + } + }; + } + + @GwtIncompatible("InputStream") + static InputStream asInputStream(final GwtWorkarounds.ByteInput input) { + Preconditions.checkNotNull(input); + return new InputStream() { + public int read() throws IOException { + return input.read(); + } + + public int read(byte[] b, int off, int len) throws IOException { + Preconditions.checkNotNull(b); + Preconditions.checkPositionIndexes(off, off + len, b.length); + if (len == 0) { + return 0; + } else { + int firstByte = this.read(); + if (firstByte == -1) { + return -1; + } else { + b[off] = (byte)firstByte; + + for(int dst = 1; dst < len; ++dst) { + int readByte = this.read(); + if (readByte == -1) { + return dst; + } + + b[off + dst] = (byte)readByte; + } + + return len; + } + } + } + + public void close() throws IOException { + input.close(); + } + }; + } + + @GwtIncompatible("OutputStream") + static OutputStream asOutputStream(final GwtWorkarounds.ByteOutput output) { + Preconditions.checkNotNull(output); + return new OutputStream() { + public void write(int b) throws IOException { + output.write((byte)b); + } + + public void flush() throws IOException { + output.flush(); + } + + public void close() throws IOException { + output.close(); + } + }; + } + + @GwtIncompatible("Writer") + static GwtWorkarounds.CharOutput asCharOutput(final Writer writer) { + Preconditions.checkNotNull(writer); + return new GwtWorkarounds.CharOutput() { + public void write(char c) throws IOException { + writer.append(c); + } + + public void flush() throws IOException { + writer.flush(); + } + + public void close() throws IOException { + writer.close(); + } + }; + } + + static GwtWorkarounds.CharOutput stringBuilderOutput(int initialSize) { + final StringBuilder builder = new StringBuilder(initialSize); + return new GwtWorkarounds.CharOutput() { + public void write(char c) { + builder.append(c); + } + + public void flush() { + } + + public void close() { + } + + public String toString() { + return builder.toString(); + } + }; + } + + interface CharOutput { + void write(char var1) throws IOException; + + void flush() throws IOException; + + void close() throws IOException; + } + + interface ByteOutput { + void write(byte var1) throws IOException; + + void flush() throws IOException; + + void close() throws IOException; + } + + interface ByteInput { + int read() throws IOException; + + void close() throws IOException; + } + + interface CharInput { + int read() throws IOException; + + void close() throws IOException; + } +} diff --git a/src/main/com/google/common/io/InputSupplier.java b/src/main/com/google/common/io/InputSupplier.java new file mode 100644 index 0000000..d5a30ea --- /dev/null +++ b/src/main/com/google/common/io/InputSupplier.java @@ -0,0 +1,9 @@ +package com.google.common.io; + +import java.io.IOException; + +/** @deprecated */ +@Deprecated +public interface InputSupplier { + T getInput() throws IOException; +} diff --git a/src/main/com/google/common/io/LineBuffer.java b/src/main/com/google/common/io/LineBuffer.java new file mode 100644 index 0000000..245fcd6 --- /dev/null +++ b/src/main/com/google/common/io/LineBuffer.java @@ -0,0 +1,53 @@ +package com.google.common.io; + +import java.io.IOException; + +abstract class LineBuffer { + private StringBuilder line = new StringBuilder(); + private boolean sawReturn; + + protected void add(char[] cbuf, int off, int len) throws IOException { + int pos = off; + if (this.sawReturn && len > 0 && this.finishLine(cbuf[off] == '\n')) { + pos = off + 1; + } + + int start = pos; + + for(int end = off + len; pos < end; ++pos) { + switch(cbuf[pos]) { + case '\n': + this.line.append(cbuf, start, pos - start); + this.finishLine(true); + start = pos + 1; + break; + case '\r': + this.line.append(cbuf, start, pos - start); + this.sawReturn = true; + if (pos + 1 < end && this.finishLine(cbuf[pos + 1] == '\n')) { + ++pos; + } + + start = pos + 1; + } + } + + this.line.append(cbuf, start, off + len - start); + } + + private boolean finishLine(boolean sawNewline) throws IOException { + this.handleLine(this.line.toString(), this.sawReturn ? (sawNewline ? "\r\n" : "\r") : (sawNewline ? "\n" : "")); + this.line = new StringBuilder(); + this.sawReturn = false; + return sawNewline; + } + + protected void finish() throws IOException { + if (this.sawReturn || this.line.length() > 0) { + this.finishLine(false); + } + + } + + protected abstract void handleLine(String var1, String var2) throws IOException; +} diff --git a/src/main/com/google/common/io/LineProcessor.java b/src/main/com/google/common/io/LineProcessor.java new file mode 100644 index 0000000..f5cf8de --- /dev/null +++ b/src/main/com/google/common/io/LineProcessor.java @@ -0,0 +1,11 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import java.io.IOException; + +@Beta +public interface LineProcessor { + boolean processLine(String var1) throws IOException; + + T getResult(); +} diff --git a/src/main/com/google/common/io/LineReader.java b/src/main/com/google/common/io/LineReader.java new file mode 100644 index 0000000..a461bbc --- /dev/null +++ b/src/main/com/google/common/io/LineReader.java @@ -0,0 +1,48 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.Reader; +import java.nio.CharBuffer; +import java.util.LinkedList; +import java.util.Queue; + +@Beta +public final class LineReader { + private final Readable readable; + private final Reader reader; + private final char[] buf = new char[4096]; + private final CharBuffer cbuf; + private final Queue lines; + private final LineBuffer lineBuf; + + public LineReader(Readable readable) { + this.cbuf = CharBuffer.wrap(this.buf); + this.lines = new LinkedList(); + this.lineBuf = new LineBuffer() { + protected void handleLine(String line, String end) { + LineReader.this.lines.add(line); + } + }; + this.readable = (Readable)Preconditions.checkNotNull(readable); + this.reader = readable instanceof Reader ? (Reader)readable : null; + } + + public String readLine() throws IOException { + while(true) { + if (this.lines.peek() == null) { + this.cbuf.clear(); + int read = this.reader != null ? this.reader.read(this.buf, 0, this.buf.length) : this.readable.read(this.cbuf); + if (read != -1) { + this.lineBuf.add(this.buf, 0, read); + continue; + } + + this.lineBuf.finish(); + } + + return (String)this.lines.poll(); + } + } +} diff --git a/src/main/com/google/common/io/LittleEndianDataInputStream.java b/src/main/com/google/common/io/LittleEndianDataInputStream.java new file mode 100644 index 0000000..754e820 --- /dev/null +++ b/src/main/com/google/common/io/LittleEndianDataInputStream.java @@ -0,0 +1,107 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +@Beta +public final class LittleEndianDataInputStream extends FilterInputStream implements DataInput { + public LittleEndianDataInputStream(InputStream in) { + super((InputStream)Preconditions.checkNotNull(in)); + } + + public String readLine() { + throw new UnsupportedOperationException("readLine is not supported"); + } + + public void readFully(byte[] b) throws IOException { + ByteStreams.readFully(this, b); + } + + public void readFully(byte[] b, int off, int len) throws IOException { + ByteStreams.readFully(this, b, off, len); + } + + public int skipBytes(int n) throws IOException { + return (int)this.in.skip((long)n); + } + + public int readUnsignedByte() throws IOException { + int b1 = this.in.read(); + if (0 > b1) { + throw new EOFException(); + } else { + return b1; + } + } + + public int readUnsignedShort() throws IOException { + byte b1 = this.readAndCheckByte(); + byte b2 = this.readAndCheckByte(); + return Ints.fromBytes((byte)0, (byte)0, b2, b1); + } + + public int readInt() throws IOException { + byte b1 = this.readAndCheckByte(); + byte b2 = this.readAndCheckByte(); + byte b3 = this.readAndCheckByte(); + byte b4 = this.readAndCheckByte(); + return Ints.fromBytes(b4, b3, b2, b1); + } + + public long readLong() throws IOException { + byte b1 = this.readAndCheckByte(); + byte b2 = this.readAndCheckByte(); + byte b3 = this.readAndCheckByte(); + byte b4 = this.readAndCheckByte(); + byte b5 = this.readAndCheckByte(); + byte b6 = this.readAndCheckByte(); + byte b7 = this.readAndCheckByte(); + byte b8 = this.readAndCheckByte(); + return Longs.fromBytes(b8, b7, b6, b5, b4, b3, b2, b1); + } + + public float readFloat() throws IOException { + return Float.intBitsToFloat(this.readInt()); + } + + public double readDouble() throws IOException { + return Double.longBitsToDouble(this.readLong()); + } + + public String readUTF() throws IOException { + return (new DataInputStream(this.in)).readUTF(); + } + + public short readShort() throws IOException { + return (short)this.readUnsignedShort(); + } + + public char readChar() throws IOException { + return (char)this.readUnsignedShort(); + } + + public byte readByte() throws IOException { + return (byte)this.readUnsignedByte(); + } + + public boolean readBoolean() throws IOException { + return this.readUnsignedByte() != 0; + } + + private byte readAndCheckByte() throws IOException, EOFException { + int b1 = this.in.read(); + if (-1 == b1) { + throw new EOFException(); + } else { + return (byte)b1; + } + } +} diff --git a/src/main/com/google/common/io/LittleEndianDataOutputStream.java b/src/main/com/google/common/io/LittleEndianDataOutputStream.java new file mode 100644 index 0000000..86b52c4 --- /dev/null +++ b/src/main/com/google/common/io/LittleEndianDataOutputStream.java @@ -0,0 +1,79 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Longs; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +@Beta +public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput { + public LittleEndianDataOutputStream(OutputStream out) { + super(new DataOutputStream((OutputStream)Preconditions.checkNotNull(out))); + } + + public void write(byte[] b, int off, int len) throws IOException { + this.out.write(b, off, len); + } + + public void writeBoolean(boolean v) throws IOException { + ((DataOutputStream)this.out).writeBoolean(v); + } + + public void writeByte(int v) throws IOException { + ((DataOutputStream)this.out).writeByte(v); + } + + /** @deprecated */ + @Deprecated + public void writeBytes(String s) throws IOException { + ((DataOutputStream)this.out).writeBytes(s); + } + + public void writeChar(int v) throws IOException { + this.writeShort(v); + } + + public void writeChars(String s) throws IOException { + for(int i = 0; i < s.length(); ++i) { + this.writeChar(s.charAt(i)); + } + + } + + public void writeDouble(double v) throws IOException { + this.writeLong(Double.doubleToLongBits(v)); + } + + public void writeFloat(float v) throws IOException { + this.writeInt(Float.floatToIntBits(v)); + } + + public void writeInt(int v) throws IOException { + this.out.write(255 & v); + this.out.write(255 & v >> 8); + this.out.write(255 & v >> 16); + this.out.write(255 & v >> 24); + } + + public void writeLong(long v) throws IOException { + byte[] bytes = Longs.toByteArray(Long.reverseBytes(v)); + this.write(bytes, 0, bytes.length); + } + + public void writeShort(int v) throws IOException { + this.out.write(255 & v); + this.out.write(255 & v >> 8); + } + + public void writeUTF(String str) throws IOException { + ((DataOutputStream)this.out).writeUTF(str); + } + + public void close() throws IOException { + this.out.close(); + } +} diff --git a/src/main/com/google/common/io/MultiInputStream.java b/src/main/com/google/common/io/MultiInputStream.java new file mode 100644 index 0000000..6b12573 --- /dev/null +++ b/src/main/com/google/common/io/MultiInputStream.java @@ -0,0 +1,85 @@ +package com.google.common.io; + +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import javax.annotation.Nullable; + +final class MultiInputStream extends InputStream { + private Iterator it; + private InputStream in; + + public MultiInputStream(Iterator it) throws IOException { + this.it = (Iterator)Preconditions.checkNotNull(it); + this.advance(); + } + + public void close() throws IOException { + if (this.in != null) { + try { + this.in.close(); + } finally { + this.in = null; + } + } + + } + + private void advance() throws IOException { + this.close(); + if (this.it.hasNext()) { + this.in = ((ByteSource)this.it.next()).openStream(); + } + + } + + public int available() throws IOException { + return this.in == null ? 0 : this.in.available(); + } + + public boolean markSupported() { + return false; + } + + public int read() throws IOException { + if (this.in == null) { + return -1; + } else { + int result = this.in.read(); + if (result == -1) { + this.advance(); + return this.read(); + } else { + return result; + } + } + } + + public int read(@Nullable byte[] b, int off, int len) throws IOException { + if (this.in == null) { + return -1; + } else { + int result = this.in.read(b, off, len); + if (result == -1) { + this.advance(); + return this.read(b, off, len); + } else { + return result; + } + } + } + + public long skip(long n) throws IOException { + if (this.in != null && n > 0L) { + long result = this.in.skip(n); + if (result != 0L) { + return result; + } else { + return this.read() == -1 ? 0L : 1L + this.in.skip(n - 1L); + } + } else { + return 0L; + } + } +} diff --git a/src/main/com/google/common/io/MultiReader.java b/src/main/com/google/common/io/MultiReader.java new file mode 100644 index 0000000..3deef15 --- /dev/null +++ b/src/main/com/google/common/io/MultiReader.java @@ -0,0 +1,70 @@ +package com.google.common.io; + +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.Reader; +import java.util.Iterator; +import javax.annotation.Nullable; + +class MultiReader extends Reader { + private final Iterator it; + private Reader current; + + MultiReader(Iterator readers) throws IOException { + this.it = readers; + this.advance(); + } + + private void advance() throws IOException { + this.close(); + if (this.it.hasNext()) { + this.current = ((CharSource)this.it.next()).openStream(); + } + + } + + public int read(@Nullable char[] cbuf, int off, int len) throws IOException { + if (this.current == null) { + return -1; + } else { + int result = this.current.read(cbuf, off, len); + if (result == -1) { + this.advance(); + return this.read(cbuf, off, len); + } else { + return result; + } + } + } + + public long skip(long n) throws IOException { + Preconditions.checkArgument(n >= 0L, "n is negative"); + if (n > 0L) { + while(this.current != null) { + long result = this.current.skip(n); + if (result > 0L) { + return result; + } + + this.advance(); + } + } + + return 0L; + } + + public boolean ready() throws IOException { + return this.current != null && this.current.ready(); + } + + public void close() throws IOException { + if (this.current != null) { + try { + this.current.close(); + } finally { + this.current = null; + } + } + + } +} diff --git a/src/main/com/google/common/io/OutputSupplier.java b/src/main/com/google/common/io/OutputSupplier.java new file mode 100644 index 0000000..4eee2d0 --- /dev/null +++ b/src/main/com/google/common/io/OutputSupplier.java @@ -0,0 +1,9 @@ +package com.google.common.io; + +import java.io.IOException; + +/** @deprecated */ +@Deprecated +public interface OutputSupplier { + T getOutput() throws IOException; +} diff --git a/src/main/com/google/common/io/PatternFilenameFilter.java b/src/main/com/google/common/io/PatternFilenameFilter.java new file mode 100644 index 0000000..912d8c9 --- /dev/null +++ b/src/main/com/google/common/io/PatternFilenameFilter.java @@ -0,0 +1,25 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.File; +import java.io.FilenameFilter; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +@Beta +public final class PatternFilenameFilter implements FilenameFilter { + private final Pattern pattern; + + public PatternFilenameFilter(String patternStr) { + this(Pattern.compile(patternStr)); + } + + public PatternFilenameFilter(Pattern pattern) { + this.pattern = (Pattern)Preconditions.checkNotNull(pattern); + } + + public boolean accept(@Nullable File dir, String fileName) { + return this.pattern.matcher(fileName).matches(); + } +} diff --git a/src/main/com/google/common/io/Resources.java b/src/main/com/google/common/io/Resources.java new file mode 100644 index 0000000..a4effbd --- /dev/null +++ b/src/main/com/google/common/io/Resources.java @@ -0,0 +1,92 @@ +package com.google.common.io; + +import com.google.common.annotations.Beta; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.List; + +@Beta +public final class Resources { + private Resources() { + } + + public static ByteSource asByteSource(URL url) { + return new Resources.UrlByteSource(url); + } + + public static CharSource asCharSource(URL url, Charset charset) { + return asByteSource(url).asCharSource(charset); + } + + public static byte[] toByteArray(URL url) throws IOException { + return asByteSource(url).read(); + } + + public static String toString(URL url, Charset charset) throws IOException { + return asCharSource(url, charset).read(); + } + + public static T readLines(URL url, Charset charset, LineProcessor callback) throws IOException { + return asCharSource(url, charset).readLines(callback); + } + + public static List readLines(URL url, Charset charset) throws IOException { + return (List)readLines(url, charset, new LineProcessor>() { + final List result = Lists.newArrayList(); + + public boolean processLine(String line) { + this.result.add(line); + return true; + } + + public List getResult() { + return this.result; + } + }); + } + + public static void copy(URL from, OutputStream to) throws IOException { + asByteSource(from).copyTo(to); + } + + public static URL getResource(String resourceName) { + ClassLoader loader = (ClassLoader)MoreObjects.firstNonNull(Thread.currentThread().getContextClassLoader(), Resources.class.getClassLoader()); + URL url = loader.getResource(resourceName); + Preconditions.checkArgument(url != null, "resource %s not found.", resourceName); + return url; + } + + public static URL getResource(Class contextClass, String resourceName) { + URL url = contextClass.getResource(resourceName); + Preconditions.checkArgument(url != null, "resource %s relative to %s not found.", resourceName, contextClass.getName()); + return url; + } + + private static final class UrlByteSource extends ByteSource { + private final URL url; + + private UrlByteSource(URL url) { + this.url = (URL)Preconditions.checkNotNull(url); + } + + public InputStream openStream() throws IOException { + return this.url.openStream(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.url)); + return (new StringBuilder(24 + var1.length())).append("Resources.asByteSource(").append(var1).append(")").toString(); + } + + // $FF: synthetic method + UrlByteSource(URL x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/io/package-info.java b/src/main/com/google/common/io/package-info.java new file mode 100644 index 0000000..09a0245 --- /dev/null +++ b/src/main/com/google/common/io/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.io; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/math/BigIntegerMath.java b/src/main/com/google/common/math/BigIntegerMath.java new file mode 100644 index 0000000..f43385c --- /dev/null +++ b/src/main/com/google/common/math/BigIntegerMath.java @@ -0,0 +1,280 @@ +package com.google.common.math; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; + +@GwtCompatible( + emulated = true +) +public final class BigIntegerMath { + @VisibleForTesting + static final int SQRT2_PRECOMPUTE_THRESHOLD = 256; + @VisibleForTesting + static final BigInteger SQRT2_PRECOMPUTED_BITS = new BigInteger("16a09e667f3bcc908b2fb1366ea957d3e3adec17512775099da2f590b0667322a", 16); + private static final double LN_10 = Math.log(10.0D); + private static final double LN_2 = Math.log(2.0D); + + public static boolean isPowerOfTwo(BigInteger x) { + Preconditions.checkNotNull(x); + return x.signum() > 0 && x.getLowestSetBit() == x.bitLength() - 1; + } + + public static int log2(BigInteger x, RoundingMode mode) { + MathPreconditions.checkPositive("x", (BigInteger)Preconditions.checkNotNull(x)); + int logFloor = x.bitLength() - 1; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(isPowerOfTwo(x)); + case DOWN: + case FLOOR: + return logFloor; + case UP: + case CEILING: + return isPowerOfTwo(x) ? logFloor : logFloor + 1; + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + BigInteger halfPower; + if (logFloor < 256) { + halfPower = SQRT2_PRECOMPUTED_BITS.shiftRight(256 - logFloor); + if (x.compareTo(halfPower) <= 0) { + return logFloor; + } + + return logFloor + 1; + } + + halfPower = x.pow(2); + int logX2Floor = halfPower.bitLength() - 1; + return logX2Floor < 2 * logFloor + 1 ? logFloor : logFloor + 1; + default: + throw new AssertionError(); + } + } + + @GwtIncompatible("TODO") + public static int log10(BigInteger x, RoundingMode mode) { + MathPreconditions.checkPositive("x", x); + if (fitsInLong(x)) { + return LongMath.log10(x.longValue(), mode); + } else { + int approxLog10 = (int)((double)log2(x, RoundingMode.FLOOR) * LN_2 / LN_10); + BigInteger approxPow = BigInteger.TEN.pow(approxLog10); + int approxCmp = approxPow.compareTo(x); + if (approxCmp > 0) { + do { + --approxLog10; + approxPow = approxPow.divide(BigInteger.TEN); + approxCmp = approxPow.compareTo(x); + } while(approxCmp > 0); + } else { + BigInteger nextPow = BigInteger.TEN.multiply(approxPow); + + for(int nextCmp = nextPow.compareTo(x); nextCmp <= 0; nextCmp = nextPow.compareTo(x)) { + ++approxLog10; + approxPow = nextPow; + approxCmp = nextCmp; + nextPow = BigInteger.TEN.multiply(nextPow); + } + } + + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(approxCmp == 0); + case DOWN: + case FLOOR: + return approxLog10; + case UP: + case CEILING: + return approxPow.equals(x) ? approxLog10 : approxLog10 + 1; + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + BigInteger x2 = x.pow(2); + BigInteger halfPowerSquared = approxPow.pow(2).multiply(BigInteger.TEN); + return x2.compareTo(halfPowerSquared) <= 0 ? approxLog10 : approxLog10 + 1; + default: + throw new AssertionError(); + } + } + } + + @GwtIncompatible("TODO") + public static BigInteger sqrt(BigInteger x, RoundingMode mode) { + MathPreconditions.checkNonNegative("x", x); + if (fitsInLong(x)) { + return BigInteger.valueOf(LongMath.sqrt(x.longValue(), mode)); + } else { + BigInteger sqrtFloor = sqrtFloor(x); + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(sqrtFloor.pow(2).equals(x)); + case DOWN: + case FLOOR: + return sqrtFloor; + case UP: + case CEILING: + int sqrtFloorInt = sqrtFloor.intValue(); + boolean sqrtFloorIsExact = sqrtFloorInt * sqrtFloorInt == x.intValue() && sqrtFloor.pow(2).equals(x); + return sqrtFloorIsExact ? sqrtFloor : sqrtFloor.add(BigInteger.ONE); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + BigInteger halfSquare = sqrtFloor.pow(2).add(sqrtFloor); + return halfSquare.compareTo(x) >= 0 ? sqrtFloor : sqrtFloor.add(BigInteger.ONE); + default: + throw new AssertionError(); + } + } + } + + @GwtIncompatible("TODO") + private static BigInteger sqrtFloor(BigInteger x) { + int log2 = log2(x, RoundingMode.FLOOR); + BigInteger sqrt0; + if (log2 < 1023) { + sqrt0 = sqrtApproxWithDoubles(x); + } else { + int shift = log2 - 52 & -2; + sqrt0 = sqrtApproxWithDoubles(x.shiftRight(shift)).shiftLeft(shift >> 1); + } + + BigInteger sqrt1 = sqrt0.add(x.divide(sqrt0)).shiftRight(1); + if (sqrt0.equals(sqrt1)) { + return sqrt0; + } else { + do { + sqrt0 = sqrt1; + sqrt1 = sqrt1.add(x.divide(sqrt1)).shiftRight(1); + } while(sqrt1.compareTo(sqrt0) < 0); + + return sqrt0; + } + } + + @GwtIncompatible("TODO") + private static BigInteger sqrtApproxWithDoubles(BigInteger x) { + return DoubleMath.roundToBigInteger(Math.sqrt(DoubleUtils.bigToDouble(x)), RoundingMode.HALF_EVEN); + } + + @GwtIncompatible("TODO") + public static BigInteger divide(BigInteger p, BigInteger q, RoundingMode mode) { + BigDecimal pDec = new BigDecimal(p); + BigDecimal qDec = new BigDecimal(q); + return pDec.divide(qDec, 0, mode).toBigIntegerExact(); + } + + public static BigInteger factorial(int n) { + MathPreconditions.checkNonNegative("n", n); + if (n < LongMath.factorials.length) { + return BigInteger.valueOf(LongMath.factorials[n]); + } else { + int approxSize = IntMath.divide(n * IntMath.log2(n, RoundingMode.CEILING), 64, RoundingMode.CEILING); + ArrayList bignums = new ArrayList(approxSize); + int startingNumber = LongMath.factorials.length; + long product = LongMath.factorials[startingNumber - 1]; + int shift = Long.numberOfTrailingZeros(product); + product >>= shift; + int productBits = LongMath.log2(product, RoundingMode.FLOOR) + 1; + int bits = LongMath.log2((long)startingNumber, RoundingMode.FLOOR) + 1; + int nextPowerOfTwo = 1 << bits - 1; + + for(long num = (long)startingNumber; num <= (long)n; ++num) { + if ((num & (long)nextPowerOfTwo) != 0L) { + nextPowerOfTwo <<= 1; + ++bits; + } + + int tz = Long.numberOfTrailingZeros(num); + long normalizedNum = num >> tz; + shift += tz; + int normalizedBits = bits - tz; + if (normalizedBits + productBits >= 64) { + bignums.add(BigInteger.valueOf(product)); + product = 1L; + boolean var16 = false; + } + + product *= normalizedNum; + productBits = LongMath.log2(product, RoundingMode.FLOOR) + 1; + } + + if (product > 1L) { + bignums.add(BigInteger.valueOf(product)); + } + + return listProduct(bignums).shiftLeft(shift); + } + } + + static BigInteger listProduct(List nums) { + return listProduct(nums, 0, nums.size()); + } + + static BigInteger listProduct(List nums, int start, int end) { + switch(end - start) { + case 0: + return BigInteger.ONE; + case 1: + return (BigInteger)nums.get(start); + case 2: + return ((BigInteger)nums.get(start)).multiply((BigInteger)nums.get(start + 1)); + case 3: + return ((BigInteger)nums.get(start)).multiply((BigInteger)nums.get(start + 1)).multiply((BigInteger)nums.get(start + 2)); + default: + int m = end + start >>> 1; + return listProduct(nums, start, m).multiply(listProduct(nums, m, end)); + } + } + + public static BigInteger binomial(int n, int k) { + MathPreconditions.checkNonNegative("n", n); + MathPreconditions.checkNonNegative("k", k); + Preconditions.checkArgument(k <= n, "k (%s) > n (%s)", k, n); + if (k > n >> 1) { + k = n - k; + } + + if (k < LongMath.biggestBinomials.length && n <= LongMath.biggestBinomials[k]) { + return BigInteger.valueOf(LongMath.binomial(n, k)); + } else { + BigInteger accum = BigInteger.ONE; + long numeratorAccum = (long)n; + long denominatorAccum = 1L; + int bits = LongMath.log2((long)n, RoundingMode.CEILING); + int numeratorBits = bits; + + for(int i = 1; i < k; ++i) { + int p = n - i; + int q = i + 1; + if (numeratorBits + bits >= 63) { + accum = accum.multiply(BigInteger.valueOf(numeratorAccum)).divide(BigInteger.valueOf(denominatorAccum)); + numeratorAccum = (long)p; + denominatorAccum = (long)q; + numeratorBits = bits; + } else { + numeratorAccum *= (long)p; + denominatorAccum *= (long)q; + numeratorBits += bits; + } + } + + return accum.multiply(BigInteger.valueOf(numeratorAccum)).divide(BigInteger.valueOf(denominatorAccum)); + } + } + + @GwtIncompatible("TODO") + static boolean fitsInLong(BigInteger x) { + return x.bitLength() <= 63; + } + + private BigIntegerMath() { + } +} diff --git a/src/main/com/google/common/math/DoubleMath.java b/src/main/com/google/common/math/DoubleMath.java new file mode 100644 index 0000000..0c6dbb2 --- /dev/null +++ b/src/main/com/google/common/math/DoubleMath.java @@ -0,0 +1,281 @@ +package com.google.common.math; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.primitives.Booleans; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Iterator; + +@GwtCompatible( + emulated = true +) +public final class DoubleMath { + private static final double MIN_INT_AS_DOUBLE = -2.147483648E9D; + private static final double MAX_INT_AS_DOUBLE = 2.147483647E9D; + private static final double MIN_LONG_AS_DOUBLE = -9.223372036854776E18D; + private static final double MAX_LONG_AS_DOUBLE_PLUS_ONE = 9.223372036854776E18D; + private static final double LN_2 = Math.log(2.0D); + @VisibleForTesting + static final int MAX_FACTORIAL = 170; + @VisibleForTesting + static final double[] everySixteenthFactorial = new double[]{1.0D, 2.0922789888E13D, 2.631308369336935E35D, 1.2413915592536073E61D, 1.2688693218588417E89D, 7.156945704626381E118D, 9.916779348709496E149D, 1.974506857221074E182D, 3.856204823625804E215D, 5.5502938327393044E249D, 4.7147236359920616E284D}; + + @GwtIncompatible("#isMathematicalInteger, com.google.common.math.DoubleUtils") + static double roundIntermediate(double x, RoundingMode mode) { + if (!DoubleUtils.isFinite(x)) { + throw new ArithmeticException("input is infinite or NaN"); + } else { + double z; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(isMathematicalInteger(x)); + return x; + case FLOOR: + if (!(x >= 0.0D) && !isMathematicalInteger(x)) { + return x - 1.0D; + } + + return x; + case CEILING: + if (!(x <= 0.0D) && !isMathematicalInteger(x)) { + return x + 1.0D; + } + + return x; + case DOWN: + return x; + case UP: + if (isMathematicalInteger(x)) { + return x; + } + + return x + Math.copySign(1.0D, x); + case HALF_EVEN: + return Math.rint(x); + case HALF_UP: + z = Math.rint(x); + if (Math.abs(x - z) == 0.5D) { + return x + Math.copySign(0.5D, x); + } + + return z; + case HALF_DOWN: + z = Math.rint(x); + if (Math.abs(x - z) == 0.5D) { + return x; + } + + return z; + default: + throw new AssertionError(); + } + } + } + + @GwtIncompatible("#roundIntermediate") + public static int roundToInt(double x, RoundingMode mode) { + double z = roundIntermediate(x, mode); + MathPreconditions.checkInRange(z > -2.147483649E9D & z < 2.147483648E9D); + return (int)z; + } + + @GwtIncompatible("#roundIntermediate") + public static long roundToLong(double x, RoundingMode mode) { + double z = roundIntermediate(x, mode); + MathPreconditions.checkInRange(-9.223372036854776E18D - z < 1.0D & z < 9.223372036854776E18D); + return (long)z; + } + + @GwtIncompatible("#roundIntermediate, java.lang.Math.getExponent, com.google.common.math.DoubleUtils") + public static BigInteger roundToBigInteger(double x, RoundingMode mode) { + x = roundIntermediate(x, mode); + if (-9.223372036854776E18D - x < 1.0D & x < 9.223372036854776E18D) { + return BigInteger.valueOf((long)x); + } else { + int exponent = Math.getExponent(x); + long significand = DoubleUtils.getSignificand(x); + BigInteger result = BigInteger.valueOf(significand).shiftLeft(exponent - 52); + return x < 0.0D ? result.negate() : result; + } + } + + @GwtIncompatible("com.google.common.math.DoubleUtils") + public static boolean isPowerOfTwo(double x) { + return x > 0.0D && DoubleUtils.isFinite(x) && LongMath.isPowerOfTwo(DoubleUtils.getSignificand(x)); + } + + public static double log2(double x) { + return Math.log(x) / LN_2; + } + + @GwtIncompatible("java.lang.Math.getExponent, com.google.common.math.DoubleUtils") + public static int log2(double x, RoundingMode mode) { + Preconditions.checkArgument(x > 0.0D && DoubleUtils.isFinite(x), "x must be positive and finite"); + int exponent = Math.getExponent(x); + if (!DoubleUtils.isNormal(x)) { + return log2(x * 4.503599627370496E15D, mode) - 52; + } else { + boolean increment; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(isPowerOfTwo(x)); + case FLOOR: + increment = false; + break; + case CEILING: + increment = !isPowerOfTwo(x); + break; + case DOWN: + increment = exponent < 0 & !isPowerOfTwo(x); + break; + case UP: + increment = exponent >= 0 & !isPowerOfTwo(x); + break; + case HALF_EVEN: + case HALF_UP: + case HALF_DOWN: + double xScaled = DoubleUtils.scaleNormalize(x); + increment = xScaled * xScaled > 2.0D; + break; + default: + throw new AssertionError(); + } + + return increment ? exponent + 1 : exponent; + } + } + + @GwtIncompatible("java.lang.Math.getExponent, com.google.common.math.DoubleUtils") + public static boolean isMathematicalInteger(double x) { + return DoubleUtils.isFinite(x) && (x == 0.0D || 52 - Long.numberOfTrailingZeros(DoubleUtils.getSignificand(x)) <= Math.getExponent(x)); + } + + public static double factorial(int n) { + MathPreconditions.checkNonNegative("n", n); + if (n > 170) { + return Double.POSITIVE_INFINITY; + } else { + double accum = 1.0D; + + for(int i = 1 + (n & -16); i <= n; ++i) { + accum *= (double)i; + } + + return accum * everySixteenthFactorial[n >> 4]; + } + } + + public static boolean fuzzyEquals(double a, double b, double tolerance) { + MathPreconditions.checkNonNegative("tolerance", tolerance); + return Math.copySign(a - b, 1.0D) <= tolerance || a == b || Double.isNaN(a) && Double.isNaN(b); + } + + public static int fuzzyCompare(double a, double b, double tolerance) { + if (fuzzyEquals(a, b, tolerance)) { + return 0; + } else if (a < b) { + return -1; + } else { + return a > b ? 1 : Booleans.compare(Double.isNaN(a), Double.isNaN(b)); + } + } + + @GwtIncompatible("MeanAccumulator") + public static double mean(double... values) { + DoubleMath.MeanAccumulator accumulator = new DoubleMath.MeanAccumulator(); + double[] arr$ = values; + int len$ = values.length; + + for(int i$ = 0; i$ < len$; ++i$) { + double value = arr$[i$]; + accumulator.add(value); + } + + return accumulator.mean(); + } + + @GwtIncompatible("MeanAccumulator") + public static double mean(int... values) { + DoubleMath.MeanAccumulator accumulator = new DoubleMath.MeanAccumulator(); + int[] arr$ = values; + int len$ = values.length; + + for(int i$ = 0; i$ < len$; ++i$) { + int value = arr$[i$]; + accumulator.add((double)value); + } + + return accumulator.mean(); + } + + @GwtIncompatible("MeanAccumulator") + public static double mean(long... values) { + DoubleMath.MeanAccumulator accumulator = new DoubleMath.MeanAccumulator(); + long[] arr$ = values; + int len$ = values.length; + + for(int i$ = 0; i$ < len$; ++i$) { + long value = arr$[i$]; + accumulator.add((double)value); + } + + return accumulator.mean(); + } + + @GwtIncompatible("MeanAccumulator") + public static double mean(Iterable values) { + DoubleMath.MeanAccumulator accumulator = new DoubleMath.MeanAccumulator(); + Iterator i$ = values.iterator(); + + while(i$.hasNext()) { + Number value = (Number)i$.next(); + accumulator.add(value.doubleValue()); + } + + return accumulator.mean(); + } + + @GwtIncompatible("MeanAccumulator") + public static double mean(Iterator values) { + DoubleMath.MeanAccumulator accumulator = new DoubleMath.MeanAccumulator(); + + while(values.hasNext()) { + accumulator.add(((Number)values.next()).doubleValue()); + } + + return accumulator.mean(); + } + + private DoubleMath() { + } + + @GwtIncompatible("com.google.common.math.DoubleUtils") + private static final class MeanAccumulator { + private long count; + private double mean; + + private MeanAccumulator() { + this.count = 0L; + this.mean = 0.0D; + } + + void add(double value) { + Preconditions.checkArgument(DoubleUtils.isFinite(value)); + ++this.count; + this.mean += (value - this.mean) / (double)this.count; + } + + double mean() { + Preconditions.checkArgument(this.count > 0L, "Cannot take mean of 0 values"); + return this.mean; + } + + // $FF: synthetic method + MeanAccumulator(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/math/DoubleUtils.java b/src/main/com/google/common/math/DoubleUtils.java new file mode 100644 index 0000000..839f8a7 --- /dev/null +++ b/src/main/com/google/common/math/DoubleUtils.java @@ -0,0 +1,68 @@ +package com.google.common.math; + +import com.google.common.base.Preconditions; +import java.math.BigInteger; + +final class DoubleUtils { + static final long SIGNIFICAND_MASK = 4503599627370495L; + static final long EXPONENT_MASK = 9218868437227405312L; + static final long SIGN_MASK = Long.MIN_VALUE; + static final int SIGNIFICAND_BITS = 52; + static final int EXPONENT_BIAS = 1023; + static final long IMPLICIT_BIT = 4503599627370496L; + private static final long ONE_BITS = Double.doubleToRawLongBits(1.0D); + + private DoubleUtils() { + } + + static double nextDown(double d) { + return -Math.nextUp(-d); + } + + static long getSignificand(double d) { + Preconditions.checkArgument(isFinite(d), "not a normal value"); + int exponent = Math.getExponent(d); + long bits = Double.doubleToRawLongBits(d); + bits &= 4503599627370495L; + return exponent == -1023 ? bits << 1 : bits | 4503599627370496L; + } + + static boolean isFinite(double d) { + return Math.getExponent(d) <= 1023; + } + + static boolean isNormal(double d) { + return Math.getExponent(d) >= -1022; + } + + static double scaleNormalize(double x) { + long significand = Double.doubleToRawLongBits(x) & 4503599627370495L; + return Double.longBitsToDouble(significand | ONE_BITS); + } + + static double bigToDouble(BigInteger x) { + BigInteger absX = x.abs(); + int exponent = absX.bitLength() - 1; + if (exponent < 63) { + return (double)x.longValue(); + } else if (exponent > 1023) { + return (double)x.signum() * Double.POSITIVE_INFINITY; + } else { + int shift = exponent - 52 - 1; + long twiceSignifFloor = absX.shiftRight(shift).longValue(); + long signifFloor = twiceSignifFloor >> 1; + signifFloor &= 4503599627370495L; + boolean increment = (twiceSignifFloor & 1L) != 0L && ((signifFloor & 1L) != 0L || absX.getLowestSetBit() < shift); + long signifRounded = increment ? signifFloor + 1L : signifFloor; + long bits = (long)(exponent + 1023) << 52; + bits += signifRounded; + bits |= (long)x.signum() & Long.MIN_VALUE; + return Double.longBitsToDouble(bits); + } + } + + static double ensureNonNegative(double value) { + Preconditions.checkArgument(!Double.isNaN(value)); + return value > 0.0D ? value : 0.0D; + } +} diff --git a/src/main/com/google/common/math/IntMath.java b/src/main/com/google/common/math/IntMath.java new file mode 100644 index 0000000..cbbd5f8 --- /dev/null +++ b/src/main/com/google/common/math/IntMath.java @@ -0,0 +1,327 @@ +package com.google.common.math; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.math.RoundingMode; + +@GwtCompatible( + emulated = true +) +public final class IntMath { + @VisibleForTesting + static final int MAX_POWER_OF_SQRT2_UNSIGNED = -1257966797; + @VisibleForTesting + static final byte[] maxLog10ForLeadingZeros = new byte[]{9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0}; + @VisibleForTesting + static final int[] powersOf10 = new int[]{1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + @VisibleForTesting + static final int[] halfPowersOf10 = new int[]{3, 31, 316, 3162, 31622, 316227, 3162277, 31622776, 316227766, Integer.MAX_VALUE}; + @VisibleForTesting + static final int FLOOR_SQRT_MAX_INT = 46340; + private static final int[] factorials = new int[]{1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600}; + @VisibleForTesting + static int[] biggestBinomials = new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, 65536, 2345, 477, 193, 110, 75, 58, 49, 43, 39, 37, 35, 34, 34, 33}; + + public static boolean isPowerOfTwo(int x) { + return x > 0 & (x & x - 1) == 0; + } + + @VisibleForTesting + static int lessThanBranchFree(int x, int y) { + return ~(~(x - y)) >>> 31; + } + + public static int log2(int x, RoundingMode mode) { + MathPreconditions.checkPositive("x", x); + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(isPowerOfTwo(x)); + case DOWN: + case FLOOR: + return 31 - Integer.numberOfLeadingZeros(x); + case UP: + case CEILING: + return 32 - Integer.numberOfLeadingZeros(x - 1); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + int leadingZeros = Integer.numberOfLeadingZeros(x); + int cmp = -1257966797 >>> leadingZeros; + int logFloor = 31 - leadingZeros; + return logFloor + lessThanBranchFree(cmp, x); + default: + throw new AssertionError(); + } + } + + @GwtIncompatible("need BigIntegerMath to adequately test") + public static int log10(int x, RoundingMode mode) { + MathPreconditions.checkPositive("x", x); + int logFloor = log10Floor(x); + int floorPow = powersOf10[logFloor]; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(x == floorPow); + case DOWN: + case FLOOR: + return logFloor; + case UP: + case CEILING: + return logFloor + lessThanBranchFree(floorPow, x); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + return logFloor + lessThanBranchFree(halfPowersOf10[logFloor], x); + default: + throw new AssertionError(); + } + } + + private static int log10Floor(int x) { + int y = maxLog10ForLeadingZeros[Integer.numberOfLeadingZeros(x)]; + return y - lessThanBranchFree(x, powersOf10[y]); + } + + @GwtIncompatible("failing tests") + public static int pow(int b, int k) { + MathPreconditions.checkNonNegative("exponent", k); + switch(b) { + case -2: + if (k < 32) { + return (k & 1) == 0 ? 1 << k : -(1 << k); + } + + return 0; + case -1: + return (k & 1) == 0 ? 1 : -1; + case 0: + return k == 0 ? 1 : 0; + case 1: + return 1; + case 2: + return k < 32 ? 1 << k : 0; + default: + int accum = 1; + + while(true) { + switch(k) { + case 0: + return accum; + case 1: + return b * accum; + } + + accum *= (k & 1) == 0 ? 1 : b; + b *= b; + k >>= 1; + } + } + } + + @GwtIncompatible("need BigIntegerMath to adequately test") + public static int sqrt(int x, RoundingMode mode) { + MathPreconditions.checkNonNegative("x", x); + int sqrtFloor = sqrtFloor(x); + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(sqrtFloor * sqrtFloor == x); + case DOWN: + case FLOOR: + return sqrtFloor; + case UP: + case CEILING: + return sqrtFloor + lessThanBranchFree(sqrtFloor * sqrtFloor, x); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + int halfSquare = sqrtFloor * sqrtFloor + sqrtFloor; + return sqrtFloor + lessThanBranchFree(halfSquare, x); + default: + throw new AssertionError(); + } + } + + private static int sqrtFloor(int x) { + return (int)Math.sqrt((double)x); + } + + public static int divide(int p, int q, RoundingMode mode) { + Preconditions.checkNotNull(mode); + if (q == 0) { + throw new ArithmeticException("/ by zero"); + } else { + int div = p / q; + int rem = p - q * div; + if (rem == 0) { + return div; + } else { + int signum = 1 | (p ^ q) >> 31; + boolean increment; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(rem == 0); + case DOWN: + increment = false; + break; + case FLOOR: + increment = signum < 0; + break; + case UP: + increment = true; + break; + case CEILING: + increment = signum > 0; + break; + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + int absRem = Math.abs(rem); + int cmpRemToHalfDivisor = absRem - (Math.abs(q) - absRem); + if (cmpRemToHalfDivisor == 0) { + increment = mode == RoundingMode.HALF_UP || mode == RoundingMode.HALF_EVEN & (div & 1) != 0; + } else { + increment = cmpRemToHalfDivisor > 0; + } + break; + default: + throw new AssertionError(); + } + + return increment ? div + signum : div; + } + } + } + + public static int mod(int x, int m) { + if (m <= 0) { + throw new ArithmeticException((new StringBuilder(31)).append("Modulus ").append(m).append(" must be > 0").toString()); + } else { + int result = x % m; + return result >= 0 ? result : result + m; + } + } + + public static int gcd(int a, int b) { + MathPreconditions.checkNonNegative("a", a); + MathPreconditions.checkNonNegative("b", b); + if (a == 0) { + return b; + } else if (b == 0) { + return a; + } else { + int aTwos = Integer.numberOfTrailingZeros(a); + a >>= aTwos; + int bTwos = Integer.numberOfTrailingZeros(b); + + for(b >>= bTwos; a != b; a >>= Integer.numberOfTrailingZeros(a)) { + int delta = a - b; + int minDeltaOrZero = delta & delta >> 31; + a = delta - minDeltaOrZero - minDeltaOrZero; + b += minDeltaOrZero; + } + + return a << Math.min(aTwos, bTwos); + } + } + + public static int checkedAdd(int a, int b) { + long result = (long)a + (long)b; + MathPreconditions.checkNoOverflow(result == (long)((int)result)); + return (int)result; + } + + public static int checkedSubtract(int a, int b) { + long result = (long)a - (long)b; + MathPreconditions.checkNoOverflow(result == (long)((int)result)); + return (int)result; + } + + public static int checkedMultiply(int a, int b) { + long result = (long)a * (long)b; + MathPreconditions.checkNoOverflow(result == (long)((int)result)); + return (int)result; + } + + public static int checkedPow(int b, int k) { + MathPreconditions.checkNonNegative("exponent", k); + switch(b) { + case -2: + MathPreconditions.checkNoOverflow(k < 32); + return (k & 1) == 0 ? 1 << k : -1 << k; + case -1: + return (k & 1) == 0 ? 1 : -1; + case 0: + return k == 0 ? 1 : 0; + case 1: + return 1; + case 2: + MathPreconditions.checkNoOverflow(k < 31); + return 1 << k; + default: + int accum = 1; + + while(true) { + switch(k) { + case 0: + return accum; + case 1: + return checkedMultiply(accum, b); + } + + if ((k & 1) != 0) { + accum = checkedMultiply(accum, b); + } + + k >>= 1; + if (k > 0) { + MathPreconditions.checkNoOverflow(-46340 <= b & b <= 46340); + b *= b; + } + } + } + } + + public static int factorial(int n) { + MathPreconditions.checkNonNegative("n", n); + return n < factorials.length ? factorials[n] : Integer.MAX_VALUE; + } + + @GwtIncompatible("need BigIntegerMath to adequately test") + public static int binomial(int n, int k) { + MathPreconditions.checkNonNegative("n", n); + MathPreconditions.checkNonNegative("k", k); + Preconditions.checkArgument(k <= n, "k (%s) > n (%s)", k, n); + if (k > n >> 1) { + k = n - k; + } + + if (k < biggestBinomials.length && n <= biggestBinomials[k]) { + switch(k) { + case 0: + return 1; + case 1: + return n; + default: + long result = 1L; + + for(int i = 0; i < k; ++i) { + result *= (long)(n - i); + result /= (long)(i + 1); + } + + return (int)result; + } + } else { + return Integer.MAX_VALUE; + } + } + + public static int mean(int x, int y) { + return (x & y) + ((x ^ y) >> 1); + } + + private IntMath() { + } +} diff --git a/src/main/com/google/common/math/LongMath.java b/src/main/com/google/common/math/LongMath.java new file mode 100644 index 0000000..99de0b1 --- /dev/null +++ b/src/main/com/google/common/math/LongMath.java @@ -0,0 +1,407 @@ +package com.google.common.math; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.math.RoundingMode; + +@GwtCompatible( + emulated = true +) +public final class LongMath { + @VisibleForTesting + static final long MAX_POWER_OF_SQRT2_UNSIGNED = -5402926248376769404L; + @VisibleForTesting + static final byte[] maxLog10ForLeadingZeros = new byte[]{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 = new long[]{1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L, 100000000L, 1000000000L, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L, 1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L}; + @GwtIncompatible("TODO") + @VisibleForTesting + static final long[] halfPowersOf10 = new long[]{3L, 31L, 316L, 3162L, 31622L, 316227L, 3162277L, 31622776L, 316227766L, 3162277660L, 31622776601L, 316227766016L, 3162277660168L, 31622776601683L, 316227766016837L, 3162277660168379L, 31622776601683793L, 316227766016837933L, 3162277660168379331L}; + @VisibleForTesting + static final long FLOOR_SQRT_MAX_LONG = 3037000499L; + static final long[] factorials = new long[]{1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L, 87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L, 2432902008176640000L}; + static final int[] biggestBinomials = new int[]{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}; + @VisibleForTesting + static final int[] biggestSimpleBinomials = new int[]{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}; + + public static boolean isPowerOfTwo(long x) { + return x > 0L & (x & x - 1L) == 0L; + } + + @VisibleForTesting + static int lessThanBranchFree(long x, long y) { + return (int)(~(~(x - y)) >>> 63); + } + + public static int log2(long x, RoundingMode mode) { + MathPreconditions.checkPositive("x", x); + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(isPowerOfTwo(x)); + case DOWN: + case FLOOR: + return 63 - Long.numberOfLeadingZeros(x); + case UP: + case CEILING: + return 64 - Long.numberOfLeadingZeros(x - 1L); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + int leadingZeros = Long.numberOfLeadingZeros(x); + long cmp = -5402926248376769404L >>> leadingZeros; + int logFloor = 63 - leadingZeros; + return logFloor + lessThanBranchFree(cmp, x); + default: + throw new AssertionError("impossible"); + } + } + + @GwtIncompatible("TODO") + public static int log10(long x, RoundingMode mode) { + MathPreconditions.checkPositive("x", x); + int logFloor = log10Floor(x); + long floorPow = powersOf10[logFloor]; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(x == floorPow); + case DOWN: + case FLOOR: + return logFloor; + case UP: + case CEILING: + return logFloor + lessThanBranchFree(floorPow, x); + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + return logFloor + lessThanBranchFree(halfPowersOf10[logFloor], x); + default: + throw new AssertionError(); + } + } + + @GwtIncompatible("TODO") + static int log10Floor(long x) { + int y = maxLog10ForLeadingZeros[Long.numberOfLeadingZeros(x)]; + return y - lessThanBranchFree(x, powersOf10[y]); + } + + @GwtIncompatible("TODO") + public static long pow(long b, int k) { + MathPreconditions.checkNonNegative("exponent", k); + if (-2L <= b && b <= 2L) { + switch((int)b) { + case -2: + if (k < 64) { + return (k & 1) == 0 ? 1L << k : -(1L << k); + } + + return 0L; + case -1: + return (k & 1) == 0 ? 1L : -1L; + case 0: + return k == 0 ? 1L : 0L; + case 1: + return 1L; + case 2: + return k < 64 ? 1L << k : 0L; + default: + throw new AssertionError(); + } + } else { + long accum = 1L; + + while(true) { + switch(k) { + case 0: + return accum; + case 1: + return accum * b; + } + + accum *= (k & 1) == 0 ? 1L : b; + b *= b; + k >>= 1; + } + } + } + + @GwtIncompatible("TODO") + public static long sqrt(long x, RoundingMode mode) { + MathPreconditions.checkNonNegative("x", x); + if (fitsInInt(x)) { + return (long)IntMath.sqrt((int)x, mode); + } else { + long guess = (long)Math.sqrt((double)x); + long guessSquared = guess * guess; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(guessSquared == x); + return guess; + case DOWN: + case FLOOR: + if (x < guessSquared) { + return guess - 1L; + } + + return guess; + case UP: + case CEILING: + if (x > guessSquared) { + return guess + 1L; + } + + return guess; + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + long sqrtFloor = guess - (long)(x < guessSquared ? 1 : 0); + long halfSquare = sqrtFloor * sqrtFloor + sqrtFloor; + return sqrtFloor + (long)lessThanBranchFree(halfSquare, x); + default: + throw new AssertionError(); + } + } + } + + @GwtIncompatible("TODO") + public static long divide(long p, long q, RoundingMode mode) { + Preconditions.checkNotNull(mode); + long div = p / q; + long rem = p - q * div; + if (rem == 0L) { + return div; + } else { + int signum = 1 | (int)((p ^ q) >> 63); + boolean increment; + switch(mode) { + case UNNECESSARY: + MathPreconditions.checkRoundingUnnecessary(rem == 0L); + case DOWN: + increment = false; + break; + case FLOOR: + increment = signum < 0; + break; + case UP: + increment = true; + break; + case CEILING: + increment = signum > 0; + break; + case HALF_DOWN: + case HALF_UP: + case HALF_EVEN: + long absRem = Math.abs(rem); + long cmpRemToHalfDivisor = absRem - (Math.abs(q) - absRem); + if (cmpRemToHalfDivisor == 0L) { + increment = mode == RoundingMode.HALF_UP | mode == RoundingMode.HALF_EVEN & (div & 1L) != 0L; + } else { + increment = cmpRemToHalfDivisor > 0L; + } + break; + default: + throw new AssertionError(); + } + + return increment ? div + (long)signum : div; + } + } + + @GwtIncompatible("TODO") + public static int mod(long x, int m) { + return (int)mod(x, (long)m); + } + + @GwtIncompatible("TODO") + public static long mod(long x, long m) { + if (m <= 0L) { + throw new ArithmeticException("Modulus must be positive"); + } else { + long result = x % m; + return result >= 0L ? result : result + m; + } + } + + public static long gcd(long a, long b) { + MathPreconditions.checkNonNegative("a", a); + MathPreconditions.checkNonNegative("b", b); + if (a == 0L) { + return b; + } else if (b == 0L) { + return a; + } else { + int aTwos = Long.numberOfTrailingZeros(a); + a >>= aTwos; + int bTwos = Long.numberOfTrailingZeros(b); + + for(b >>= bTwos; a != b; a >>= Long.numberOfTrailingZeros(a)) { + long delta = a - b; + long minDeltaOrZero = delta & delta >> 63; + a = delta - minDeltaOrZero - minDeltaOrZero; + b += minDeltaOrZero; + } + + return a << Math.min(aTwos, bTwos); + } + } + + @GwtIncompatible("TODO") + public static long checkedAdd(long a, long b) { + long result = a + b; + MathPreconditions.checkNoOverflow((a ^ b) < 0L | (a ^ result) >= 0L); + return result; + } + + @GwtIncompatible("TODO") + public static long checkedSubtract(long a, long b) { + long result = a - b; + MathPreconditions.checkNoOverflow((a ^ b) >= 0L | (a ^ result) >= 0L); + return result; + } + + @GwtIncompatible("TODO") + public static long checkedMultiply(long a, long b) { + int leadingZeros = Long.numberOfLeadingZeros(a) + Long.numberOfLeadingZeros(~a) + Long.numberOfLeadingZeros(b) + Long.numberOfLeadingZeros(~b); + if (leadingZeros > 65) { + return a * b; + } else { + MathPreconditions.checkNoOverflow(leadingZeros >= 64); + MathPreconditions.checkNoOverflow(a >= 0L | b != Long.MIN_VALUE); + long result = a * b; + MathPreconditions.checkNoOverflow(a == 0L || result / a == b); + return result; + } + } + + @GwtIncompatible("TODO") + public static long checkedPow(long b, int k) { + MathPreconditions.checkNonNegative("exponent", k); + if (b >= -2L & b <= 2L) { + switch((int)b) { + case -2: + MathPreconditions.checkNoOverflow(k < 64); + return (k & 1) == 0 ? 1L << k : -1L << k; + case -1: + return (k & 1) == 0 ? 1L : -1L; + case 0: + return k == 0 ? 1L : 0L; + case 1: + return 1L; + case 2: + MathPreconditions.checkNoOverflow(k < 63); + return 1L << k; + default: + throw new AssertionError(); + } + } else { + long accum = 1L; + + while(true) { + switch(k) { + case 0: + return accum; + case 1: + return checkedMultiply(accum, b); + } + + if ((k & 1) != 0) { + accum = checkedMultiply(accum, b); + } + + k >>= 1; + if (k > 0) { + MathPreconditions.checkNoOverflow(b <= 3037000499L); + b *= b; + } + } + } + } + + @GwtIncompatible("TODO") + public static long factorial(int n) { + MathPreconditions.checkNonNegative("n", n); + return n < factorials.length ? factorials[n] : Long.MAX_VALUE; + } + + public static long binomial(int n, int k) { + MathPreconditions.checkNonNegative("n", n); + MathPreconditions.checkNonNegative("k", k); + Preconditions.checkArgument(k <= n, "k (%s) > n (%s)", k, n); + if (k > n >> 1) { + k = n - k; + } + + switch(k) { + case 0: + return 1L; + case 1: + return (long)n; + default: + if (n < factorials.length) { + return factorials[n] / (factorials[k] * factorials[n - k]); + } else if (k < biggestBinomials.length && n <= biggestBinomials[k]) { + if (k < biggestSimpleBinomials.length && n <= biggestSimpleBinomials[k]) { + long result = (long)(n--); + + for(int i = 2; i <= k; ++i) { + result *= (long)n; + result /= (long)i; + --n; + } + + return result; + } else { + int nBits = log2((long)n, RoundingMode.CEILING); + long result = 1L; + long numerator = (long)(n--); + long denominator = 1L; + int numeratorBits = nBits; + + for(int i = 2; i <= k; --n) { + if (numeratorBits + nBits < 63) { + numerator *= (long)n; + denominator *= (long)i; + numeratorBits += nBits; + } else { + result = multiplyFraction(result, numerator, denominator); + numerator = (long)n; + denominator = (long)i; + numeratorBits = nBits; + } + + ++i; + } + + return multiplyFraction(result, numerator, denominator); + } + } else { + return Long.MAX_VALUE; + } + } + } + + static long multiplyFraction(long x, long numerator, long denominator) { + if (x == 1L) { + return numerator / denominator; + } else { + long commonDivisor = gcd(x, denominator); + x /= commonDivisor; + denominator /= commonDivisor; + return x * (numerator / denominator); + } + } + + static boolean fitsInInt(long x) { + return (long)((int)x) == x; + } + + public static long mean(long x, long y) { + return (x & y) + ((x ^ y) >> 1); + } + + private LongMath() { + } +} diff --git a/src/main/com/google/common/math/MathPreconditions.java b/src/main/com/google/common/math/MathPreconditions.java new file mode 100644 index 0000000..146709f --- /dev/null +++ b/src/main/com/google/common/math/MathPreconditions.java @@ -0,0 +1,94 @@ +package com.google.common.math; + +import com.google.common.annotations.GwtCompatible; +import java.math.BigInteger; +import javax.annotation.Nullable; + +@GwtCompatible +final class MathPreconditions { + static int checkPositive(@Nullable String role, int x) { + if (x <= 0) { + String var2 = String.valueOf(String.valueOf(role)); + throw new IllegalArgumentException((new StringBuilder(26 + var2.length())).append(var2).append(" (").append(x).append(") must be > 0").toString()); + } else { + return x; + } + } + + static long checkPositive(@Nullable String role, long x) { + if (x <= 0L) { + String var3 = String.valueOf(String.valueOf(role)); + throw new IllegalArgumentException((new StringBuilder(35 + var3.length())).append(var3).append(" (").append(x).append(") must be > 0").toString()); + } else { + return x; + } + } + + static BigInteger checkPositive(@Nullable String role, BigInteger x) { + if (x.signum() <= 0) { + String var2 = String.valueOf(String.valueOf(role)); + String var3 = String.valueOf(String.valueOf(x)); + throw new IllegalArgumentException((new StringBuilder(15 + var2.length() + var3.length())).append(var2).append(" (").append(var3).append(") must be > 0").toString()); + } else { + return x; + } + } + + static int checkNonNegative(@Nullable String role, int x) { + if (x < 0) { + String var2 = String.valueOf(String.valueOf(role)); + throw new IllegalArgumentException((new StringBuilder(27 + var2.length())).append(var2).append(" (").append(x).append(") must be >= 0").toString()); + } else { + return x; + } + } + + static long checkNonNegative(@Nullable String role, long x) { + if (x < 0L) { + String var3 = String.valueOf(String.valueOf(role)); + throw new IllegalArgumentException((new StringBuilder(36 + var3.length())).append(var3).append(" (").append(x).append(") must be >= 0").toString()); + } else { + return x; + } + } + + static BigInteger checkNonNegative(@Nullable String role, BigInteger x) { + if (x.signum() < 0) { + String var2 = String.valueOf(String.valueOf(role)); + String var3 = String.valueOf(String.valueOf(x)); + throw new IllegalArgumentException((new StringBuilder(16 + var2.length() + var3.length())).append(var2).append(" (").append(var3).append(") must be >= 0").toString()); + } else { + return x; + } + } + + static double checkNonNegative(@Nullable String role, double x) { + if (!(x >= 0.0D)) { + String var3 = String.valueOf(String.valueOf(role)); + throw new IllegalArgumentException((new StringBuilder(40 + var3.length())).append(var3).append(" (").append(x).append(") must be >= 0").toString()); + } else { + return x; + } + } + + static void checkRoundingUnnecessary(boolean condition) { + if (!condition) { + throw new ArithmeticException("mode was UNNECESSARY, but rounding was necessary"); + } + } + + static void checkInRange(boolean condition) { + if (!condition) { + throw new ArithmeticException("not in range"); + } + } + + static void checkNoOverflow(boolean condition) { + if (!condition) { + throw new ArithmeticException("overflow"); + } + } + + private MathPreconditions() { + } +} diff --git a/src/main/com/google/common/math/package-info.java b/src/main/com/google/common/math/package-info.java new file mode 100644 index 0000000..f1e8fac --- /dev/null +++ b/src/main/com/google/common/math/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.math; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/net/HostAndPort.java b/src/main/com/google/common/net/HostAndPort.java new file mode 100644 index 0000000..9a45286 --- /dev/null +++ b/src/main/com/google/common/net/HostAndPort.java @@ -0,0 +1,171 @@ +package com.google.common.net; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import java.io.Serializable; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Beta +@Immutable +@GwtCompatible +public final class HostAndPort implements Serializable { + private static final int NO_PORT = -1; + private final String host; + private final int port; + private final boolean hasBracketlessColons; + private static final long serialVersionUID = 0L; + + private HostAndPort(String host, int port, boolean hasBracketlessColons) { + this.host = host; + this.port = port; + this.hasBracketlessColons = hasBracketlessColons; + } + + public String getHostText() { + return this.host; + } + + public boolean hasPort() { + return this.port >= 0; + } + + public int getPort() { + Preconditions.checkState(this.hasPort()); + return this.port; + } + + public int getPortOrDefault(int defaultPort) { + return this.hasPort() ? this.port : defaultPort; + } + + public static HostAndPort fromParts(String host, int port) { + Preconditions.checkArgument(isValidPort(port), "Port out of range: %s", port); + HostAndPort parsedHost = fromString(host); + Preconditions.checkArgument(!parsedHost.hasPort(), "Host has a port: %s", host); + return new HostAndPort(parsedHost.host, port, parsedHost.hasBracketlessColons); + } + + public static HostAndPort fromHost(String host) { + HostAndPort parsedHost = fromString(host); + Preconditions.checkArgument(!parsedHost.hasPort(), "Host has a port: %s", host); + return parsedHost; + } + + public static HostAndPort fromString(String hostPortString) { + Preconditions.checkNotNull(hostPortString); + String portString = null; + boolean hasBracketlessColons = false; + String host; + int port; + if (hostPortString.startsWith("[")) { + String[] hostAndPort = getHostAndPortFromBracketedHost(hostPortString); + host = hostAndPort[0]; + portString = hostAndPort[1]; + } else { + port = hostPortString.indexOf(58); + if (port >= 0 && hostPortString.indexOf(58, port + 1) == -1) { + host = hostPortString.substring(0, port); + portString = hostPortString.substring(port + 1); + } else { + host = hostPortString; + hasBracketlessColons = port >= 0; + } + } + + port = -1; + if (!Strings.isNullOrEmpty(portString)) { + Preconditions.checkArgument(!portString.startsWith("+"), "Unparseable port number: %s", hostPortString); + + try { + port = Integer.parseInt(portString); + } catch (NumberFormatException var6) { + IllegalArgumentException var10000 = new IllegalArgumentException; + String var10003 = String.valueOf(hostPortString); + String var10002; + if (var10003.length() != 0) { + var10002 = "Unparseable port number: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Unparseable port number: "); + } + + var10000.(var10002); + throw var10000; + } + + Preconditions.checkArgument(isValidPort(port), "Port number out of range: %s", hostPortString); + } + + return new HostAndPort(host, port, hasBracketlessColons); + } + + private static String[] getHostAndPortFromBracketedHost(String hostPortString) { + int colonIndex = false; + int closeBracketIndex = false; + Preconditions.checkArgument(hostPortString.charAt(0) == '[', "Bracketed host-port string must start with a bracket: %s", hostPortString); + int colonIndex = hostPortString.indexOf(58); + int closeBracketIndex = hostPortString.lastIndexOf(93); + Preconditions.checkArgument(colonIndex > -1 && closeBracketIndex > colonIndex, "Invalid bracketed host/port: %s", hostPortString); + String host = hostPortString.substring(1, closeBracketIndex); + if (closeBracketIndex + 1 == hostPortString.length()) { + return new String[]{host, ""}; + } else { + Preconditions.checkArgument(hostPortString.charAt(closeBracketIndex + 1) == ':', "Only a colon may follow a close bracket: %s", hostPortString); + + for(int i = closeBracketIndex + 2; i < hostPortString.length(); ++i) { + Preconditions.checkArgument(Character.isDigit(hostPortString.charAt(i)), "Port must be numeric: %s", hostPortString); + } + + return new String[]{host, hostPortString.substring(closeBracketIndex + 2)}; + } + } + + public HostAndPort withDefaultPort(int defaultPort) { + Preconditions.checkArgument(isValidPort(defaultPort)); + return !this.hasPort() && this.port != defaultPort ? new HostAndPort(this.host, defaultPort, this.hasBracketlessColons) : this; + } + + public HostAndPort requireBracketsForIPv6() { + Preconditions.checkArgument(!this.hasBracketlessColons, "Possible bracketless IPv6 literal: %s", this.host); + return this; + } + + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } else if (!(other instanceof HostAndPort)) { + return false; + } else { + HostAndPort that = (HostAndPort)other; + return Objects.equal(this.host, that.host) && this.port == that.port && this.hasBracketlessColons == that.hasBracketlessColons; + } + } + + public int hashCode() { + return Objects.hashCode(this.host, this.port, this.hasBracketlessColons); + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.host.length() + 8); + if (this.host.indexOf(58) >= 0) { + builder.append('[').append(this.host).append(']'); + } else { + builder.append(this.host); + } + + if (this.hasPort()) { + builder.append(':').append(this.port); + } + + return builder.toString(); + } + + private static boolean isValidPort(int port) { + return port >= 0 && port <= 65535; + } +} diff --git a/src/main/com/google/common/net/HostSpecifier.java b/src/main/com/google/common/net/HostSpecifier.java new file mode 100644 index 0000000..f5fbd09 --- /dev/null +++ b/src/main/com/google/common/net/HostSpecifier.java @@ -0,0 +1,101 @@ +package com.google.common.net; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.net.InetAddress; +import java.text.ParseException; +import javax.annotation.Nullable; + +@Beta +public final class HostSpecifier { + private final String canonicalForm; + + private HostSpecifier(String canonicalForm) { + this.canonicalForm = canonicalForm; + } + + public static HostSpecifier fromValid(String specifier) { + HostAndPort parsedHost = HostAndPort.fromString(specifier); + Preconditions.checkArgument(!parsedHost.hasPort()); + String host = parsedHost.getHostText(); + InetAddress addr = null; + + try { + addr = InetAddresses.forString(host); + } catch (IllegalArgumentException var5) { + } + + if (addr != null) { + return new HostSpecifier(InetAddresses.toUriString(addr)); + } else { + InternetDomainName domain = InternetDomainName.from(host); + if (domain.hasPublicSuffix()) { + return new HostSpecifier(domain.toString()); + } else { + IllegalArgumentException var10000 = new IllegalArgumentException; + String var10003 = String.valueOf(host); + String var10002; + if (var10003.length() != 0) { + var10002 = "Domain name does not have a recognized public suffix: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Domain name does not have a recognized public suffix: "); + } + + var10000.(var10002); + throw var10000; + } + } + } + + public static HostSpecifier from(String specifier) throws ParseException { + try { + return fromValid(specifier); + } catch (IllegalArgumentException var3) { + ParseException var10000 = new ParseException; + String var10003 = String.valueOf(specifier); + String var10002; + if (var10003.length() != 0) { + var10002 = "Invalid host specifier: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Invalid host specifier: "); + } + + var10000.(var10002, 0); + ParseException parseException = var10000; + parseException.initCause(var3); + throw parseException; + } + } + + public static boolean isValid(String specifier) { + try { + fromValid(specifier); + return true; + } catch (IllegalArgumentException var2) { + return false; + } + } + + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } else if (other instanceof HostSpecifier) { + HostSpecifier that = (HostSpecifier)other; + return this.canonicalForm.equals(that.canonicalForm); + } else { + return false; + } + } + + public int hashCode() { + return this.canonicalForm.hashCode(); + } + + public String toString() { + return this.canonicalForm; + } +} diff --git a/src/main/com/google/common/net/HttpHeaders.java b/src/main/com/google/common/net/HttpHeaders.java new file mode 100644 index 0000000..21fbf03 --- /dev/null +++ b/src/main/com/google/common/net/HttpHeaders.java @@ -0,0 +1,95 @@ +package com.google.common.net; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +@GwtCompatible +public final class HttpHeaders { + public static final String CACHE_CONTROL = "Cache-Control"; + public static final String CONTENT_LENGTH = "Content-Length"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String DATE = "Date"; + public static final String PRAGMA = "Pragma"; + public static final String VIA = "Via"; + public static final String WARNING = "Warning"; + public static final String ACCEPT = "Accept"; + public static final String ACCEPT_CHARSET = "Accept-Charset"; + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; + public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; + public static final String AUTHORIZATION = "Authorization"; + public static final String CONNECTION = "Connection"; + public static final String COOKIE = "Cookie"; + public static final String EXPECT = "Expect"; + public static final String FROM = "From"; + @Beta + public static final String FOLLOW_ONLY_WHEN_PRERENDER_SHOWN = "Follow-Only-When-Prerender-Shown"; + public static final String HOST = "Host"; + public static final String IF_MATCH = "If-Match"; + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + public static final String IF_NONE_MATCH = "If-None-Match"; + public static final String IF_RANGE = "If-Range"; + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + public static final String LAST_EVENT_ID = "Last-Event-ID"; + public static final String MAX_FORWARDS = "Max-Forwards"; + public static final String ORIGIN = "Origin"; + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + public static final String RANGE = "Range"; + public static final String REFERER = "Referer"; + public static final String TE = "TE"; + public static final String UPGRADE = "Upgrade"; + public static final String USER_AGENT = "User-Agent"; + public static final String ACCEPT_RANGES = "Accept-Ranges"; + public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; + public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; + public static final String AGE = "Age"; + public static final String ALLOW = "Allow"; + public static final String CONTENT_DISPOSITION = "Content-Disposition"; + public static final String CONTENT_ENCODING = "Content-Encoding"; + public static final String CONTENT_LANGUAGE = "Content-Language"; + public static final String CONTENT_LOCATION = "Content-Location"; + public static final String CONTENT_MD5 = "Content-MD5"; + public static final String CONTENT_RANGE = "Content-Range"; + public static final String CONTENT_SECURITY_POLICY = "Content-Security-Policy"; + public static final String CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only"; + public static final String ETAG = "ETag"; + public static final String EXPIRES = "Expires"; + public static final String LAST_MODIFIED = "Last-Modified"; + public static final String LINK = "Link"; + public static final String LOCATION = "Location"; + public static final String P3P = "P3P"; + public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + public static final String REFRESH = "Refresh"; + public static final String RETRY_AFTER = "Retry-After"; + public static final String SERVER = "Server"; + public static final String SET_COOKIE = "Set-Cookie"; + public static final String SET_COOKIE2 = "Set-Cookie2"; + public static final String STRICT_TRANSPORT_SECURITY = "Strict-Transport-Security"; + public static final String TIMING_ALLOW_ORIGIN = "Timing-Allow-Origin"; + public static final String TRAILER = "Trailer"; + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + public static final String VARY = "Vary"; + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + public static final String DNT = "DNT"; + public static final String X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"; + public static final String X_DO_NOT_TRACK = "X-Do-Not-Track"; + public static final String X_FORWARDED_FOR = "X-Forwarded-For"; + public static final String X_FORWARDED_PROTO = "X-Forwarded-Proto"; + public static final String X_FRAME_OPTIONS = "X-Frame-Options"; + public static final String X_POWERED_BY = "X-Powered-By"; + @Beta + public static final String PUBLIC_KEY_PINS = "Public-Key-Pins"; + @Beta + public static final String PUBLIC_KEY_PINS_REPORT_ONLY = "Public-Key-Pins-Report-Only"; + public static final String X_REQUESTED_WITH = "X-Requested-With"; + public static final String X_USER_IP = "X-User-IP"; + public static final String X_XSS_PROTECTION = "X-XSS-Protection"; + + private HttpHeaders() { + } +} diff --git a/src/main/com/google/common/net/InetAddresses.java b/src/main/com/google/common/net/InetAddresses.java new file mode 100644 index 0000000..84b5908 --- /dev/null +++ b/src/main/com/google/common/net/InetAddresses.java @@ -0,0 +1,551 @@ +package com.google.common.net; + +import com.google.common.annotations.Beta; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.hash.Hashing; +import com.google.common.io.ByteStreams; +import com.google.common.primitives.Ints; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import javax.annotation.Nullable; + +@Beta +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 InetAddresses() { + } + + private static Inet4Address getInet4Address(byte[] bytes) { + Preconditions.checkArgument(bytes.length == 4, "Byte array has invalid length for an IPv4 address: %s != 4.", bytes.length); + return (Inet4Address)bytesToInetAddress(bytes); + } + + public static InetAddress forString(String ipString) { + byte[] addr = ipStringToBytes(ipString); + if (addr == null) { + throw new IllegalArgumentException(String.format("'%s' is not an IP string literal.", ipString)); + } else { + return bytesToInetAddress(addr); + } + } + + public static boolean isInetAddress(String ipString) { + return ipStringToBytes(ipString) != null; + } + + private static byte[] ipStringToBytes(String ipString) { + boolean hasColon = false; + boolean hasDot = false; + + for(int i = 0; i < ipString.length(); ++i) { + char c = ipString.charAt(i); + if (c == '.') { + hasDot = true; + } else if (c == ':') { + if (hasDot) { + return null; + } + + hasColon = true; + } else if (Character.digit(c, 16) == -1) { + return null; + } + } + + if (hasColon) { + if (hasDot) { + ipString = convertDottedQuadToHex(ipString); + if (ipString == null) { + return null; + } + } + + return textToNumericFormatV6(ipString); + } else if (hasDot) { + return textToNumericFormatV4(ipString); + } else { + return null; + } + } + + private static byte[] textToNumericFormatV4(String ipString) { + String[] address = ipString.split("\\.", 5); + if (address.length != 4) { + return null; + } else { + byte[] bytes = new byte[4]; + + try { + for(int i = 0; i < bytes.length; ++i) { + bytes[i] = parseOctet(address[i]); + } + + return bytes; + } catch (NumberFormatException var4) { + return null; + } + } + } + + private static byte[] textToNumericFormatV6(String ipString) { + String[] parts = ipString.split(":", 10); + if (parts.length >= 3 && parts.length <= 9) { + int skipIndex = -1; + + int partsHi; + for(partsHi = 1; partsHi < parts.length - 1; ++partsHi) { + if (parts[partsHi].length() == 0) { + if (skipIndex >= 0) { + return null; + } + + skipIndex = partsHi; + } + } + + int partsLo; + if (skipIndex >= 0) { + partsHi = skipIndex; + partsLo = parts.length - skipIndex - 1; + if (parts[0].length() == 0) { + partsHi = skipIndex - 1; + if (partsHi != 0) { + return null; + } + } + + if (parts[parts.length - 1].length() == 0) { + --partsLo; + if (partsLo != 0) { + return null; + } + } + } else { + partsHi = parts.length; + partsLo = 0; + } + + int partsSkipped; + label77: { + partsSkipped = 8 - (partsHi + partsLo); + if (skipIndex >= 0) { + if (partsSkipped >= 1) { + break label77; + } + } else if (partsSkipped == 0) { + break label77; + } + + return null; + } + + ByteBuffer rawBytes = ByteBuffer.allocate(16); + + try { + int i; + for(i = 0; i < partsHi; ++i) { + rawBytes.putShort(parseHextet(parts[i])); + } + + for(i = 0; i < partsSkipped; ++i) { + rawBytes.putShort((short)0); + } + + for(i = partsLo; i > 0; --i) { + rawBytes.putShort(parseHextet(parts[parts.length - i])); + } + } catch (NumberFormatException var8) { + return null; + } + + return rawBytes.array(); + } else { + return null; + } + } + + private static String convertDottedQuadToHex(String ipString) { + int lastColon = ipString.lastIndexOf(58); + String initialPart = ipString.substring(0, lastColon + 1); + String dottedQuad = ipString.substring(lastColon + 1); + byte[] quad = textToNumericFormatV4(dottedQuad); + if (quad == null) { + return null; + } else { + String penultimate = Integer.toHexString((quad[0] & 255) << 8 | quad[1] & 255); + String ultimate = Integer.toHexString((quad[2] & 255) << 8 | quad[3] & 255); + String var7 = String.valueOf(String.valueOf(initialPart)); + String var8 = String.valueOf(String.valueOf(penultimate)); + String var9 = String.valueOf(String.valueOf(ultimate)); + return (new StringBuilder(1 + var7.length() + var8.length() + var9.length())).append(var7).append(var8).append(":").append(var9).toString(); + } + } + + private static byte parseOctet(String ipPart) { + int octet = Integer.parseInt(ipPart); + if (octet <= 255 && (!ipPart.startsWith("0") || ipPart.length() <= 1)) { + return (byte)octet; + } else { + throw new NumberFormatException(); + } + } + + private static short parseHextet(String ipPart) { + int hextet = Integer.parseInt(ipPart, 16); + if (hextet > 65535) { + throw new NumberFormatException(); + } else { + return (short)hextet; + } + } + + private static InetAddress bytesToInetAddress(byte[] addr) { + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException var2) { + throw new AssertionError(var2); + } + } + + public static String toAddrString(InetAddress ip) { + Preconditions.checkNotNull(ip); + if (ip instanceof Inet4Address) { + return ip.getHostAddress(); + } else { + Preconditions.checkArgument(ip instanceof Inet6Address); + byte[] bytes = ip.getAddress(); + int[] hextets = new int[8]; + + for(int i = 0; i < hextets.length; ++i) { + hextets[i] = Ints.fromBytes((byte)0, (byte)0, bytes[2 * i], bytes[2 * i + 1]); + } + + compressLongestRunOfZeroes(hextets); + return hextetsToIPv6String(hextets); + } + } + + private static void compressLongestRunOfZeroes(int[] hextets) { + int bestRunStart = -1; + int bestRunLength = -1; + int runStart = -1; + + for(int i = 0; i < hextets.length + 1; ++i) { + if (i < hextets.length && hextets[i] == 0) { + if (runStart < 0) { + runStart = i; + } + } else if (runStart >= 0) { + int runLength = i - runStart; + if (runLength > bestRunLength) { + bestRunStart = runStart; + bestRunLength = runLength; + } + + runStart = -1; + } + } + + if (bestRunLength >= 2) { + Arrays.fill(hextets, bestRunStart, bestRunStart + bestRunLength, -1); + } + + } + + private static String hextetsToIPv6String(int[] hextets) { + StringBuilder buf = new StringBuilder(39); + boolean lastWasNumber = false; + + for(int i = 0; i < hextets.length; ++i) { + boolean thisIsNumber = hextets[i] >= 0; + if (thisIsNumber) { + if (lastWasNumber) { + buf.append(':'); + } + + buf.append(Integer.toHexString(hextets[i])); + } else if (i == 0 || lastWasNumber) { + buf.append("::"); + } + + lastWasNumber = thisIsNumber; + } + + return buf.toString(); + } + + public static String toUriString(InetAddress ip) { + if (ip instanceof Inet6Address) { + String var1 = String.valueOf(String.valueOf(toAddrString(ip))); + return (new StringBuilder(2 + var1.length())).append("[").append(var1).append("]").toString(); + } else { + return toAddrString(ip); + } + } + + public static InetAddress forUriString(String hostAddr) { + Preconditions.checkNotNull(hostAddr); + String ipString; + byte expectBytes; + if (hostAddr.startsWith("[") && hostAddr.endsWith("]")) { + ipString = hostAddr.substring(1, hostAddr.length() - 1); + expectBytes = 16; + } else { + ipString = hostAddr; + expectBytes = 4; + } + + byte[] addr = ipStringToBytes(ipString); + if (addr != null && addr.length == expectBytes) { + return bytesToInetAddress(addr); + } else { + throw new IllegalArgumentException(String.format("Not a valid URI IP literal: '%s'", hostAddr)); + } + } + + public static boolean isUriInetAddress(String ipString) { + try { + forUriString(ipString); + return true; + } catch (IllegalArgumentException var2) { + return false; + } + } + + public static boolean isCompatIPv4Address(Inet6Address ip) { + if (!ip.isIPv4CompatibleAddress()) { + return false; + } else { + byte[] bytes = ip.getAddress(); + return bytes[12] != 0 || bytes[13] != 0 || bytes[14] != 0 || bytes[15] != 0 && bytes[15] != 1; + } + } + + 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)); + } + + public static boolean is6to4Address(Inet6Address ip) { + byte[] bytes = ip.getAddress(); + return bytes[0] == 32 && bytes[1] == 2; + } + + 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)); + } + + public static boolean isTeredoAddress(Inet6Address ip) { + byte[] bytes = ip.getAddress(); + return bytes[0] == 32 && bytes[1] == 1 && bytes[2] == 0 && bytes[3] == 0; + } + + public static InetAddresses.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)); + int flags = ByteStreams.newDataInput(bytes, 8).readShort() & '\uffff'; + int port = ~ByteStreams.newDataInput(bytes, 10).readShort() & '\uffff'; + byte[] clientBytes = Arrays.copyOfRange(bytes, 12, 16); + + for(int i = 0; i < clientBytes.length; ++i) { + clientBytes[i] = (byte)(~clientBytes[i]); + } + + Inet4Address client = getInet4Address(clientBytes); + return new InetAddresses.TeredoInfo(server, client, port, flags); + } + + public static boolean isIsatapAddress(Inet6Address ip) { + if (isTeredoAddress(ip)) { + return false; + } else { + byte[] bytes = ip.getAddress(); + if ((bytes[8] | 3) != 3) { + return false; + } else { + return bytes[9] == 0 && bytes[10] == 94 && bytes[11] == -2; + } + } + } + + 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)); + } + + public static boolean hasEmbeddedIPv4ClientAddress(Inet6Address ip) { + return isCompatIPv4Address(ip) || is6to4Address(ip) || isTeredoAddress(ip); + } + + public static Inet4Address getEmbeddedIPv4ClientAddress(Inet6Address ip) { + if (isCompatIPv4Address(ip)) { + return getCompatIPv4Address(ip); + } else if (is6to4Address(ip)) { + return get6to4IPv4Address(ip); + } else if (isTeredoAddress(ip)) { + return getTeredoInfo(ip).getClient(); + } else { + throw new IllegalArgumentException(String.format("'%s' has no embedded IPv4 address.", toAddrString(ip))); + } + } + + public static boolean isMappedIPv4Address(String ipString) { + byte[] bytes = ipStringToBytes(ipString); + if (bytes != null && bytes.length == 16) { + int i; + for(i = 0; i < 10; ++i) { + if (bytes[i] != 0) { + return false; + } + } + + for(i = 10; i < 12; ++i) { + if (bytes[i] != -1) { + return false; + } + } + + return true; + } else { + return false; + } + } + + public static Inet4Address getCoercedIPv4Address(InetAddress ip) { + if (ip instanceof Inet4Address) { + return (Inet4Address)ip; + } else { + byte[] bytes = ip.getAddress(); + boolean leadingBytesOfZero = true; + + for(int i = 0; i < 15; ++i) { + if (bytes[i] != 0) { + leadingBytesOfZero = false; + break; + } + } + + if (leadingBytesOfZero && bytes[15] == 1) { + return LOOPBACK4; + } else if (leadingBytesOfZero && bytes[15] == 0) { + return ANY4; + } else { + Inet6Address ip6 = (Inet6Address)ip; + long addressAsLong = 0L; + if (hasEmbeddedIPv4ClientAddress(ip6)) { + addressAsLong = (long)getEmbeddedIPv4ClientAddress(ip6).hashCode(); + } else { + addressAsLong = ByteBuffer.wrap(ip6.getAddress(), 0, 8).getLong(); + } + + int coercedHash = Hashing.murmur3_32().hashLong(addressAsLong).asInt(); + coercedHash |= -536870912; + if (coercedHash == -1) { + coercedHash = -2; + } + + return getInet4Address(Ints.toByteArray(coercedHash)); + } + } + } + + public static int coerceToInteger(InetAddress ip) { + return ByteStreams.newDataInput(getCoercedIPv4Address(ip).getAddress()).readInt(); + } + + public static Inet4Address fromInteger(int address) { + return getInet4Address(Ints.toByteArray(address)); + } + + 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]; + } + + return InetAddress.getByAddress(reversed); + } + + public static InetAddress decrement(InetAddress address) { + byte[] addr = address.getAddress(); + + int i; + for(i = addr.length - 1; i >= 0 && addr[i] == 0; --i) { + addr[i] = -1; + } + + Preconditions.checkArgument(i >= 0, "Decrementing %s would wrap.", address); + --addr[i]; + return bytesToInetAddress(addr); + } + + public static InetAddress increment(InetAddress address) { + byte[] addr = address.getAddress(); + + int i; + for(i = addr.length - 1; i >= 0 && addr[i] == -1; --i) { + addr[i] = 0; + } + + Preconditions.checkArgument(i >= 0, "Incrementing %s would wrap.", address); + ++addr[i]; + return bytesToInetAddress(addr); + } + + public static boolean isMaximum(InetAddress address) { + byte[] addr = address.getAddress(); + + for(int i = 0; i < addr.length; ++i) { + if (addr[i] != -1) { + return false; + } + } + + return true; + } + + @Beta + public static final class TeredoInfo { + private final Inet4Address server; + private final Inet4Address client; + private final int port; + private final int flags; + + public TeredoInfo(@Nullable Inet4Address server, @Nullable Inet4Address client, int port, int flags) { + Preconditions.checkArgument(port >= 0 && port <= 65535, "port '%s' is out of range (0 <= port <= 0xffff)", port); + Preconditions.checkArgument(flags >= 0 && flags <= 65535, "flags '%s' is out of range (0 <= flags <= 0xffff)", flags); + this.server = (Inet4Address)MoreObjects.firstNonNull(server, InetAddresses.ANY4); + this.client = (Inet4Address)MoreObjects.firstNonNull(client, InetAddresses.ANY4); + this.port = port; + this.flags = flags; + } + + public Inet4Address getServer() { + return this.server; + } + + public Inet4Address getClient() { + return this.client; + } + + public int getPort() { + return this.port; + } + + public int getFlags() { + return this.flags; + } + } +} diff --git a/src/main/com/google/common/net/InternetDomainName.java b/src/main/com/google/common/net/InternetDomainName.java new file mode 100644 index 0000000..fd8f153 --- /dev/null +++ b/src/main/com/google/common/net/InternetDomainName.java @@ -0,0 +1,190 @@ +package com.google.common.net; + +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.Joiner; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import com.google.thirdparty.publicsuffix.PublicSuffixPatterns; +import java.util.List; +import javax.annotation.Nullable; + +@Beta +@GwtCompatible +public final class InternetDomainName { + private static final CharMatcher DOTS_MATCHER = CharMatcher.anyOf(".。.。"); + private static final Splitter DOT_SPLITTER = Splitter.on('.'); + private static final Joiner DOT_JOINER = Joiner.on('.'); + private static final int NO_PUBLIC_SUFFIX_FOUND = -1; + private static final String DOT_REGEX = "\\."; + private static final int MAX_PARTS = 127; + private static final int MAX_LENGTH = 253; + private static final int MAX_DOMAIN_PART_LENGTH = 63; + private final String name; + private final ImmutableList parts; + private final int publicSuffixIndex; + private static final CharMatcher DASH_MATCHER = CharMatcher.anyOf("-_"); + private static final CharMatcher PART_CHAR_MATCHER; + + InternetDomainName(String name) { + name = Ascii.toLowerCase(DOTS_MATCHER.replaceFrom(name, '.')); + if (name.endsWith(".")) { + name = name.substring(0, name.length() - 1); + } + + Preconditions.checkArgument(name.length() <= 253, "Domain name too long: '%s':", name); + this.name = name; + this.parts = ImmutableList.copyOf(DOT_SPLITTER.split(name)); + Preconditions.checkArgument(this.parts.size() <= 127, "Domain has too many parts: '%s'", name); + Preconditions.checkArgument(validateSyntax(this.parts), "Not a valid domain name: '%s'", name); + this.publicSuffixIndex = this.findPublicSuffix(); + } + + private int findPublicSuffix() { + int partsSize = this.parts.size(); + + for(int i = 0; i < partsSize; ++i) { + String ancestorName = DOT_JOINER.join((Iterable)this.parts.subList(i, partsSize)); + if (PublicSuffixPatterns.EXACT.containsKey(ancestorName)) { + return i; + } + + if (PublicSuffixPatterns.EXCLUDED.containsKey(ancestorName)) { + return i + 1; + } + + if (matchesWildcardPublicSuffix(ancestorName)) { + return i; + } + } + + return -1; + } + + public static InternetDomainName from(String domain) { + return new InternetDomainName((String)Preconditions.checkNotNull(domain)); + } + + private static boolean validateSyntax(List parts) { + int lastIndex = parts.size() - 1; + if (!validatePart((String)parts.get(lastIndex), true)) { + return false; + } else { + for(int i = 0; i < lastIndex; ++i) { + String part = (String)parts.get(i); + if (!validatePart(part, false)) { + return false; + } + } + + return true; + } + } + + private static boolean validatePart(String part, boolean isFinalPart) { + if (part.length() >= 1 && part.length() <= 63) { + String asciiChars = CharMatcher.ASCII.retainFrom(part); + if (!PART_CHAR_MATCHER.matchesAllOf(asciiChars)) { + return false; + } else if (!DASH_MATCHER.matches(part.charAt(0)) && !DASH_MATCHER.matches(part.charAt(part.length() - 1))) { + return !isFinalPart || !CharMatcher.DIGIT.matches(part.charAt(0)); + } else { + return false; + } + } else { + return false; + } + } + + public ImmutableList parts() { + return this.parts; + } + + public boolean isPublicSuffix() { + return this.publicSuffixIndex == 0; + } + + public boolean hasPublicSuffix() { + return this.publicSuffixIndex != -1; + } + + public InternetDomainName publicSuffix() { + return this.hasPublicSuffix() ? this.ancestor(this.publicSuffixIndex) : null; + } + + public boolean isUnderPublicSuffix() { + return this.publicSuffixIndex > 0; + } + + public boolean isTopPrivateDomain() { + return this.publicSuffixIndex == 1; + } + + public InternetDomainName topPrivateDomain() { + if (this.isTopPrivateDomain()) { + return this; + } else { + Preconditions.checkState(this.isUnderPublicSuffix(), "Not under a public suffix: %s", this.name); + return this.ancestor(this.publicSuffixIndex - 1); + } + } + + public boolean hasParent() { + return this.parts.size() > 1; + } + + public InternetDomainName parent() { + Preconditions.checkState(this.hasParent(), "Domain '%s' has no parent", this.name); + return this.ancestor(1); + } + + private InternetDomainName ancestor(int levels) { + return from(DOT_JOINER.join((Iterable)this.parts.subList(levels, this.parts.size()))); + } + + public InternetDomainName child(String leftParts) { + String var2 = String.valueOf(String.valueOf((String)Preconditions.checkNotNull(leftParts))); + String var3 = String.valueOf(String.valueOf(this.name)); + return from((new StringBuilder(1 + var2.length() + var3.length())).append(var2).append(".").append(var3).toString()); + } + + public static boolean isValid(String name) { + try { + from(name); + return true; + } catch (IllegalArgumentException var2) { + return false; + } + } + + private static boolean matchesWildcardPublicSuffix(String domain) { + String[] pieces = domain.split("\\.", 2); + return pieces.length == 2 && PublicSuffixPatterns.UNDER.containsKey(pieces[1]); + } + + public String toString() { + return this.name; + } + + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } else if (object instanceof InternetDomainName) { + InternetDomainName that = (InternetDomainName)object; + return this.name.equals(that.name); + } else { + return false; + } + } + + public int hashCode() { + return this.name.hashCode(); + } + + static { + PART_CHAR_MATCHER = CharMatcher.JAVA_LETTER_OR_DIGIT.or(DASH_MATCHER); + } +} diff --git a/src/main/com/google/common/net/MediaType.java b/src/main/com/google/common/net/MediaType.java new file mode 100644 index 0000000..44fbde5 --- /dev/null +++ b/src/main/com/google/common/net/MediaType.java @@ -0,0 +1,498 @@ +package com.google.common.net; + +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.Charsets; +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.ListMultimap; +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.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +@Beta +@GwtCompatible +@Immutable +public final class MediaType { + private static final String CHARSET_ATTRIBUTE = "charset"; + private static final ImmutableListMultimap UTF_8_CONSTANT_PARAMETERS; + private static final CharMatcher TOKEN_MATCHER; + private static final CharMatcher QUOTED_TEXT_MATCHER; + private static final CharMatcher LINEAR_WHITE_SPACE; + 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 = "*"; + private static final Map KNOWN_TYPES; + public static final MediaType ANY_TYPE; + public static final MediaType ANY_TEXT_TYPE; + public static final MediaType ANY_IMAGE_TYPE; + public static final MediaType ANY_AUDIO_TYPE; + public static final MediaType ANY_VIDEO_TYPE; + public static final MediaType ANY_APPLICATION_TYPE; + public static final MediaType CACHE_MANIFEST_UTF_8; + public static final MediaType CSS_UTF_8; + public static final MediaType CSV_UTF_8; + public static final MediaType HTML_UTF_8; + public static final MediaType I_CALENDAR_UTF_8; + public static final MediaType PLAIN_TEXT_UTF_8; + public static final MediaType TEXT_JAVASCRIPT_UTF_8; + public static final MediaType TSV_UTF_8; + public static final MediaType VCARD_UTF_8; + public static final MediaType WML_UTF_8; + public static final MediaType XML_UTF_8; + public static final MediaType BMP; + public static final MediaType CRW; + public static final MediaType GIF; + public static final MediaType ICO; + public static final MediaType JPEG; + public static final MediaType PNG; + public static final MediaType PSD; + public static final MediaType SVG_UTF_8; + public static final MediaType TIFF; + public static final MediaType WEBP; + public static final MediaType MP4_AUDIO; + public static final MediaType MPEG_AUDIO; + public static final MediaType OGG_AUDIO; + public static final MediaType WEBM_AUDIO; + public static final MediaType MP4_VIDEO; + public static final MediaType MPEG_VIDEO; + public static final MediaType OGG_VIDEO; + public static final MediaType QUICKTIME; + public static final MediaType WEBM_VIDEO; + public static final MediaType WMV; + public static final MediaType APPLICATION_XML_UTF_8; + public static final MediaType ATOM_UTF_8; + public static final MediaType BZIP2; + public static final MediaType EOT; + public static final MediaType EPUB; + public static final MediaType FORM_DATA; + public static final MediaType KEY_ARCHIVE; + public static final MediaType APPLICATION_BINARY; + public static final MediaType GZIP; + public static final MediaType JAVASCRIPT_UTF_8; + public static final MediaType JSON_UTF_8; + public static final MediaType KML; + public static final MediaType KMZ; + public static final MediaType MBOX; + public static final MediaType APPLE_MOBILE_CONFIG; + public static final MediaType MICROSOFT_EXCEL; + public static final MediaType MICROSOFT_POWERPOINT; + public static final MediaType MICROSOFT_WORD; + public static final MediaType OCTET_STREAM; + public static final MediaType OGG_CONTAINER; + public static final MediaType OOXML_DOCUMENT; + public static final MediaType OOXML_PRESENTATION; + public static final MediaType OOXML_SHEET; + public static final MediaType OPENDOCUMENT_GRAPHICS; + public static final MediaType OPENDOCUMENT_PRESENTATION; + public static final MediaType OPENDOCUMENT_SPREADSHEET; + public static final MediaType OPENDOCUMENT_TEXT; + public static final MediaType PDF; + public static final MediaType POSTSCRIPT; + public static final MediaType PROTOBUF; + public static final MediaType RDF_XML_UTF_8; + public static final MediaType RTF_UTF_8; + public static final MediaType SFNT; + public static final MediaType SHOCKWAVE_FLASH; + public static final MediaType SKETCHUP; + public static final MediaType TAR; + public static final MediaType WOFF; + public static final MediaType XHTML_UTF_8; + public static final MediaType XRD_UTF_8; + public static final MediaType ZIP; + private final String type; + private final String subtype; + private final ImmutableListMultimap parameters; + private static final Joiner.MapJoiner PARAMETER_JOINER; + + private static MediaType createConstant(String type, String subtype) { + return addKnownType(new MediaType(type, subtype, ImmutableListMultimap.of())); + } + + private static MediaType createConstantUtf8(String type, String subtype) { + return addKnownType(new MediaType(type, subtype, UTF_8_CONSTANT_PARAMETERS)); + } + + private static MediaType addKnownType(MediaType mediaType) { + KNOWN_TYPES.put(mediaType, mediaType); + return mediaType; + } + + private MediaType(String type, String subtype, ImmutableListMultimap parameters) { + this.type = type; + this.subtype = subtype; + this.parameters = parameters; + } + + public String type() { + return this.type; + } + + public String subtype() { + return this.subtype; + } + + public ImmutableListMultimap parameters() { + return this.parameters; + } + + private Map> parametersAsMap() { + return Maps.transformValues((Map)this.parameters.asMap(), new Function, ImmutableMultiset>() { + public ImmutableMultiset apply(Collection input) { + return ImmutableMultiset.copyOf((Iterable)input); + } + }); + } + + public Optional charset() { + ImmutableSet charsetValues = ImmutableSet.copyOf((Collection)this.parameters.get("charset")); + switch(charsetValues.size()) { + case 0: + return Optional.absent(); + case 1: + return Optional.of(Charset.forName((String)Iterables.getOnlyElement(charsetValues))); + default: + String var2 = String.valueOf(String.valueOf(charsetValues)); + throw new IllegalStateException((new StringBuilder(33 + var2.length())).append("Multiple charset values defined: ").append(var2).toString()); + } + } + + public MediaType withoutParameters() { + return this.parameters.isEmpty() ? this : create(this.type, this.subtype); + } + + public MediaType withParameters(Multimap parameters) { + return create(this.type, this.subtype, parameters); + } + + public MediaType withParameter(String attribute, String value) { + Preconditions.checkNotNull(attribute); + Preconditions.checkNotNull(value); + String normalizedAttribute = normalizeToken(attribute); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + Iterator i$ = this.parameters.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + String key = (String)entry.getKey(); + if (!normalizedAttribute.equals(key)) { + builder.put(key, entry.getValue()); + } + } + + builder.put(normalizedAttribute, normalizeParameterValue(normalizedAttribute, value)); + MediaType mediaType = new MediaType(this.type, this.subtype, builder.build()); + return (MediaType)MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); + } + + public MediaType withCharset(Charset charset) { + Preconditions.checkNotNull(charset); + return this.withParameter("charset", charset.name()); + } + + public boolean hasWildcard() { + return "*".equals(this.type) || "*".equals(this.subtype); + } + + public boolean is(MediaType mediaTypeRange) { + return (mediaTypeRange.type.equals("*") || mediaTypeRange.type.equals(this.type)) && (mediaTypeRange.subtype.equals("*") || mediaTypeRange.subtype.equals(this.subtype)) && this.parameters.entries().containsAll(mediaTypeRange.parameters.entries()); + } + + public static MediaType create(String type, String subtype) { + return create(type, subtype, ImmutableListMultimap.of()); + } + + static MediaType createApplicationType(String subtype) { + return create("application", subtype); + } + + static MediaType createAudioType(String subtype) { + return create("audio", subtype); + } + + static MediaType createImageType(String subtype) { + return create("image", subtype); + } + + static MediaType createTextType(String subtype) { + return create("text", subtype); + } + + static MediaType createVideoType(String subtype) { + return create("video", subtype); + } + + private static MediaType create(String type, String subtype, Multimap parameters) { + Preconditions.checkNotNull(type); + Preconditions.checkNotNull(subtype); + Preconditions.checkNotNull(parameters); + String normalizedType = normalizeToken(type); + String normalizedSubtype = normalizeToken(subtype); + Preconditions.checkArgument(!"*".equals(normalizedType) || "*".equals(normalizedSubtype), "A wildcard type cannot be used with a non-wildcard subtype"); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + Iterator i$ = parameters.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + String attribute = normalizeToken((String)entry.getKey()); + builder.put(attribute, normalizeParameterValue(attribute, (String)entry.getValue())); + } + + MediaType mediaType = new MediaType(normalizedType, normalizedSubtype, builder.build()); + return (MediaType)MoreObjects.firstNonNull(KNOWN_TYPES.get(mediaType), mediaType); + } + + private static String normalizeToken(String token) { + Preconditions.checkArgument(TOKEN_MATCHER.matchesAllOf(token)); + return Ascii.toLowerCase(token); + } + + private static String normalizeParameterValue(String attribute, String value) { + return "charset".equals(attribute) ? Ascii.toLowerCase(value) : value; + } + + public static MediaType parse(String input) { + Preconditions.checkNotNull(input); + MediaType.Tokenizer tokenizer = new MediaType.Tokenizer(input); + + String subtype; + try { + String type = tokenizer.consumeToken(TOKEN_MATCHER); + tokenizer.consumeCharacter('/'); + subtype = tokenizer.consumeToken(TOKEN_MATCHER); + + ImmutableListMultimap.Builder parameters; + String attribute; + String value; + for(parameters = ImmutableListMultimap.builder(); tokenizer.hasMore(); parameters.put(attribute, value)) { + tokenizer.consumeCharacter(';'); + tokenizer.consumeTokenIfPresent(LINEAR_WHITE_SPACE); + attribute = tokenizer.consumeToken(TOKEN_MATCHER); + tokenizer.consumeCharacter('='); + if ('"' != tokenizer.previewChar()) { + value = tokenizer.consumeToken(TOKEN_MATCHER); + } else { + tokenizer.consumeCharacter('"'); + StringBuilder valueBuilder = new StringBuilder(); + + while('"' != tokenizer.previewChar()) { + if ('\\' == tokenizer.previewChar()) { + tokenizer.consumeCharacter('\\'); + valueBuilder.append(tokenizer.consumeCharacter(CharMatcher.ASCII)); + } else { + valueBuilder.append(tokenizer.consumeToken(QUOTED_TEXT_MATCHER)); + } + } + + value = valueBuilder.toString(); + tokenizer.consumeCharacter('"'); + } + } + + return create(type, subtype, parameters.build()); + } catch (IllegalStateException var8) { + subtype = String.valueOf(String.valueOf(input)); + throw new IllegalArgumentException((new StringBuilder(18 + subtype.length())).append("Could not parse '").append(subtype).append("'").toString(), var8); + } + } + + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof MediaType)) { + return false; + } else { + MediaType that = (MediaType)obj; + return this.type.equals(that.type) && this.subtype.equals(that.subtype) && this.parametersAsMap().equals(that.parametersAsMap()); + } + } + + public int hashCode() { + return Objects.hashCode(this.type, this.subtype, this.parametersAsMap()); + } + + public String toString() { + StringBuilder builder = (new StringBuilder()).append(this.type).append('/').append(this.subtype); + if (!this.parameters.isEmpty()) { + builder.append("; "); + Multimap quotedParameters = Multimaps.transformValues((ListMultimap)this.parameters, new Function() { + public String apply(String value) { + return MediaType.TOKEN_MATCHER.matchesAllOf(value) ? value : MediaType.escapeAndQuote(value); + } + }); + PARAMETER_JOINER.appendTo((StringBuilder)builder, (Iterable)quotedParameters.entries()); + } + + return builder.toString(); + } + + private static String escapeAndQuote(String value) { + StringBuilder escaped = (new StringBuilder(value.length() + 16)).append('"'); + char[] arr$ = value.toCharArray(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char ch = arr$[i$]; + if (ch == '\r' || ch == '\\' || ch == '"') { + escaped.append('\\'); + } + + escaped.append(ch); + } + + return escaped.append('"').toString(); + } + + static { + UTF_8_CONSTANT_PARAMETERS = ImmutableListMultimap.of("charset", Ascii.toLowerCase(Charsets.UTF_8.name())); + TOKEN_MATCHER = CharMatcher.ASCII.and(CharMatcher.JAVA_ISO_CONTROL.negate()).and(CharMatcher.isNot(' ')).and(CharMatcher.noneOf("()<>@,;:\\\"/[]?=")); + QUOTED_TEXT_MATCHER = CharMatcher.ASCII.and(CharMatcher.noneOf("\"\\\r")); + LINEAR_WHITE_SPACE = CharMatcher.anyOf(" \t\r\n"); + KNOWN_TYPES = Maps.newHashMap(); + ANY_TYPE = createConstant("*", "*"); + ANY_TEXT_TYPE = createConstant("text", "*"); + ANY_IMAGE_TYPE = createConstant("image", "*"); + ANY_AUDIO_TYPE = createConstant("audio", "*"); + ANY_VIDEO_TYPE = createConstant("video", "*"); + ANY_APPLICATION_TYPE = createConstant("application", "*"); + CACHE_MANIFEST_UTF_8 = createConstantUtf8("text", "cache-manifest"); + CSS_UTF_8 = createConstantUtf8("text", "css"); + CSV_UTF_8 = createConstantUtf8("text", "csv"); + HTML_UTF_8 = createConstantUtf8("text", "html"); + I_CALENDAR_UTF_8 = createConstantUtf8("text", "calendar"); + PLAIN_TEXT_UTF_8 = createConstantUtf8("text", "plain"); + TEXT_JAVASCRIPT_UTF_8 = createConstantUtf8("text", "javascript"); + TSV_UTF_8 = createConstantUtf8("text", "tab-separated-values"); + VCARD_UTF_8 = createConstantUtf8("text", "vcard"); + WML_UTF_8 = createConstantUtf8("text", "vnd.wap.wml"); + XML_UTF_8 = createConstantUtf8("text", "xml"); + BMP = createConstant("image", "bmp"); + CRW = createConstant("image", "x-canon-crw"); + GIF = createConstant("image", "gif"); + ICO = createConstant("image", "vnd.microsoft.icon"); + JPEG = createConstant("image", "jpeg"); + PNG = createConstant("image", "png"); + PSD = createConstant("image", "vnd.adobe.photoshop"); + SVG_UTF_8 = createConstantUtf8("image", "svg+xml"); + TIFF = createConstant("image", "tiff"); + WEBP = createConstant("image", "webp"); + MP4_AUDIO = createConstant("audio", "mp4"); + MPEG_AUDIO = createConstant("audio", "mpeg"); + OGG_AUDIO = createConstant("audio", "ogg"); + WEBM_AUDIO = createConstant("audio", "webm"); + MP4_VIDEO = createConstant("video", "mp4"); + MPEG_VIDEO = createConstant("video", "mpeg"); + OGG_VIDEO = createConstant("video", "ogg"); + QUICKTIME = createConstant("video", "quicktime"); + WEBM_VIDEO = createConstant("video", "webm"); + WMV = createConstant("video", "x-ms-wmv"); + APPLICATION_XML_UTF_8 = createConstantUtf8("application", "xml"); + ATOM_UTF_8 = createConstantUtf8("application", "atom+xml"); + BZIP2 = createConstant("application", "x-bzip2"); + EOT = createConstant("application", "vnd.ms-fontobject"); + EPUB = createConstant("application", "epub+zip"); + FORM_DATA = createConstant("application", "x-www-form-urlencoded"); + KEY_ARCHIVE = createConstant("application", "pkcs12"); + APPLICATION_BINARY = createConstant("application", "binary"); + GZIP = createConstant("application", "x-gzip"); + JAVASCRIPT_UTF_8 = createConstantUtf8("application", "javascript"); + JSON_UTF_8 = createConstantUtf8("application", "json"); + KML = createConstant("application", "vnd.google-earth.kml+xml"); + KMZ = createConstant("application", "vnd.google-earth.kmz"); + MBOX = createConstant("application", "mbox"); + APPLE_MOBILE_CONFIG = createConstant("application", "x-apple-aspen-config"); + MICROSOFT_EXCEL = createConstant("application", "vnd.ms-excel"); + MICROSOFT_POWERPOINT = createConstant("application", "vnd.ms-powerpoint"); + MICROSOFT_WORD = createConstant("application", "msword"); + OCTET_STREAM = createConstant("application", "octet-stream"); + OGG_CONTAINER = createConstant("application", "ogg"); + OOXML_DOCUMENT = createConstant("application", "vnd.openxmlformats-officedocument.wordprocessingml.document"); + OOXML_PRESENTATION = createConstant("application", "vnd.openxmlformats-officedocument.presentationml.presentation"); + OOXML_SHEET = createConstant("application", "vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + OPENDOCUMENT_GRAPHICS = createConstant("application", "vnd.oasis.opendocument.graphics"); + OPENDOCUMENT_PRESENTATION = createConstant("application", "vnd.oasis.opendocument.presentation"); + OPENDOCUMENT_SPREADSHEET = createConstant("application", "vnd.oasis.opendocument.spreadsheet"); + OPENDOCUMENT_TEXT = createConstant("application", "vnd.oasis.opendocument.text"); + PDF = createConstant("application", "pdf"); + POSTSCRIPT = createConstant("application", "postscript"); + PROTOBUF = createConstant("application", "protobuf"); + RDF_XML_UTF_8 = createConstantUtf8("application", "rdf+xml"); + RTF_UTF_8 = createConstantUtf8("application", "rtf"); + SFNT = createConstant("application", "font-sfnt"); + SHOCKWAVE_FLASH = createConstant("application", "x-shockwave-flash"); + SKETCHUP = createConstant("application", "vnd.sketchup.skp"); + TAR = createConstant("application", "x-tar"); + WOFF = createConstant("application", "font-woff"); + XHTML_UTF_8 = createConstantUtf8("application", "xhtml+xml"); + XRD_UTF_8 = createConstantUtf8("application", "xrd+xml"); + ZIP = createConstant("application", "zip"); + PARAMETER_JOINER = Joiner.on("; ").withKeyValueSeparator("="); + } + + private static final class Tokenizer { + final String input; + int position = 0; + + Tokenizer(String input) { + this.input = input; + } + + String consumeTokenIfPresent(CharMatcher matcher) { + Preconditions.checkState(this.hasMore()); + int startPosition = this.position; + this.position = matcher.negate().indexIn(this.input, startPosition); + return this.hasMore() ? this.input.substring(startPosition, this.position) : this.input.substring(startPosition); + } + + String consumeToken(CharMatcher matcher) { + int startPosition = this.position; + String token = this.consumeTokenIfPresent(matcher); + Preconditions.checkState(this.position != startPosition); + return token; + } + + char consumeCharacter(CharMatcher matcher) { + Preconditions.checkState(this.hasMore()); + char c = this.previewChar(); + Preconditions.checkState(matcher.matches(c)); + ++this.position; + return c; + } + + char consumeCharacter(char c) { + Preconditions.checkState(this.hasMore()); + Preconditions.checkState(this.previewChar() == c); + ++this.position; + return c; + } + + char previewChar() { + Preconditions.checkState(this.hasMore()); + return this.input.charAt(this.position); + } + + boolean hasMore() { + return this.position >= 0 && this.position < this.input.length(); + } + } +} diff --git a/src/main/com/google/common/net/PercentEscaper.java b/src/main/com/google/common/net/PercentEscaper.java new file mode 100644 index 0000000..b65f67f --- /dev/null +++ b/src/main/com/google/common/net/PercentEscaper.java @@ -0,0 +1,145 @@ +package com.google.common.net; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import com.google.common.escape.UnicodeEscaper; + +@Beta +@GwtCompatible +public final class PercentEscaper extends UnicodeEscaper { + private static final char[] PLUS_SIGN = new char[]{'+'}; + private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray(); + private final boolean plusForSpace; + private final boolean[] safeOctets; + + public PercentEscaper(String safeChars, boolean plusForSpace) { + Preconditions.checkNotNull(safeChars); + if (safeChars.matches(".*[0-9A-Za-z].*")) { + throw new IllegalArgumentException("Alphanumeric characters are always 'safe' and should not be explicitly specified"); + } else { + safeChars = String.valueOf(safeChars).concat("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + if (plusForSpace && safeChars.contains(" ")) { + throw new IllegalArgumentException("plusForSpace cannot be specified when space is a 'safe' character"); + } else { + this.plusForSpace = plusForSpace; + this.safeOctets = createSafeOctets(safeChars); + } + } + } + + private static boolean[] createSafeOctets(String safeChars) { + int maxChar = -1; + char[] safeCharArray = safeChars.toCharArray(); + char[] arr$ = safeCharArray; + int len$ = safeCharArray.length; + + int len$; + for(len$ = 0; len$ < len$; ++len$) { + char c = arr$[len$]; + maxChar = Math.max(c, maxChar); + } + + boolean[] octets = new boolean[maxChar + 1]; + char[] arr$ = safeCharArray; + len$ = safeCharArray.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char c = arr$[i$]; + octets[c] = true; + } + + return octets; + } + + protected int nextEscapeIndex(CharSequence csq, int index, int end) { + Preconditions.checkNotNull(csq); + + while(index < end) { + char c = csq.charAt(index); + if (c >= this.safeOctets.length || !this.safeOctets[c]) { + break; + } + + ++index; + } + + return index; + } + + public String escape(String s) { + Preconditions.checkNotNull(s); + int slen = s.length(); + + for(int index = 0; index < slen; ++index) { + char c = s.charAt(index); + if (c >= this.safeOctets.length || !this.safeOctets[c]) { + return this.escapeSlow(s, index); + } + } + + return s; + } + + protected char[] escape(int cp) { + if (cp < this.safeOctets.length && this.safeOctets[cp]) { + return null; + } else if (cp == 32 && this.plusForSpace) { + return PLUS_SIGN; + } else { + char[] dest; + if (cp <= 127) { + dest = new char[]{'%', UPPER_HEX_DIGITS[cp >>> 4], UPPER_HEX_DIGITS[cp & 15]}; + return dest; + } else if (cp <= 2047) { + dest = new char[]{'%', '\u0000', '\u0000', '%', '\u0000', UPPER_HEX_DIGITS[cp & 15]}; + cp >>>= 4; + dest[4] = UPPER_HEX_DIGITS[8 | cp & 3]; + cp >>>= 2; + dest[2] = UPPER_HEX_DIGITS[cp & 15]; + cp >>>= 4; + dest[1] = UPPER_HEX_DIGITS[12 | cp]; + return dest; + } else if (cp <= 65535) { + dest = new char[9]; + dest[0] = '%'; + dest[1] = 'E'; + dest[3] = '%'; + dest[6] = '%'; + dest[8] = UPPER_HEX_DIGITS[cp & 15]; + cp >>>= 4; + dest[7] = UPPER_HEX_DIGITS[8 | cp & 3]; + cp >>>= 2; + dest[5] = UPPER_HEX_DIGITS[cp & 15]; + cp >>>= 4; + dest[4] = UPPER_HEX_DIGITS[8 | cp & 3]; + cp >>>= 2; + dest[2] = UPPER_HEX_DIGITS[cp]; + return dest; + } else if (cp <= 1114111) { + dest = new char[12]; + dest[0] = '%'; + dest[1] = 'F'; + dest[3] = '%'; + dest[6] = '%'; + dest[9] = '%'; + dest[11] = UPPER_HEX_DIGITS[cp & 15]; + cp >>>= 4; + dest[10] = UPPER_HEX_DIGITS[8 | cp & 3]; + cp >>>= 2; + dest[8] = UPPER_HEX_DIGITS[cp & 15]; + cp >>>= 4; + dest[7] = UPPER_HEX_DIGITS[8 | cp & 3]; + cp >>>= 2; + dest[5] = UPPER_HEX_DIGITS[cp & 15]; + cp >>>= 4; + dest[4] = UPPER_HEX_DIGITS[8 | cp & 3]; + cp >>>= 2; + dest[2] = UPPER_HEX_DIGITS[cp & 7]; + return dest; + } else { + throw new IllegalArgumentException((new StringBuilder(43)).append("Invalid unicode character value ").append(cp).toString()); + } + } + } +} diff --git a/src/main/com/google/common/net/UrlEscapers.java b/src/main/com/google/common/net/UrlEscapers.java new file mode 100644 index 0000000..7a23fd7 --- /dev/null +++ b/src/main/com/google/common/net/UrlEscapers.java @@ -0,0 +1,30 @@ +package com.google.common.net; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.escape.Escaper; + +@Beta +@GwtCompatible +public final class UrlEscapers { + static final String URL_FORM_PARAMETER_OTHER_SAFE_CHARS = "-_.*"; + static final String URL_PATH_OTHER_SAFE_CHARS_LACKING_PLUS = "-._~!$'()*,;&=@:"; + private static final Escaper URL_FORM_PARAMETER_ESCAPER = new PercentEscaper("-_.*", true); + private static final Escaper URL_PATH_SEGMENT_ESCAPER = new PercentEscaper("-._~!$'()*,;&=@:+", false); + private static final Escaper URL_FRAGMENT_ESCAPER = new PercentEscaper("-._~!$'()*,;&=@:+/?", false); + + private UrlEscapers() { + } + + public static Escaper urlFormParameterEscaper() { + return URL_FORM_PARAMETER_ESCAPER; + } + + public static Escaper urlPathSegmentEscaper() { + return URL_PATH_SEGMENT_ESCAPER; + } + + public static Escaper urlFragmentEscaper() { + return URL_FRAGMENT_ESCAPER; + } +} diff --git a/src/main/com/google/common/net/package-info.java b/src/main/com/google/common/net/package-info.java new file mode 100644 index 0000000..eb8fe00 --- /dev/null +++ b/src/main/com/google/common/net/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.net; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/primitives/Booleans.java b/src/main/com/google/common/primitives/Booleans.java new file mode 100644 index 0000000..33fb4f2 --- /dev/null +++ b/src/main/com/google/common/primitives/Booleans.java @@ -0,0 +1,318 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +@GwtCompatible +public final class Booleans { + private Booleans() { + } + + public static int hashCode(boolean value) { + return value ? 1231 : 1237; + } + + public static int compare(boolean a, boolean b) { + return a == b ? 0 : (a ? 1 : -1); + } + + public static boolean contains(boolean[] array, boolean target) { + boolean[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + boolean value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(boolean[] array, boolean target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(boolean[] array, boolean target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(boolean[] array, boolean[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(boolean[] array, boolean target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(boolean[] array, boolean target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static boolean[] concat(boolean[]... arrays) { + int length = 0; + boolean[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + boolean[] array = arr$[i$]; + length += array.length; + } + + boolean[] result = new boolean[length]; + pos = 0; + boolean[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + boolean[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + public static boolean[] ensureCapacity(boolean[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static boolean[] copyOf(boolean[] original, int length) { + boolean[] copy = new boolean[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static String join(String separator, boolean... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 7); + builder.append(array[0]); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return Booleans.LexicographicalComparator.INSTANCE; + } + + public static boolean[] toArray(Collection collection) { + if (collection instanceof Booleans.BooleanArrayAsList) { + return ((Booleans.BooleanArrayAsList)collection).toBooleanArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + boolean[] array = new boolean[len]; + + for(int i = 0; i < len; ++i) { + array[i] = (Boolean)Preconditions.checkNotNull(boxedArray[i]); + } + + return array; + } + } + + public static List asList(boolean... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Booleans.BooleanArrayAsList(backingArray)); + } + + @Beta + public static int countTrue(boolean... values) { + int count = 0; + boolean[] arr$ = values; + int len$ = values.length; + + for(int i$ = 0; i$ < len$; ++i$) { + boolean value = arr$[i$]; + if (value) { + ++count; + } + } + + return count; + } + + @GwtCompatible + private static class BooleanArrayAsList extends AbstractList implements RandomAccess, Serializable { + final boolean[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + BooleanArrayAsList(boolean[] array) { + this(array, 0, array.length); + } + + BooleanArrayAsList(boolean[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Boolean get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Boolean && Booleans.indexOf(this.array, (Boolean)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Boolean) { + int i = Booleans.indexOf(this.array, (Boolean)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Boolean) { + int i = Booleans.lastIndexOf(this.array, (Boolean)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Boolean set(int index, Boolean element) { + Preconditions.checkElementIndex(index, this.size()); + boolean oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Boolean)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Booleans.BooleanArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Booleans.BooleanArrayAsList) { + Booleans.BooleanArrayAsList that = (Booleans.BooleanArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Booleans.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 7); + builder.append(this.array[this.start] ? "[true" : "[false"); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(this.array[i] ? ", true" : ", false"); + } + + return builder.append(']').toString(); + } + + boolean[] toBooleanArray() { + int size = this.size(); + boolean[] result = new boolean[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(boolean[] left, boolean[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = Booleans.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } +} diff --git a/src/main/com/google/common/primitives/Bytes.java b/src/main/com/google/common/primitives/Bytes.java new file mode 100644 index 0000000..324dd14 --- /dev/null +++ b/src/main/com/google/common/primitives/Bytes.java @@ -0,0 +1,259 @@ +package com.google.common.primitives; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.RandomAccess; + +@GwtCompatible +public final class Bytes { + private Bytes() { + } + + public static int hashCode(byte value) { + return value; + } + + public static boolean contains(byte[] array, byte target) { + byte[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + byte value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(byte[] array, byte target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(byte[] array, byte target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(byte[] array, byte[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(byte[] array, byte target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(byte[] array, byte target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static byte[] concat(byte[]... arrays) { + int length = 0; + byte[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + byte[] array = arr$[i$]; + length += array.length; + } + + byte[] result = new byte[length]; + pos = 0; + byte[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + byte[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + public static byte[] ensureCapacity(byte[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static byte[] copyOf(byte[] original, int length) { + byte[] copy = new byte[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static byte[] toArray(Collection collection) { + if (collection instanceof Bytes.ByteArrayAsList) { + return ((Bytes.ByteArrayAsList)collection).toByteArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + byte[] array = new byte[len]; + + for(int i = 0; i < len; ++i) { + array[i] = ((Number)Preconditions.checkNotNull(boxedArray[i])).byteValue(); + } + + return array; + } + } + + public static List asList(byte... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Bytes.ByteArrayAsList(backingArray)); + } + + @GwtCompatible + private static class ByteArrayAsList extends AbstractList implements RandomAccess, Serializable { + final byte[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + ByteArrayAsList(byte[] array) { + this(array, 0, array.length); + } + + ByteArrayAsList(byte[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Byte get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Byte && Bytes.indexOf(this.array, (Byte)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Byte) { + int i = Bytes.indexOf(this.array, (Byte)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Byte) { + int i = Bytes.lastIndexOf(this.array, (Byte)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Byte set(int index, Byte element) { + Preconditions.checkElementIndex(index, this.size()); + byte oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Byte)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Bytes.ByteArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Bytes.ByteArrayAsList) { + Bytes.ByteArrayAsList that = (Bytes.ByteArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Bytes.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 5); + builder.append('[').append(this.array[this.start]); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(", ").append(this.array[i]); + } + + return builder.append(']').toString(); + } + + byte[] toByteArray() { + int size = this.size(); + byte[] result = new byte[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } +} diff --git a/src/main/com/google/common/primitives/Chars.java b/src/main/com/google/common/primitives/Chars.java new file mode 100644 index 0000000..7b517e5 --- /dev/null +++ b/src/main/com/google/common/primitives/Chars.java @@ -0,0 +1,366 @@ +package com.google.common.primitives; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +@GwtCompatible( + emulated = true +) +public final class Chars { + public static final int BYTES = 2; + + private Chars() { + } + + public static int hashCode(char value) { + return value; + } + + public static char checkedCast(long value) { + char result = (char)((int)value); + if ((long)result != value) { + throw new IllegalArgumentException((new StringBuilder(34)).append("Out of range: ").append(value).toString()); + } else { + return result; + } + } + + public static char saturatedCast(long value) { + if (value > 65535L) { + return '\uffff'; + } else { + return value < 0L ? '\u0000' : (char)((int)value); + } + } + + public static int compare(char a, char b) { + return a - b; + } + + public static boolean contains(char[] array, char target) { + char[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(char[] array, char target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(char[] array, char target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(char[] array, char[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(char[] array, char target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(char[] array, char target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static char min(char... array) { + Preconditions.checkArgument(array.length > 0); + char min = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + public static char max(char... array) { + Preconditions.checkArgument(array.length > 0); + char max = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + public static char[] concat(char[]... arrays) { + int length = 0; + char[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + char[] array = arr$[i$]; + length += array.length; + } + + char[] result = new char[length]; + pos = 0; + char[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + @GwtIncompatible("doesn't work") + public static byte[] toByteArray(char value) { + return new byte[]{(byte)(value >> 8), (byte)value}; + } + + @GwtIncompatible("doesn't work") + public static char fromByteArray(byte[] bytes) { + Preconditions.checkArgument(bytes.length >= 2, "array too small: %s < %s", bytes.length, 2); + return fromBytes(bytes[0], bytes[1]); + } + + @GwtIncompatible("doesn't work") + public static char fromBytes(byte b1, byte b2) { + return (char)(b1 << 8 | b2 & 255); + } + + public static char[] ensureCapacity(char[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static char[] copyOf(char[] original, int length) { + char[] copy = new char[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static String join(String separator, char... array) { + Preconditions.checkNotNull(separator); + int len = array.length; + if (len == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(len + separator.length() * (len - 1)); + builder.append(array[0]); + + for(int i = 1; i < len; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return Chars.LexicographicalComparator.INSTANCE; + } + + public static char[] toArray(Collection collection) { + if (collection instanceof Chars.CharArrayAsList) { + return ((Chars.CharArrayAsList)collection).toCharArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + char[] array = new char[len]; + + for(int i = 0; i < len; ++i) { + array[i] = (Character)Preconditions.checkNotNull(boxedArray[i]); + } + + return array; + } + } + + public static List asList(char... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Chars.CharArrayAsList(backingArray)); + } + + @GwtCompatible + private static class CharArrayAsList extends AbstractList implements RandomAccess, Serializable { + final char[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + CharArrayAsList(char[] array) { + this(array, 0, array.length); + } + + CharArrayAsList(char[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Character get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Character && Chars.indexOf(this.array, (Character)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Character) { + int i = Chars.indexOf(this.array, (Character)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Character) { + int i = Chars.lastIndexOf(this.array, (Character)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Character set(int index, Character element) { + Preconditions.checkElementIndex(index, this.size()); + char oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Character)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Chars.CharArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Chars.CharArrayAsList) { + Chars.CharArrayAsList that = (Chars.CharArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Chars.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 3); + builder.append('[').append(this.array[this.start]); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(", ").append(this.array[i]); + } + + return builder.append(']').toString(); + } + + char[] toCharArray() { + int size = this.size(); + char[] result = new char[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(char[] left, char[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = Chars.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } +} diff --git a/src/main/com/google/common/primitives/Doubles.java b/src/main/com/google/common/primitives/Doubles.java new file mode 100644 index 0000000..f03210f --- /dev/null +++ b/src/main/com/google/common/primitives/Doubles.java @@ -0,0 +1,391 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Converter; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Doubles { + public static final int BYTES = 8; + @GwtIncompatible("regular expressions") + static final Pattern FLOATING_POINT_PATTERN = fpPattern(); + + private Doubles() { + } + + public static int hashCode(double value) { + return Double.valueOf(value).hashCode(); + } + + public static int compare(double a, double b) { + return Double.compare(a, b); + } + + public static boolean isFinite(double value) { + return Double.NEGATIVE_INFINITY < value & value < Double.POSITIVE_INFINITY; + } + + public static boolean contains(double[] array, double target) { + double[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + double value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(double[] array, double target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(double[] array, double target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(double[] array, double[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(double[] array, double target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(double[] array, double target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static double min(double... array) { + Preconditions.checkArgument(array.length > 0); + double min = array[0]; + + for(int i = 1; i < array.length; ++i) { + min = Math.min(min, array[i]); + } + + return min; + } + + public static double max(double... array) { + Preconditions.checkArgument(array.length > 0); + double max = array[0]; + + for(int i = 1; i < array.length; ++i) { + max = Math.max(max, array[i]); + } + + return max; + } + + public static double[] concat(double[]... arrays) { + int length = 0; + double[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + double[] array = arr$[i$]; + length += array.length; + } + + double[] result = new double[length]; + pos = 0; + double[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + double[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + @Beta + public static Converter stringConverter() { + return Doubles.DoubleConverter.INSTANCE; + } + + public static double[] ensureCapacity(double[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static double[] copyOf(double[] original, int length) { + double[] copy = new double[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static String join(String separator, double... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 12); + builder.append(array[0]); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return Doubles.LexicographicalComparator.INSTANCE; + } + + public static double[] toArray(Collection collection) { + if (collection instanceof Doubles.DoubleArrayAsList) { + return ((Doubles.DoubleArrayAsList)collection).toDoubleArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + double[] array = new double[len]; + + for(int i = 0; i < len; ++i) { + array[i] = ((Number)Preconditions.checkNotNull(boxedArray[i])).doubleValue(); + } + + return array; + } + } + + public static List asList(double... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Doubles.DoubleArrayAsList(backingArray)); + } + + @GwtIncompatible("regular expressions") + private static Pattern fpPattern() { + String decimal = "(?:\\d++(?:\\.\\d*+)?|\\.\\d++)"; + String completeDec = String.valueOf(decimal).concat("(?:[eE][+-]?\\d++)?[fFdD]?"); + String hex = "(?:\\p{XDigit}++(?:\\.\\p{XDigit}*+)?|\\.\\p{XDigit}++)"; + String var4 = String.valueOf(String.valueOf(hex)); + String completeHex = (new StringBuilder(25 + var4.length())).append("0[xX]").append(var4).append("[pP][+-]?\\d++[fFdD]?").toString(); + String var6 = String.valueOf(String.valueOf(completeDec)); + String var7 = String.valueOf(String.valueOf(completeHex)); + String fpPattern = (new StringBuilder(23 + var6.length() + var7.length())).append("[+-]?(?:NaN|Infinity|").append(var6).append("|").append(var7).append(")").toString(); + return Pattern.compile(fpPattern); + } + + @Nullable + @GwtIncompatible("regular expressions") + @Beta + public static Double tryParse(String string) { + if (FLOATING_POINT_PATTERN.matcher(string).matches()) { + try { + return Double.parseDouble(string); + } catch (NumberFormatException var2) { + } + } + + return null; + } + + @GwtCompatible + private static class DoubleArrayAsList extends AbstractList implements RandomAccess, Serializable { + final double[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + DoubleArrayAsList(double[] array) { + this(array, 0, array.length); + } + + DoubleArrayAsList(double[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Double get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Double && Doubles.indexOf(this.array, (Double)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Double) { + int i = Doubles.indexOf(this.array, (Double)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Double) { + int i = Doubles.lastIndexOf(this.array, (Double)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Double set(int index, Double element) { + Preconditions.checkElementIndex(index, this.size()); + double oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Double)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Doubles.DoubleArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Doubles.DoubleArrayAsList) { + Doubles.DoubleArrayAsList that = (Doubles.DoubleArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Doubles.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 12); + builder.append('[').append(this.array[this.start]); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(", ").append(this.array[i]); + } + + return builder.append(']').toString(); + } + + double[] toDoubleArray() { + int size = this.size(); + double[] result = new double[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(double[] left, double[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = Double.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } + + private static final class DoubleConverter extends Converter implements Serializable { + static final Doubles.DoubleConverter INSTANCE = new Doubles.DoubleConverter(); + private static final long serialVersionUID = 1L; + + protected Double doForward(String value) { + return Double.valueOf(value); + } + + protected String doBackward(Double value) { + return value.toString(); + } + + public String toString() { + return "Doubles.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + } +} diff --git a/src/main/com/google/common/primitives/Floats.java b/src/main/com/google/common/primitives/Floats.java new file mode 100644 index 0000000..7ad0af3 --- /dev/null +++ b/src/main/com/google/common/primitives/Floats.java @@ -0,0 +1,375 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Converter; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class Floats { + public static final int BYTES = 4; + + private Floats() { + } + + public static int hashCode(float value) { + return Float.valueOf(value).hashCode(); + } + + public static int compare(float a, float b) { + return Float.compare(a, b); + } + + public static boolean isFinite(float value) { + return Float.NEGATIVE_INFINITY < value & value < Float.POSITIVE_INFINITY; + } + + public static boolean contains(float[] array, float target) { + float[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + float value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(float[] array, float target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(float[] array, float target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(float[] array, float[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(float[] array, float target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(float[] array, float target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static float min(float... array) { + Preconditions.checkArgument(array.length > 0); + float min = array[0]; + + for(int i = 1; i < array.length; ++i) { + min = Math.min(min, array[i]); + } + + return min; + } + + public static float max(float... array) { + Preconditions.checkArgument(array.length > 0); + float max = array[0]; + + for(int i = 1; i < array.length; ++i) { + max = Math.max(max, array[i]); + } + + return max; + } + + public static float[] concat(float[]... arrays) { + int length = 0; + float[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + float[] array = arr$[i$]; + length += array.length; + } + + float[] result = new float[length]; + pos = 0; + float[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + float[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + @Beta + public static Converter stringConverter() { + return Floats.FloatConverter.INSTANCE; + } + + public static float[] ensureCapacity(float[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static float[] copyOf(float[] original, int length) { + float[] copy = new float[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static String join(String separator, float... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 12); + builder.append(array[0]); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return Floats.LexicographicalComparator.INSTANCE; + } + + public static float[] toArray(Collection collection) { + if (collection instanceof Floats.FloatArrayAsList) { + return ((Floats.FloatArrayAsList)collection).toFloatArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + float[] array = new float[len]; + + for(int i = 0; i < len; ++i) { + array[i] = ((Number)Preconditions.checkNotNull(boxedArray[i])).floatValue(); + } + + return array; + } + } + + public static List asList(float... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Floats.FloatArrayAsList(backingArray)); + } + + @Nullable + @GwtIncompatible("regular expressions") + @Beta + public static Float tryParse(String string) { + if (Doubles.FLOATING_POINT_PATTERN.matcher(string).matches()) { + try { + return Float.parseFloat(string); + } catch (NumberFormatException var2) { + } + } + + return null; + } + + @GwtCompatible + private static class FloatArrayAsList extends AbstractList implements RandomAccess, Serializable { + final float[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + FloatArrayAsList(float[] array) { + this(array, 0, array.length); + } + + FloatArrayAsList(float[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Float get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Float && Floats.indexOf(this.array, (Float)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Float) { + int i = Floats.indexOf(this.array, (Float)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Float) { + int i = Floats.lastIndexOf(this.array, (Float)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Float set(int index, Float element) { + Preconditions.checkElementIndex(index, this.size()); + float oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Float)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Floats.FloatArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Floats.FloatArrayAsList) { + Floats.FloatArrayAsList that = (Floats.FloatArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Floats.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 12); + builder.append('[').append(this.array[this.start]); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(", ").append(this.array[i]); + } + + return builder.append(']').toString(); + } + + float[] toFloatArray() { + int size = this.size(); + float[] result = new float[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(float[] left, float[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = Float.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } + + private static final class FloatConverter extends Converter implements Serializable { + static final Floats.FloatConverter INSTANCE = new Floats.FloatConverter(); + private static final long serialVersionUID = 1L; + + protected Float doForward(String value) { + return Float.valueOf(value); + } + + protected String doBackward(Float value) { + return value.toString(); + } + + public String toString() { + return "Floats.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + } +} diff --git a/src/main/com/google/common/primitives/Ints.java b/src/main/com/google/common/primitives/Ints.java new file mode 100644 index 0000000..5cb9ca8 --- /dev/null +++ b/src/main/com/google/common/primitives/Ints.java @@ -0,0 +1,464 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Converter; +import com.google.common.base.Preconditions; +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; +import javax.annotation.CheckForNull; + +@GwtCompatible( + emulated = true +) +public final class Ints { + public static final int BYTES = 4; + public static final int MAX_POWER_OF_TWO = 1073741824; + private static final byte[] asciiDigits = new byte[128]; + + private Ints() { + } + + public static int hashCode(int value) { + return value; + } + + public static int checkedCast(long value) { + int result = (int)value; + if ((long)result != value) { + throw new IllegalArgumentException((new StringBuilder(34)).append("Out of range: ").append(value).toString()); + } else { + return result; + } + } + + public static int saturatedCast(long value) { + if (value > 2147483647L) { + return Integer.MAX_VALUE; + } else { + return value < -2147483648L ? Integer.MIN_VALUE : (int)value; + } + } + + public static int compare(int a, int b) { + return a < b ? -1 : (a > b ? 1 : 0); + } + + public static boolean contains(int[] array, int target) { + int[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + int value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(int[] array, int target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(int[] array, int target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(int[] array, int[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(int[] array, int target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(int[] array, int target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int min(int... array) { + Preconditions.checkArgument(array.length > 0); + int min = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + public static int max(int... array) { + Preconditions.checkArgument(array.length > 0); + int max = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + public static int[] concat(int[]... arrays) { + int length = 0; + int[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + int[] array = arr$[i$]; + length += array.length; + } + + int[] result = new int[length]; + pos = 0; + int[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + int[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + @GwtIncompatible("doesn't work") + public static byte[] toByteArray(int value) { + return new byte[]{(byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value}; + } + + @GwtIncompatible("doesn't work") + public static int fromByteArray(byte[] bytes) { + Preconditions.checkArgument(bytes.length >= 4, "array too small: %s < %s", bytes.length, 4); + return fromBytes(bytes[0], bytes[1], bytes[2], bytes[3]); + } + + @GwtIncompatible("doesn't work") + public static int fromBytes(byte b1, byte b2, byte b3, byte b4) { + return b1 << 24 | (b2 & 255) << 16 | (b3 & 255) << 8 | b4 & 255; + } + + @Beta + public static Converter stringConverter() { + return Ints.IntConverter.INSTANCE; + } + + public static int[] ensureCapacity(int[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static int[] copyOf(int[] original, int length) { + int[] copy = new int[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static String join(String separator, int... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(array[0]); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return Ints.LexicographicalComparator.INSTANCE; + } + + public static int[] toArray(Collection collection) { + if (collection instanceof Ints.IntArrayAsList) { + return ((Ints.IntArrayAsList)collection).toIntArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + int[] array = new int[len]; + + for(int i = 0; i < len; ++i) { + array[i] = ((Number)Preconditions.checkNotNull(boxedArray[i])).intValue(); + } + + return array; + } + } + + public static List asList(int... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Ints.IntArrayAsList(backingArray)); + } + + private static int digit(char c) { + return c < 128 ? asciiDigits[c] : -1; + } + + @CheckForNull + @Beta + public static Integer tryParse(String string) { + return tryParse(string, 10); + } + + @CheckForNull + static Integer tryParse(String string, int radix) { + if (((String)Preconditions.checkNotNull(string)).isEmpty()) { + return null; + } else if (radix >= 2 && radix <= 36) { + boolean negative = string.charAt(0) == '-'; + int index = negative ? 1 : 0; + if (index == string.length()) { + return null; + } else { + int digit = digit(string.charAt(index++)); + if (digit >= 0 && digit < radix) { + int accum = -digit; + + for(int cap = Integer.MIN_VALUE / radix; index < string.length(); accum -= digit) { + digit = digit(string.charAt(index++)); + if (digit < 0 || digit >= radix || accum < cap) { + return null; + } + + accum *= radix; + if (accum < Integer.MIN_VALUE + digit) { + return null; + } + } + + if (negative) { + return accum; + } else if (accum == Integer.MIN_VALUE) { + return null; + } else { + return -accum; + } + } else { + return null; + } + } + } else { + throw new IllegalArgumentException((new StringBuilder(65)).append("radix must be between MIN_RADIX and MAX_RADIX but was ").append(radix).toString()); + } + } + + static { + Arrays.fill(asciiDigits, (byte)-1); + + int i; + for(i = 0; i <= 9; ++i) { + asciiDigits[48 + i] = (byte)i; + } + + for(i = 0; i <= 26; ++i) { + asciiDigits[65 + i] = (byte)(10 + i); + asciiDigits[97 + i] = (byte)(10 + i); + } + + } + + @GwtCompatible + private static class IntArrayAsList extends AbstractList implements RandomAccess, Serializable { + final int[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + IntArrayAsList(int[] array) { + this(array, 0, array.length); + } + + IntArrayAsList(int[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Integer get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Integer && Ints.indexOf(this.array, (Integer)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Integer) { + int i = Ints.indexOf(this.array, (Integer)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Integer) { + int i = Ints.lastIndexOf(this.array, (Integer)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Integer set(int index, Integer element) { + Preconditions.checkElementIndex(index, this.size()); + int oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Integer)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Ints.IntArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Ints.IntArrayAsList) { + Ints.IntArrayAsList that = (Ints.IntArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Ints.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 5); + builder.append('[').append(this.array[this.start]); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(", ").append(this.array[i]); + } + + return builder.append(']').toString(); + } + + int[] toIntArray() { + int size = this.size(); + int[] result = new int[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(int[] left, int[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = Ints.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } + + private static final class IntConverter extends Converter implements Serializable { + static final Ints.IntConverter INSTANCE = new Ints.IntConverter(); + private static final long serialVersionUID = 1L; + + protected Integer doForward(String value) { + return Integer.decode(value); + } + + protected String doBackward(Integer value) { + return value.toString(); + } + + public String toString() { + return "Ints.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + } +} diff --git a/src/main/com/google/common/primitives/Longs.java b/src/main/com/google/common/primitives/Longs.java new file mode 100644 index 0000000..aec6f8a --- /dev/null +++ b/src/main/com/google/common/primitives/Longs.java @@ -0,0 +1,417 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Converter; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +@GwtCompatible +public final class Longs { + public static final int BYTES = 8; + public static final long MAX_POWER_OF_TWO = 4611686018427387904L; + + private Longs() { + } + + public static int hashCode(long value) { + return (int)(value ^ value >>> 32); + } + + public static int compare(long a, long b) { + return a < b ? -1 : (a > b ? 1 : 0); + } + + public static boolean contains(long[] array, long target) { + long[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + long value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(long[] array, long target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(long[] array, long target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(long[] array, long[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(long[] array, long target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(long[] array, long target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static long min(long... array) { + Preconditions.checkArgument(array.length > 0); + long min = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + public static long max(long... array) { + Preconditions.checkArgument(array.length > 0); + long max = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + public static long[] concat(long[]... arrays) { + int length = 0; + long[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + long[] array = arr$[i$]; + length += array.length; + } + + long[] result = new long[length]; + pos = 0; + long[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + long[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + public static byte[] toByteArray(long value) { + byte[] result = new byte[8]; + + for(int i = 7; i >= 0; --i) { + result[i] = (byte)((int)(value & 255L)); + value >>= 8; + } + + return result; + } + + public static long fromByteArray(byte[] bytes) { + Preconditions.checkArgument(bytes.length >= 8, "array too small: %s < %s", bytes.length, 8); + return fromBytes(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]); + } + + public static long fromBytes(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7, byte b8) { + return ((long)b1 & 255L) << 56 | ((long)b2 & 255L) << 48 | ((long)b3 & 255L) << 40 | ((long)b4 & 255L) << 32 | ((long)b5 & 255L) << 24 | ((long)b6 & 255L) << 16 | ((long)b7 & 255L) << 8 | (long)b8 & 255L; + } + + @Beta + public static Long tryParse(String string) { + if (((String)Preconditions.checkNotNull(string)).isEmpty()) { + return null; + } else { + boolean negative = string.charAt(0) == '-'; + int index = negative ? 1 : 0; + if (index == string.length()) { + return null; + } else { + int digit = string.charAt(index++) - 48; + if (digit >= 0 && digit <= 9) { + long accum; + for(accum = (long)(-digit); index < string.length(); accum -= (long)digit) { + digit = string.charAt(index++) - 48; + if (digit < 0 || digit > 9 || accum < -922337203685477580L) { + return null; + } + + accum *= 10L; + if (accum < Long.MIN_VALUE + (long)digit) { + return null; + } + } + + if (negative) { + return accum; + } else if (accum == Long.MIN_VALUE) { + return null; + } else { + return -accum; + } + } else { + return null; + } + } + } + } + + @Beta + public static Converter stringConverter() { + return Longs.LongConverter.INSTANCE; + } + + public static long[] ensureCapacity(long[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static long[] copyOf(long[] original, int length) { + long[] copy = new long[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static String join(String separator, long... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 10); + builder.append(array[0]); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return Longs.LexicographicalComparator.INSTANCE; + } + + public static long[] toArray(Collection collection) { + if (collection instanceof Longs.LongArrayAsList) { + return ((Longs.LongArrayAsList)collection).toLongArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + long[] array = new long[len]; + + for(int i = 0; i < len; ++i) { + array[i] = ((Number)Preconditions.checkNotNull(boxedArray[i])).longValue(); + } + + return array; + } + } + + public static List asList(long... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Longs.LongArrayAsList(backingArray)); + } + + @GwtCompatible + private static class LongArrayAsList extends AbstractList implements RandomAccess, Serializable { + final long[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + LongArrayAsList(long[] array) { + this(array, 0, array.length); + } + + LongArrayAsList(long[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Long get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Long && Longs.indexOf(this.array, (Long)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Long) { + int i = Longs.indexOf(this.array, (Long)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Long) { + int i = Longs.lastIndexOf(this.array, (Long)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Long set(int index, Long element) { + Preconditions.checkElementIndex(index, this.size()); + long oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Long)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Longs.LongArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Longs.LongArrayAsList) { + Longs.LongArrayAsList that = (Longs.LongArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Longs.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 10); + builder.append('[').append(this.array[this.start]); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(", ").append(this.array[i]); + } + + return builder.append(']').toString(); + } + + long[] toLongArray() { + int size = this.size(); + long[] result = new long[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(long[] left, long[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = Longs.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } + + private static final class LongConverter extends Converter implements Serializable { + static final Longs.LongConverter INSTANCE = new Longs.LongConverter(); + private static final long serialVersionUID = 1L; + + protected Long doForward(String value) { + return Long.decode(value); + } + + protected String doBackward(Long value) { + return value.toString(); + } + + public String toString() { + return "Longs.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + } +} diff --git a/src/main/com/google/common/primitives/ParseRequest.java b/src/main/com/google/common/primitives/ParseRequest.java new file mode 100644 index 0000000..60c9ba1 --- /dev/null +++ b/src/main/com/google/common/primitives/ParseRequest.java @@ -0,0 +1,41 @@ +package com.google.common.primitives; + +import com.google.common.annotations.GwtCompatible; + +@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"); + } else { + char firstChar = stringValue.charAt(0); + String rawValue; + byte radix; + if (!stringValue.startsWith("0x") && !stringValue.startsWith("0X")) { + 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; + } + } else { + rawValue = stringValue.substring(2); + radix = 16; + } + + return new ParseRequest(rawValue, radix); + } + } +} diff --git a/src/main/com/google/common/primitives/Primitives.java b/src/main/com/google/common/primitives/Primitives.java new file mode 100644 index 0000000..e74e888 --- /dev/null +++ b/src/main/com/google/common/primitives/Primitives.java @@ -0,0 +1,60 @@ +package com.google.common.primitives; + +import com.google.common.base.Preconditions; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public final class Primitives { + private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; + private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; + + private Primitives() { + } + + private static void add(Map, Class> forward, Map, Class> backward, Class key, Class value) { + forward.put(key, value); + backward.put(value, key); + } + + public static Set> allPrimitiveTypes() { + return PRIMITIVE_TO_WRAPPER_TYPE.keySet(); + } + + public static Set> allWrapperTypes() { + return WRAPPER_TO_PRIMITIVE_TYPE.keySet(); + } + + public static boolean isWrapperType(Class type) { + return WRAPPER_TO_PRIMITIVE_TYPE.containsKey(Preconditions.checkNotNull(type)); + } + + public static Class wrap(Class type) { + Preconditions.checkNotNull(type); + Class wrapped = (Class)PRIMITIVE_TO_WRAPPER_TYPE.get(type); + return wrapped == null ? type : wrapped; + } + + public static Class unwrap(Class type) { + Preconditions.checkNotNull(type); + Class unwrapped = (Class)WRAPPER_TO_PRIMITIVE_TYPE.get(type); + return unwrapped == null ? type : unwrapped; + } + + static { + Map, Class> primToWrap = new HashMap(16); + Map, Class> wrapToPrim = new HashMap(16); + add(primToWrap, wrapToPrim, Boolean.TYPE, Boolean.class); + add(primToWrap, wrapToPrim, Byte.TYPE, Byte.class); + add(primToWrap, wrapToPrim, Character.TYPE, Character.class); + add(primToWrap, wrapToPrim, Double.TYPE, Double.class); + add(primToWrap, wrapToPrim, Float.TYPE, Float.class); + add(primToWrap, wrapToPrim, Integer.TYPE, Integer.class); + add(primToWrap, wrapToPrim, Long.TYPE, Long.class); + add(primToWrap, wrapToPrim, Short.TYPE, Short.class); + add(primToWrap, wrapToPrim, Void.TYPE, Void.class); + PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); + WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); + } +} diff --git a/src/main/com/google/common/primitives/Shorts.java b/src/main/com/google/common/primitives/Shorts.java new file mode 100644 index 0000000..ee75f53 --- /dev/null +++ b/src/main/com/google/common/primitives/Shorts.java @@ -0,0 +1,394 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Converter; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.RandomAccess; + +@GwtCompatible( + emulated = true +) +public final class Shorts { + public static final int BYTES = 2; + public static final short MAX_POWER_OF_TWO = 16384; + + private Shorts() { + } + + public static int hashCode(short value) { + return value; + } + + public static short checkedCast(long value) { + short result = (short)((int)value); + if ((long)result != value) { + throw new IllegalArgumentException((new StringBuilder(34)).append("Out of range: ").append(value).toString()); + } else { + return result; + } + } + + public static short saturatedCast(long value) { + if (value > 32767L) { + return 32767; + } else { + return value < -32768L ? -32768 : (short)((int)value); + } + } + + public static int compare(short a, short b) { + return a - b; + } + + public static boolean contains(short[] array, short target) { + short[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + short value = arr$[i$]; + if (value == target) { + return true; + } + } + + return false; + } + + public static int indexOf(short[] array, short target) { + return indexOf(array, target, 0, array.length); + } + + private static int indexOf(short[] array, short target, int start, int end) { + for(int i = start; i < end; ++i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static int indexOf(short[] array, short[] target) { + Preconditions.checkNotNull(array, "array"); + Preconditions.checkNotNull(target, "target"); + if (target.length == 0) { + return 0; + } else { + label28: + 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 label28; + } + } + + return i; + } + + return -1; + } + } + + public static int lastIndexOf(short[] array, short target) { + return lastIndexOf(array, target, 0, array.length); + } + + private static int lastIndexOf(short[] array, short target, int start, int end) { + for(int i = end - 1; i >= start; --i) { + if (array[i] == target) { + return i; + } + } + + return -1; + } + + public static short min(short... array) { + Preconditions.checkArgument(array.length > 0); + short min = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + public static short max(short... array) { + Preconditions.checkArgument(array.length > 0); + short max = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + public static short[] concat(short[]... arrays) { + int length = 0; + short[][] arr$ = arrays; + int pos = arrays.length; + + for(int i$ = 0; i$ < pos; ++i$) { + short[] array = arr$[i$]; + length += array.length; + } + + short[] result = new short[length]; + pos = 0; + short[][] arr$ = arrays; + int len$ = arrays.length; + + for(int i$ = 0; i$ < len$; ++i$) { + short[] array = arr$[i$]; + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + + return result; + } + + @GwtIncompatible("doesn't work") + public static byte[] toByteArray(short value) { + return new byte[]{(byte)(value >> 8), (byte)value}; + } + + @GwtIncompatible("doesn't work") + public static short fromByteArray(byte[] bytes) { + Preconditions.checkArgument(bytes.length >= 2, "array too small: %s < %s", bytes.length, 2); + return fromBytes(bytes[0], bytes[1]); + } + + @GwtIncompatible("doesn't work") + public static short fromBytes(byte b1, byte b2) { + return (short)(b1 << 8 | b2 & 255); + } + + @Beta + public static Converter stringConverter() { + return Shorts.ShortConverter.INSTANCE; + } + + public static short[] ensureCapacity(short[] array, int minLength, int padding) { + Preconditions.checkArgument(minLength >= 0, "Invalid minLength: %s", minLength); + Preconditions.checkArgument(padding >= 0, "Invalid padding: %s", padding); + return array.length < minLength ? copyOf(array, minLength + padding) : array; + } + + private static short[] copyOf(short[] original, int length) { + short[] copy = new short[length]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, length)); + return copy; + } + + public static String join(String separator, short... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 6); + builder.append(array[0]); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return Shorts.LexicographicalComparator.INSTANCE; + } + + public static short[] toArray(Collection collection) { + if (collection instanceof Shorts.ShortArrayAsList) { + return ((Shorts.ShortArrayAsList)collection).toShortArray(); + } else { + Object[] boxedArray = collection.toArray(); + int len = boxedArray.length; + short[] array = new short[len]; + + for(int i = 0; i < len; ++i) { + array[i] = ((Number)Preconditions.checkNotNull(boxedArray[i])).shortValue(); + } + + return array; + } + } + + public static List asList(short... backingArray) { + return (List)(backingArray.length == 0 ? Collections.emptyList() : new Shorts.ShortArrayAsList(backingArray)); + } + + @GwtCompatible + private static class ShortArrayAsList extends AbstractList implements RandomAccess, Serializable { + final short[] array; + final int start; + final int end; + private static final long serialVersionUID = 0L; + + ShortArrayAsList(short[] array) { + this(array, 0, array.length); + } + + ShortArrayAsList(short[] array, int start, int end) { + this.array = array; + this.start = start; + this.end = end; + } + + public int size() { + return this.end - this.start; + } + + public boolean isEmpty() { + return false; + } + + public Short get(int index) { + Preconditions.checkElementIndex(index, this.size()); + return this.array[this.start + index]; + } + + public boolean contains(Object target) { + return target instanceof Short && Shorts.indexOf(this.array, (Short)target, this.start, this.end) != -1; + } + + public int indexOf(Object target) { + if (target instanceof Short) { + int i = Shorts.indexOf(this.array, (Short)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public int lastIndexOf(Object target) { + if (target instanceof Short) { + int i = Shorts.lastIndexOf(this.array, (Short)target, this.start, this.end); + if (i >= 0) { + return i - this.start; + } + } + + return -1; + } + + public Short set(int index, Short element) { + Preconditions.checkElementIndex(index, this.size()); + short oldValue = this.array[this.start + index]; + this.array[this.start + index] = (Short)Preconditions.checkNotNull(element); + return oldValue; + } + + public List subList(int fromIndex, int toIndex) { + int size = this.size(); + Preconditions.checkPositionIndexes(fromIndex, toIndex, size); + return (List)(fromIndex == toIndex ? Collections.emptyList() : new Shorts.ShortArrayAsList(this.array, this.start + fromIndex, this.start + toIndex)); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof Shorts.ShortArrayAsList) { + Shorts.ShortArrayAsList that = (Shorts.ShortArrayAsList)object; + int size = this.size(); + if (that.size() != size) { + return false; + } else { + for(int i = 0; i < size; ++i) { + if (this.array[this.start + i] != that.array[that.start + i]) { + return false; + } + } + + return true; + } + } else { + return super.equals(object); + } + } + + public int hashCode() { + int result = 1; + + for(int i = this.start; i < this.end; ++i) { + result = 31 * result + Shorts.hashCode(this.array[i]); + } + + return result; + } + + public String toString() { + StringBuilder builder = new StringBuilder(this.size() * 6); + builder.append('[').append(this.array[this.start]); + + for(int i = this.start + 1; i < this.end; ++i) { + builder.append(", ").append(this.array[i]); + } + + return builder.append(']').toString(); + } + + short[] toShortArray() { + int size = this.size(); + short[] result = new short[size]; + System.arraycopy(this.array, this.start, result, 0, size); + return result; + } + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(short[] left, short[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = Shorts.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } + + private static final class ShortConverter extends Converter implements Serializable { + static final Shorts.ShortConverter INSTANCE = new Shorts.ShortConverter(); + private static final long serialVersionUID = 1L; + + protected Short doForward(String value) { + return Short.decode(value); + } + + protected String doBackward(Short value) { + return value.toString(); + } + + public String toString() { + return "Shorts.stringConverter()"; + } + + private Object readResolve() { + return INSTANCE; + } + } +} diff --git a/src/main/com/google/common/primitives/SignedBytes.java b/src/main/com/google/common/primitives/SignedBytes.java new file mode 100644 index 0000000..2d3e7b0 --- /dev/null +++ b/src/main/com/google/common/primitives/SignedBytes.java @@ -0,0 +1,97 @@ +package com.google.common.primitives; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Comparator; + +@GwtCompatible +public final class SignedBytes { + public static final byte MAX_POWER_OF_TWO = 64; + + private SignedBytes() { + } + + public static byte checkedCast(long value) { + byte result = (byte)((int)value); + if ((long)result != value) { + throw new IllegalArgumentException((new StringBuilder(34)).append("Out of range: ").append(value).toString()); + } else { + return result; + } + } + + public static byte saturatedCast(long value) { + if (value > 127L) { + return 127; + } else { + return value < -128L ? -128 : (byte)((int)value); + } + } + + public static int compare(byte a, byte b) { + return a - b; + } + + public static byte min(byte... array) { + Preconditions.checkArgument(array.length > 0); + byte min = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + public static byte max(byte... array) { + Preconditions.checkArgument(array.length > 0); + byte max = array[0]; + + for(int i = 1; i < array.length; ++i) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + public static String join(String separator, byte... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(array[0]); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(array[i]); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return SignedBytes.LexicographicalComparator.INSTANCE; + } + + private static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(byte[] left, byte[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = SignedBytes.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } +} diff --git a/src/main/com/google/common/primitives/UnsignedBytes.java b/src/main/com/google/common/primitives/UnsignedBytes.java new file mode 100644 index 0000000..0871cde --- /dev/null +++ b/src/main/com/google/common/primitives/UnsignedBytes.java @@ -0,0 +1,229 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.lang.reflect.Field; +import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Comparator; +import sun.misc.Unsafe; + +public final class UnsignedBytes { + public static final byte MAX_POWER_OF_TWO = -128; + public static final byte MAX_VALUE = -1; + private static final int UNSIGNED_MASK = 255; + + private UnsignedBytes() { + } + + public static int toInt(byte value) { + return value & 255; + } + + public static byte checkedCast(long value) { + if (value >> 8 != 0L) { + throw new IllegalArgumentException((new StringBuilder(34)).append("Out of range: ").append(value).toString()); + } else { + return (byte)((int)value); + } + } + + public static byte saturatedCast(long value) { + if (value > (long)toInt((byte)-1)) { + return -1; + } else { + return value < 0L ? 0 : (byte)((int)value); + } + } + + public static int compare(byte a, byte b) { + return toInt(a) - toInt(b); + } + + public static byte min(byte... array) { + Preconditions.checkArgument(array.length > 0); + int min = toInt(array[0]); + + for(int i = 1; i < array.length; ++i) { + int next = toInt(array[i]); + if (next < min) { + min = next; + } + } + + return (byte)min; + } + + public static byte max(byte... array) { + Preconditions.checkArgument(array.length > 0); + int max = toInt(array[0]); + + for(int i = 1; i < array.length; ++i) { + int next = toInt(array[i]); + if (next > max) { + max = next; + } + } + + return (byte)max; + } + + @Beta + public static String toString(byte x) { + return toString(x, 10); + } + + @Beta + public static String toString(byte x, int radix) { + Preconditions.checkArgument(radix >= 2 && radix <= 36, "radix (%s) must be between Character.MIN_RADIX and Character.MAX_RADIX", radix); + return Integer.toString(toInt(x), radix); + } + + @Beta + public static byte parseUnsignedByte(String string) { + return parseUnsignedByte(string, 10); + } + + @Beta + public static byte parseUnsignedByte(String string, int radix) { + int parse = Integer.parseInt((String)Preconditions.checkNotNull(string), radix); + if (parse >> 8 == 0) { + return (byte)parse; + } else { + throw new NumberFormatException((new StringBuilder(25)).append("out of range: ").append(parse).toString()); + } + } + + public static String join(String separator, byte... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * (3 + separator.length())); + builder.append(toInt(array[0])); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(toString(array[i])); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return UnsignedBytes.LexicographicalComparatorHolder.BEST_COMPARATOR; + } + + @VisibleForTesting + static Comparator lexicographicalComparatorJavaImpl() { + return UnsignedBytes.LexicographicalComparatorHolder.PureJavaComparator.INSTANCE; + } + + @VisibleForTesting + static class LexicographicalComparatorHolder { + static final String UNSAFE_COMPARATOR_NAME = String.valueOf(UnsignedBytes.LexicographicalComparatorHolder.class.getName()).concat("$UnsafeComparator"); + static final Comparator BEST_COMPARATOR = getBestComparator(); + + static Comparator getBestComparator() { + try { + Class theClass = Class.forName(UNSAFE_COMPARATOR_NAME); + Comparator comparator = (Comparator)theClass.getEnumConstants()[0]; + return comparator; + } catch (Throwable var2) { + return UnsignedBytes.lexicographicalComparatorJavaImpl(); + } + } + + static enum PureJavaComparator implements Comparator { + INSTANCE; + + public int compare(byte[] left, byte[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + int result = UnsignedBytes.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + } + + @VisibleForTesting + static enum UnsafeComparator implements Comparator { + INSTANCE; + + static final boolean BIG_ENDIAN = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN); + static final Unsafe theUnsafe = getUnsafe(); + static final int BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + + private static Unsafe getUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException var2) { + try { + return (Unsafe)AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Unsafe run() throws Exception { + Class k = Unsafe.class; + Field[] arr$ = k.getDeclaredFields(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Field f = arr$[i$]; + f.setAccessible(true); + Object x = f.get((Object)null); + if (k.isInstance(x)) { + return (Unsafe)k.cast(x); + } + } + + throw new NoSuchFieldError("the Unsafe"); + } + }); + } catch (PrivilegedActionException var1) { + throw new RuntimeException("Could not initialize intrinsics", var1.getCause()); + } + } + } + + public int compare(byte[] left, byte[] right) { + int minLength = Math.min(left.length, right.length); + int minWords = minLength / 8; + + int i; + for(i = 0; i < minWords * 8; i += 8) { + long lw = theUnsafe.getLong(left, (long)BYTE_ARRAY_BASE_OFFSET + (long)i); + long rw = theUnsafe.getLong(right, (long)BYTE_ARRAY_BASE_OFFSET + (long)i); + if (lw != rw) { + if (BIG_ENDIAN) { + return UnsignedLongs.compare(lw, rw); + } + + int n = Long.numberOfTrailingZeros(lw ^ rw) & -8; + return (int)((lw >>> n & 255L) - (rw >>> n & 255L)); + } + } + + for(i = minWords * 8; i < minLength; ++i) { + int result = UnsignedBytes.compare(left[i], right[i]); + if (result != 0) { + return result; + } + } + + return left.length - right.length; + } + + static { + if (theUnsafe.arrayIndexScale(byte[].class) != 1) { + throw new AssertionError(); + } + } + } + } +} diff --git a/src/main/com/google/common/primitives/UnsignedInteger.java b/src/main/com/google/common/primitives/UnsignedInteger.java new file mode 100644 index 0000000..327037b --- /dev/null +++ b/src/main/com/google/common/primitives/UnsignedInteger.java @@ -0,0 +1,117 @@ +package com.google.common.primitives; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.annotations.GwtIncompatible; +import com.google.common.base.Preconditions; +import java.math.BigInteger; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +@GwtCompatible( + emulated = true +) +public final class UnsignedInteger extends Number implements Comparable { + public static final UnsignedInteger ZERO = fromIntBits(0); + public static final UnsignedInteger ONE = fromIntBits(1); + public static final UnsignedInteger MAX_VALUE = fromIntBits(-1); + private final int value; + + private UnsignedInteger(int value) { + this.value = value & -1; + } + + public static UnsignedInteger fromIntBits(int bits) { + return new UnsignedInteger(bits); + } + + public static UnsignedInteger valueOf(long value) { + Preconditions.checkArgument((value & 4294967295L) == value, "value (%s) is outside the range for an unsigned integer value", value); + return fromIntBits((int)value); + } + + public static UnsignedInteger valueOf(BigInteger value) { + Preconditions.checkNotNull(value); + Preconditions.checkArgument(value.signum() >= 0 && value.bitLength() <= 32, "value (%s) is outside the range for an unsigned integer value", value); + return fromIntBits(value.intValue()); + } + + public static UnsignedInteger valueOf(String string) { + return valueOf(string, 10); + } + + public static UnsignedInteger valueOf(String string, int radix) { + return fromIntBits(UnsignedInts.parseUnsignedInt(string, radix)); + } + + @CheckReturnValue + public UnsignedInteger plus(UnsignedInteger val) { + return fromIntBits(this.value + ((UnsignedInteger)Preconditions.checkNotNull(val)).value); + } + + @CheckReturnValue + public UnsignedInteger minus(UnsignedInteger val) { + return fromIntBits(this.value - ((UnsignedInteger)Preconditions.checkNotNull(val)).value); + } + + @CheckReturnValue + @GwtIncompatible("Does not truncate correctly") + public UnsignedInteger times(UnsignedInteger val) { + return fromIntBits(this.value * ((UnsignedInteger)Preconditions.checkNotNull(val)).value); + } + + @CheckReturnValue + public UnsignedInteger dividedBy(UnsignedInteger val) { + return fromIntBits(UnsignedInts.divide(this.value, ((UnsignedInteger)Preconditions.checkNotNull(val)).value)); + } + + @CheckReturnValue + public UnsignedInteger mod(UnsignedInteger val) { + return fromIntBits(UnsignedInts.remainder(this.value, ((UnsignedInteger)Preconditions.checkNotNull(val)).value)); + } + + public int intValue() { + return this.value; + } + + public long longValue() { + return UnsignedInts.toLong(this.value); + } + + public float floatValue() { + return (float)this.longValue(); + } + + public double doubleValue() { + return (double)this.longValue(); + } + + public BigInteger bigIntegerValue() { + return BigInteger.valueOf(this.longValue()); + } + + public int compareTo(UnsignedInteger other) { + Preconditions.checkNotNull(other); + return UnsignedInts.compare(this.value, other.value); + } + + public int hashCode() { + return this.value; + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof UnsignedInteger) { + UnsignedInteger other = (UnsignedInteger)obj; + return this.value == other.value; + } else { + return false; + } + } + + public String toString() { + return this.toString(10); + } + + public String toString(int radix) { + return UnsignedInts.toString(this.value, radix); + } +} diff --git a/src/main/com/google/common/primitives/UnsignedInts.java b/src/main/com/google/common/primitives/UnsignedInts.java new file mode 100644 index 0000000..e38e0ef --- /dev/null +++ b/src/main/com/google/common/primitives/UnsignedInts.java @@ -0,0 +1,147 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.util.Comparator; + +@Beta +@GwtCompatible +public final class UnsignedInts { + static final long INT_MASK = 4294967295L; + + private UnsignedInts() { + } + + static int flip(int value) { + return value ^ Integer.MIN_VALUE; + } + + public static int compare(int a, int b) { + return Ints.compare(flip(a), flip(b)); + } + + public static long toLong(int value) { + return (long)value & 4294967295L; + } + + public static int min(int... array) { + Preconditions.checkArgument(array.length > 0); + int min = flip(array[0]); + + for(int i = 1; i < array.length; ++i) { + int next = flip(array[i]); + if (next < min) { + min = next; + } + } + + return flip(min); + } + + public static int max(int... array) { + Preconditions.checkArgument(array.length > 0); + int max = flip(array[0]); + + for(int i = 1; i < array.length; ++i) { + int next = flip(array[i]); + if (next > max) { + max = next; + } + } + + return flip(max); + } + + public static String join(String separator, int... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(toString(array[0])); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(toString(array[i])); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return UnsignedInts.LexicographicalComparator.INSTANCE; + } + + public static int divide(int dividend, int divisor) { + return (int)(toLong(dividend) / toLong(divisor)); + } + + public static int remainder(int dividend, int divisor) { + return (int)(toLong(dividend) % toLong(divisor)); + } + + public static int decode(String stringValue) { + ParseRequest request = ParseRequest.fromString(stringValue); + + try { + return parseUnsignedInt(request.rawValue, request.radix); + } catch (NumberFormatException var4) { + NumberFormatException var10000 = new NumberFormatException; + String var10003 = String.valueOf(stringValue); + String var10002; + if (var10003.length() != 0) { + var10002 = "Error parsing value: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Error parsing value: "); + } + + var10000.(var10002); + NumberFormatException decodeException = var10000; + decodeException.initCause(var4); + throw decodeException; + } + } + + public static int parseUnsignedInt(String s) { + return parseUnsignedInt(s, 10); + } + + public static int parseUnsignedInt(String string, int radix) { + Preconditions.checkNotNull(string); + long result = Long.parseLong(string, radix); + if ((result & 4294967295L) != result) { + String var4 = String.valueOf(String.valueOf(string)); + throw new NumberFormatException((new StringBuilder(69 + var4.length())).append("Input ").append(var4).append(" in base ").append(radix).append(" is not in the range of an unsigned integer").toString()); + } else { + return (int)result; + } + } + + public static String toString(int x) { + return toString(x, 10); + } + + public static String toString(int x, int radix) { + long asLong = (long)x & 4294967295L; + return Long.toString(asLong, radix); + } + + static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(int[] left, int[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + if (left[i] != right[i]) { + return UnsignedInts.compare(left[i], right[i]); + } + } + + return left.length - right.length; + } + } +} diff --git a/src/main/com/google/common/primitives/UnsignedLong.java b/src/main/com/google/common/primitives/UnsignedLong.java new file mode 100644 index 0000000..79b711f --- /dev/null +++ b/src/main/com/google/common/primitives/UnsignedLong.java @@ -0,0 +1,130 @@ +package com.google.common.primitives; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.io.Serializable; +import java.math.BigInteger; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +@GwtCompatible( + serializable = true +) +public final class UnsignedLong extends Number implements Comparable, Serializable { + private static final long UNSIGNED_MASK = Long.MAX_VALUE; + public static final UnsignedLong ZERO = new UnsignedLong(0L); + public static final UnsignedLong ONE = new UnsignedLong(1L); + public static final UnsignedLong MAX_VALUE = new UnsignedLong(-1L); + private final long value; + + private UnsignedLong(long value) { + this.value = value; + } + + public static UnsignedLong fromLongBits(long bits) { + return new UnsignedLong(bits); + } + + public static UnsignedLong valueOf(long value) { + Preconditions.checkArgument(value >= 0L, "value (%s) is outside the range for an unsigned long value", value); + return fromLongBits(value); + } + + public static UnsignedLong valueOf(BigInteger value) { + Preconditions.checkNotNull(value); + Preconditions.checkArgument(value.signum() >= 0 && value.bitLength() <= 64, "value (%s) is outside the range for an unsigned long value", value); + return fromLongBits(value.longValue()); + } + + public static UnsignedLong valueOf(String string) { + return valueOf(string, 10); + } + + public static UnsignedLong valueOf(String string, int radix) { + return fromLongBits(UnsignedLongs.parseUnsignedLong(string, radix)); + } + + public UnsignedLong plus(UnsignedLong val) { + return fromLongBits(this.value + ((UnsignedLong)Preconditions.checkNotNull(val)).value); + } + + public UnsignedLong minus(UnsignedLong val) { + return fromLongBits(this.value - ((UnsignedLong)Preconditions.checkNotNull(val)).value); + } + + @CheckReturnValue + public UnsignedLong times(UnsignedLong val) { + return fromLongBits(this.value * ((UnsignedLong)Preconditions.checkNotNull(val)).value); + } + + @CheckReturnValue + public UnsignedLong dividedBy(UnsignedLong val) { + return fromLongBits(UnsignedLongs.divide(this.value, ((UnsignedLong)Preconditions.checkNotNull(val)).value)); + } + + @CheckReturnValue + public UnsignedLong mod(UnsignedLong val) { + return fromLongBits(UnsignedLongs.remainder(this.value, ((UnsignedLong)Preconditions.checkNotNull(val)).value)); + } + + public int intValue() { + return (int)this.value; + } + + public long longValue() { + return this.value; + } + + public float floatValue() { + float fValue = (float)(this.value & Long.MAX_VALUE); + if (this.value < 0L) { + fValue += 9.223372E18F; + } + + return fValue; + } + + public double doubleValue() { + double dValue = (double)(this.value & Long.MAX_VALUE); + if (this.value < 0L) { + dValue += 9.223372036854776E18D; + } + + return dValue; + } + + public BigInteger bigIntegerValue() { + BigInteger bigInt = BigInteger.valueOf(this.value & Long.MAX_VALUE); + if (this.value < 0L) { + bigInt = bigInt.setBit(63); + } + + return bigInt; + } + + public int compareTo(UnsignedLong o) { + Preconditions.checkNotNull(o); + return UnsignedLongs.compare(this.value, o.value); + } + + public int hashCode() { + return Longs.hashCode(this.value); + } + + public boolean equals(@Nullable Object obj) { + if (obj instanceof UnsignedLong) { + UnsignedLong other = (UnsignedLong)obj; + return this.value == other.value; + } else { + return false; + } + } + + public String toString() { + return UnsignedLongs.toString(this.value); + } + + public String toString(int radix) { + return UnsignedLongs.toString(this.value, radix); + } +} diff --git a/src/main/com/google/common/primitives/UnsignedLongs.java b/src/main/com/google/common/primitives/UnsignedLongs.java new file mode 100644 index 0000000..eac9428 --- /dev/null +++ b/src/main/com/google/common/primitives/UnsignedLongs.java @@ -0,0 +1,236 @@ +package com.google.common.primitives; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Preconditions; +import java.math.BigInteger; +import java.util.Comparator; + +@Beta +@GwtCompatible +public final class UnsignedLongs { + public static final long MAX_VALUE = -1L; + private static final long[] maxValueDivs = new long[37]; + private static final int[] maxValueMods = new int[37]; + private static final int[] maxSafeDigits = new int[37]; + + private UnsignedLongs() { + } + + private static long flip(long a) { + return a ^ Long.MIN_VALUE; + } + + public static int compare(long a, long b) { + return Longs.compare(flip(a), flip(b)); + } + + public static long min(long... array) { + Preconditions.checkArgument(array.length > 0); + long min = flip(array[0]); + + for(int i = 1; i < array.length; ++i) { + long next = flip(array[i]); + if (next < min) { + min = next; + } + } + + return flip(min); + } + + public static long max(long... array) { + Preconditions.checkArgument(array.length > 0); + long max = flip(array[0]); + + for(int i = 1; i < array.length; ++i) { + long next = flip(array[i]); + if (next > max) { + max = next; + } + } + + return flip(max); + } + + public static String join(String separator, long... array) { + Preconditions.checkNotNull(separator); + if (array.length == 0) { + return ""; + } else { + StringBuilder builder = new StringBuilder(array.length * 5); + builder.append(toString(array[0])); + + for(int i = 1; i < array.length; ++i) { + builder.append(separator).append(toString(array[i])); + } + + return builder.toString(); + } + } + + public static Comparator lexicographicalComparator() { + return UnsignedLongs.LexicographicalComparator.INSTANCE; + } + + public static long divide(long dividend, long divisor) { + if (divisor < 0L) { + return compare(dividend, divisor) < 0 ? 0L : 1L; + } else if (dividend >= 0L) { + return dividend / divisor; + } else { + long quotient = (dividend >>> 1) / divisor << 1; + long rem = dividend - quotient * divisor; + return quotient + (long)(compare(rem, divisor) >= 0 ? 1 : 0); + } + } + + public static long remainder(long dividend, long divisor) { + if (divisor < 0L) { + return compare(dividend, divisor) < 0 ? dividend : dividend - divisor; + } else if (dividend >= 0L) { + return dividend % divisor; + } else { + long quotient = (dividend >>> 1) / divisor << 1; + long rem = dividend - quotient * divisor; + return rem - (compare(rem, divisor) >= 0 ? divisor : 0L); + } + } + + public static long parseUnsignedLong(String s) { + return parseUnsignedLong(s, 10); + } + + public static long decode(String stringValue) { + ParseRequest request = ParseRequest.fromString(stringValue); + + try { + return parseUnsignedLong(request.rawValue, request.radix); + } catch (NumberFormatException var4) { + NumberFormatException var10000 = new NumberFormatException; + String var10003 = String.valueOf(stringValue); + String var10002; + if (var10003.length() != 0) { + var10002 = "Error parsing value: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Error parsing value: "); + } + + var10000.(var10002); + NumberFormatException decodeException = var10000; + decodeException.initCause(var4); + throw decodeException; + } + } + + public static long parseUnsignedLong(String s, int radix) { + Preconditions.checkNotNull(s); + if (s.length() == 0) { + throw new NumberFormatException("empty string"); + } else if (radix >= 2 && radix <= 36) { + int max_safe_pos = maxSafeDigits[radix] - 1; + long value = 0L; + + for(int pos = 0; pos < s.length(); ++pos) { + int digit = Character.digit(s.charAt(pos), radix); + if (digit == -1) { + throw new NumberFormatException(s); + } + + if (pos > max_safe_pos && overflowInParse(value, digit, radix)) { + NumberFormatException var10000 = new NumberFormatException; + String var10003 = String.valueOf(s); + String var10002; + if (var10003.length() != 0) { + var10002 = "Too large for unsigned long: ".concat(var10003); + } else { + String var10004 = new String; + var10002 = var10004; + var10004.("Too large for unsigned long: "); + } + + var10000.(var10002); + throw var10000; + } + + value = value * (long)radix + (long)digit; + } + + return value; + } else { + throw new NumberFormatException((new StringBuilder(26)).append("illegal radix: ").append(radix).toString()); + } + } + + private static boolean overflowInParse(long current, int digit, int radix) { + if (current >= 0L) { + if (current < maxValueDivs[radix]) { + return false; + } else if (current > maxValueDivs[radix]) { + return true; + } else { + return digit > maxValueMods[radix]; + } + } else { + return true; + } + } + + public static String toString(long x) { + return toString(x, 10); + } + + public static String toString(long x, int radix) { + Preconditions.checkArgument(radix >= 2 && radix <= 36, "radix (%s) must be between Character.MIN_RADIX and Character.MAX_RADIX", radix); + if (x == 0L) { + return "0"; + } else { + char[] buf = new char[64]; + int i = buf.length; + if (x < 0L) { + long quotient = divide(x, (long)radix); + long rem = x - quotient * (long)radix; + --i; + buf[i] = Character.forDigit((int)rem, radix); + x = quotient; + } + + while(x > 0L) { + --i; + buf[i] = Character.forDigit((int)(x % (long)radix), radix); + x /= (long)radix; + } + + return new String(buf, i, buf.length - i); + } + } + + static { + BigInteger overflow = new BigInteger("10000000000000000", 16); + + for(int i = 2; i <= 36; ++i) { + maxValueDivs[i] = divide(-1L, (long)i); + maxValueMods[i] = (int)remainder(-1L, (long)i); + maxSafeDigits[i] = overflow.toString(i).length() - 1; + } + + } + + static enum LexicographicalComparator implements Comparator { + INSTANCE; + + public int compare(long[] left, long[] right) { + int minLength = Math.min(left.length, right.length); + + for(int i = 0; i < minLength; ++i) { + if (left[i] != right[i]) { + return UnsignedLongs.compare(left[i], right[i]); + } + } + + return left.length - right.length; + } + } +} diff --git a/src/main/com/google/common/primitives/package-info.java b/src/main/com/google/common/primitives/package-info.java new file mode 100644 index 0000000..b277c15 --- /dev/null +++ b/src/main/com/google/common/primitives/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.primitives; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/reflect/AbstractInvocationHandler.java b/src/main/com/google/common/reflect/AbstractInvocationHandler.java new file mode 100644 index 0000000..8f2223a --- /dev/null +++ b/src/main/com/google/common/reflect/AbstractInvocationHandler.java @@ -0,0 +1,50 @@ +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 java.util.Arrays; +import javax.annotation.Nullable; + +@Beta +public abstract class AbstractInvocationHandler implements InvocationHandler { + private static final Object[] NO_ARGS = new Object[0]; + + 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 this.hashCode(); + } else if (args.length == 1 && method.getName().equals("equals") && method.getParameterTypes()[0] == Object.class) { + Object arg = args[0]; + if (arg == null) { + return false; + } else { + return proxy == arg ? true : isProxyOfSameInterfaces(arg, proxy.getClass()) && this.equals(Proxy.getInvocationHandler(arg)); + } + } else { + return args.length == 0 && method.getName().equals("toString") ? this.toString() : this.handleInvocation(proxy, method, args); + } + } + + protected abstract Object handleInvocation(Object var1, Method var2, Object[] var3) throws Throwable; + + public boolean equals(Object obj) { + return super.equals(obj); + } + + public int hashCode() { + return super.hashCode(); + } + + public String toString() { + return super.toString(); + } + + private static boolean isProxyOfSameInterfaces(Object arg, Class proxyClass) { + return proxyClass.isInstance(arg) || Proxy.isProxyClass(arg.getClass()) && Arrays.equals(arg.getClass().getInterfaces(), proxyClass.getInterfaces()); + } +} diff --git a/src/main/com/google/common/reflect/ClassPath.java b/src/main/com/google/common/reflect/ClassPath.java new file mode 100644 index 0000000..867b6a3 --- /dev/null +++ b/src/main/com/google/common/reflect/ClassPath.java @@ -0,0 +1,388 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.CharMatcher; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; +import com.google.common.collect.FluentIterable; +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 com.google.common.collect.Sets; +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.Iterator; +import java.util.LinkedHashMap; +import java.util.Set; +import java.util.Map.Entry; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.jar.Attributes.Name; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +@Beta +public final class ClassPath { + private static final Logger logger = Logger.getLogger(ClassPath.class.getName()); + private static final Predicate IS_TOP_LEVEL = new Predicate() { + public boolean apply(ClassPath.ClassInfo info) { + return info.className.indexOf(36) == -1; + } + }; + private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR = Splitter.on(" ").omitEmptyStrings(); + private static final String CLASS_FILE_NAME_EXTENSION = ".class"; + private final ImmutableSet resources; + + private ClassPath(ImmutableSet resources) { + this.resources = resources; + } + + public static ClassPath from(ClassLoader classloader) throws IOException { + ClassPath.Scanner scanner = new ClassPath.Scanner(); + Iterator i$ = getClassPathEntries(classloader).entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + scanner.scan((URI)entry.getKey(), (ClassLoader)entry.getValue()); + } + + return new ClassPath(scanner.getResources()); + } + + public ImmutableSet getResources() { + return this.resources; + } + + public ImmutableSet getAllClasses() { + return FluentIterable.from((Iterable)this.resources).filter(ClassPath.ClassInfo.class).toSet(); + } + + public ImmutableSet getTopLevelClasses() { + return FluentIterable.from((Iterable)this.resources).filter(ClassPath.ClassInfo.class).filter(IS_TOP_LEVEL).toSet(); + } + + public ImmutableSet getTopLevelClasses(String packageName) { + Preconditions.checkNotNull(packageName); + ImmutableSet.Builder builder = ImmutableSet.builder(); + Iterator i$ = this.getTopLevelClasses().iterator(); + + while(i$.hasNext()) { + ClassPath.ClassInfo classInfo = (ClassPath.ClassInfo)i$.next(); + if (classInfo.getPackageName().equals(packageName)) { + builder.add((Object)classInfo); + } + } + + return builder.build(); + } + + public ImmutableSet getTopLevelClassesRecursive(String packageName) { + Preconditions.checkNotNull(packageName); + String var3 = String.valueOf(String.valueOf(packageName)); + String packagePrefix = (new StringBuilder(1 + var3.length())).append(var3).append(".").toString(); + ImmutableSet.Builder builder = ImmutableSet.builder(); + Iterator i$ = this.getTopLevelClasses().iterator(); + + while(i$.hasNext()) { + ClassPath.ClassInfo classInfo = (ClassPath.ClassInfo)i$.next(); + if (classInfo.getName().startsWith(packagePrefix)) { + builder.add((Object)classInfo); + } + } + + return builder.build(); + } + + @VisibleForTesting + static ImmutableMap getClassPathEntries(ClassLoader classloader) { + LinkedHashMap entries = Maps.newLinkedHashMap(); + ClassLoader parent = classloader.getParent(); + if (parent != null) { + entries.putAll(getClassPathEntries(parent)); + } + + if (classloader instanceof URLClassLoader) { + URLClassLoader urlClassLoader = (URLClassLoader)classloader; + URL[] arr$ = urlClassLoader.getURLs(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + URL entry = arr$[i$]; + + URI uri; + try { + uri = entry.toURI(); + } catch (URISyntaxException var10) { + throw new IllegalArgumentException(var10); + } + + if (!entries.containsKey(uri)) { + entries.put(uri, classloader); + } + } + } + + return ImmutableMap.copyOf(entries); + } + + @VisibleForTesting + static String getClassName(String filename) { + int classNameEnd = filename.length() - ".class".length(); + return filename.substring(0, classNameEnd).replace('/', '.'); + } + + @VisibleForTesting + static final class Scanner { + private final ImmutableSortedSet.Builder resources = new ImmutableSortedSet.Builder(Ordering.usingToString()); + private final Set scannedUris = Sets.newHashSet(); + + ImmutableSortedSet getResources() { + return this.resources.build(); + } + + void scan(URI uri, ClassLoader classloader) throws IOException { + if (uri.getScheme().equals("file") && this.scannedUris.add(uri)) { + this.scanFrom(new File(uri), classloader); + } + + } + + @VisibleForTesting + void scanFrom(File file, ClassLoader classloader) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + this.scanDirectory(file, classloader); + } else { + this.scanJar(file, classloader); + } + + } + } + + private void scanDirectory(File directory, ClassLoader classloader) throws IOException { + this.scanDirectory(directory, classloader, "", ImmutableSet.of()); + } + + private void scanDirectory(File directory, ClassLoader classloader, String packagePrefix, ImmutableSet ancestors) throws IOException { + File canonical = directory.getCanonicalFile(); + if (!ancestors.contains(canonical)) { + File[] files = directory.listFiles(); + if (files == null) { + Logger var16 = ClassPath.logger; + String var15 = String.valueOf(String.valueOf(directory)); + var16.warning((new StringBuilder(22 + var15.length())).append("Cannot read directory ").append(var15).toString()); + } else { + ImmutableSet newAncestors = ImmutableSet.builder().addAll((Iterable)ancestors).add((Object)canonical).build(); + File[] arr$ = files; + int len$ = files.length; + + for(int i$ = 0; i$ < len$; ++i$) { + File f = arr$[i$]; + String name = f.getName(); + String resourceName; + if (f.isDirectory()) { + resourceName = String.valueOf(String.valueOf(packagePrefix)); + String var14 = String.valueOf(String.valueOf(name)); + this.scanDirectory(f, classloader, (new StringBuilder(1 + resourceName.length() + var14.length())).append(resourceName).append(var14).append("/").toString(), newAncestors); + } else { + String var10000 = String.valueOf(packagePrefix); + String var10001 = String.valueOf(name); + if (var10001.length() != 0) { + var10000 = var10000.concat(var10001); + } else { + String var10002 = new String; + var10001 = var10000; + var10000 = var10002; + var10002.(var10001); + } + + resourceName = var10000; + if (!resourceName.equals("META-INF/MANIFEST.MF")) { + this.resources.add((Object)ClassPath.ResourceInfo.of(resourceName, classloader)); + } + } + } + + } + } + } + + private void scanJar(File file, ClassLoader classloader) throws IOException { + JarFile jarFile; + try { + jarFile = new JarFile(file); + } catch (IOException var13) { + return; + } + + try { + Iterator i$ = getClassPathFromManifest(file, jarFile.getManifest()).iterator(); + + while(i$.hasNext()) { + URI uri = (URI)i$.next(); + this.scan(uri, classloader); + } + + Enumeration entries = jarFile.entries(); + + while(entries.hasMoreElements()) { + JarEntry entry = (JarEntry)entries.nextElement(); + if (!entry.isDirectory() && !entry.getName().equals("META-INF/MANIFEST.MF")) { + this.resources.add((Object)ClassPath.ResourceInfo.of(entry.getName(), classloader)); + } + } + } finally { + try { + jarFile.close(); + } catch (IOException var12) { + } + + } + + } + + @VisibleForTesting + static ImmutableSet getClassPathFromManifest(File jarFile, @Nullable Manifest manifest) { + if (manifest == null) { + return ImmutableSet.of(); + } else { + ImmutableSet.Builder builder = ImmutableSet.builder(); + String classpathAttribute = manifest.getMainAttributes().getValue(Name.CLASS_PATH.toString()); + if (classpathAttribute != null) { + Iterator i$ = ClassPath.CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute).iterator(); + + while(true) { + URI uri; + while(true) { + if (!i$.hasNext()) { + return builder.build(); + } + + String path = (String)i$.next(); + + try { + uri = getClassPathEntry(jarFile, path); + break; + } catch (URISyntaxException var8) { + Logger var10000 = ClassPath.logger; + String var10002 = String.valueOf(path); + String var10001; + if (var10002.length() != 0) { + var10001 = "Invalid Class-Path entry: ".concat(var10002); + } else { + String var10003 = new String; + var10001 = var10003; + var10003.("Invalid Class-Path entry: "); + } + + var10000.warning(var10001); + } + } + + builder.add((Object)uri); + } + } else { + return builder.build(); + } + } + } + + @VisibleForTesting + static URI getClassPathEntry(File jarFile, String path) throws URISyntaxException { + URI uri = new URI(path); + return uri.isAbsolute() ? uri : (new File(jarFile.getParentFile(), path.replace('/', File.separatorChar))).toURI(); + } + } + + @Beta + public static final class ClassInfo extends ClassPath.ResourceInfo { + private final String className; + + ClassInfo(String resourceName, ClassLoader loader) { + super(resourceName, loader); + this.className = ClassPath.getClassName(resourceName); + } + + public String getPackageName() { + return Reflection.getPackageName(this.className); + } + + public String getSimpleName() { + int lastDollarSign = this.className.lastIndexOf(36); + String packageName; + if (lastDollarSign != -1) { + packageName = this.className.substring(lastDollarSign + 1); + return CharMatcher.DIGIT.trimLeadingFrom(packageName); + } else { + packageName = this.getPackageName(); + return packageName.isEmpty() ? this.className : this.className.substring(packageName.length() + 1); + } + } + + public String getName() { + return this.className; + } + + public Class load() { + try { + return this.loader.loadClass(this.className); + } catch (ClassNotFoundException var2) { + throw new IllegalStateException(var2); + } + } + + public String toString() { + return this.className; + } + } + + @Beta + public static class ResourceInfo { + private final String resourceName; + final ClassLoader loader; + + static ClassPath.ResourceInfo of(String resourceName, ClassLoader loader) { + return (ClassPath.ResourceInfo)(resourceName.endsWith(".class") ? new ClassPath.ClassInfo(resourceName, loader) : new ClassPath.ResourceInfo(resourceName, loader)); + } + + ResourceInfo(String resourceName, ClassLoader loader) { + this.resourceName = (String)Preconditions.checkNotNull(resourceName); + this.loader = (ClassLoader)Preconditions.checkNotNull(loader); + } + + public final URL url() { + return (URL)Preconditions.checkNotNull(this.loader.getResource(this.resourceName), "Failed to load resource: %s", this.resourceName); + } + + public final String getResourceName() { + return this.resourceName; + } + + public int hashCode() { + return this.resourceName.hashCode(); + } + + public boolean equals(Object obj) { + if (!(obj instanceof ClassPath.ResourceInfo)) { + return false; + } else { + ClassPath.ResourceInfo that = (ClassPath.ResourceInfo)obj; + return this.resourceName.equals(that.resourceName) && this.loader == that.loader; + } + } + + public String toString() { + return this.resourceName; + } + } +} diff --git a/src/main/com/google/common/reflect/Element.java b/src/main/com/google/common/reflect/Element.java new file mode 100644 index 0000000..c8956e9 --- /dev/null +++ b/src/main/com/google/common/reflect/Element.java @@ -0,0 +1,124 @@ +package com.google.common.reflect; + +import com.google.common.base.Preconditions; +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import javax.annotation.Nullable; + +class Element extends AccessibleObject implements Member { + private final AccessibleObject accessibleObject; + private final Member member; + + Element(M member) { + Preconditions.checkNotNull(member); + this.accessibleObject = member; + this.member = (Member)member; + } + + public TypeToken getOwnerType() { + return TypeToken.of(this.getDeclaringClass()); + } + + public final boolean isAnnotationPresent(Class annotationClass) { + return this.accessibleObject.isAnnotationPresent(annotationClass); + } + + public final A getAnnotation(Class annotationClass) { + return this.accessibleObject.getAnnotation(annotationClass); + } + + public final Annotation[] getAnnotations() { + return this.accessibleObject.getAnnotations(); + } + + public final Annotation[] getDeclaredAnnotations() { + return this.accessibleObject.getDeclaredAnnotations(); + } + + public final void setAccessible(boolean flag) throws SecurityException { + this.accessibleObject.setAccessible(flag); + } + + public final boolean isAccessible() { + return this.accessibleObject.isAccessible(); + } + + public Class getDeclaringClass() { + return this.member.getDeclaringClass(); + } + + public final String getName() { + return this.member.getName(); + } + + public final int getModifiers() { + return this.member.getModifiers(); + } + + public final boolean isSynthetic() { + return this.member.isSynthetic(); + } + + public final boolean isPublic() { + return Modifier.isPublic(this.getModifiers()); + } + + public final boolean isProtected() { + return Modifier.isProtected(this.getModifiers()); + } + + public final boolean isPackagePrivate() { + return !this.isPrivate() && !this.isPublic() && !this.isProtected(); + } + + public final boolean isPrivate() { + return Modifier.isPrivate(this.getModifiers()); + } + + public final boolean isStatic() { + return Modifier.isStatic(this.getModifiers()); + } + + public final boolean isFinal() { + return Modifier.isFinal(this.getModifiers()); + } + + public final boolean isAbstract() { + return Modifier.isAbstract(this.getModifiers()); + } + + public final boolean isNative() { + return Modifier.isNative(this.getModifiers()); + } + + public final boolean isSynchronized() { + return Modifier.isSynchronized(this.getModifiers()); + } + + final boolean isVolatile() { + return Modifier.isVolatile(this.getModifiers()); + } + + final boolean isTransient() { + return Modifier.isTransient(this.getModifiers()); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof Element)) { + return false; + } else { + Element that = (Element)obj; + return this.getOwnerType().equals(that.getOwnerType()) && this.member.equals(that.member); + } + } + + public int hashCode() { + return this.member.hashCode(); + } + + public String toString() { + return this.member.toString(); + } +} diff --git a/src/main/com/google/common/reflect/ImmutableTypeToInstanceMap.java b/src/main/com/google/common/reflect/ImmutableTypeToInstanceMap.java new file mode 100644 index 0000000..ee2ea3b --- /dev/null +++ b/src/main/com/google/common/reflect/ImmutableTypeToInstanceMap.java @@ -0,0 +1,80 @@ +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; + +@Beta +public final class ImmutableTypeToInstanceMap extends ForwardingMap, B> implements TypeToInstanceMap { + private final ImmutableMap, B> delegate; + + public static ImmutableTypeToInstanceMap of() { + return new ImmutableTypeToInstanceMap(ImmutableMap.of()); + } + + public static ImmutableTypeToInstanceMap.Builder builder() { + return new ImmutableTypeToInstanceMap.Builder(); + } + + private ImmutableTypeToInstanceMap(ImmutableMap, B> delegate) { + this.delegate = delegate; + } + + public T getInstance(TypeToken type) { + return this.trustedGet(type.rejectTypeVariables()); + } + + public T putInstance(TypeToken type, T value) { + throw new UnsupportedOperationException(); + } + + public T getInstance(Class type) { + return this.trustedGet(TypeToken.of(type)); + } + + public T putInstance(Class type, T value) { + throw new UnsupportedOperationException(); + } + + protected Map, B> delegate() { + return this.delegate; + } + + private T trustedGet(TypeToken type) { + return this.delegate.get(type); + } + + // $FF: synthetic method + ImmutableTypeToInstanceMap(ImmutableMap x0, Object x1) { + this(x0); + } + + @Beta + public static final class Builder { + private final ImmutableMap.Builder, B> mapBuilder; + + private Builder() { + this.mapBuilder = ImmutableMap.builder(); + } + + public ImmutableTypeToInstanceMap.Builder put(Class key, T value) { + this.mapBuilder.put(TypeToken.of(key), value); + return this; + } + + public ImmutableTypeToInstanceMap.Builder put(TypeToken key, T value) { + this.mapBuilder.put(key.rejectTypeVariables(), value); + return this; + } + + public ImmutableTypeToInstanceMap build() { + return new ImmutableTypeToInstanceMap(this.mapBuilder.build()); + } + + // $FF: synthetic method + Builder(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/reflect/Invokable.java b/src/main/com/google/common/reflect/Invokable.java new file mode 100644 index 0000000..aa89727 --- /dev/null +++ b/src/main/com/google/common/reflect/Invokable.java @@ -0,0 +1,218 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +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; + +@Beta +public abstract class Invokable extends Element implements GenericDeclaration { + Invokable(M member) { + super(member); + } + + public static Invokable from(Method method) { + return new Invokable.MethodInvokable(method); + } + + public static Invokable from(Constructor constructor) { + return new Invokable.ConstructorInvokable(constructor); + } + + public abstract boolean isOverridable(); + + public abstract boolean isVarArgs(); + + public final R invoke(@Nullable T receiver, Object... args) throws InvocationTargetException, IllegalAccessException { + return this.invokeInternal(receiver, (Object[])Preconditions.checkNotNull(args)); + } + + public final TypeToken getReturnType() { + return TypeToken.of(this.getGenericReturnType()); + } + + public final ImmutableList getParameters() { + Type[] parameterTypes = this.getGenericParameterTypes(); + Annotation[][] annotations = this.getParameterAnnotations(); + ImmutableList.Builder builder = ImmutableList.builder(); + + for(int i = 0; i < parameterTypes.length; ++i) { + builder.add((Object)(new Parameter(this, i, TypeToken.of(parameterTypes[i]), annotations[i]))); + } + + return builder.build(); + } + + public final ImmutableList> getExceptionTypes() { + ImmutableList.Builder> builder = ImmutableList.builder(); + Type[] arr$ = this.getGenericExceptionTypes(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type type = arr$[i$]; + TypeToken exceptionType = TypeToken.of(type); + builder.add((Object)exceptionType); + } + + return builder.build(); + } + + public final Invokable returning(Class returnType) { + return this.returning(TypeToken.of(returnType)); + } + + public final Invokable returning(TypeToken returnType) { + if (!returnType.isAssignableFrom(this.getReturnType())) { + String var2 = String.valueOf(String.valueOf(this.getReturnType())); + String var3 = String.valueOf(String.valueOf(returnType)); + throw new IllegalArgumentException((new StringBuilder(35 + var2.length() + var3.length())).append("Invokable is known to return ").append(var2).append(", not ").append(var3).toString()); + } else { + return this; + } + } + + public final Class getDeclaringClass() { + return super.getDeclaringClass(); + } + + public TypeToken getOwnerType() { + return TypeToken.of(this.getDeclaringClass()); + } + + abstract Object invokeInternal(@Nullable Object var1, Object[] var2) throws InvocationTargetException, IllegalAccessException; + + abstract Type[] getGenericParameterTypes(); + + abstract Type[] getGenericExceptionTypes(); + + abstract Annotation[][] getParameterAnnotations(); + + abstract Type getGenericReturnType(); + + static class ConstructorInvokable extends Invokable { + final Constructor constructor; + + ConstructorInvokable(Constructor constructor) { + super(constructor); + this.constructor = constructor; + } + + final Object invokeInternal(@Nullable Object receiver, Object[] args) throws InvocationTargetException, IllegalAccessException { + try { + return this.constructor.newInstance(args); + } catch (InstantiationException var5) { + String var4 = String.valueOf(String.valueOf(this.constructor)); + throw new RuntimeException((new StringBuilder(8 + var4.length())).append(var4).append(" failed.").toString(), var5); + } + } + + Type getGenericReturnType() { + Class declaringClass = this.getDeclaringClass(); + TypeVariable[] typeParams = declaringClass.getTypeParameters(); + return (Type)(typeParams.length > 0 ? Types.newParameterizedType(declaringClass, typeParams) : declaringClass); + } + + Type[] getGenericParameterTypes() { + Type[] types = this.constructor.getGenericParameterTypes(); + if (types.length > 0 && this.mayNeedHiddenThis()) { + Class[] rawParamTypes = this.constructor.getParameterTypes(); + if (types.length == rawParamTypes.length && rawParamTypes[0] == this.getDeclaringClass().getEnclosingClass()) { + return (Type[])Arrays.copyOfRange(types, 1, types.length); + } + } + + return types; + } + + Type[] getGenericExceptionTypes() { + return this.constructor.getGenericExceptionTypes(); + } + + final Annotation[][] getParameterAnnotations() { + return this.constructor.getParameterAnnotations(); + } + + public final TypeVariable[] getTypeParameters() { + TypeVariable[] declaredByClass = this.getDeclaringClass().getTypeParameters(); + TypeVariable[] declaredByConstructor = this.constructor.getTypeParameters(); + TypeVariable[] result = new TypeVariable[declaredByClass.length + declaredByConstructor.length]; + System.arraycopy(declaredByClass, 0, result, 0, declaredByClass.length); + System.arraycopy(declaredByConstructor, 0, result, declaredByClass.length, declaredByConstructor.length); + return result; + } + + public final boolean isOverridable() { + return false; + } + + public final boolean isVarArgs() { + return this.constructor.isVarArgs(); + } + + private boolean mayNeedHiddenThis() { + Class declaringClass = this.constructor.getDeclaringClass(); + if (declaringClass.getEnclosingConstructor() != null) { + return true; + } else { + Method enclosingMethod = declaringClass.getEnclosingMethod(); + if (enclosingMethod != null) { + return !Modifier.isStatic(enclosingMethod.getModifiers()); + } else { + return declaringClass.getEnclosingClass() != null && !Modifier.isStatic(declaringClass.getModifiers()); + } + } + } + } + + static class MethodInvokable extends Invokable { + final Method method; + + MethodInvokable(Method method) { + super(method); + this.method = method; + } + + final Object invokeInternal(@Nullable Object receiver, Object[] args) throws InvocationTargetException, IllegalAccessException { + return this.method.invoke(receiver, args); + } + + Type getGenericReturnType() { + return this.method.getGenericReturnType(); + } + + Type[] getGenericParameterTypes() { + return this.method.getGenericParameterTypes(); + } + + Type[] getGenericExceptionTypes() { + return this.method.getGenericExceptionTypes(); + } + + final Annotation[][] getParameterAnnotations() { + return this.method.getParameterAnnotations(); + } + + public final TypeVariable[] getTypeParameters() { + return this.method.getTypeParameters(); + } + + public final boolean isOverridable() { + return !this.isFinal() && !this.isPrivate() && !this.isStatic() && !Modifier.isFinal(this.getDeclaringClass().getModifiers()); + } + + public final boolean isVarArgs() { + return this.method.isVarArgs(); + } + } +} diff --git a/src/main/com/google/common/reflect/MutableTypeToInstanceMap.java b/src/main/com/google/common/reflect/MutableTypeToInstanceMap.java new file mode 100644 index 0000000..9328edb --- /dev/null +++ b/src/main/com/google/common/reflect/MutableTypeToInstanceMap.java @@ -0,0 +1,115 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.ForwardingMap; +import com.google.common.collect.ForwardingMapEntry; +import com.google.common.collect.ForwardingSet; +import com.google.common.collect.Iterators; +import com.google.common.collect.Maps; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import javax.annotation.Nullable; + +@Beta +public final class MutableTypeToInstanceMap extends ForwardingMap, B> implements TypeToInstanceMap { + private final Map, B> backingMap = Maps.newHashMap(); + + @Nullable + public T getInstance(Class type) { + return this.trustedGet(TypeToken.of(type)); + } + + @Nullable + public T putInstance(Class type, @Nullable T value) { + return this.trustedPut(TypeToken.of(type), value); + } + + @Nullable + public T getInstance(TypeToken type) { + return this.trustedGet(type.rejectTypeVariables()); + } + + @Nullable + public T putInstance(TypeToken type, @Nullable T value) { + return this.trustedPut(type.rejectTypeVariables(), value); + } + + public B put(TypeToken key, B value) { + throw new UnsupportedOperationException("Please use putInstance() instead."); + } + + public void putAll(Map, ? extends B> map) { + throw new UnsupportedOperationException("Please use putInstance() instead."); + } + + public Set, B>> entrySet() { + return MutableTypeToInstanceMap.UnmodifiableEntry.transformEntries(super.entrySet()); + } + + protected Map, B> delegate() { + return this.backingMap; + } + + @Nullable + private T trustedPut(TypeToken type, @Nullable T value) { + return this.backingMap.put(type, value); + } + + @Nullable + private T trustedGet(TypeToken type) { + return this.backingMap.get(type); + } + + private static final class UnmodifiableEntry extends ForwardingMapEntry { + private final Entry delegate; + + static Set> transformEntries(final Set> entries) { + return new ForwardingSet>() { + protected Set> delegate() { + return entries; + } + + public Iterator> iterator() { + return MutableTypeToInstanceMap.UnmodifiableEntry.transformEntries(super.iterator()); + } + + public Object[] toArray() { + return this.standardToArray(); + } + + public T[] toArray(T[] array) { + return this.standardToArray(array); + } + }; + } + + private static Iterator> transformEntries(Iterator> entries) { + return Iterators.transform(entries, new Function, Entry>() { + public Entry apply(Entry entry) { + return new MutableTypeToInstanceMap.UnmodifiableEntry(entry); + } + }); + } + + private UnmodifiableEntry(Entry delegate) { + this.delegate = (Entry)Preconditions.checkNotNull(delegate); + } + + protected Entry delegate() { + return this.delegate; + } + + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + + // $FF: synthetic method + UnmodifiableEntry(Entry x0, Object x1) { + this(x0); + } + } +} diff --git a/src/main/com/google/common/reflect/Parameter.java b/src/main/com/google/common/reflect/Parameter.java new file mode 100644 index 0000000..91248ff --- /dev/null +++ b/src/main/com/google/common/reflect/Parameter.java @@ -0,0 +1,95 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Iterator; +import javax.annotation.Nullable; + +@Beta +public final class Parameter implements AnnotatedElement { + private final Invokable declaration; + private final int position; + private final TypeToken type; + private final ImmutableList annotations; + + Parameter(Invokable declaration, int position, TypeToken type, Annotation[] annotations) { + this.declaration = declaration; + this.position = position; + this.type = type; + this.annotations = ImmutableList.copyOf((Object[])annotations); + } + + public TypeToken getType() { + return this.type; + } + + public Invokable getDeclaringInvokable() { + return this.declaration; + } + + public boolean isAnnotationPresent(Class annotationType) { + return this.getAnnotation(annotationType) != null; + } + + @Nullable + public A getAnnotation(Class annotationType) { + Preconditions.checkNotNull(annotationType); + Iterator i$ = this.annotations.iterator(); + + Annotation annotation; + do { + if (!i$.hasNext()) { + return null; + } + + annotation = (Annotation)i$.next(); + } while(!annotationType.isInstance(annotation)); + + return (Annotation)annotationType.cast(annotation); + } + + public Annotation[] getAnnotations() { + return this.getDeclaredAnnotations(); + } + + public A[] getAnnotationsByType(Class annotationType) { + return this.getDeclaredAnnotationsByType(annotationType); + } + + public Annotation[] getDeclaredAnnotations() { + return (Annotation[])this.annotations.toArray(new Annotation[this.annotations.size()]); + } + + @Nullable + public A getDeclaredAnnotation(Class annotationType) { + Preconditions.checkNotNull(annotationType); + return (Annotation)FluentIterable.from((Iterable)this.annotations).filter(annotationType).first().orNull(); + } + + public A[] getDeclaredAnnotationsByType(Class annotationType) { + return (Annotation[])FluentIterable.from((Iterable)this.annotations).filter(annotationType).toArray(annotationType); + } + + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof Parameter)) { + return false; + } else { + Parameter that = (Parameter)obj; + return this.position == that.position && this.declaration.equals(that.declaration); + } + } + + public int hashCode() { + return this.position; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.type)); + int var2 = this.position; + return (new StringBuilder(15 + var1.length())).append(var1).append(" arg").append(var2).toString(); + } +} diff --git a/src/main/com/google/common/reflect/Reflection.java b/src/main/com/google/common/reflect/Reflection.java new file mode 100644 index 0000000..9bd67fb --- /dev/null +++ b/src/main/com/google/common/reflect/Reflection.java @@ -0,0 +1,44 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; + +@Beta +public final class Reflection { + public static String getPackageName(Class clazz) { + return getPackageName(clazz.getName()); + } + + public static String getPackageName(String classFullName) { + int lastDot = classFullName.lastIndexOf(46); + return lastDot < 0 ? "" : classFullName.substring(0, lastDot); + } + + public static void initialize(Class... classes) { + Class[] arr$ = classes; + int len$ = classes.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Class clazz = arr$[i$]; + + try { + Class.forName(clazz.getName(), true, clazz.getClassLoader()); + } catch (ClassNotFoundException var6) { + throw new AssertionError(var6); + } + } + + } + + public static T newProxy(Class interfaceType, InvocationHandler handler) { + Preconditions.checkNotNull(handler); + Preconditions.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/src/main/com/google/common/reflect/TypeCapture.java b/src/main/com/google/common/reflect/TypeCapture.java new file mode 100644 index 0000000..0343498 --- /dev/null +++ b/src/main/com/google/common/reflect/TypeCapture.java @@ -0,0 +1,13 @@ +package com.google.common.reflect; + +import com.google.common.base.Preconditions; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +abstract class TypeCapture { + final Type capture() { + Type superclass = this.getClass().getGenericSuperclass(); + Preconditions.checkArgument(superclass instanceof ParameterizedType, "%s isn't parameterized", superclass); + return ((ParameterizedType)superclass).getActualTypeArguments()[0]; + } +} diff --git a/src/main/com/google/common/reflect/TypeParameter.java b/src/main/com/google/common/reflect/TypeParameter.java new file mode 100644 index 0000000..0cd23b9 --- /dev/null +++ b/src/main/com/google/common/reflect/TypeParameter.java @@ -0,0 +1,35 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import javax.annotation.Nullable; + +@Beta +public abstract class TypeParameter extends TypeCapture { + final TypeVariable typeVariable; + + protected TypeParameter() { + Type type = this.capture(); + Preconditions.checkArgument(type instanceof TypeVariable, "%s should be a type variable.", type); + this.typeVariable = (TypeVariable)type; + } + + public final int hashCode() { + return this.typeVariable.hashCode(); + } + + public final boolean equals(@Nullable Object o) { + if (o instanceof TypeParameter) { + TypeParameter that = (TypeParameter)o; + return this.typeVariable.equals(that.typeVariable); + } else { + return false; + } + } + + public String toString() { + return this.typeVariable.toString(); + } +} diff --git a/src/main/com/google/common/reflect/TypeResolver.java b/src/main/com/google/common/reflect/TypeResolver.java new file mode 100644 index 0000000..bed7ee2 --- /dev/null +++ b/src/main/com/google/common/reflect/TypeResolver.java @@ -0,0 +1,356 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +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.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; + +@Beta +public final class TypeResolver { + private final TypeResolver.TypeTable typeTable; + + public TypeResolver() { + this.typeTable = new TypeResolver.TypeTable(); + } + + private TypeResolver(TypeResolver.TypeTable typeTable) { + this.typeTable = typeTable; + } + + static TypeResolver accordingTo(Type type) { + return (new TypeResolver()).where(TypeResolver.TypeMappingIntrospector.getTypeMappings(type)); + } + + public TypeResolver where(Type formal, Type actual) { + Map mappings = Maps.newHashMap(); + populateTypeMappings(mappings, (Type)Preconditions.checkNotNull(formal), (Type)Preconditions.checkNotNull(actual)); + return this.where(mappings); + } + + TypeResolver where(Map mappings) { + return new TypeResolver(this.typeTable.where(mappings)); + } + + private static void populateTypeMappings(final Map mappings, Type from, final Type to) { + if (!from.equals(to)) { + (new TypeVisitor() { + void visitTypeVariable(TypeVariable typeVariable) { + mappings.put(new TypeResolver.TypeVariableKey(typeVariable), to); + } + + void visitWildcardType(WildcardType fromWildcardType) { + WildcardType toWildcardType = (WildcardType)TypeResolver.expectArgument(WildcardType.class, to); + Type[] fromUpperBounds = fromWildcardType.getUpperBounds(); + Type[] toUpperBounds = toWildcardType.getUpperBounds(); + Type[] fromLowerBounds = fromWildcardType.getLowerBounds(); + Type[] toLowerBounds = toWildcardType.getLowerBounds(); + Preconditions.checkArgument(fromUpperBounds.length == toUpperBounds.length && fromLowerBounds.length == toLowerBounds.length, "Incompatible type: %s vs. %s", fromWildcardType, to); + + int i; + for(i = 0; i < fromUpperBounds.length; ++i) { + TypeResolver.populateTypeMappings(mappings, fromUpperBounds[i], toUpperBounds[i]); + } + + for(i = 0; i < fromLowerBounds.length; ++i) { + TypeResolver.populateTypeMappings(mappings, fromLowerBounds[i], toLowerBounds[i]); + } + + } + + void visitParameterizedType(ParameterizedType fromParameterizedType) { + ParameterizedType toParameterizedType = (ParameterizedType)TypeResolver.expectArgument(ParameterizedType.class, to); + Preconditions.checkArgument(fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()), "Inconsistent raw type: %s vs. %s", fromParameterizedType, to); + Type[] fromArgs = fromParameterizedType.getActualTypeArguments(); + Type[] toArgs = toParameterizedType.getActualTypeArguments(); + Preconditions.checkArgument(fromArgs.length == toArgs.length, "%s not compatible with %s", fromParameterizedType, toParameterizedType); + + for(int i = 0; i < fromArgs.length; ++i) { + TypeResolver.populateTypeMappings(mappings, fromArgs[i], toArgs[i]); + } + + } + + void visitGenericArrayType(GenericArrayType fromArrayType) { + Type componentType = Types.getComponentType(to); + Preconditions.checkArgument(componentType != null, "%s is not an array type.", to); + TypeResolver.populateTypeMappings(mappings, fromArrayType.getGenericComponentType(), componentType); + } + + void visitClass(Class fromClass) { + String var2 = String.valueOf(String.valueOf(fromClass)); + throw new IllegalArgumentException((new StringBuilder(21 + var2.length())).append("No type mapping from ").append(var2).toString()); + } + }).visit(new Type[]{from}); + } + } + + public Type resolveType(Type type) { + Preconditions.checkNotNull(type); + if (type instanceof TypeVariable) { + return this.typeTable.resolve((TypeVariable)type); + } else if (type instanceof ParameterizedType) { + return this.resolveParameterizedType((ParameterizedType)type); + } else if (type instanceof GenericArrayType) { + return this.resolveGenericArrayType((GenericArrayType)type); + } else { + return (Type)(type instanceof WildcardType ? this.resolveWildcardType((WildcardType)type) : type); + } + } + + private Type[] resolveTypes(Type[] types) { + Type[] result = new Type[types.length]; + + for(int i = 0; i < types.length; ++i) { + result[i] = this.resolveType(types[i]); + } + + return result; + } + + private WildcardType resolveWildcardType(WildcardType type) { + Type[] lowerBounds = type.getLowerBounds(); + Type[] upperBounds = type.getUpperBounds(); + return new Types.WildcardTypeImpl(this.resolveTypes(lowerBounds), this.resolveTypes(upperBounds)); + } + + private Type resolveGenericArrayType(GenericArrayType type) { + Type componentType = type.getGenericComponentType(); + Type resolvedComponentType = this.resolveType(componentType); + return Types.newArrayType(resolvedComponentType); + } + + private ParameterizedType resolveParameterizedType(ParameterizedType type) { + Type owner = type.getOwnerType(); + Type resolvedOwner = owner == null ? null : this.resolveType(owner); + Type resolvedRawType = this.resolveType(type.getRawType()); + Type[] args = type.getActualTypeArguments(); + Type[] resolvedArgs = this.resolveTypes(args); + return Types.newParameterizedTypeWithOwner(resolvedOwner, (Class)resolvedRawType, resolvedArgs); + } + + private static T expectArgument(Class type, Object arg) { + try { + return type.cast(arg); + } catch (ClassCastException var5) { + String var3 = String.valueOf(String.valueOf(arg)); + String var4 = String.valueOf(String.valueOf(type.getSimpleName())); + throw new IllegalArgumentException((new StringBuilder(10 + var3.length() + var4.length())).append(var3).append(" is not a ").append(var4).toString()); + } + } + + // $FF: synthetic method + TypeResolver(TypeResolver.TypeTable x0, Object x1) { + this(x0); + } + + static final class TypeVariableKey { + private final TypeVariable var; + + TypeVariableKey(TypeVariable var) { + this.var = (TypeVariable)Preconditions.checkNotNull(var); + } + + public int hashCode() { + return Objects.hashCode(this.var.getGenericDeclaration(), this.var.getName()); + } + + public boolean equals(Object obj) { + if (obj instanceof TypeResolver.TypeVariableKey) { + TypeResolver.TypeVariableKey that = (TypeResolver.TypeVariableKey)obj; + return this.equalsTypeVariable(that.var); + } else { + return false; + } + } + + public String toString() { + return this.var.toString(); + } + + static Object forLookup(Type t) { + return t instanceof TypeVariable ? new TypeResolver.TypeVariableKey((TypeVariable)t) : null; + } + + boolean equalsType(Type type) { + return type instanceof TypeVariable ? this.equalsTypeVariable((TypeVariable)type) : false; + } + + private boolean equalsTypeVariable(TypeVariable that) { + return this.var.getGenericDeclaration().equals(that.getGenericDeclaration()) && this.var.getName().equals(that.getName()); + } + } + + private static final class WildcardCapturer { + private final AtomicInteger id; + + private WildcardCapturer() { + this.id = new AtomicInteger(); + } + + Type capture(Type type) { + Preconditions.checkNotNull(type); + if (type instanceof Class) { + return type; + } else if (type instanceof TypeVariable) { + return type; + } else if (type instanceof GenericArrayType) { + GenericArrayType arrayType = (GenericArrayType)type; + return Types.newArrayType(this.capture(arrayType.getGenericComponentType())); + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)type; + return Types.newParameterizedTypeWithOwner(this.captureNullable(parameterizedType.getOwnerType()), (Class)parameterizedType.getRawType(), this.capture(parameterizedType.getActualTypeArguments())); + } else if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType)type; + Type[] lowerBounds = wildcardType.getLowerBounds(); + if (lowerBounds.length == 0) { + Type[] upperBounds = wildcardType.getUpperBounds(); + int var6 = this.id.incrementAndGet(); + String var7 = String.valueOf(String.valueOf(Joiner.on('&').join((Object[])upperBounds))); + String name = (new StringBuilder(33 + var7.length())).append("capture#").append(var6).append("-of ? extends ").append(var7).toString(); + return Types.newArtificialTypeVariable(TypeResolver.WildcardCapturer.class, name, wildcardType.getUpperBounds()); + } else { + return type; + } + } else { + throw new AssertionError("must have been one of the known types"); + } + } + + private Type captureNullable(@Nullable Type type) { + return type == null ? null : this.capture(type); + } + + private Type[] capture(Type[] types) { + Type[] result = new Type[types.length]; + + for(int i = 0; i < types.length; ++i) { + result[i] = this.capture(types[i]); + } + + return result; + } + + // $FF: synthetic method + WildcardCapturer(Object x0) { + this(); + } + } + + private static final class TypeMappingIntrospector extends TypeVisitor { + private static final TypeResolver.WildcardCapturer wildcardCapturer = new TypeResolver.WildcardCapturer(); + private final Map mappings = Maps.newHashMap(); + + static ImmutableMap getTypeMappings(Type contextType) { + TypeResolver.TypeMappingIntrospector introspector = new TypeResolver.TypeMappingIntrospector(); + introspector.visit(new Type[]{wildcardCapturer.capture(contextType)}); + return ImmutableMap.copyOf(introspector.mappings); + } + + void visitClass(Class clazz) { + this.visit(new Type[]{clazz.getGenericSuperclass()}); + this.visit(clazz.getGenericInterfaces()); + } + + void visitParameterizedType(ParameterizedType parameterizedType) { + Class rawClass = (Class)parameterizedType.getRawType(); + TypeVariable[] vars = rawClass.getTypeParameters(); + Type[] typeArgs = parameterizedType.getActualTypeArguments(); + Preconditions.checkState(vars.length == typeArgs.length); + + for(int i = 0; i < vars.length; ++i) { + this.map(new TypeResolver.TypeVariableKey(vars[i]), typeArgs[i]); + } + + this.visit(new Type[]{rawClass}); + this.visit(new Type[]{parameterizedType.getOwnerType()}); + } + + void visitTypeVariable(TypeVariable t) { + this.visit(t.getBounds()); + } + + void visitWildcardType(WildcardType t) { + this.visit(t.getUpperBounds()); + } + + private void map(TypeResolver.TypeVariableKey var, Type arg) { + if (!this.mappings.containsKey(var)) { + for(Type t = arg; t != null; t = (Type)this.mappings.get(TypeResolver.TypeVariableKey.forLookup(t))) { + if (var.equalsType(t)) { + for(Type x = arg; x != null; x = (Type)this.mappings.remove(TypeResolver.TypeVariableKey.forLookup(x))) { + } + + return; + } + } + + this.mappings.put(var, arg); + } + } + } + + private static class TypeTable { + private final ImmutableMap map; + + TypeTable() { + this.map = ImmutableMap.of(); + } + + private TypeTable(ImmutableMap map) { + this.map = map; + } + + final TypeResolver.TypeTable where(Map mappings) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.putAll(this.map); + Iterator i$ = mappings.entrySet().iterator(); + + while(i$.hasNext()) { + Entry mapping = (Entry)i$.next(); + TypeResolver.TypeVariableKey variable = (TypeResolver.TypeVariableKey)mapping.getKey(); + Type type = (Type)mapping.getValue(); + Preconditions.checkArgument(!variable.equalsType(type), "Type variable %s bound to itself", variable); + builder.put(variable, type); + } + + return new TypeResolver.TypeTable(builder.build()); + } + + final Type resolve(final TypeVariable var) { + TypeResolver.TypeTable guarded = new TypeResolver.TypeTable() { + public Type resolveInternal(TypeVariable intermediateVar, TypeResolver.TypeTable forDependent) { + return (Type)(intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration()) ? intermediateVar : TypeTable.this.resolveInternal(intermediateVar, forDependent)); + } + }; + return this.resolveInternal(var, guarded); + } + + Type resolveInternal(TypeVariable var, TypeResolver.TypeTable forDependants) { + Type type = (Type)this.map.get(new TypeResolver.TypeVariableKey(var)); + if (type == null) { + Type[] bounds = var.getBounds(); + if (bounds.length == 0) { + return var; + } else { + Type[] resolvedBounds = (new TypeResolver(forDependants)).resolveTypes(bounds); + return Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY && Arrays.equals(bounds, resolvedBounds) ? var : Types.newArtificialTypeVariable(var.getGenericDeclaration(), var.getName(), resolvedBounds); + } + } else { + return (new TypeResolver(forDependants)).resolveType(type); + } + } + } +} diff --git a/src/main/com/google/common/reflect/TypeToInstanceMap.java b/src/main/com/google/common/reflect/TypeToInstanceMap.java new file mode 100644 index 0000000..e7f371a --- /dev/null +++ b/src/main/com/google/common/reflect/TypeToInstanceMap.java @@ -0,0 +1,20 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import java.util.Map; +import javax.annotation.Nullable; + +@Beta +public interface TypeToInstanceMap extends Map, B> { + @Nullable + T getInstance(Class var1); + + @Nullable + T putInstance(Class var1, @Nullable T var2); + + @Nullable + T getInstance(TypeToken var1); + + @Nullable + T putInstance(TypeToken var1, @Nullable T var2); +} diff --git a/src/main/com/google/common/reflect/TypeToken.java b/src/main/com/google/common/reflect/TypeToken.java new file mode 100644 index 0000000..efdb5dc --- /dev/null +++ b/src/main/com/google/common/reflect/TypeToken.java @@ -0,0 +1,876 @@ +package com.google.common.reflect; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.base.Preconditions; +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 com.google.common.primitives.Primitives; +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.Collection; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +@Beta +public abstract class TypeToken extends TypeCapture implements Serializable { + private final Type runtimeType; + private transient TypeResolver typeResolver; + + protected TypeToken() { + this.runtimeType = this.capture(); + Preconditions.checkState(!(this.runtimeType instanceof TypeVariable), "Cannot construct a TypeToken for a type variable.\nYou probably meant to call new TypeToken<%s>(getClass()) that can resolve the type variable for you.\nIf you do need to create a TypeToken of a type variable, please use TypeToken.of() instead.", this.runtimeType); + } + + 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 = (Type)Preconditions.checkNotNull(type); + } + + public static TypeToken of(Class type) { + return new TypeToken.SimpleTypeToken(type); + } + + public static TypeToken of(Type type) { + return new TypeToken.SimpleTypeToken(type); + } + + public final Class getRawType() { + Class rawType = getRawType(this.runtimeType); + return rawType; + } + + private ImmutableSet> getImmediateRawTypes() { + ImmutableSet> result = getRawTypes(this.runtimeType); + return result; + } + + public final Type getType() { + return this.runtimeType; + } + + public final TypeToken where(TypeParameter typeParam, TypeToken typeArg) { + TypeResolver resolver = (new TypeResolver()).where(ImmutableMap.of(new TypeResolver.TypeVariableKey(typeParam.typeVariable), typeArg.runtimeType)); + return new TypeToken.SimpleTypeToken(resolver.resolveType(this.runtimeType)); + } + + public final TypeToken where(TypeParameter typeParam, Class typeArg) { + return this.where(typeParam, of(typeArg)); + } + + public final TypeToken resolveType(Type type) { + Preconditions.checkNotNull(type); + TypeResolver resolver = this.typeResolver; + if (resolver == null) { + resolver = this.typeResolver = TypeResolver.accordingTo(this.runtimeType); + } + + return of(resolver.resolveType(type)); + } + + private Type[] resolveInPlace(Type[] types) { + for(int i = 0; i < types.length; ++i) { + types[i] = this.resolveType(types[i]).getType(); + } + + return types; + } + + private TypeToken resolveSupertype(Type type) { + TypeToken supertype = this.resolveType(type); + supertype.typeResolver = this.typeResolver; + return supertype; + } + + @Nullable + final TypeToken getGenericSuperclass() { + if (this.runtimeType instanceof TypeVariable) { + return this.boundAsSuperclass(((TypeVariable)this.runtimeType).getBounds()[0]); + } else if (this.runtimeType instanceof WildcardType) { + return this.boundAsSuperclass(((WildcardType)this.runtimeType).getUpperBounds()[0]); + } else { + Type superclass = this.getRawType().getGenericSuperclass(); + if (superclass == null) { + return null; + } else { + TypeToken superToken = this.resolveSupertype(superclass); + return superToken; + } + } + } + + @Nullable + private TypeToken boundAsSuperclass(Type bound) { + TypeToken token = of(bound); + return token.getRawType().isInterface() ? null : token; + } + + final ImmutableList> getGenericInterfaces() { + if (this.runtimeType instanceof TypeVariable) { + return this.boundsAsInterfaces(((TypeVariable)this.runtimeType).getBounds()); + } else if (this.runtimeType instanceof WildcardType) { + return this.boundsAsInterfaces(((WildcardType)this.runtimeType).getUpperBounds()); + } else { + ImmutableList.Builder> builder = ImmutableList.builder(); + Type[] arr$ = this.getRawType().getGenericInterfaces(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type interfaceType = arr$[i$]; + TypeToken resolvedInterface = this.resolveSupertype(interfaceType); + builder.add((Object)resolvedInterface); + } + + return builder.build(); + } + } + + private ImmutableList> boundsAsInterfaces(Type[] bounds) { + ImmutableList.Builder> builder = ImmutableList.builder(); + Type[] arr$ = bounds; + int len$ = bounds.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type bound = arr$[i$]; + TypeToken boundType = of(bound); + if (boundType.getRawType().isInterface()) { + builder.add((Object)boundType); + } + } + + return builder.build(); + } + + public final TypeToken.TypeSet getTypes() { + return new TypeToken.TypeSet(); + } + + public final TypeToken getSupertype(Class superclass) { + Preconditions.checkArgument(superclass.isAssignableFrom(this.getRawType()), "%s is not a super class of %s", superclass, this); + if (this.runtimeType instanceof TypeVariable) { + return this.getSupertypeFromUpperBounds(superclass, ((TypeVariable)this.runtimeType).getBounds()); + } else if (this.runtimeType instanceof WildcardType) { + return this.getSupertypeFromUpperBounds(superclass, ((WildcardType)this.runtimeType).getUpperBounds()); + } else if (superclass.isArray()) { + return this.getArraySupertype(superclass); + } else { + TypeToken supertype = this.resolveSupertype(toGenericType(superclass).runtimeType); + return supertype; + } + } + + public final TypeToken getSubtype(Class subclass) { + Preconditions.checkArgument(!(this.runtimeType instanceof TypeVariable), "Cannot get subtype of type variable <%s>", this); + if (this.runtimeType instanceof WildcardType) { + return this.getSubtypeFromLowerBounds(subclass, ((WildcardType)this.runtimeType).getLowerBounds()); + } else { + Preconditions.checkArgument(this.getRawType().isAssignableFrom(subclass), "%s isn't a subclass of %s", subclass, this); + if (this.isArray()) { + return this.getArraySubtype(subclass); + } else { + TypeToken subtype = of(this.resolveTypeArgsForSubclass(subclass)); + return subtype; + } + } + } + + public final boolean isAssignableFrom(TypeToken type) { + return this.isAssignableFrom(type.runtimeType); + } + + public final boolean isAssignableFrom(Type type) { + return isAssignable((Type)Preconditions.checkNotNull(type), this.runtimeType); + } + + public final boolean isArray() { + return this.getComponentType() != null; + } + + public final boolean isPrimitive() { + return this.runtimeType instanceof Class && ((Class)this.runtimeType).isPrimitive(); + } + + public final TypeToken wrap() { + if (this.isPrimitive()) { + Class type = (Class)this.runtimeType; + return of(Primitives.wrap(type)); + } else { + return this; + } + } + + private boolean isWrapper() { + return Primitives.allWrapperTypes().contains(this.runtimeType); + } + + public final TypeToken unwrap() { + if (this.isWrapper()) { + Class type = (Class)this.runtimeType; + return of(Primitives.unwrap(type)); + } else { + return this; + } + } + + @Nullable + public final TypeToken getComponentType() { + Type componentType = Types.getComponentType(this.runtimeType); + return componentType == null ? null : of(componentType); + } + + public final Invokable method(Method method) { + Preconditions.checkArgument(of(method.getDeclaringClass()).isAssignableFrom(this), "%s not declared by %s", method, this); + return new Invokable.MethodInvokable(method) { + Type getGenericReturnType() { + return TypeToken.this.resolveType(super.getGenericReturnType()).getType(); + } + + Type[] getGenericParameterTypes() { + return TypeToken.this.resolveInPlace(super.getGenericParameterTypes()); + } + + Type[] getGenericExceptionTypes() { + return TypeToken.this.resolveInPlace(super.getGenericExceptionTypes()); + } + + public TypeToken getOwnerType() { + return TypeToken.this; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.getOwnerType())); + String var2 = String.valueOf(String.valueOf(super.toString())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append(".").append(var2).toString(); + } + }; + } + + public final Invokable constructor(Constructor constructor) { + Preconditions.checkArgument(constructor.getDeclaringClass() == this.getRawType(), "%s not declared by %s", constructor, this.getRawType()); + return new Invokable.ConstructorInvokable(constructor) { + Type getGenericReturnType() { + return TypeToken.this.resolveType(super.getGenericReturnType()).getType(); + } + + Type[] getGenericParameterTypes() { + return TypeToken.this.resolveInPlace(super.getGenericParameterTypes()); + } + + Type[] getGenericExceptionTypes() { + return TypeToken.this.resolveInPlace(super.getGenericExceptionTypes()); + } + + public TypeToken getOwnerType() { + return TypeToken.this; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.getOwnerType())); + String var2 = String.valueOf(String.valueOf(Joiner.on(", ").join((Object[])this.getGenericParameterTypes()))); + return (new StringBuilder(2 + var1.length() + var2.length())).append(var1).append("(").append(var2).append(")").toString(); + } + }; + } + + public boolean equals(@Nullable Object o) { + if (o instanceof TypeToken) { + TypeToken that = (TypeToken)o; + return this.runtimeType.equals(that.runtimeType); + } else { + return false; + } + } + + public int hashCode() { + return this.runtimeType.hashCode(); + } + + public String toString() { + return Types.toString(this.runtimeType); + } + + protected Object writeReplace() { + return of((new TypeResolver()).resolveType(this.runtimeType)); + } + + final TypeToken rejectTypeVariables() { + (new TypeVisitor() { + void visitTypeVariable(TypeVariable type) { + String var2 = String.valueOf(String.valueOf(TypeToken.this.runtimeType)); + throw new IllegalArgumentException((new StringBuilder(58 + var2.length())).append(var2).append("contains a type variable and is not safe for the operation").toString()); + } + + void visitWildcardType(WildcardType type) { + this.visit(type.getLowerBounds()); + this.visit(type.getUpperBounds()); + } + + void visitParameterizedType(ParameterizedType type) { + this.visit(type.getActualTypeArguments()); + this.visit(new Type[]{type.getOwnerType()}); + } + + void visitGenericArrayType(GenericArrayType type) { + this.visit(new Type[]{type.getGenericComponentType()}); + } + }).visit(new Type[]{this.runtimeType}); + return this; + } + + private static boolean isAssignable(Type from, Type to) { + if (to.equals(from)) { + return true; + } else if (to instanceof WildcardType) { + return isAssignableToWildcardType(from, (WildcardType)to); + } else if (from instanceof TypeVariable) { + return isAssignableFromAny(((TypeVariable)from).getBounds(), to); + } else if (from instanceof WildcardType) { + return isAssignableFromAny(((WildcardType)from).getUpperBounds(), to); + } else if (from instanceof GenericArrayType) { + return isAssignableFromGenericArrayType((GenericArrayType)from, to); + } else if (to instanceof Class) { + return isAssignableToClass(from, (Class)to); + } else if (to instanceof ParameterizedType) { + return isAssignableToParameterizedType(from, (ParameterizedType)to); + } else { + return to instanceof GenericArrayType ? isAssignableToGenericArrayType(from, (GenericArrayType)to) : false; + } + } + + private static boolean isAssignableFromAny(Type[] fromTypes, Type to) { + Type[] arr$ = fromTypes; + int len$ = fromTypes.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type from = arr$[i$]; + 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) { + 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; + } else { + Type fromSubtypeBound = subtypeBound(from); + return fromSubtypeBound == null ? false : isAssignable(toSubtypeBound, fromSubtypeBound); + } + } + + private static boolean isAssignableToParameterizedType(Type from, ParameterizedType to) { + Class matchedClass = getRawType(to); + if (!matchedClass.isAssignableFrom(getRawType(from))) { + return false; + } else { + Type[] typeParams = matchedClass.getTypeParameters(); + Type[] toTypeArgs = to.getActualTypeArguments(); + TypeToken fromTypeToken = of(from); + + for(int i = 0; i < typeParams.length; ++i) { + 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; + return !fromClass.isArray() ? false : 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; + } else { + 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; + } else { + return to instanceof WildcardType ? isAssignableToWildcardType(from, (WildcardType)to) : false; + } + } + + private static Type supertypeBound(Type type) { + return type instanceof WildcardType ? supertypeBound((WildcardType)type) : 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 { + String var2 = String.valueOf(String.valueOf(type)); + throw new AssertionError((new StringBuilder(59 + var2.length())).append("There should be at most one upper bound for wildcard type: ").append(var2).toString()); + } + } + + @Nullable + private static Type subtypeBound(Type type) { + return type instanceof WildcardType ? subtypeBound((WildcardType)type) : 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 { + String var2 = String.valueOf(String.valueOf(type)); + throw new AssertionError((new StringBuilder(46 + var2.length())).append("Wildcard should have at most one lower bound: ").append(var2).toString()); + } + } + + @VisibleForTesting + static Class getRawType(Type type) { + return (Class)getRawTypes(type).iterator().next(); + } + + @VisibleForTesting + static ImmutableSet> getRawTypes(Type type) { + Preconditions.checkNotNull(type); + final ImmutableSet.Builder> builder = ImmutableSet.builder(); + (new TypeVisitor() { + void visitTypeVariable(TypeVariable t) { + this.visit(t.getBounds()); + } + + void visitWildcardType(WildcardType t) { + this.visit(t.getUpperBounds()); + } + + void visitParameterizedType(ParameterizedType t) { + builder.add((Object)((Class)t.getRawType())); + } + + void visitClass(Class t) { + builder.add((Object)t); + } + + void visitGenericArrayType(GenericArrayType t) { + builder.add((Object)Types.getArrayClass(TypeToken.getRawType(t.getGenericComponentType()))); + } + }).visit(new Type[]{type}); + return builder.build(); + } + + @VisibleForTesting + static TypeToken toGenericType(Class cls) { + TypeToken type; + if (cls.isArray()) { + Type arrayOfGenericType = Types.newArrayType(toGenericType(cls.getComponentType()).runtimeType); + type = of(arrayOfGenericType); + return type; + } else { + TypeVariable>[] typeParams = cls.getTypeParameters(); + if (typeParams.length > 0) { + type = of((Type)Types.newParameterizedType(cls, typeParams)); + return type; + } else { + return of(cls); + } + } + } + + private TypeToken getSupertypeFromUpperBounds(Class supertype, Type[] upperBounds) { + Type[] arr$ = upperBounds; + int len$ = upperBounds.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type upperBound = arr$[i$]; + TypeToken bound = of(upperBound); + if (of(supertype).isAssignableFrom(bound)) { + TypeToken result = bound.getSupertype(supertype); + return result; + } + } + + String var9 = String.valueOf(String.valueOf(supertype)); + String var10 = String.valueOf(String.valueOf(this)); + throw new IllegalArgumentException((new StringBuilder(23 + var9.length() + var10.length())).append(var9).append(" isn't a super type of ").append(var10).toString()); + } + + private TypeToken getSubtypeFromLowerBounds(Class subclass, Type[] lowerBounds) { + int len$ = lowerBounds.length; + int i$ = 0; + if (i$ < len$) { + Type lowerBound = lowerBounds[i$]; + TypeToken bound = of(lowerBound); + return bound.getSubtype(subclass); + } else { + String var3 = String.valueOf(String.valueOf(subclass)); + String var8 = String.valueOf(String.valueOf(this)); + throw new IllegalArgumentException((new StringBuilder(21 + var3.length() + var8.length())).append(var3).append(" isn't a subclass of ").append(var8).toString()); + } + } + + private TypeToken getArraySupertype(Class supertype) { + TypeToken componentType = (TypeToken)Preconditions.checkNotNull(this.getComponentType(), "%s isn't a super type of %s", supertype, this); + TypeToken componentSupertype = componentType.getSupertype(supertype.getComponentType()); + TypeToken result = of(newArrayClassOrGenericArrayType(componentSupertype.runtimeType)); + return result; + } + + private TypeToken getArraySubtype(Class subclass) { + TypeToken componentSubtype = this.getComponentType().getSubtype(subclass.getComponentType()); + TypeToken result = of(newArrayClassOrGenericArrayType(componentSubtype.runtimeType)); + return result; + } + + private Type resolveTypeArgsForSubclass(Class subclass) { + if (this.runtimeType instanceof Class) { + return subclass; + } else { + TypeToken genericSubtype = toGenericType(subclass); + Type supertypeWithArgsFromSubtype = genericSubtype.getSupertype(this.getRawType()).runtimeType; + return (new TypeResolver()).where(supertypeWithArgsFromSubtype, this.runtimeType).resolveType(genericSubtype.runtimeType); + } + } + + private static Type newArrayClassOrGenericArrayType(Type componentType) { + return Types.JavaVersion.JAVA7.newArrayType(componentType); + } + + // $FF: synthetic method + TypeToken(Type x0, Object x1) { + this(x0); + } + + private abstract static class TypeCollector { + static final TypeToken.TypeCollector> FOR_GENERIC_TYPE = new TypeToken.TypeCollector>() { + Class getRawType(TypeToken type) { + return type.getRawType(); + } + + Iterable> getInterfaces(TypeToken type) { + return type.getGenericInterfaces(); + } + + @Nullable + TypeToken getSuperclass(TypeToken type) { + return type.getGenericSuperclass(); + } + }; + static final TypeToken.TypeCollector> FOR_RAW_TYPE = new TypeToken.TypeCollector>() { + Class getRawType(Class type) { + return type; + } + + Iterable> getInterfaces(Class type) { + return Arrays.asList(type.getInterfaces()); + } + + @Nullable + Class getSuperclass(Class type) { + return type.getSuperclass(); + } + }; + + private TypeCollector() { + } + + final TypeToken.TypeCollector classesOnly() { + return new TypeToken.TypeCollector.ForwardingTypeCollector(this) { + Iterable getInterfaces(K type) { + return ImmutableSet.of(); + } + + ImmutableList collectTypes(Iterable types) { + ImmutableList.Builder builder = ImmutableList.builder(); + Iterator i$ = types.iterator(); + + while(i$.hasNext()) { + K type = i$.next(); + if (!this.getRawType(type).isInterface()) { + builder.add(type); + } + } + + return super.collectTypes(builder.build()); + } + }; + } + + final ImmutableList collectTypes(K type) { + return this.collectTypes((Iterable)ImmutableList.of(type)); + } + + ImmutableList collectTypes(Iterable types) { + Map map = Maps.newHashMap(); + Iterator i$ = types.iterator(); + + while(i$.hasNext()) { + K type = i$.next(); + this.collectTypes(type, map); + } + + return sortKeysByValue(map, Ordering.natural().reverse()); + } + + private int collectTypes(K type, Map map) { + Integer existing = (Integer)map.get(this); + if (existing != null) { + return existing; + } else { + int aboveMe = this.getRawType(type).isInterface() ? 1 : 0; + + Object interfaceType; + for(Iterator i$ = this.getInterfaces(type).iterator(); i$.hasNext(); aboveMe = Math.max(aboveMe, this.collectTypes(interfaceType, map))) { + interfaceType = i$.next(); + } + + K superclass = this.getSuperclass(type); + if (superclass != null) { + aboveMe = Math.max(aboveMe, this.collectTypes(superclass, map)); + } + + map.put(type, aboveMe + 1); + return aboveMe + 1; + } + } + + private static ImmutableList sortKeysByValue(final Map map, final Comparator valueComparator) { + Ordering keyOrdering = new Ordering() { + 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 var1); + + abstract Iterable getInterfaces(K var1); + + @Nullable + abstract K getSuperclass(K var1); + + // $FF: synthetic method + TypeCollector(Object x0) { + this(); + } + + private static class ForwardingTypeCollector extends TypeToken.TypeCollector { + private final TypeToken.TypeCollector delegate; + + ForwardingTypeCollector(TypeToken.TypeCollector delegate) { + super(null); + this.delegate = delegate; + } + + Class getRawType(K type) { + return this.delegate.getRawType(type); + } + + Iterable getInterfaces(K type) { + return this.delegate.getInterfaces(type); + } + + K getSuperclass(K type) { + return this.delegate.getSuperclass(type); + } + } + } + + private static final class SimpleTypeToken extends TypeToken { + private static final long serialVersionUID = 0L; + + SimpleTypeToken(Type type) { + super(type, null); + } + } + + private static enum TypeFilter implements Predicate> { + IGNORE_TYPE_VARIABLE_OR_WILDCARD { + public boolean apply(TypeToken type) { + return !(type.runtimeType instanceof TypeVariable) && !(type.runtimeType instanceof WildcardType); + } + }, + INTERFACE_ONLY { + public boolean apply(TypeToken type) { + return type.getRawType().isInterface(); + } + }; + + private TypeFilter() { + } + + // $FF: synthetic method + TypeFilter(Object x2) { + this(); + } + } + + private final class ClassSet extends TypeToken.TypeSet { + private transient ImmutableSet> classes; + private static final long serialVersionUID = 0L; + + private ClassSet() { + super(); + } + + protected Set> delegate() { + ImmutableSet> result = this.classes; + if (result == null) { + ImmutableList> collectedTypes = TypeToken.TypeCollector.FOR_GENERIC_TYPE.classesOnly().collectTypes((Object)TypeToken.this); + return this.classes = FluentIterable.from((Iterable)collectedTypes).filter((Predicate)TypeToken.TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD).toSet(); + } else { + return result; + } + } + + public TypeToken.TypeSet classes() { + return this; + } + + public Set> rawTypes() { + ImmutableList> collectedTypes = TypeToken.TypeCollector.FOR_RAW_TYPE.classesOnly().collectTypes((Iterable)TypeToken.this.getImmediateRawTypes()); + return ImmutableSet.copyOf((Collection)collectedTypes); + } + + public TypeToken.TypeSet interfaces() { + throw new UnsupportedOperationException("classes().interfaces() not supported."); + } + + private Object readResolve() { + return TypeToken.this.getTypes().classes(); + } + + // $FF: synthetic method + ClassSet(Object x1) { + this(); + } + } + + private final class InterfaceSet extends TypeToken.TypeSet { + private final transient TypeToken.TypeSet allTypes; + private transient ImmutableSet> interfaces; + private static final long serialVersionUID = 0L; + + InterfaceSet(TypeToken.TypeSet allTypes) { + super(); + this.allTypes = allTypes; + } + + protected Set> delegate() { + ImmutableSet> result = this.interfaces; + return result == null ? (this.interfaces = FluentIterable.from((Iterable)this.allTypes).filter((Predicate)TypeToken.TypeFilter.INTERFACE_ONLY).toSet()) : result; + } + + public TypeToken.TypeSet interfaces() { + return this; + } + + public Set> rawTypes() { + ImmutableList> collectedTypes = TypeToken.TypeCollector.FOR_RAW_TYPE.collectTypes((Iterable)TypeToken.this.getImmediateRawTypes()); + return FluentIterable.from((Iterable)collectedTypes).filter(new Predicate>() { + public boolean apply(Class type) { + return type.isInterface(); + } + }).toSet(); + } + + public TypeToken.TypeSet classes() { + throw new UnsupportedOperationException("interfaces().classes() not supported."); + } + + private Object readResolve() { + return TypeToken.this.getTypes().interfaces(); + } + } + + public class TypeSet extends ForwardingSet> implements Serializable { + private transient ImmutableSet> types; + private static final long serialVersionUID = 0L; + + TypeSet() { + } + + public TypeToken.TypeSet interfaces() { + return TypeToken.this.new InterfaceSet(this); + } + + public TypeToken.TypeSet classes() { + return TypeToken.this.new ClassSet(); + } + + protected Set> delegate() { + ImmutableSet> filteredTypes = this.types; + if (filteredTypes == null) { + ImmutableList> collectedTypes = TypeToken.TypeCollector.FOR_GENERIC_TYPE.collectTypes((Object)TypeToken.this); + return this.types = FluentIterable.from((Iterable)collectedTypes).filter((Predicate)TypeToken.TypeFilter.IGNORE_TYPE_VARIABLE_OR_WILDCARD).toSet(); + } else { + return filteredTypes; + } + } + + public Set> rawTypes() { + ImmutableList> collectedTypes = TypeToken.TypeCollector.FOR_RAW_TYPE.collectTypes((Iterable)TypeToken.this.getImmediateRawTypes()); + return ImmutableSet.copyOf((Collection)collectedTypes); + } + } +} diff --git a/src/main/com/google/common/reflect/TypeVisitor.java b/src/main/com/google/common/reflect/TypeVisitor.java new file mode 100644 index 0000000..3eaf839 --- /dev/null +++ b/src/main/com/google/common/reflect/TypeVisitor.java @@ -0,0 +1,69 @@ +package com.google.common.reflect; + +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.Set; +import javax.annotation.concurrent.NotThreadSafe; + +@NotThreadSafe +abstract class TypeVisitor { + private final Set visited = Sets.newHashSet(); + + public final void visit(Type... types) { + Type[] arr$ = types; + int len$ = types.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type type = arr$[i$]; + if (type != null && this.visited.add(type)) { + boolean succeeded = false; + + try { + if (type instanceof TypeVariable) { + this.visitTypeVariable((TypeVariable)type); + } else if (type instanceof WildcardType) { + this.visitWildcardType((WildcardType)type); + } else if (type instanceof ParameterizedType) { + this.visitParameterizedType((ParameterizedType)type); + } else if (type instanceof Class) { + this.visitClass((Class)type); + } else { + if (!(type instanceof GenericArrayType)) { + String var7 = String.valueOf(String.valueOf(type)); + throw new AssertionError((new StringBuilder(14 + var7.length())).append("Unknown type: ").append(var7).toString()); + } + + this.visitGenericArrayType((GenericArrayType)type); + } + + succeeded = true; + } finally { + if (!succeeded) { + this.visited.remove(type); + } + + } + } + } + + } + + void visitClass(Class t) { + } + + void visitGenericArrayType(GenericArrayType t) { + } + + void visitParameterizedType(ParameterizedType t) { + } + + void visitTypeVariable(TypeVariable t) { + } + + void visitWildcardType(WildcardType t) { + } +} diff --git a/src/main/com/google/common/reflect/Types.java b/src/main/com/google/common/reflect/Types.java new file mode 100644 index 0000000..4a41ce3 --- /dev/null +++ b/src/main/com/google/common/reflect/Types.java @@ -0,0 +1,490 @@ +package com.google.common.reflect; + +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.Preconditions; +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.AnnotatedElement; +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.InvocationTargetException; +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.Collection; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; +import javax.annotation.Nullable; + +final class Types { + private static final Function TYPE_NAME = new Function() { + public String apply(Type from) { + return Types.JavaVersion.CURRENT.typeName(from); + } + }; + private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null"); + + static Type newArrayType(Type componentType) { + if (componentType instanceof WildcardType) { + WildcardType wildcard = (WildcardType)componentType; + Type[] lowerBounds = wildcard.getLowerBounds(); + Preconditions.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(); + Preconditions.checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound."); + return subtypeOf(newArrayType(upperBounds[0])); + } + } else { + return Types.JavaVersion.CURRENT.newArrayType(componentType); + } + } + + static ParameterizedType newParameterizedTypeWithOwner(@Nullable Type ownerType, Class rawType, Type... arguments) { + if (ownerType == null) { + return newParameterizedType(rawType, arguments); + } else { + Preconditions.checkNotNull(arguments); + Preconditions.checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType); + return new Types.ParameterizedTypeImpl(ownerType, rawType, arguments); + } + } + + static ParameterizedType newParameterizedType(Class rawType, Type... arguments) { + return new Types.ParameterizedTypeImpl(Types.ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments); + } + + static TypeVariable newArtificialTypeVariable(D declaration, String name, Type... bounds) { + return new Types.TypeVariableImpl(declaration, name, bounds.length == 0 ? new Type[]{Object.class} : bounds); + } + + @VisibleForTesting + static WildcardType subtypeOf(Type upperBound) { + return new Types.WildcardTypeImpl(new Type[0], new Type[]{upperBound}); + } + + @VisibleForTesting + static WildcardType supertypeOf(Type lowerBound) { + return new Types.WildcardTypeImpl(new Type[]{lowerBound}, new Type[]{Object.class}); + } + + static String toString(Type type) { + return type instanceof Class ? ((Class)type).getName() : type.toString(); + } + + @Nullable + static Type getComponentType(Type type) { + Preconditions.checkNotNull(type); + final AtomicReference result = new AtomicReference(); + (new TypeVisitor() { + void visitTypeVariable(TypeVariable t) { + result.set(Types.subtypeOfComponentType(t.getBounds())); + } + + void visitWildcardType(WildcardType t) { + result.set(Types.subtypeOfComponentType(t.getUpperBounds())); + } + + void visitGenericArrayType(GenericArrayType t) { + result.set(t.getGenericComponentType()); + } + + void visitClass(Class t) { + result.set(t.getComponentType()); + } + }).visit(new Type[]{type}); + return (Type)result.get(); + } + + @Nullable + private static Type subtypeOfComponentType(Type[] bounds) { + Type[] arr$ = bounds; + int len$ = bounds.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type bound = arr$[i$]; + Type componentType = getComponentType(bound); + if (componentType != null) { + if (componentType instanceof Class) { + Class componentClass = (Class)componentType; + if (componentClass.isPrimitive()) { + return componentClass; + } + } + + return subtypeOf(componentType); + } + } + + return null; + } + + private static Type[] toArray(Collection types) { + return (Type[])types.toArray(new Type[types.size()]); + } + + private static Iterable filterUpperBounds(Iterable bounds) { + return Iterables.filter(bounds, Predicates.not(Predicates.equalTo(Object.class))); + } + + private static void disallowPrimitiveType(Type[] types, String usedAs) { + Type[] arr$ = types; + int len$ = types.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type type = arr$[i$]; + if (type instanceof Class) { + Class cls = (Class)type; + Preconditions.checkArgument(!cls.isPrimitive(), "Primitive type '%s' used as %s", cls, usedAs); + } + } + + } + + static Class getArrayClass(Class componentType) { + return Array.newInstance(componentType, 0).getClass(); + } + + private Types() { + } + + static final class NativeTypeVariableEquals { + static final boolean NATIVE_TYPE_VARIABLE_ONLY = !Types.NativeTypeVariableEquals.class.getTypeParameters()[0].equals(Types.newArtificialTypeVariable(Types.NativeTypeVariableEquals.class, "X")); + } + + static enum JavaVersion { + JAVA6 { + GenericArrayType newArrayType(Type componentType) { + return new Types.GenericArrayTypeImpl(componentType); + } + + Type usedInGenericType(Type type) { + Preconditions.checkNotNull(type); + if (type instanceof Class) { + Class cls = (Class)type; + if (cls.isArray()) { + return new Types.GenericArrayTypeImpl(cls.getComponentType()); + } + } + + return type; + } + }, + JAVA7 { + Type newArrayType(Type componentType) { + return (Type)(componentType instanceof Class ? Types.getArrayClass((Class)componentType) : new Types.GenericArrayTypeImpl(componentType)); + } + + Type usedInGenericType(Type type) { + return (Type)Preconditions.checkNotNull(type); + } + }, + JAVA8 { + Type newArrayType(Type componentType) { + return JAVA7.newArrayType(componentType); + } + + Type usedInGenericType(Type type) { + return JAVA7.usedInGenericType(type); + } + + String typeName(Type type) { + try { + Method getTypeName = Type.class.getMethod("getTypeName"); + return (String)getTypeName.invoke(type); + } catch (NoSuchMethodException var3) { + throw new AssertionError("Type.getTypeName should be available in Java 8"); + } catch (InvocationTargetException var4) { + throw new RuntimeException(var4); + } catch (IllegalAccessException var5) { + throw new RuntimeException(var5); + } + } + }; + + static final Types.JavaVersion CURRENT; + + private JavaVersion() { + } + + abstract Type newArrayType(Type var1); + + abstract Type usedInGenericType(Type var1); + + String typeName(Type type) { + return Types.toString(type); + } + + final ImmutableList usedInGenericType(Type[] types) { + ImmutableList.Builder builder = ImmutableList.builder(); + Type[] arr$ = types; + int len$ = types.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Type type = arr$[i$]; + builder.add((Object)this.usedInGenericType(type)); + } + + return builder.build(); + } + + // $FF: synthetic method + JavaVersion(Object x2) { + this(); + } + + static { + if (AnnotatedElement.class.isAssignableFrom(TypeVariable.class)) { + CURRENT = JAVA8; + } else if ((new TypeCapture() { + }).capture() instanceof Class) { + CURRENT = JAVA7; + } else { + CURRENT = JAVA6; + } + + } + } + + static final class WildcardTypeImpl implements WildcardType, Serializable { + private final ImmutableList lowerBounds; + private final ImmutableList upperBounds; + private static final long serialVersionUID = 0L; + + WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) { + Types.disallowPrimitiveType(lowerBounds, "lower bound for wildcard"); + Types.disallowPrimitiveType(upperBounds, "upper bound for wildcard"); + this.lowerBounds = Types.JavaVersion.CURRENT.usedInGenericType(lowerBounds); + this.upperBounds = Types.JavaVersion.CURRENT.usedInGenericType(upperBounds); + } + + public Type[] getLowerBounds() { + return Types.toArray(this.lowerBounds); + } + + public Type[] getUpperBounds() { + return Types.toArray(this.upperBounds); + } + + public boolean equals(Object obj) { + if (!(obj instanceof WildcardType)) { + return false; + } else { + WildcardType that = (WildcardType)obj; + return this.lowerBounds.equals(Arrays.asList(that.getLowerBounds())) && this.upperBounds.equals(Arrays.asList(that.getUpperBounds())); + } + } + + public int hashCode() { + return this.lowerBounds.hashCode() ^ this.upperBounds.hashCode(); + } + + public String toString() { + StringBuilder builder = new StringBuilder("?"); + Iterator i$ = this.lowerBounds.iterator(); + + Type upperBound; + while(i$.hasNext()) { + upperBound = (Type)i$.next(); + builder.append(" super ").append(Types.JavaVersion.CURRENT.typeName(upperBound)); + } + + i$ = Types.filterUpperBounds(this.upperBounds).iterator(); + + while(i$.hasNext()) { + upperBound = (Type)i$.next(); + builder.append(" extends ").append(Types.JavaVersion.CURRENT.typeName(upperBound)); + } + + return builder.toString(); + } + } + + private static final class TypeVariableImpl implements TypeVariable { + private final D genericDeclaration; + private final String name; + private final ImmutableList bounds; + + TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) { + Types.disallowPrimitiveType(bounds, "bound for type variable"); + this.genericDeclaration = (GenericDeclaration)Preconditions.checkNotNull(genericDeclaration); + this.name = (String)Preconditions.checkNotNull(name); + this.bounds = ImmutableList.copyOf((Object[])bounds); + } + + public Type[] getBounds() { + return Types.toArray(this.bounds); + } + + public D getGenericDeclaration() { + return this.genericDeclaration; + } + + public String getName() { + return this.name; + } + + public String toString() { + return this.name; + } + + public int hashCode() { + return this.genericDeclaration.hashCode() ^ this.name.hashCode(); + } + + public boolean equals(Object obj) { + if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { + if (!(obj instanceof Types.TypeVariableImpl)) { + return false; + } else { + Types.TypeVariableImpl that = (Types.TypeVariableImpl)obj; + return this.name.equals(that.getName()) && this.genericDeclaration.equals(that.getGenericDeclaration()) && this.bounds.equals(that.bounds); + } + } else if (!(obj instanceof TypeVariable)) { + return false; + } else { + TypeVariable that = (TypeVariable)obj; + return this.name.equals(that.getName()) && this.genericDeclaration.equals(that.getGenericDeclaration()); + } + } + } + + private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { + private final Type ownerType; + private final ImmutableList argumentsList; + private final Class rawType; + private static final long serialVersionUID = 0L; + + ParameterizedTypeImpl(@Nullable Type ownerType, Class rawType, Type[] typeArguments) { + Preconditions.checkNotNull(rawType); + Preconditions.checkArgument(typeArguments.length == rawType.getTypeParameters().length); + Types.disallowPrimitiveType(typeArguments, "type parameter"); + this.ownerType = ownerType; + this.rawType = rawType; + this.argumentsList = Types.JavaVersion.CURRENT.usedInGenericType(typeArguments); + } + + public Type[] getActualTypeArguments() { + return Types.toArray(this.argumentsList); + } + + public Type getRawType() { + return this.rawType; + } + + public Type getOwnerType() { + return this.ownerType; + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + if (this.ownerType != null) { + builder.append(Types.JavaVersion.CURRENT.typeName(this.ownerType)).append('.'); + } + + builder.append(this.rawType.getName()).append('<').append(Types.COMMA_JOINER.join(Iterables.transform(this.argumentsList, Types.TYPE_NAME))).append('>'); + return builder.toString(); + } + + public int hashCode() { + return (this.ownerType == null ? 0 : this.ownerType.hashCode()) ^ this.argumentsList.hashCode() ^ this.rawType.hashCode(); + } + + public boolean equals(Object other) { + if (!(other instanceof ParameterizedType)) { + return false; + } else { + ParameterizedType that = (ParameterizedType)other; + return this.getRawType().equals(that.getRawType()) && Objects.equal(this.getOwnerType(), that.getOwnerType()) && Arrays.equals(this.getActualTypeArguments(), that.getActualTypeArguments()); + } + } + } + + private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable { + private final Type componentType; + private static final long serialVersionUID = 0L; + + GenericArrayTypeImpl(Type componentType) { + this.componentType = Types.JavaVersion.CURRENT.usedInGenericType(componentType); + } + + public Type getGenericComponentType() { + return this.componentType; + } + + public String toString() { + return String.valueOf(Types.toString(this.componentType)).concat("[]"); + } + + public int hashCode() { + return this.componentType.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof GenericArrayType) { + GenericArrayType that = (GenericArrayType)obj; + return Objects.equal(this.getGenericComponentType(), that.getGenericComponentType()); + } else { + return false; + } + } + } + + private static enum ClassOwnership { + OWNED_BY_ENCLOSING_CLASS { + @Nullable + Class getOwnerType(Class rawType) { + return rawType.getEnclosingClass(); + } + }, + LOCAL_CLASS_HAS_NO_OWNER { + @Nullable + Class getOwnerType(Class rawType) { + return rawType.isLocalClass() ? null : rawType.getEnclosingClass(); + } + }; + + static final Types.ClassOwnership JVM_BEHAVIOR = detectJvmBehavior(); + + private ClassOwnership() { + } + + @Nullable + abstract Class getOwnerType(Class var1); + + private static Types.ClassOwnership detectJvmBehavior() { + class LocalClass { + } + + Class subclass = (new LocalClass() { + }).getClass(); + ParameterizedType parameterizedType = (ParameterizedType)subclass.getGenericSuperclass(); + Types.ClassOwnership[] arr$ = values(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Types.ClassOwnership behavior = arr$[i$]; + if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) { + return behavior; + } + } + + throw new AssertionError(); + } + + // $FF: synthetic method + ClassOwnership(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/reflect/package-info.java b/src/main/com/google/common/reflect/package-info.java new file mode 100644 index 0000000..d7e25ee --- /dev/null +++ b/src/main/com/google/common/reflect/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.reflect; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/util/concurrent/AbstractCheckedFuture.java b/src/main/com/google/common/util/concurrent/AbstractCheckedFuture.java new file mode 100644 index 0000000..b03e7a2 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AbstractCheckedFuture.java @@ -0,0 +1,42 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Beta +public abstract class AbstractCheckedFuture extends ForwardingListenableFuture.SimpleForwardingListenableFuture implements CheckedFuture { + protected AbstractCheckedFuture(ListenableFuture delegate) { + super(delegate); + } + + protected abstract X mapException(Exception var1); + + public V checkedGet() throws X { + try { + return this.get(); + } catch (InterruptedException var2) { + Thread.currentThread().interrupt(); + throw this.mapException(var2); + } catch (CancellationException var3) { + throw this.mapException(var3); + } catch (ExecutionException var4) { + throw this.mapException(var4); + } + } + + public V checkedGet(long timeout, TimeUnit unit) throws TimeoutException, X { + try { + return this.get(timeout, unit); + } catch (InterruptedException var5) { + Thread.currentThread().interrupt(); + throw this.mapException(var5); + } catch (CancellationException var6) { + throw this.mapException(var6); + } catch (ExecutionException var7) { + throw this.mapException(var7); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/AbstractExecutionThreadService.java b/src/main/com/google/common/util/concurrent/AbstractExecutionThreadService.java new file mode 100644 index 0000000..a3d2c80 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AbstractExecutionThreadService.java @@ -0,0 +1,129 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +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; + +@Beta +public abstract class AbstractExecutionThreadService implements Service { + private static final Logger logger = Logger.getLogger(AbstractExecutionThreadService.class.getName()); + private final Service delegate = new AbstractService() { + protected final void doStart() { + Executor executor = MoreExecutors.renamingDecorator(AbstractExecutionThreadService.this.executor(), new Supplier() { + public String get() { + return AbstractExecutionThreadService.this.serviceName(); + } + }); + executor.execute(new Runnable() { + public void run() { + try { + AbstractExecutionThreadService.this.startUp(); + notifyStarted(); + if (isRunning()) { + try { + AbstractExecutionThreadService.this.run(); + } catch (Throwable var4) { + try { + AbstractExecutionThreadService.this.shutDown(); + } catch (Exception var3) { + AbstractExecutionThreadService.logger.log(Level.WARNING, "Error while attempting to shut down the service after failure.", var3); + } + + throw var4; + } + } + + AbstractExecutionThreadService.this.shutDown(); + notifyStopped(); + } catch (Throwable var5) { + notifyFailed(var5); + throw Throwables.propagate(var5); + } + } + }); + } + + protected void doStop() { + AbstractExecutionThreadService.this.triggerShutdown(); + } + }; + + protected AbstractExecutionThreadService() { + } + + protected void startUp() throws Exception { + } + + protected abstract void run() throws Exception; + + protected void shutDown() throws Exception { + } + + protected void triggerShutdown() { + } + + protected Executor executor() { + return new Executor() { + public void execute(Runnable command) { + MoreExecutors.newThread(AbstractExecutionThreadService.this.serviceName(), command).start(); + } + }; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.serviceName())); + String var2 = String.valueOf(String.valueOf(this.state())); + return (new StringBuilder(3 + var1.length() + var2.length())).append(var1).append(" [").append(var2).append("]").toString(); + } + + public final boolean isRunning() { + return this.delegate.isRunning(); + } + + public final Service.State state() { + return this.delegate.state(); + } + + public final void addListener(Service.Listener listener, Executor executor) { + this.delegate.addListener(listener, executor); + } + + public final Throwable failureCause() { + return this.delegate.failureCause(); + } + + public final Service startAsync() { + this.delegate.startAsync(); + return this; + } + + public final Service stopAsync() { + this.delegate.stopAsync(); + return this; + } + + public final void awaitRunning() { + this.delegate.awaitRunning(); + } + + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + this.delegate.awaitRunning(timeout, unit); + } + + public final void awaitTerminated() { + this.delegate.awaitTerminated(); + } + + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + this.delegate.awaitTerminated(timeout, unit); + } + + protected String serviceName() { + return this.getClass().getSimpleName(); + } +} diff --git a/src/main/com/google/common/util/concurrent/AbstractFuture.java b/src/main/com/google/common/util/concurrent/AbstractFuture.java new file mode 100644 index 0000000..8c53911 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AbstractFuture.java @@ -0,0 +1,169 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import java.util.concurrent.CancellationException; +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.AbstractQueuedSynchronizer; +import javax.annotation.Nullable; + +public abstract class AbstractFuture implements ListenableFuture { + private final AbstractFuture.Sync sync = new AbstractFuture.Sync(); + private final ExecutionList executionList = new ExecutionList(); + + protected AbstractFuture() { + } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException { + return this.sync.get(unit.toNanos(timeout)); + } + + public V get() throws InterruptedException, ExecutionException { + return this.sync.get(); + } + + public boolean isDone() { + return this.sync.isDone(); + } + + public boolean isCancelled() { + return this.sync.isCancelled(); + } + + public boolean cancel(boolean mayInterruptIfRunning) { + if (!this.sync.cancel(mayInterruptIfRunning)) { + return false; + } else { + this.executionList.execute(); + if (mayInterruptIfRunning) { + this.interruptTask(); + } + + return true; + } + } + + protected void interruptTask() { + } + + protected final boolean wasInterrupted() { + return this.sync.wasInterrupted(); + } + + public void addListener(Runnable listener, Executor exec) { + this.executionList.add(listener, exec); + } + + protected boolean set(@Nullable V value) { + boolean result = this.sync.set(value); + if (result) { + this.executionList.execute(); + } + + return result; + } + + protected boolean setException(Throwable throwable) { + boolean result = this.sync.setException((Throwable)Preconditions.checkNotNull(throwable)); + if (result) { + this.executionList.execute(); + } + + return result; + } + + static final CancellationException cancellationExceptionWithCause(@Nullable String message, @Nullable Throwable cause) { + CancellationException exception = new CancellationException(message); + exception.initCause(cause); + return exception; + } + + static final class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 0L; + static final int RUNNING = 0; + 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; + + protected int tryAcquireShared(int ignored) { + return this.isDone() ? 1 : -1; + } + + protected boolean tryReleaseShared(int finalState) { + this.setState(finalState); + return true; + } + + V get(long nanos) throws TimeoutException, CancellationException, ExecutionException, InterruptedException { + if (!this.tryAcquireSharedNanos(-1, nanos)) { + throw new TimeoutException("Timeout waiting for task."); + } else { + return this.getValue(); + } + } + + V get() throws CancellationException, ExecutionException, InterruptedException { + this.acquireSharedInterruptibly(-1); + return this.getValue(); + } + + private V getValue() throws CancellationException, ExecutionException { + int state = this.getState(); + switch(state) { + case 2: + if (this.exception != null) { + throw new ExecutionException(this.exception); + } + + return this.value; + case 4: + case 8: + throw AbstractFuture.cancellationExceptionWithCause("Task was cancelled.", this.exception); + default: + throw new IllegalStateException((new StringBuilder(49)).append("Error, synchronizer in invalid state: ").append(state).toString()); + } + } + + boolean isDone() { + return (this.getState() & 14) != 0; + } + + boolean isCancelled() { + return (this.getState() & 12) != 0; + } + + boolean wasInterrupted() { + return this.getState() == 8; + } + + boolean set(@Nullable V v) { + return this.complete(v, (Throwable)null, 2); + } + + boolean setException(Throwable t) { + return this.complete((Object)null, t, 2); + } + + boolean cancel(boolean interrupt) { + return this.complete((Object)null, (Throwable)null, interrupt ? 8 : 4); + } + + private boolean complete(@Nullable V v, @Nullable Throwable t, int finalState) { + boolean doCompletion = this.compareAndSetState(0, 1); + if (doCompletion) { + this.value = v; + this.exception = (Throwable)((finalState & 12) != 0 ? new CancellationException("Future.cancel() was called.") : t); + this.releaseShared(finalState); + } else if (this.getState() == 1) { + this.acquireShared(-1); + } + + return doCompletion; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/AbstractIdleService.java b/src/main/com/google/common/util/concurrent/AbstractIdleService.java new file mode 100644 index 0000000..86a24bd --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AbstractIdleService.java @@ -0,0 +1,115 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Beta +public abstract class AbstractIdleService implements Service { + private final Supplier threadNameSupplier = new Supplier() { + public String get() { + String var1 = String.valueOf(String.valueOf(AbstractIdleService.this.serviceName())); + String var2 = String.valueOf(String.valueOf(AbstractIdleService.this.state())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append(" ").append(var2).toString(); + } + }; + private final Service delegate = new AbstractService() { + protected final void doStart() { + MoreExecutors.renamingDecorator(AbstractIdleService.this.executor(), AbstractIdleService.this.threadNameSupplier).execute(new Runnable() { + public void run() { + try { + AbstractIdleService.this.startUp(); + notifyStarted(); + } catch (Throwable var2) { + notifyFailed(var2); + throw Throwables.propagate(var2); + } + } + }); + } + + protected final void doStop() { + MoreExecutors.renamingDecorator(AbstractIdleService.this.executor(), AbstractIdleService.this.threadNameSupplier).execute(new Runnable() { + public void run() { + try { + AbstractIdleService.this.shutDown(); + notifyStopped(); + } catch (Throwable var2) { + notifyFailed(var2); + throw Throwables.propagate(var2); + } + } + }); + } + }; + + protected AbstractIdleService() { + } + + protected abstract void startUp() throws Exception; + + protected abstract void shutDown() throws Exception; + + protected Executor executor() { + return new Executor() { + public void execute(Runnable command) { + MoreExecutors.newThread((String)AbstractIdleService.this.threadNameSupplier.get(), command).start(); + } + }; + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.serviceName())); + String var2 = String.valueOf(String.valueOf(this.state())); + return (new StringBuilder(3 + var1.length() + var2.length())).append(var1).append(" [").append(var2).append("]").toString(); + } + + public final boolean isRunning() { + return this.delegate.isRunning(); + } + + public final Service.State state() { + return this.delegate.state(); + } + + public final void addListener(Service.Listener listener, Executor executor) { + this.delegate.addListener(listener, executor); + } + + public final Throwable failureCause() { + return this.delegate.failureCause(); + } + + public final Service startAsync() { + this.delegate.startAsync(); + return this; + } + + public final Service stopAsync() { + this.delegate.stopAsync(); + return this; + } + + public final void awaitRunning() { + this.delegate.awaitRunning(); + } + + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + this.delegate.awaitRunning(timeout, unit); + } + + public final void awaitTerminated() { + this.delegate.awaitTerminated(); + } + + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + this.delegate.awaitTerminated(timeout, unit); + } + + protected String serviceName() { + return this.getClass().getSimpleName(); + } +} diff --git a/src/main/com/google/common/util/concurrent/AbstractListeningExecutorService.java b/src/main/com/google/common/util/concurrent/AbstractListeningExecutorService.java new file mode 100644 index 0000000..c1e2994 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AbstractListeningExecutorService.java @@ -0,0 +1,29 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import javax.annotation.Nullable; + +@Beta +public abstract class AbstractListeningExecutorService extends AbstractExecutorService implements ListeningExecutorService { + protected final ListenableFutureTask newTaskFor(Runnable runnable, T value) { + return ListenableFutureTask.create(runnable, value); + } + + protected final ListenableFutureTask newTaskFor(Callable callable) { + return ListenableFutureTask.create(callable); + } + + public ListenableFuture submit(Runnable task) { + return (ListenableFuture)super.submit(task); + } + + public ListenableFuture submit(Runnable task, @Nullable T result) { + return (ListenableFuture)super.submit(task, result); + } + + public ListenableFuture submit(Callable task) { + return (ListenableFuture)super.submit(task); + } +} diff --git a/src/main/com/google/common/util/concurrent/AbstractScheduledService.java b/src/main/com/google/common/util/concurrent/AbstractScheduledService.java new file mode 100644 index 0000000..0a166a4 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AbstractScheduledService.java @@ -0,0 +1,293 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +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.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.concurrent.GuardedBy; + +@Beta +public abstract class AbstractScheduledService implements Service { + private static final Logger logger = Logger.getLogger(AbstractScheduledService.class.getName()); + private final AbstractService delegate = new AbstractService() { + private volatile Future runningTask; + private volatile ScheduledExecutorService executorService; + private final ReentrantLock lock = new ReentrantLock(); + private final Runnable task = new Runnable() { + public void run() { + lock.lock(); + + try { + AbstractScheduledService.this.runOneIteration(); + } catch (Throwable var8) { + try { + AbstractScheduledService.this.shutDown(); + } catch (Exception var7) { + AbstractScheduledService.logger.log(Level.WARNING, "Error while attempting to shut down the service after failure.", var7); + } + + notifyFailed(var8); + throw Throwables.propagate(var8); + } finally { + lock.unlock(); + } + + } + }; + + protected final void doStart() { + this.executorService = MoreExecutors.renamingDecorator(AbstractScheduledService.this.executor(), new Supplier() { + public String get() { + String var1 = String.valueOf(String.valueOf(AbstractScheduledService.this.serviceName())); + String var2 = String.valueOf(String.valueOf(state())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append(" ").append(var2).toString(); + } + }); + this.executorService.execute(new Runnable() { + public void run() { + lock.lock(); + + try { + AbstractScheduledService.this.startUp(); + runningTask = AbstractScheduledService.this.scheduler().schedule(AbstractScheduledService.this.delegate, executorService, task); + notifyStarted(); + } catch (Throwable var5) { + notifyFailed(var5); + throw Throwables.propagate(var5); + } finally { + lock.unlock(); + } + + } + }); + } + + protected final void doStop() { + this.runningTask.cancel(false); + this.executorService.execute(new Runnable() { + public void run() { + try { + lock.lock(); + + try { + if (state() != Service.State.STOPPING) { + return; + } + + AbstractScheduledService.this.shutDown(); + } finally { + lock.unlock(); + } + + notifyStopped(); + } catch (Throwable var5) { + notifyFailed(var5); + throw Throwables.propagate(var5); + } + } + }); + } + }; + + protected AbstractScheduledService() { + } + + protected abstract void runOneIteration() throws Exception; + + protected void startUp() throws Exception { + } + + protected void shutDown() throws Exception { + } + + protected abstract AbstractScheduledService.Scheduler scheduler(); + + protected ScheduledExecutorService executor() { + final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + public Thread newThread(Runnable runnable) { + return MoreExecutors.newThread(AbstractScheduledService.this.serviceName(), runnable); + } + }); + this.addListener(new Service.Listener() { + public void terminated(Service.State from) { + executor.shutdown(); + } + + public void failed(Service.State from, Throwable failure) { + executor.shutdown(); + } + }, MoreExecutors.directExecutor()); + return executor; + } + + protected String serviceName() { + return this.getClass().getSimpleName(); + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.serviceName())); + String var2 = String.valueOf(String.valueOf(this.state())); + return (new StringBuilder(3 + var1.length() + var2.length())).append(var1).append(" [").append(var2).append("]").toString(); + } + + public final boolean isRunning() { + return this.delegate.isRunning(); + } + + public final Service.State state() { + return this.delegate.state(); + } + + public final void addListener(Service.Listener listener, Executor executor) { + this.delegate.addListener(listener, executor); + } + + public final Throwable failureCause() { + return this.delegate.failureCause(); + } + + public final Service startAsync() { + this.delegate.startAsync(); + return this; + } + + public final Service stopAsync() { + this.delegate.stopAsync(); + return this; + } + + public final void awaitRunning() { + this.delegate.awaitRunning(); + } + + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + this.delegate.awaitRunning(timeout, unit); + } + + public final void awaitTerminated() { + this.delegate.awaitTerminated(); + } + + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + this.delegate.awaitTerminated(timeout, unit); + } + + @Beta + public abstract static class CustomScheduler extends AbstractScheduledService.Scheduler { + public CustomScheduler() { + super(null); + } + + final Future schedule(AbstractService service, ScheduledExecutorService executor, Runnable runnable) { + AbstractScheduledService.CustomScheduler.ReschedulableCallable task = new AbstractScheduledService.CustomScheduler.ReschedulableCallable(service, executor, runnable); + task.reschedule(); + return task; + } + + protected abstract AbstractScheduledService.CustomScheduler.Schedule getNextSchedule() throws Exception; + + @Beta + protected static final class Schedule { + private final long delay; + private final TimeUnit unit; + + public Schedule(long delay, TimeUnit unit) { + this.delay = delay; + this.unit = (TimeUnit)Preconditions.checkNotNull(unit); + } + } + + private class ReschedulableCallable extends ForwardingFuture implements Callable { + private final Runnable wrappedRunnable; + private final ScheduledExecutorService executor; + private final AbstractService service; + private final ReentrantLock lock = new ReentrantLock(); + @GuardedBy("lock") + private Future currentFuture; + + ReschedulableCallable(AbstractService service, ScheduledExecutorService executor, Runnable runnable) { + this.wrappedRunnable = runnable; + this.executor = executor; + this.service = service; + } + + public Void call() throws Exception { + this.wrappedRunnable.run(); + this.reschedule(); + return null; + } + + public void reschedule() { + this.lock.lock(); + + try { + if (this.currentFuture == null || !this.currentFuture.isCancelled()) { + AbstractScheduledService.CustomScheduler.Schedule schedule = CustomScheduler.this.getNextSchedule(); + this.currentFuture = this.executor.schedule(this, schedule.delay, schedule.unit); + } + } catch (Throwable var5) { + this.service.notifyFailed(var5); + } finally { + this.lock.unlock(); + } + + } + + public boolean cancel(boolean mayInterruptIfRunning) { + this.lock.lock(); + + boolean var2; + try { + var2 = this.currentFuture.cancel(mayInterruptIfRunning); + } finally { + this.lock.unlock(); + } + + return var2; + } + + protected Future delegate() { + throw new UnsupportedOperationException("Only cancel is supported by this future"); + } + } + } + + public abstract static class Scheduler { + public static AbstractScheduledService.Scheduler newFixedDelaySchedule(final long initialDelay, final long delay, final TimeUnit unit) { + return new AbstractScheduledService.Scheduler() { + public Future schedule(AbstractService service, ScheduledExecutorService executor, Runnable task) { + return executor.scheduleWithFixedDelay(task, initialDelay, delay, unit); + } + }; + } + + public static AbstractScheduledService.Scheduler newFixedRateSchedule(final long initialDelay, final long period, final TimeUnit unit) { + return new AbstractScheduledService.Scheduler() { + public Future schedule(AbstractService service, ScheduledExecutorService executor, Runnable task) { + return executor.scheduleAtFixedRate(task, initialDelay, period, unit); + } + }; + } + + abstract Future schedule(AbstractService var1, ScheduledExecutorService var2, Runnable var3); + + private Scheduler() { + } + + // $FF: synthetic method + Scheduler(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/AbstractService.java b/src/main/com/google/common/util/concurrent/AbstractService.java new file mode 100644 index 0000000..8ec0bac --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AbstractService.java @@ -0,0 +1,423 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.Immutable; + +@Beta +public abstract class AbstractService implements Service { + private static final ListenerCallQueue.Callback STARTING_CALLBACK = new ListenerCallQueue.Callback("starting()") { + void call(Service.Listener listener) { + listener.starting(); + } + }; + private static final ListenerCallQueue.Callback RUNNING_CALLBACK = new ListenerCallQueue.Callback("running()") { + void call(Service.Listener listener) { + listener.running(); + } + }; + private static final ListenerCallQueue.Callback STOPPING_FROM_STARTING_CALLBACK; + private static final ListenerCallQueue.Callback STOPPING_FROM_RUNNING_CALLBACK; + private static final ListenerCallQueue.Callback TERMINATED_FROM_NEW_CALLBACK; + private static final ListenerCallQueue.Callback TERMINATED_FROM_RUNNING_CALLBACK; + private static final ListenerCallQueue.Callback TERMINATED_FROM_STOPPING_CALLBACK; + private final Monitor monitor = new Monitor(); + private final Monitor.Guard isStartable; + private final Monitor.Guard isStoppable; + private final Monitor.Guard hasReachedRunning; + private final Monitor.Guard isStopped; + @GuardedBy("monitor") + private final List> listeners; + @GuardedBy("monitor") + private volatile AbstractService.StateSnapshot snapshot; + + private static ListenerCallQueue.Callback terminatedCallback(final Service.State from) { + String var1 = String.valueOf(String.valueOf(from)); + return new ListenerCallQueue.Callback((new StringBuilder(21 + var1.length())).append("terminated({from = ").append(var1).append("})").toString()) { + void call(Service.Listener listener) { + listener.terminated(from); + } + }; + } + + private static ListenerCallQueue.Callback stoppingCallback(final Service.State from) { + String var1 = String.valueOf(String.valueOf(from)); + return new ListenerCallQueue.Callback((new StringBuilder(19 + var1.length())).append("stopping({from = ").append(var1).append("})").toString()) { + void call(Service.Listener listener) { + listener.stopping(from); + } + }; + } + + protected AbstractService() { + this.isStartable = new Monitor.Guard(this.monitor) { + public boolean isSatisfied() { + return AbstractService.this.state() == Service.State.NEW; + } + }; + this.isStoppable = new Monitor.Guard(this.monitor) { + public boolean isSatisfied() { + return AbstractService.this.state().compareTo(Service.State.RUNNING) <= 0; + } + }; + this.hasReachedRunning = new Monitor.Guard(this.monitor) { + public boolean isSatisfied() { + return AbstractService.this.state().compareTo(Service.State.RUNNING) >= 0; + } + }; + this.isStopped = new Monitor.Guard(this.monitor) { + public boolean isSatisfied() { + return AbstractService.this.state().isTerminal(); + } + }; + this.listeners = Collections.synchronizedList(new ArrayList()); + this.snapshot = new AbstractService.StateSnapshot(Service.State.NEW); + } + + protected abstract void doStart(); + + protected abstract void doStop(); + + public final Service startAsync() { + if (this.monitor.enterIf(this.isStartable)) { + try { + this.snapshot = new AbstractService.StateSnapshot(Service.State.STARTING); + this.starting(); + this.doStart(); + } catch (Throwable var5) { + this.notifyFailed(var5); + } finally { + this.monitor.leave(); + this.executeListeners(); + } + + return this; + } else { + String var1 = String.valueOf(String.valueOf(this)); + throw new IllegalStateException((new StringBuilder(33 + var1.length())).append("Service ").append(var1).append(" has already been started").toString()); + } + } + + public final Service stopAsync() { + if (this.monitor.enterIf(this.isStoppable)) { + try { + Service.State previous = this.state(); + switch(previous) { + case NEW: + this.snapshot = new AbstractService.StateSnapshot(Service.State.TERMINATED); + this.terminated(Service.State.NEW); + break; + case STARTING: + this.snapshot = new AbstractService.StateSnapshot(Service.State.STARTING, true, (Throwable)null); + this.stopping(Service.State.STARTING); + break; + case RUNNING: + this.snapshot = new AbstractService.StateSnapshot(Service.State.STOPPING); + this.stopping(Service.State.RUNNING); + this.doStop(); + break; + case STOPPING: + case TERMINATED: + case FAILED: + String var2 = String.valueOf(String.valueOf(previous)); + throw new AssertionError((new StringBuilder(45 + var2.length())).append("isStoppable is incorrectly implemented, saw: ").append(var2).toString()); + default: + String var3 = String.valueOf(String.valueOf(previous)); + throw new AssertionError((new StringBuilder(18 + var3.length())).append("Unexpected state: ").append(var3).toString()); + } + } catch (Throwable var7) { + this.notifyFailed(var7); + } finally { + this.monitor.leave(); + this.executeListeners(); + } + } + + return this; + } + + public final void awaitRunning() { + this.monitor.enterWhenUninterruptibly(this.hasReachedRunning); + + try { + this.checkCurrentState(Service.State.RUNNING); + } finally { + this.monitor.leave(); + } + + } + + public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { + if (this.monitor.enterWhenUninterruptibly(this.hasReachedRunning, timeout, unit)) { + try { + this.checkCurrentState(Service.State.RUNNING); + } finally { + this.monitor.leave(); + } + + } else { + String var4 = String.valueOf(String.valueOf(this)); + String var5 = String.valueOf(String.valueOf(this.state())); + throw new TimeoutException((new StringBuilder(66 + var4.length() + var5.length())).append("Timed out waiting for ").append(var4).append(" to reach the RUNNING state. ").append("Current state: ").append(var5).toString()); + } + } + + public final void awaitTerminated() { + this.monitor.enterWhenUninterruptibly(this.isStopped); + + try { + this.checkCurrentState(Service.State.TERMINATED); + } finally { + this.monitor.leave(); + } + + } + + public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { + if (this.monitor.enterWhenUninterruptibly(this.isStopped, timeout, unit)) { + try { + this.checkCurrentState(Service.State.TERMINATED); + } finally { + this.monitor.leave(); + } + + } else { + String var4 = String.valueOf(String.valueOf(this)); + String var5 = String.valueOf(String.valueOf(this.state())); + throw new TimeoutException((new StringBuilder(65 + var4.length() + var5.length())).append("Timed out waiting for ").append(var4).append(" to reach a terminal state. ").append("Current state: ").append(var5).toString()); + } + } + + @GuardedBy("monitor") + private void checkCurrentState(Service.State expected) { + Service.State actual = this.state(); + if (actual != expected) { + String var3; + if (actual == Service.State.FAILED) { + var3 = String.valueOf(String.valueOf(expected)); + throw new IllegalStateException((new StringBuilder(55 + var3.length())).append("Expected the service to be ").append(var3).append(", but the service has FAILED").toString(), this.failureCause()); + } else { + var3 = String.valueOf(String.valueOf(expected)); + String var4 = String.valueOf(String.valueOf(actual)); + throw new IllegalStateException((new StringBuilder(37 + var3.length() + var4.length())).append("Expected the service to be ").append(var3).append(", but was ").append(var4).toString()); + } + } + } + + protected final void notifyStarted() { + this.monitor.enter(); + + try { + if (this.snapshot.state != Service.State.STARTING) { + String var2 = String.valueOf(String.valueOf(this.snapshot.state)); + IllegalStateException failure = new IllegalStateException((new StringBuilder(43 + var2.length())).append("Cannot notifyStarted() when the service is ").append(var2).toString()); + this.notifyFailed(failure); + throw failure; + } + + if (this.snapshot.shutdownWhenStartupFinishes) { + this.snapshot = new AbstractService.StateSnapshot(Service.State.STOPPING); + this.doStop(); + } else { + this.snapshot = new AbstractService.StateSnapshot(Service.State.RUNNING); + this.running(); + } + } finally { + this.monitor.leave(); + this.executeListeners(); + } + + } + + protected final void notifyStopped() { + this.monitor.enter(); + + try { + Service.State previous = this.snapshot.state; + if (previous != Service.State.STOPPING && previous != Service.State.RUNNING) { + String var3 = String.valueOf(String.valueOf(previous)); + IllegalStateException failure = new IllegalStateException((new StringBuilder(43 + var3.length())).append("Cannot notifyStopped() when the service is ").append(var3).toString()); + this.notifyFailed(failure); + throw failure; + } + + this.snapshot = new AbstractService.StateSnapshot(Service.State.TERMINATED); + this.terminated(previous); + } finally { + this.monitor.leave(); + this.executeListeners(); + } + + } + + protected final void notifyFailed(Throwable cause) { + Preconditions.checkNotNull(cause); + this.monitor.enter(); + + try { + Service.State previous = this.state(); + switch(previous) { + case NEW: + case TERMINATED: + String var3 = String.valueOf(String.valueOf(previous)); + throw new IllegalStateException((new StringBuilder(22 + var3.length())).append("Failed while in state:").append(var3).toString(), cause); + case STARTING: + case RUNNING: + case STOPPING: + this.snapshot = new AbstractService.StateSnapshot(Service.State.FAILED, false, cause); + this.failed(previous, cause); + case FAILED: + break; + default: + String var4 = String.valueOf(String.valueOf(previous)); + throw new AssertionError((new StringBuilder(18 + var4.length())).append("Unexpected state: ").append(var4).toString()); + } + } finally { + this.monitor.leave(); + this.executeListeners(); + } + + } + + public final boolean isRunning() { + return this.state() == Service.State.RUNNING; + } + + public final Service.State state() { + return this.snapshot.externalState(); + } + + public final Throwable failureCause() { + return this.snapshot.failureCause(); + } + + public final void addListener(Service.Listener listener, Executor executor) { + Preconditions.checkNotNull(listener, "listener"); + Preconditions.checkNotNull(executor, "executor"); + this.monitor.enter(); + + try { + if (!this.state().isTerminal()) { + this.listeners.add(new ListenerCallQueue(listener, executor)); + } + } finally { + this.monitor.leave(); + } + + } + + public String toString() { + String var1 = String.valueOf(String.valueOf(this.getClass().getSimpleName())); + String var2 = String.valueOf(String.valueOf(this.state())); + return (new StringBuilder(3 + var1.length() + var2.length())).append(var1).append(" [").append(var2).append("]").toString(); + } + + private void executeListeners() { + if (!this.monitor.isOccupiedByCurrentThread()) { + for(int i = 0; i < this.listeners.size(); ++i) { + ((ListenerCallQueue)this.listeners.get(i)).execute(); + } + } + + } + + @GuardedBy("monitor") + private void starting() { + STARTING_CALLBACK.enqueueOn(this.listeners); + } + + @GuardedBy("monitor") + private void running() { + RUNNING_CALLBACK.enqueueOn(this.listeners); + } + + @GuardedBy("monitor") + private void stopping(Service.State from) { + if (from == Service.State.STARTING) { + STOPPING_FROM_STARTING_CALLBACK.enqueueOn(this.listeners); + } else { + if (from != Service.State.RUNNING) { + throw new AssertionError(); + } + + STOPPING_FROM_RUNNING_CALLBACK.enqueueOn(this.listeners); + } + + } + + @GuardedBy("monitor") + private void terminated(Service.State from) { + switch(from) { + case NEW: + TERMINATED_FROM_NEW_CALLBACK.enqueueOn(this.listeners); + break; + case STARTING: + case TERMINATED: + case FAILED: + default: + throw new AssertionError(); + case RUNNING: + TERMINATED_FROM_RUNNING_CALLBACK.enqueueOn(this.listeners); + break; + case STOPPING: + TERMINATED_FROM_STOPPING_CALLBACK.enqueueOn(this.listeners); + } + + } + + @GuardedBy("monitor") + private void failed(final Service.State from, final Throwable cause) { + String var3 = String.valueOf(String.valueOf(from)); + String var4 = String.valueOf(String.valueOf(cause)); + (new ListenerCallQueue.Callback((new StringBuilder(27 + var3.length() + var4.length())).append("failed({from = ").append(var3).append(", cause = ").append(var4).append("})").toString()) { + void call(Service.Listener listener) { + listener.failed(from, cause); + } + }).enqueueOn(this.listeners); + } + + static { + STOPPING_FROM_STARTING_CALLBACK = stoppingCallback(Service.State.STARTING); + STOPPING_FROM_RUNNING_CALLBACK = stoppingCallback(Service.State.RUNNING); + TERMINATED_FROM_NEW_CALLBACK = terminatedCallback(Service.State.NEW); + TERMINATED_FROM_RUNNING_CALLBACK = terminatedCallback(Service.State.RUNNING); + TERMINATED_FROM_STOPPING_CALLBACK = terminatedCallback(Service.State.STOPPING); + } + + @Immutable + private static final class StateSnapshot { + final Service.State state; + final boolean shutdownWhenStartupFinishes; + @Nullable + final Throwable failure; + + StateSnapshot(Service.State internalState) { + this(internalState, false, (Throwable)null); + } + + StateSnapshot(Service.State internalState, boolean shutdownWhenStartupFinishes, @Nullable Throwable failure) { + Preconditions.checkArgument(!shutdownWhenStartupFinishes || internalState == Service.State.STARTING, "shudownWhenStartupFinishes can only be set if state is STARTING. Got %s instead.", internalState); + Preconditions.checkArgument(!(failure != null ^ internalState == Service.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; + } + + Service.State externalState() { + return this.shutdownWhenStartupFinishes && this.state == Service.State.STARTING ? Service.State.STOPPING : this.state; + } + + Throwable failureCause() { + Preconditions.checkState(this.state == Service.State.FAILED, "failureCause() is only valid if the service has failed, service is %s", this.state); + return this.failure; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/AsyncFunction.java b/src/main/com/google/common/util/concurrent/AsyncFunction.java new file mode 100644 index 0000000..60adbf6 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AsyncFunction.java @@ -0,0 +1,5 @@ +package com.google.common.util.concurrent; + +public interface AsyncFunction { + ListenableFuture apply(I var1) throws Exception; +} diff --git a/src/main/com/google/common/util/concurrent/AsyncSettableFuture.java b/src/main/com/google/common/util/concurrent/AsyncSettableFuture.java new file mode 100644 index 0000000..620f569 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AsyncSettableFuture.java @@ -0,0 +1,56 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import javax.annotation.Nullable; + +final class AsyncSettableFuture extends ForwardingListenableFuture { + private final AsyncSettableFuture.NestedFuture nested = new AsyncSettableFuture.NestedFuture(); + private final ListenableFuture dereferenced; + + public static AsyncSettableFuture create() { + return new AsyncSettableFuture(); + } + + private AsyncSettableFuture() { + this.dereferenced = Futures.dereference(this.nested); + } + + protected ListenableFuture delegate() { + return this.dereferenced; + } + + public boolean setFuture(ListenableFuture future) { + return this.nested.setFuture((ListenableFuture)Preconditions.checkNotNull(future)); + } + + public boolean setValue(@Nullable V value) { + return this.setFuture(Futures.immediateFuture(value)); + } + + public boolean setException(Throwable exception) { + return this.setFuture(Futures.immediateFailedFuture(exception)); + } + + public boolean isSet() { + return this.nested.isDone(); + } + + private static final class NestedFuture extends AbstractFuture> { + private NestedFuture() { + } + + boolean setFuture(ListenableFuture value) { + boolean result = this.set(value); + if (this.isCancelled()) { + value.cancel(this.wasInterrupted()); + } + + return result; + } + + // $FF: synthetic method + NestedFuture(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/AtomicDouble.java b/src/main/com/google/common/util/concurrent/AtomicDouble.java new file mode 100644 index 0000000..a871cc6 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AtomicDouble.java @@ -0,0 +1,104 @@ +package com.google.common.util.concurrent; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +public class AtomicDouble extends Number implements Serializable { + private static final long serialVersionUID = 0L; + private transient volatile long value; + private static final AtomicLongFieldUpdater updater = AtomicLongFieldUpdater.newUpdater(AtomicDouble.class, "value"); + + public AtomicDouble(double initialValue) { + this.value = Double.doubleToRawLongBits(initialValue); + } + + public AtomicDouble() { + } + + public final double get() { + return Double.longBitsToDouble(this.value); + } + + public final void set(double newValue) { + long next = Double.doubleToRawLongBits(newValue); + this.value = next; + } + + public final void lazySet(double newValue) { + this.set(newValue); + } + + public final double getAndSet(double newValue) { + long next = Double.doubleToRawLongBits(newValue); + return Double.longBitsToDouble(updater.getAndSet(this, next)); + } + + public final boolean compareAndSet(double expect, double update) { + return updater.compareAndSet(this, Double.doubleToRawLongBits(expect), Double.doubleToRawLongBits(update)); + } + + public final boolean weakCompareAndSet(double expect, double update) { + return updater.weakCompareAndSet(this, Double.doubleToRawLongBits(expect), Double.doubleToRawLongBits(update)); + } + + public final double getAndAdd(double delta) { + long current; + double currentVal; + long next; + do { + current = this.value; + currentVal = Double.longBitsToDouble(current); + double nextVal = currentVal + delta; + next = Double.doubleToRawLongBits(nextVal); + } while(!updater.compareAndSet(this, current, next)); + + return currentVal; + } + + public final double addAndGet(double delta) { + long current; + double nextVal; + long next; + do { + current = this.value; + double currentVal = Double.longBitsToDouble(current); + nextVal = currentVal + delta; + next = Double.doubleToRawLongBits(nextVal); + } while(!updater.compareAndSet(this, current, next)); + + return nextVal; + } + + public String toString() { + return Double.toString(this.get()); + } + + public int intValue() { + return (int)this.get(); + } + + public long longValue() { + return (long)this.get(); + } + + public float floatValue() { + return (float)this.get(); + } + + public double doubleValue() { + return this.get(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeDouble(this.get()); + } + + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + this.set(s.readDouble()); + } +} diff --git a/src/main/com/google/common/util/concurrent/AtomicDoubleArray.java b/src/main/com/google/common/util/concurrent/AtomicDoubleArray.java new file mode 100644 index 0000000..f228328 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AtomicDoubleArray.java @@ -0,0 +1,128 @@ +package com.google.common.util.concurrent; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicLongArray; + +public class AtomicDoubleArray implements Serializable { + private static final long serialVersionUID = 0L; + private transient AtomicLongArray longs; + + public AtomicDoubleArray(int length) { + this.longs = new AtomicLongArray(length); + } + + public AtomicDoubleArray(double[] array) { + int len = array.length; + long[] longArray = new long[len]; + + for(int i = 0; i < len; ++i) { + longArray[i] = Double.doubleToRawLongBits(array[i]); + } + + this.longs = new AtomicLongArray(longArray); + } + + public final int length() { + return this.longs.length(); + } + + public final double get(int i) { + return Double.longBitsToDouble(this.longs.get(i)); + } + + public final void set(int i, double newValue) { + long next = Double.doubleToRawLongBits(newValue); + this.longs.set(i, next); + } + + public final void lazySet(int i, double newValue) { + this.set(i, newValue); + } + + public final double getAndSet(int i, double newValue) { + long next = Double.doubleToRawLongBits(newValue); + return Double.longBitsToDouble(this.longs.getAndSet(i, next)); + } + + public final boolean compareAndSet(int i, double expect, double update) { + return this.longs.compareAndSet(i, Double.doubleToRawLongBits(expect), Double.doubleToRawLongBits(update)); + } + + public final boolean weakCompareAndSet(int i, double expect, double update) { + return this.longs.weakCompareAndSet(i, Double.doubleToRawLongBits(expect), Double.doubleToRawLongBits(update)); + } + + public final double getAndAdd(int i, double delta) { + long current; + double currentVal; + long next; + do { + current = this.longs.get(i); + currentVal = Double.longBitsToDouble(current); + double nextVal = currentVal + delta; + next = Double.doubleToRawLongBits(nextVal); + } while(!this.longs.compareAndSet(i, current, next)); + + return currentVal; + } + + public double addAndGet(int i, double delta) { + long current; + double nextVal; + long next; + do { + current = this.longs.get(i); + double currentVal = Double.longBitsToDouble(current); + nextVal = currentVal + delta; + next = Double.doubleToRawLongBits(nextVal); + } while(!this.longs.compareAndSet(i, current, next)); + + return nextVal; + } + + public String toString() { + int iMax = this.length() - 1; + if (iMax == -1) { + return "[]"; + } else { + StringBuilder b = new StringBuilder(19 * (iMax + 1)); + b.append('['); + int i = 0; + + while(true) { + b.append(Double.longBitsToDouble(this.longs.get(i))); + if (i == iMax) { + return b.append(']').toString(); + } + + b.append(',').append(' '); + ++i; + } + } + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + int length = this.length(); + s.writeInt(length); + + for(int i = 0; i < length; ++i) { + s.writeDouble(this.get(i)); + } + + } + + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); + int length = s.readInt(); + this.longs = new AtomicLongArray(length); + + for(int i = 0; i < length; ++i) { + this.set(i, s.readDouble()); + } + + } +} diff --git a/src/main/com/google/common/util/concurrent/AtomicLongMap.java b/src/main/com/google/common/util/concurrent/AtomicLongMap.java new file mode 100644 index 0000000..9ec243b --- /dev/null +++ b/src/main/com/google/common/util/concurrent/AtomicLongMap.java @@ -0,0 +1,268 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +@GwtCompatible +public final class AtomicLongMap { + private final ConcurrentHashMap map; + private transient Map asMap; + + private AtomicLongMap(ConcurrentHashMap map) { + this.map = (ConcurrentHashMap)Preconditions.checkNotNull(map); + } + + public static AtomicLongMap create() { + return new AtomicLongMap(new ConcurrentHashMap()); + } + + public static AtomicLongMap create(Map m) { + AtomicLongMap result = create(); + result.putAll(m); + return result; + } + + public long get(K key) { + AtomicLong atomic = (AtomicLong)this.map.get(key); + return atomic == null ? 0L : atomic.get(); + } + + public long incrementAndGet(K key) { + return this.addAndGet(key, 1L); + } + + public long decrementAndGet(K key) { + return this.addAndGet(key, -1L); + } + + public long addAndGet(K key, long delta) { + AtomicLong atomic; + label23: + do { + atomic = (AtomicLong)this.map.get(key); + if (atomic == null) { + atomic = (AtomicLong)this.map.putIfAbsent(key, new AtomicLong(delta)); + if (atomic == null) { + return delta; + } + } + + long oldValue; + long newValue; + do { + oldValue = atomic.get(); + if (oldValue == 0L) { + continue label23; + } + + newValue = oldValue + delta; + } while(!atomic.compareAndSet(oldValue, newValue)); + + return newValue; + } while(!this.map.replace(key, atomic, new AtomicLong(delta))); + + return delta; + } + + public long getAndIncrement(K key) { + return this.getAndAdd(key, 1L); + } + + public long getAndDecrement(K key) { + return this.getAndAdd(key, -1L); + } + + public long getAndAdd(K key, long delta) { + AtomicLong atomic; + label23: + do { + atomic = (AtomicLong)this.map.get(key); + if (atomic == null) { + atomic = (AtomicLong)this.map.putIfAbsent(key, new AtomicLong(delta)); + if (atomic == null) { + return 0L; + } + } + + long oldValue; + long newValue; + do { + oldValue = atomic.get(); + if (oldValue == 0L) { + continue label23; + } + + newValue = oldValue + delta; + } while(!atomic.compareAndSet(oldValue, newValue)); + + return oldValue; + } while(!this.map.replace(key, atomic, new AtomicLong(delta))); + + return 0L; + } + + public long put(K key, long newValue) { + AtomicLong atomic; + label23: + do { + atomic = (AtomicLong)this.map.get(key); + if (atomic == null) { + atomic = (AtomicLong)this.map.putIfAbsent(key, new AtomicLong(newValue)); + if (atomic == null) { + return 0L; + } + } + + long oldValue; + do { + oldValue = atomic.get(); + if (oldValue == 0L) { + continue label23; + } + } while(!atomic.compareAndSet(oldValue, newValue)); + + return oldValue; + } while(!this.map.replace(key, atomic, new AtomicLong(newValue))); + + return 0L; + } + + public void putAll(Map m) { + Iterator i$ = m.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + this.put(entry.getKey(), (Long)entry.getValue()); + } + + } + + public long remove(K key) { + AtomicLong atomic = (AtomicLong)this.map.get(key); + if (atomic == null) { + return 0L; + } else { + long oldValue; + do { + oldValue = atomic.get(); + } while(oldValue != 0L && !atomic.compareAndSet(oldValue, 0L)); + + this.map.remove(key, atomic); + return oldValue; + } + } + + public void removeAllZeros() { + Iterator i$ = this.map.keySet().iterator(); + + while(i$.hasNext()) { + K key = i$.next(); + AtomicLong atomic = (AtomicLong)this.map.get(key); + if (atomic != null && atomic.get() == 0L) { + this.map.remove(key, atomic); + } + } + + } + + public long sum() { + long sum = 0L; + + AtomicLong value; + for(Iterator i$ = this.map.values().iterator(); i$.hasNext(); sum += value.get()) { + value = (AtomicLong)i$.next(); + } + + return sum; + } + + public Map asMap() { + Map result = this.asMap; + return result == null ? (this.asMap = this.createAsMap()) : result; + } + + private Map createAsMap() { + return Collections.unmodifiableMap(Maps.transformValues((Map)this.map, new Function() { + public Long apply(AtomicLong atomic) { + return atomic.get(); + } + })); + } + + public boolean containsKey(Object key) { + return this.map.containsKey(key); + } + + public int size() { + return this.map.size(); + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + public void clear() { + this.map.clear(); + } + + public String toString() { + return this.map.toString(); + } + + long putIfAbsent(K key, long newValue) { + while(true) { + AtomicLong atomic = (AtomicLong)this.map.get(key); + if (atomic == null) { + atomic = (AtomicLong)this.map.putIfAbsent(key, new AtomicLong(newValue)); + if (atomic == null) { + return 0L; + } + } + + long oldValue = atomic.get(); + if (oldValue == 0L) { + if (!this.map.replace(key, atomic, new AtomicLong(newValue))) { + continue; + } + + return 0L; + } + + return oldValue; + } + } + + boolean replace(K key, long expectedOldValue, long newValue) { + if (expectedOldValue == 0L) { + return this.putIfAbsent(key, newValue) == 0L; + } else { + AtomicLong atomic = (AtomicLong)this.map.get(key); + return atomic == null ? false : atomic.compareAndSet(expectedOldValue, newValue); + } + } + + boolean remove(K key, long value) { + AtomicLong atomic = (AtomicLong)this.map.get(key); + if (atomic == null) { + return false; + } else { + long oldValue = atomic.get(); + if (oldValue != value) { + return false; + } else if (oldValue != 0L && !atomic.compareAndSet(oldValue, 0L)) { + return false; + } else { + this.map.remove(key, atomic); + return true; + } + } + } +} diff --git a/src/main/com/google/common/util/concurrent/Atomics.java b/src/main/com/google/common/util/concurrent/Atomics.java new file mode 100644 index 0000000..e085077 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Atomics.java @@ -0,0 +1,26 @@ +package com.google.common.util.concurrent; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicReferenceArray; +import javax.annotation.Nullable; + +public final class Atomics { + private Atomics() { + } + + public static AtomicReference newReference() { + return new AtomicReference(); + } + + public static AtomicReference newReference(@Nullable V initialValue) { + return new AtomicReference(initialValue); + } + + public static AtomicReferenceArray newReferenceArray(int length) { + return new AtomicReferenceArray(length); + } + + public static AtomicReferenceArray newReferenceArray(E[] array) { + return new AtomicReferenceArray(array); + } +} diff --git a/src/main/com/google/common/util/concurrent/Callables.java b/src/main/com/google/common/util/concurrent/Callables.java new file mode 100644 index 0000000..eaa1d64 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Callables.java @@ -0,0 +1,74 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import java.util.concurrent.Callable; +import javax.annotation.Nullable; + +public final class Callables { + private Callables() { + } + + public static Callable returning(@Nullable final T value) { + return new Callable() { + public T call() { + return value; + } + }; + } + + static Callable threadRenaming(final Callable callable, final Supplier nameSupplier) { + Preconditions.checkNotNull(nameSupplier); + Preconditions.checkNotNull(callable); + return new Callable() { + public T call() throws Exception { + Thread currentThread = Thread.currentThread(); + String oldName = currentThread.getName(); + boolean restoreName = Callables.trySetName((String)nameSupplier.get(), currentThread); + + Object var4; + try { + var4 = callable.call(); + } finally { + if (restoreName) { + Callables.trySetName(oldName, currentThread); + } + + } + + return var4; + } + }; + } + + static Runnable threadRenaming(final Runnable task, final Supplier nameSupplier) { + Preconditions.checkNotNull(nameSupplier); + Preconditions.checkNotNull(task); + return new Runnable() { + public void run() { + Thread currentThread = Thread.currentThread(); + String oldName = currentThread.getName(); + boolean restoreName = Callables.trySetName((String)nameSupplier.get(), currentThread); + + try { + task.run(); + } finally { + if (restoreName) { + Callables.trySetName(oldName, currentThread); + } + + } + + } + }; + } + + private static boolean trySetName(String threadName, Thread currentThread) { + try { + currentThread.setName(threadName); + return true; + } catch (SecurityException var3) { + return false; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/CheckedFuture.java b/src/main/com/google/common/util/concurrent/CheckedFuture.java new file mode 100644 index 0000000..44c50c3 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/CheckedFuture.java @@ -0,0 +1,12 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Beta +public interface CheckedFuture extends ListenableFuture { + V checkedGet() throws X; + + V checkedGet(long var1, TimeUnit var3) throws TimeoutException, X; +} diff --git a/src/main/com/google/common/util/concurrent/CycleDetectingLockFactory.java b/src/main/com/google/common/util/concurrent/CycleDetectingLockFactory.java new file mode 100644 index 0000000..91d4142 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/CycleDetectingLockFactory.java @@ -0,0 +1,602 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +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.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; + +@Beta +@ThreadSafe +public class CycleDetectingLockFactory { + private static final ConcurrentMap, Map> lockGraphNodesPerType = (new MapMaker()).weakKeys().makeMap(); + private static final Logger logger = Logger.getLogger(CycleDetectingLockFactory.class.getName()); + final CycleDetectingLockFactory.Policy policy; + private static final ThreadLocal> acquiredLocks = new ThreadLocal>() { + protected ArrayList initialValue() { + return Lists.newArrayListWithCapacity(3); + } + }; + + public static CycleDetectingLockFactory newInstance(CycleDetectingLockFactory.Policy policy) { + return new CycleDetectingLockFactory(policy); + } + + public ReentrantLock newReentrantLock(String lockName) { + return this.newReentrantLock(lockName, false); + } + + public ReentrantLock newReentrantLock(String lockName, boolean fair) { + return (ReentrantLock)(this.policy == CycleDetectingLockFactory.Policies.DISABLED ? new ReentrantLock(fair) : new CycleDetectingLockFactory.CycleDetectingReentrantLock(new CycleDetectingLockFactory.LockGraphNode(lockName), fair)); + } + + public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName) { + return this.newReentrantReadWriteLock(lockName, false); + } + + public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName, boolean fair) { + return (ReentrantReadWriteLock)(this.policy == CycleDetectingLockFactory.Policies.DISABLED ? new ReentrantReadWriteLock(fair) : new CycleDetectingLockFactory.CycleDetectingReentrantReadWriteLock(new CycleDetectingLockFactory.LockGraphNode(lockName), fair)); + } + + public static > CycleDetectingLockFactory.WithExplicitOrdering newInstanceWithExplicitOrdering(Class enumClass, CycleDetectingLockFactory.Policy policy) { + Preconditions.checkNotNull(enumClass); + Preconditions.checkNotNull(policy); + Map lockGraphNodes = getOrCreateNodes(enumClass); + return new CycleDetectingLockFactory.WithExplicitOrdering(policy, lockGraphNodes); + } + + private static Map getOrCreateNodes(Class clazz) { + Map existing = (Map)lockGraphNodesPerType.get(clazz); + if (existing != null) { + return existing; + } else { + Map created = createNodes(clazz); + existing = (Map)lockGraphNodesPerType.putIfAbsent(clazz, created); + return (Map)MoreObjects.firstNonNull(existing, created); + } + } + + @VisibleForTesting + static > Map createNodes(Class clazz) { + EnumMap map = Maps.newEnumMap(clazz); + E[] keys = (Enum[])clazz.getEnumConstants(); + int numKeys = keys.length; + ArrayList nodes = Lists.newArrayListWithCapacity(numKeys); + Enum[] arr$ = keys; + int len$ = keys.length; + + for(int i$ = 0; i$ < len$; ++i$) { + E key = arr$[i$]; + CycleDetectingLockFactory.LockGraphNode node = new CycleDetectingLockFactory.LockGraphNode(getLockName(key)); + nodes.add(node); + map.put(key, node); + } + + int i; + for(i = 1; i < numKeys; ++i) { + ((CycleDetectingLockFactory.LockGraphNode)nodes.get(i)).checkAcquiredLocks(CycleDetectingLockFactory.Policies.THROW, nodes.subList(0, i)); + } + + for(i = 0; i < numKeys - 1; ++i) { + ((CycleDetectingLockFactory.LockGraphNode)nodes.get(i)).checkAcquiredLocks(CycleDetectingLockFactory.Policies.DISABLED, nodes.subList(i + 1, numKeys)); + } + + return Collections.unmodifiableMap(map); + } + + private static String getLockName(Enum rank) { + String var1 = String.valueOf(String.valueOf(rank.getDeclaringClass().getSimpleName())); + String var2 = String.valueOf(String.valueOf(rank.name())); + return (new StringBuilder(1 + var1.length() + var2.length())).append(var1).append(".").append(var2).toString(); + } + + private CycleDetectingLockFactory(CycleDetectingLockFactory.Policy policy) { + this.policy = (CycleDetectingLockFactory.Policy)Preconditions.checkNotNull(policy); + } + + private void aboutToAcquire(CycleDetectingLockFactory.CycleDetectingLock lock) { + if (!lock.isAcquiredByCurrentThread()) { + ArrayList acquiredLockList = (ArrayList)acquiredLocks.get(); + CycleDetectingLockFactory.LockGraphNode node = lock.getLockGraphNode(); + node.checkAcquiredLocks(this.policy, acquiredLockList); + acquiredLockList.add(node); + } + + } + + private void lockStateChanged(CycleDetectingLockFactory.CycleDetectingLock lock) { + if (!lock.isAcquiredByCurrentThread()) { + ArrayList acquiredLockList = (ArrayList)acquiredLocks.get(); + CycleDetectingLockFactory.LockGraphNode node = lock.getLockGraphNode(); + + for(int i = acquiredLockList.size() - 1; i >= 0; --i) { + if (acquiredLockList.get(i) == node) { + acquiredLockList.remove(i); + break; + } + } + } + + } + + // $FF: synthetic method + CycleDetectingLockFactory(CycleDetectingLockFactory.Policy x0, Object x1) { + this(x0); + } + + private class CycleDetectingReentrantWriteLock extends WriteLock { + final CycleDetectingLockFactory.CycleDetectingReentrantReadWriteLock readWriteLock; + + CycleDetectingReentrantWriteLock(CycleDetectingLockFactory.CycleDetectingReentrantReadWriteLock readWriteLock) { + super(readWriteLock); + this.readWriteLock = readWriteLock; + } + + public void lock() { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + try { + super.lock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + } + + public void lockInterruptibly() throws InterruptedException { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + try { + super.lockInterruptibly(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + } + + public boolean tryLock() { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + boolean var1; + try { + var1 = super.tryLock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + return var1; + } + + public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + boolean var4; + try { + var4 = super.tryLock(timeout, unit); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + return var4; + } + + public void unlock() { + try { + super.unlock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + } + } + + private class CycleDetectingReentrantReadLock extends ReadLock { + final CycleDetectingLockFactory.CycleDetectingReentrantReadWriteLock readWriteLock; + + CycleDetectingReentrantReadLock(CycleDetectingLockFactory.CycleDetectingReentrantReadWriteLock readWriteLock) { + super(readWriteLock); + this.readWriteLock = readWriteLock; + } + + public void lock() { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + try { + super.lock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + } + + public void lockInterruptibly() throws InterruptedException { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + try { + super.lockInterruptibly(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + } + + public boolean tryLock() { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + boolean var1; + try { + var1 = super.tryLock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + return var1; + } + + public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { + CycleDetectingLockFactory.this.aboutToAcquire(this.readWriteLock); + + boolean var4; + try { + var4 = super.tryLock(timeout, unit); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + return var4; + } + + public void unlock() { + try { + super.unlock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this.readWriteLock); + } + + } + } + + final class CycleDetectingReentrantReadWriteLock extends ReentrantReadWriteLock implements CycleDetectingLockFactory.CycleDetectingLock { + private final CycleDetectingLockFactory.CycleDetectingReentrantReadLock readLock; + private final CycleDetectingLockFactory.CycleDetectingReentrantWriteLock writeLock; + private final CycleDetectingLockFactory.LockGraphNode lockGraphNode; + + private CycleDetectingReentrantReadWriteLock(CycleDetectingLockFactory.LockGraphNode lockGraphNode, boolean fair) { + super(fair); + this.readLock = CycleDetectingLockFactory.this.new CycleDetectingReentrantReadLock(this); + this.writeLock = CycleDetectingLockFactory.this.new CycleDetectingReentrantWriteLock(this); + this.lockGraphNode = (CycleDetectingLockFactory.LockGraphNode)Preconditions.checkNotNull(lockGraphNode); + } + + public ReadLock readLock() { + return this.readLock; + } + + public WriteLock writeLock() { + return this.writeLock; + } + + public CycleDetectingLockFactory.LockGraphNode getLockGraphNode() { + return this.lockGraphNode; + } + + public boolean isAcquiredByCurrentThread() { + return this.isWriteLockedByCurrentThread() || this.getReadHoldCount() > 0; + } + + // $FF: synthetic method + CycleDetectingReentrantReadWriteLock(CycleDetectingLockFactory.LockGraphNode x1, boolean x2, Object x3) { + this(x1, x2); + } + } + + final class CycleDetectingReentrantLock extends ReentrantLock implements CycleDetectingLockFactory.CycleDetectingLock { + private final CycleDetectingLockFactory.LockGraphNode lockGraphNode; + + private CycleDetectingReentrantLock(CycleDetectingLockFactory.LockGraphNode lockGraphNode, boolean fair) { + super(fair); + this.lockGraphNode = (CycleDetectingLockFactory.LockGraphNode)Preconditions.checkNotNull(lockGraphNode); + } + + public CycleDetectingLockFactory.LockGraphNode getLockGraphNode() { + return this.lockGraphNode; + } + + public boolean isAcquiredByCurrentThread() { + return this.isHeldByCurrentThread(); + } + + public void lock() { + CycleDetectingLockFactory.this.aboutToAcquire(this); + + try { + super.lock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this); + } + + } + + public void lockInterruptibly() throws InterruptedException { + CycleDetectingLockFactory.this.aboutToAcquire(this); + + try { + super.lockInterruptibly(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this); + } + + } + + public boolean tryLock() { + CycleDetectingLockFactory.this.aboutToAcquire(this); + + boolean var1; + try { + var1 = super.tryLock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this); + } + + return var1; + } + + public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { + CycleDetectingLockFactory.this.aboutToAcquire(this); + + boolean var4; + try { + var4 = super.tryLock(timeout, unit); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this); + } + + return var4; + } + + public void unlock() { + try { + super.unlock(); + } finally { + CycleDetectingLockFactory.this.lockStateChanged(this); + } + + } + + // $FF: synthetic method + CycleDetectingReentrantLock(CycleDetectingLockFactory.LockGraphNode x1, boolean x2, Object x3) { + this(x1, x2); + } + } + + private static class LockGraphNode { + final Map allowedPriorLocks = (new MapMaker()).weakKeys().makeMap(); + final Map disallowedPriorLocks = (new MapMaker()).weakKeys().makeMap(); + final String lockName; + + LockGraphNode(String lockName) { + this.lockName = (String)Preconditions.checkNotNull(lockName); + } + + String getLockName() { + return this.lockName; + } + + void checkAcquiredLocks(CycleDetectingLockFactory.Policy policy, List acquiredLocks) { + int i = 0; + + for(int size = acquiredLocks.size(); i < size; ++i) { + this.checkAcquiredLock(policy, (CycleDetectingLockFactory.LockGraphNode)acquiredLocks.get(i)); + } + + } + + void checkAcquiredLock(CycleDetectingLockFactory.Policy policy, CycleDetectingLockFactory.LockGraphNode acquiredLock) { + boolean var10000 = this != acquiredLock; + String var10002 = String.valueOf(acquiredLock.getLockName()); + String var10001; + if (var10002.length() != 0) { + var10001 = "Attempted to acquire multiple locks with the same rank ".concat(var10002); + } else { + String var10003 = new String; + var10001 = var10003; + var10003.("Attempted to acquire multiple locks with the same rank "); + } + + Preconditions.checkState(var10000, var10001); + if (!this.allowedPriorLocks.containsKey(acquiredLock)) { + CycleDetectingLockFactory.PotentialDeadlockException previousDeadlockException = (CycleDetectingLockFactory.PotentialDeadlockException)this.disallowedPriorLocks.get(acquiredLock); + if (previousDeadlockException != null) { + CycleDetectingLockFactory.PotentialDeadlockException exception = new CycleDetectingLockFactory.PotentialDeadlockException(acquiredLock, this, previousDeadlockException.getConflictingStackTrace()); + policy.handlePotentialDeadlock(exception); + } else { + Set seen = Sets.newIdentityHashSet(); + CycleDetectingLockFactory.ExampleStackTrace path = acquiredLock.findPathTo(this, seen); + if (path == null) { + this.allowedPriorLocks.put(acquiredLock, new CycleDetectingLockFactory.ExampleStackTrace(acquiredLock, this)); + } else { + CycleDetectingLockFactory.PotentialDeadlockException exception = new CycleDetectingLockFactory.PotentialDeadlockException(acquiredLock, this, path); + this.disallowedPriorLocks.put(acquiredLock, exception); + policy.handlePotentialDeadlock(exception); + } + + } + } + } + + @Nullable + private CycleDetectingLockFactory.ExampleStackTrace findPathTo(CycleDetectingLockFactory.LockGraphNode node, Set seen) { + if (!seen.add(this)) { + return null; + } else { + CycleDetectingLockFactory.ExampleStackTrace found = (CycleDetectingLockFactory.ExampleStackTrace)this.allowedPriorLocks.get(node); + if (found != null) { + return found; + } else { + Iterator i$ = this.allowedPriorLocks.entrySet().iterator(); + + Entry entry; + CycleDetectingLockFactory.LockGraphNode preAcquiredLock; + do { + if (!i$.hasNext()) { + return null; + } + + entry = (Entry)i$.next(); + preAcquiredLock = (CycleDetectingLockFactory.LockGraphNode)entry.getKey(); + found = preAcquiredLock.findPathTo(node, seen); + } while(found == null); + + CycleDetectingLockFactory.ExampleStackTrace path = new CycleDetectingLockFactory.ExampleStackTrace(preAcquiredLock, this); + path.setStackTrace(((CycleDetectingLockFactory.ExampleStackTrace)entry.getValue()).getStackTrace()); + path.initCause(found); + return path; + } + } + } + } + + private interface CycleDetectingLock { + CycleDetectingLockFactory.LockGraphNode getLockGraphNode(); + + boolean isAcquiredByCurrentThread(); + } + + @Beta + public static final class PotentialDeadlockException extends CycleDetectingLockFactory.ExampleStackTrace { + private final CycleDetectingLockFactory.ExampleStackTrace conflictingStackTrace; + + private PotentialDeadlockException(CycleDetectingLockFactory.LockGraphNode node1, CycleDetectingLockFactory.LockGraphNode node2, CycleDetectingLockFactory.ExampleStackTrace conflictingStackTrace) { + super(node1, node2); + this.conflictingStackTrace = conflictingStackTrace; + this.initCause(conflictingStackTrace); + } + + public CycleDetectingLockFactory.ExampleStackTrace getConflictingStackTrace() { + return this.conflictingStackTrace; + } + + public String getMessage() { + StringBuilder message = new StringBuilder(super.getMessage()); + + for(Object t = this.conflictingStackTrace; t != null; t = ((Throwable)t).getCause()) { + message.append(", ").append(((Throwable)t).getMessage()); + } + + return message.toString(); + } + + // $FF: synthetic method + PotentialDeadlockException(CycleDetectingLockFactory.LockGraphNode x0, CycleDetectingLockFactory.LockGraphNode x1, CycleDetectingLockFactory.ExampleStackTrace x2, Object x3) { + this(x0, x1, x2); + } + } + + private static class ExampleStackTrace extends IllegalStateException { + static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; + static Set EXCLUDED_CLASS_NAMES = ImmutableSet.of(CycleDetectingLockFactory.class.getName(), CycleDetectingLockFactory.ExampleStackTrace.class.getName(), CycleDetectingLockFactory.LockGraphNode.class.getName()); + + ExampleStackTrace(CycleDetectingLockFactory.LockGraphNode node1, CycleDetectingLockFactory.LockGraphNode node2) { + String var3 = String.valueOf(String.valueOf(node1.getLockName())); + String var4 = String.valueOf(String.valueOf(node2.getLockName())); + super((new StringBuilder(4 + var3.length() + var4.length())).append(var3).append(" -> ").append(var4).toString()); + StackTraceElement[] origStackTrace = this.getStackTrace(); + int i = 0; + + for(int n = origStackTrace.length; i < n; ++i) { + if (CycleDetectingLockFactory.WithExplicitOrdering.class.getName().equals(origStackTrace[i].getClassName())) { + this.setStackTrace(EMPTY_STACK_TRACE); + break; + } + + if (!EXCLUDED_CLASS_NAMES.contains(origStackTrace[i].getClassName())) { + this.setStackTrace((StackTraceElement[])Arrays.copyOfRange(origStackTrace, i, n)); + break; + } + } + + } + } + + @Beta + public static final class WithExplicitOrdering> extends CycleDetectingLockFactory { + private final Map lockGraphNodes; + + @VisibleForTesting + WithExplicitOrdering(CycleDetectingLockFactory.Policy policy, Map lockGraphNodes) { + super(policy, null); + this.lockGraphNodes = lockGraphNodes; + } + + public ReentrantLock newReentrantLock(E rank) { + return this.newReentrantLock(rank, false); + } + + public ReentrantLock newReentrantLock(E rank, boolean fair) { + return (ReentrantLock)(this.policy == CycleDetectingLockFactory.Policies.DISABLED ? new ReentrantLock(fair) : new CycleDetectingLockFactory.CycleDetectingReentrantLock((CycleDetectingLockFactory.LockGraphNode)this.lockGraphNodes.get(rank), fair)); + } + + public ReentrantReadWriteLock newReentrantReadWriteLock(E rank) { + return this.newReentrantReadWriteLock(rank, false); + } + + public ReentrantReadWriteLock newReentrantReadWriteLock(E rank, boolean fair) { + return (ReentrantReadWriteLock)(this.policy == CycleDetectingLockFactory.Policies.DISABLED ? new ReentrantReadWriteLock(fair) : new CycleDetectingLockFactory.CycleDetectingReentrantReadWriteLock((CycleDetectingLockFactory.LockGraphNode)this.lockGraphNodes.get(rank), fair)); + } + } + + @Beta + public static enum Policies implements CycleDetectingLockFactory.Policy { + THROW { + public void handlePotentialDeadlock(CycleDetectingLockFactory.PotentialDeadlockException e) { + throw e; + } + }, + WARN { + public void handlePotentialDeadlock(CycleDetectingLockFactory.PotentialDeadlockException e) { + CycleDetectingLockFactory.logger.log(Level.SEVERE, "Detected potential deadlock", e); + } + }, + DISABLED { + public void handlePotentialDeadlock(CycleDetectingLockFactory.PotentialDeadlockException e) { + } + }; + + private Policies() { + } + + // $FF: synthetic method + Policies(Object x2) { + this(); + } + } + + @Beta + @ThreadSafe + public interface Policy { + void handlePotentialDeadlock(CycleDetectingLockFactory.PotentialDeadlockException var1); + } +} diff --git a/src/main/com/google/common/util/concurrent/ExecutionError.java b/src/main/com/google/common/util/concurrent/ExecutionError.java new file mode 100644 index 0000000..6b1179a --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ExecutionError.java @@ -0,0 +1,24 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +public class ExecutionError extends Error { + private static final long serialVersionUID = 0L; + + protected ExecutionError() { + } + + protected ExecutionError(@Nullable String message) { + super(message); + } + + public ExecutionError(@Nullable String message, @Nullable Error cause) { + super(message, cause); + } + + public ExecutionError(@Nullable Error cause) { + super(cause); + } +} diff --git a/src/main/com/google/common/util/concurrent/ExecutionList.java b/src/main/com/google/common/util/concurrent/ExecutionList.java new file mode 100644 index 0000000..84f316b --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ExecutionList.java @@ -0,0 +1,84 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import javax.annotation.concurrent.GuardedBy; + +public final class ExecutionList { + @VisibleForTesting + static final Logger log = Logger.getLogger(ExecutionList.class.getName()); + @GuardedBy("this") + private ExecutionList.RunnableExecutorPair runnables; + @GuardedBy("this") + private boolean executed; + + public void add(Runnable runnable, Executor executor) { + Preconditions.checkNotNull(runnable, "Runnable was null."); + Preconditions.checkNotNull(executor, "Executor was null."); + synchronized(this) { + if (!this.executed) { + this.runnables = new ExecutionList.RunnableExecutorPair(runnable, executor, this.runnables); + return; + } + } + + executeListener(runnable, executor); + } + + public void execute() { + ExecutionList.RunnableExecutorPair list; + synchronized(this) { + if (this.executed) { + return; + } + + this.executed = true; + list = this.runnables; + this.runnables = null; + } + + ExecutionList.RunnableExecutorPair reversedList; + ExecutionList.RunnableExecutorPair tmp; + for(reversedList = null; list != null; reversedList = tmp) { + tmp = list; + list = list.next; + tmp.next = reversedList; + } + + while(reversedList != null) { + executeListener(reversedList.runnable, reversedList.executor); + reversedList = reversedList.next; + } + + } + + private static void executeListener(Runnable runnable, Executor executor) { + try { + executor.execute(runnable); + } catch (RuntimeException var5) { + Logger var10000 = log; + Level var10001 = Level.SEVERE; + String var3 = String.valueOf(String.valueOf(runnable)); + String var4 = String.valueOf(String.valueOf(executor)); + var10000.log(var10001, (new StringBuilder(57 + var3.length() + var4.length())).append("RuntimeException while executing runnable ").append(var3).append(" with executor ").append(var4).toString(), var5); + } + + } + + private static final class RunnableExecutorPair { + final Runnable runnable; + final Executor executor; + @Nullable + ExecutionList.RunnableExecutorPair next; + + RunnableExecutorPair(Runnable runnable, Executor executor, ExecutionList.RunnableExecutorPair next) { + this.runnable = runnable; + this.executor = executor; + this.next = next; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/FakeTimeLimiter.java b/src/main/com/google/common/util/concurrent/FakeTimeLimiter.java new file mode 100644 index 0000000..e676ecc --- /dev/null +++ b/src/main/com/google/common/util/concurrent/FakeTimeLimiter.java @@ -0,0 +1,21 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +@Beta +public final class FakeTimeLimiter implements TimeLimiter { + public T newProxy(T target, Class interfaceType, long timeoutDuration, TimeUnit timeoutUnit) { + Preconditions.checkNotNull(target); + Preconditions.checkNotNull(interfaceType); + Preconditions.checkNotNull(timeoutUnit); + return target; + } + + public T callWithTimeout(Callable callable, long timeoutDuration, TimeUnit timeoutUnit, boolean amInterruptible) throws Exception { + Preconditions.checkNotNull(timeoutUnit); + return callable.call(); + } +} diff --git a/src/main/com/google/common/util/concurrent/ForwardingBlockingQueue.java b/src/main/com/google/common/util/concurrent/ForwardingBlockingQueue.java new file mode 100644 index 0000000..aba59af --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ForwardingBlockingQueue.java @@ -0,0 +1,41 @@ +package com.google.common.util.concurrent; + +import com.google.common.collect.ForwardingQueue; +import java.util.Collection; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +public abstract class ForwardingBlockingQueue extends ForwardingQueue implements BlockingQueue { + protected ForwardingBlockingQueue() { + } + + protected abstract BlockingQueue delegate(); + + public int drainTo(Collection c, int maxElements) { + return this.delegate().drainTo(c, maxElements); + } + + public int drainTo(Collection c) { + return this.delegate().drainTo(c); + } + + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().offer(e, timeout, unit); + } + + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().poll(timeout, unit); + } + + public void put(E e) throws InterruptedException { + this.delegate().put(e); + } + + public int remainingCapacity() { + return this.delegate().remainingCapacity(); + } + + public E take() throws InterruptedException { + return this.delegate().take(); + } +} diff --git a/src/main/com/google/common/util/concurrent/ForwardingCheckedFuture.java b/src/main/com/google/common/util/concurrent/ForwardingCheckedFuture.java new file mode 100644 index 0000000..3e7e44a --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ForwardingCheckedFuture.java @@ -0,0 +1,32 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Beta +public abstract class ForwardingCheckedFuture extends ForwardingListenableFuture implements CheckedFuture { + public V checkedGet() throws X { + return this.delegate().checkedGet(); + } + + public V checkedGet(long timeout, TimeUnit unit) throws TimeoutException, X { + return this.delegate().checkedGet(timeout, unit); + } + + protected abstract CheckedFuture delegate(); + + @Beta + public abstract static class SimpleForwardingCheckedFuture extends ForwardingCheckedFuture { + private final CheckedFuture delegate; + + protected SimpleForwardingCheckedFuture(CheckedFuture delegate) { + this.delegate = (CheckedFuture)Preconditions.checkNotNull(delegate); + } + + protected final CheckedFuture delegate() { + return this.delegate; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/ForwardingExecutorService.java b/src/main/com/google/common/util/concurrent/ForwardingExecutorService.java new file mode 100644 index 0000000..f1d1501 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ForwardingExecutorService.java @@ -0,0 +1,70 @@ +package com.google.common.util.concurrent; + +import com.google.common.collect.ForwardingObject; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class ForwardingExecutorService extends ForwardingObject implements ExecutorService { + protected ForwardingExecutorService() { + } + + protected abstract ExecutorService delegate(); + + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().awaitTermination(timeout, unit); + } + + public List> invokeAll(Collection> tasks) throws InterruptedException { + return this.delegate().invokeAll(tasks); + } + + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate().invokeAll(tasks, timeout, unit); + } + + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return this.delegate().invokeAny(tasks); + } + + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return this.delegate().invokeAny(tasks, timeout, unit); + } + + public boolean isShutdown() { + return this.delegate().isShutdown(); + } + + public boolean isTerminated() { + return this.delegate().isTerminated(); + } + + public void shutdown() { + this.delegate().shutdown(); + } + + public List shutdownNow() { + return this.delegate().shutdownNow(); + } + + public void execute(Runnable command) { + this.delegate().execute(command); + } + + public Future submit(Callable task) { + return this.delegate().submit(task); + } + + public Future submit(Runnable task) { + return this.delegate().submit(task); + } + + public Future submit(Runnable task, T result) { + return this.delegate().submit(task, result); + } +} diff --git a/src/main/com/google/common/util/concurrent/ForwardingFuture.java b/src/main/com/google/common/util/concurrent/ForwardingFuture.java new file mode 100644 index 0000000..61b515a --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ForwardingFuture.java @@ -0,0 +1,47 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ForwardingObject; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class ForwardingFuture extends ForwardingObject implements Future { + protected ForwardingFuture() { + } + + protected abstract Future delegate(); + + public boolean cancel(boolean mayInterruptIfRunning) { + return this.delegate().cancel(mayInterruptIfRunning); + } + + public boolean isCancelled() { + return this.delegate().isCancelled(); + } + + public boolean isDone() { + return this.delegate().isDone(); + } + + public V get() throws InterruptedException, ExecutionException { + return this.delegate().get(); + } + + public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return this.delegate().get(timeout, unit); + } + + public abstract static class SimpleForwardingFuture extends ForwardingFuture { + private final Future delegate; + + protected SimpleForwardingFuture(Future delegate) { + this.delegate = (Future)Preconditions.checkNotNull(delegate); + } + + protected final Future delegate() { + return this.delegate; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/ForwardingListenableFuture.java b/src/main/com/google/common/util/concurrent/ForwardingListenableFuture.java new file mode 100644 index 0000000..ab9a87a --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ForwardingListenableFuture.java @@ -0,0 +1,27 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import java.util.concurrent.Executor; + +public abstract class ForwardingListenableFuture extends ForwardingFuture implements ListenableFuture { + protected ForwardingListenableFuture() { + } + + protected abstract ListenableFuture delegate(); + + public void addListener(Runnable listener, Executor exec) { + this.delegate().addListener(listener, exec); + } + + public abstract static class SimpleForwardingListenableFuture extends ForwardingListenableFuture { + private final ListenableFuture delegate; + + protected SimpleForwardingListenableFuture(ListenableFuture delegate) { + this.delegate = (ListenableFuture)Preconditions.checkNotNull(delegate); + } + + protected final ListenableFuture delegate() { + return this.delegate; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/ForwardingListeningExecutorService.java b/src/main/com/google/common/util/concurrent/ForwardingListeningExecutorService.java new file mode 100644 index 0000000..f693762 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ForwardingListeningExecutorService.java @@ -0,0 +1,22 @@ +package com.google.common.util.concurrent; + +import java.util.concurrent.Callable; + +public abstract class ForwardingListeningExecutorService extends ForwardingExecutorService implements ListeningExecutorService { + protected ForwardingListeningExecutorService() { + } + + protected abstract ListeningExecutorService delegate(); + + public ListenableFuture submit(Callable task) { + return this.delegate().submit(task); + } + + public ListenableFuture submit(Runnable task) { + return this.delegate().submit(task); + } + + public ListenableFuture submit(Runnable task, T result) { + return this.delegate().submit(task, result); + } +} diff --git a/src/main/com/google/common/util/concurrent/FutureCallback.java b/src/main/com/google/common/util/concurrent/FutureCallback.java new file mode 100644 index 0000000..2758753 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/FutureCallback.java @@ -0,0 +1,9 @@ +package com.google.common.util.concurrent; + +import javax.annotation.Nullable; + +public interface FutureCallback { + void onSuccess(@Nullable V var1); + + void onFailure(Throwable var1); +} diff --git a/src/main/com/google/common/util/concurrent/FutureFallback.java b/src/main/com/google/common/util/concurrent/FutureFallback.java new file mode 100644 index 0000000..0ea35d0 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/FutureFallback.java @@ -0,0 +1,8 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; + +@Beta +public interface FutureFallback { + ListenableFuture create(Throwable var1) throws Exception; +} diff --git a/src/main/com/google/common/util/concurrent/Futures.java b/src/main/com/google/common/util/concurrent/Futures.java new file mode 100644 index 0000000..2ecb268 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Futures.java @@ -0,0 +1,947 @@ +package com.google.common.util.concurrent; + +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; +import com.google.common.collect.Queues; +import com.google.common.collect.Sets; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +@Beta +public final class Futures { + private static final AsyncFunction, Object> DEREFERENCER = new AsyncFunction, Object>() { + public ListenableFuture apply(ListenableFuture input) { + return input; + } + }; + private static final Ordering> WITH_STRING_PARAM_FIRST = Ordering.natural().onResultOf(new Function, Boolean>() { + public Boolean apply(Constructor input) { + return Arrays.asList(input.getParameterTypes()).contains(String.class); + } + }).reverse(); + + private Futures() { + } + + public static CheckedFuture makeChecked(ListenableFuture future, Function mapper) { + return new Futures.MappingCheckedFuture((ListenableFuture)Preconditions.checkNotNull(future), mapper); + } + + public static ListenableFuture immediateFuture(@Nullable V value) { + return new Futures.ImmediateSuccessfulFuture(value); + } + + public static CheckedFuture immediateCheckedFuture(@Nullable V value) { + return new Futures.ImmediateSuccessfulCheckedFuture(value); + } + + public static ListenableFuture immediateFailedFuture(Throwable throwable) { + Preconditions.checkNotNull(throwable); + return new Futures.ImmediateFailedFuture(throwable); + } + + public static ListenableFuture immediateCancelledFuture() { + return new Futures.ImmediateCancelledFuture(); + } + + public static CheckedFuture immediateFailedCheckedFuture(X exception) { + Preconditions.checkNotNull(exception); + return new Futures.ImmediateFailedCheckedFuture(exception); + } + + public static ListenableFuture withFallback(ListenableFuture input, FutureFallback fallback) { + return withFallback(input, fallback, MoreExecutors.directExecutor()); + } + + public static ListenableFuture withFallback(ListenableFuture input, FutureFallback fallback, Executor executor) { + Preconditions.checkNotNull(fallback); + return new Futures.FallbackFuture(input, fallback, executor); + } + + public static ListenableFuture transform(ListenableFuture input, AsyncFunction function) { + Futures.ChainingListenableFuture output = new Futures.ChainingListenableFuture(function, input); + input.addListener(output, MoreExecutors.directExecutor()); + return output; + } + + public static ListenableFuture transform(ListenableFuture input, AsyncFunction function, Executor executor) { + Preconditions.checkNotNull(executor); + Futures.ChainingListenableFuture output = new Futures.ChainingListenableFuture(function, input); + input.addListener(rejectionPropagatingRunnable(output, output, executor), MoreExecutors.directExecutor()); + return output; + } + + private static Runnable rejectionPropagatingRunnable(final AbstractFuture outputFuture, final Runnable delegateTask, final Executor delegateExecutor) { + return new Runnable() { + public void run() { + final AtomicBoolean thrownFromDelegate = new AtomicBoolean(true); + + try { + delegateExecutor.execute(new Runnable() { + public void run() { + thrownFromDelegate.set(false); + delegateTask.run(); + } + }); + } catch (RejectedExecutionException var3) { + if (thrownFromDelegate.get()) { + outputFuture.setException(var3); + } + } + + } + }; + } + + public static ListenableFuture transform(ListenableFuture input, Function function) { + Preconditions.checkNotNull(function); + Futures.ChainingListenableFuture output = new Futures.ChainingListenableFuture(asAsyncFunction(function), input); + input.addListener(output, MoreExecutors.directExecutor()); + return output; + } + + public static ListenableFuture transform(ListenableFuture input, Function function, Executor executor) { + Preconditions.checkNotNull(function); + return transform(input, asAsyncFunction(function), executor); + } + + private static AsyncFunction asAsyncFunction(final Function function) { + return new AsyncFunction() { + public ListenableFuture apply(I input) { + O output = function.apply(input); + return Futures.immediateFuture(output); + } + }; + } + + public static Future lazyTransform(final Future input, final Function function) { + Preconditions.checkNotNull(input); + Preconditions.checkNotNull(function); + return new Future() { + public boolean cancel(boolean mayInterruptIfRunning) { + return input.cancel(mayInterruptIfRunning); + } + + public boolean isCancelled() { + return input.isCancelled(); + } + + public boolean isDone() { + return input.isDone(); + } + + public O get() throws InterruptedException, ExecutionException { + return this.applyTransformation(input.get()); + } + + public O get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return this.applyTransformation(input.get(timeout, unit)); + } + + private O applyTransformation(I inputx) throws ExecutionException { + try { + return function.apply(inputx); + } catch (Throwable var3) { + throw new ExecutionException(var3); + } + } + }; + } + + public static ListenableFuture dereference(ListenableFuture> nested) { + return transform(nested, DEREFERENCER); + } + + @Beta + public static ListenableFuture> allAsList(ListenableFuture... futures) { + return listFuture(ImmutableList.copyOf((Object[])futures), true, MoreExecutors.directExecutor()); + } + + @Beta + public static ListenableFuture> allAsList(Iterable> futures) { + return listFuture(ImmutableList.copyOf(futures), true, MoreExecutors.directExecutor()); + } + + public static ListenableFuture nonCancellationPropagating(ListenableFuture future) { + return new Futures.NonCancellationPropagatingFuture(future); + } + + @Beta + public static ListenableFuture> successfulAsList(ListenableFuture... futures) { + return listFuture(ImmutableList.copyOf((Object[])futures), false, MoreExecutors.directExecutor()); + } + + @Beta + public static ListenableFuture> successfulAsList(Iterable> futures) { + return listFuture(ImmutableList.copyOf(futures), false, MoreExecutors.directExecutor()); + } + + @Beta + public static ImmutableList> inCompletionOrder(Iterable> futures) { + final ConcurrentLinkedQueue> delegates = Queues.newConcurrentLinkedQueue(); + ImmutableList.Builder> listBuilder = ImmutableList.builder(); + SerializingExecutor executor = new SerializingExecutor(MoreExecutors.directExecutor()); + Iterator i$ = futures.iterator(); + + while(i$.hasNext()) { + final ListenableFuture future = (ListenableFuture)i$.next(); + AsyncSettableFuture delegate = AsyncSettableFuture.create(); + delegates.add(delegate); + future.addListener(new Runnable() { + public void run() { + ((AsyncSettableFuture)delegates.remove()).setFuture(future); + } + }, executor); + listBuilder.add((Object)delegate); + } + + return listBuilder.build(); + } + + public static void addCallback(ListenableFuture future, FutureCallback callback) { + addCallback(future, callback, MoreExecutors.directExecutor()); + } + + public static void addCallback(final ListenableFuture future, final FutureCallback callback, Executor executor) { + Preconditions.checkNotNull(callback); + Runnable callbackListener = new Runnable() { + public void run() { + Object value; + try { + value = Uninterruptibles.getUninterruptibly(future); + } catch (ExecutionException var3) { + callback.onFailure(var3.getCause()); + return; + } catch (RuntimeException var4) { + callback.onFailure(var4); + return; + } catch (Error var5) { + callback.onFailure(var5); + return; + } + + callback.onSuccess(value); + } + }; + future.addListener(callbackListener, executor); + } + + public static V get(Future future, Class exceptionClass) throws X { + Preconditions.checkNotNull(future); + Preconditions.checkArgument(!RuntimeException.class.isAssignableFrom(exceptionClass), "Futures.get exception type (%s) must not be a RuntimeException", exceptionClass); + + try { + return future.get(); + } catch (InterruptedException var3) { + Thread.currentThread().interrupt(); + throw newWithCause(exceptionClass, var3); + } catch (ExecutionException var4) { + wrapAndThrowExceptionOrError(var4.getCause(), exceptionClass); + throw new AssertionError(); + } + } + + public static V get(Future future, long timeout, TimeUnit unit, Class exceptionClass) throws X { + Preconditions.checkNotNull(future); + Preconditions.checkNotNull(unit); + Preconditions.checkArgument(!RuntimeException.class.isAssignableFrom(exceptionClass), "Futures.get exception type (%s) must not be a RuntimeException", exceptionClass); + + try { + return future.get(timeout, unit); + } catch (InterruptedException var6) { + Thread.currentThread().interrupt(); + throw newWithCause(exceptionClass, var6); + } catch (TimeoutException var7) { + throw newWithCause(exceptionClass, var7); + } catch (ExecutionException var8) { + wrapAndThrowExceptionOrError(var8.getCause(), exceptionClass); + throw new AssertionError(); + } + } + + private static void wrapAndThrowExceptionOrError(Throwable cause, Class exceptionClass) throws X { + if (cause instanceof Error) { + throw new ExecutionError((Error)cause); + } else if (cause instanceof RuntimeException) { + throw new UncheckedExecutionException(cause); + } else { + throw newWithCause(exceptionClass, cause); + } + } + + public static V getUnchecked(Future future) { + Preconditions.checkNotNull(future); + + try { + return Uninterruptibles.getUninterruptibly(future); + } catch (ExecutionException var2) { + wrapAndThrowUnchecked(var2.getCause()); + throw new AssertionError(); + } + } + + private static void wrapAndThrowUnchecked(Throwable cause) { + if (cause instanceof Error) { + throw new ExecutionError((Error)cause); + } else { + throw new UncheckedExecutionException(cause); + } + } + + private static X newWithCause(Class exceptionClass, Throwable cause) { + List> constructors = Arrays.asList(exceptionClass.getConstructors()); + Iterator i$ = preferringStrings(constructors).iterator(); + + Exception instance; + do { + if (!i$.hasNext()) { + String var6 = String.valueOf(String.valueOf(exceptionClass)); + throw new IllegalArgumentException((new StringBuilder(82 + var6.length())).append("No appropriate constructor for exception of type ").append(var6).append(" in response to chained exception").toString(), cause); + } + + Constructor constructor = (Constructor)i$.next(); + instance = (Exception)newFromConstructor(constructor, cause); + } while(instance == null); + + if (instance.getCause() == null) { + instance.initCause(cause); + } + + return instance; + } + + private static List> preferringStrings(List> constructors) { + return WITH_STRING_PARAM_FIRST.sortedCopy(constructors); + } + + @Nullable + private static X newFromConstructor(Constructor constructor, Throwable cause) { + Class[] paramTypes = constructor.getParameterTypes(); + Object[] params = new Object[paramTypes.length]; + + for(int i = 0; i < paramTypes.length; ++i) { + Class paramType = paramTypes[i]; + if (paramType.equals(String.class)) { + params[i] = cause.toString(); + } else { + if (!paramType.equals(Throwable.class)) { + return null; + } + + params[i] = cause; + } + } + + try { + return constructor.newInstance(params); + } catch (IllegalArgumentException var6) { + return null; + } catch (InstantiationException var7) { + return null; + } catch (IllegalAccessException var8) { + return null; + } catch (InvocationTargetException var9) { + return null; + } + } + + private static ListenableFuture> listFuture(ImmutableList> futures, boolean allMustSucceed, Executor listenerExecutor) { + return new Futures.CombinedFuture(futures, allMustSucceed, listenerExecutor, new Futures.FutureCombiner>() { + public List combine(List> values) { + List result = Lists.newArrayList(); + Iterator i$ = values.iterator(); + + while(i$.hasNext()) { + Optional element = (Optional)i$.next(); + result.add(element != null ? element.orNull() : null); + } + + return Collections.unmodifiableList(result); + } + }); + } + + private static class MappingCheckedFuture extends AbstractCheckedFuture { + final Function mapper; + + MappingCheckedFuture(ListenableFuture delegate, Function mapper) { + super(delegate); + this.mapper = (Function)Preconditions.checkNotNull(mapper); + } + + protected X mapException(Exception e) { + return (Exception)this.mapper.apply(e); + } + } + + private static class CombinedFuture extends AbstractFuture { + private static final Logger logger = Logger.getLogger(Futures.CombinedFuture.class.getName()); + ImmutableCollection> futures; + final boolean allMustSucceed; + final AtomicInteger remaining; + Futures.FutureCombiner combiner; + List> values; + final Object seenExceptionsLock = new Object(); + Set seenExceptions; + + CombinedFuture(ImmutableCollection> futures, boolean allMustSucceed, Executor listenerExecutor, Futures.FutureCombiner combiner) { + this.futures = futures; + this.allMustSucceed = allMustSucceed; + this.remaining = new AtomicInteger(futures.size()); + this.combiner = combiner; + this.values = Lists.newArrayListWithCapacity(futures.size()); + this.init(listenerExecutor); + } + + protected void init(Executor listenerExecutor) { + this.addListener(new Runnable() { + public void run() { + if (CombinedFuture.this.isCancelled()) { + Iterator i$ = CombinedFuture.this.futures.iterator(); + + while(i$.hasNext()) { + ListenableFuture future = (ListenableFuture)i$.next(); + future.cancel(CombinedFuture.this.wasInterrupted()); + } + } + + CombinedFuture.this.futures = null; + CombinedFuture.this.values = null; + CombinedFuture.this.combiner = null; + } + }, MoreExecutors.directExecutor()); + if (this.futures.isEmpty()) { + this.set(this.combiner.combine(ImmutableList.of())); + } else { + int i; + for(i = 0; i < this.futures.size(); ++i) { + this.values.add((Object)null); + } + + i = 0; + Iterator i$ = this.futures.iterator(); + + while(i$.hasNext()) { + final ListenableFuture listenable = (ListenableFuture)i$.next(); + final int index = i++; + listenable.addListener(new Runnable() { + public void run() { + CombinedFuture.this.setOneValue(index, listenable); + } + }, listenerExecutor); + } + + } + } + + private void setExceptionAndMaybeLog(Throwable throwable) { + boolean visibleFromOutputFuture = false; + boolean firstTimeSeeingThisException = true; + if (this.allMustSucceed) { + visibleFromOutputFuture = super.setException(throwable); + synchronized(this.seenExceptionsLock) { + if (this.seenExceptions == null) { + this.seenExceptions = Sets.newHashSet(); + } + + firstTimeSeeingThisException = this.seenExceptions.add(throwable); + } + } + + if (throwable instanceof Error || this.allMustSucceed && !visibleFromOutputFuture && firstTimeSeeingThisException) { + logger.log(Level.SEVERE, "input future failed.", throwable); + } + + } + + private void setOneValue(int index, Future future) { + List> localValues = this.values; + if (this.isDone() || localValues == null) { + Preconditions.checkState(this.allMustSucceed || this.isCancelled(), "Future was done before all dependencies completed"); + } + + boolean var13 = false; + + int newRemaining; + Futures.FutureCombiner localCombiner; + label303: { + label304: { + label305: { + try { + var13 = true; + Preconditions.checkState(future.isDone(), "Tried to set value from future which is not done"); + Object returnValue = Uninterruptibles.getUninterruptibly(future); + if (localValues != null) { + localValues.set(index, Optional.fromNullable(returnValue)); + var13 = false; + } else { + var13 = false; + } + break label303; + } catch (CancellationException var14) { + if (this.allMustSucceed) { + this.cancel(false); + var13 = false; + } else { + var13 = false; + } + } catch (ExecutionException var15) { + this.setExceptionAndMaybeLog(var15.getCause()); + var13 = false; + break label305; + } catch (Throwable var16) { + this.setExceptionAndMaybeLog(var16); + var13 = false; + break label304; + } finally { + if (var13) { + int newRemaining = this.remaining.decrementAndGet(); + Preconditions.checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { + Futures.FutureCombiner localCombiner = this.combiner; + if (localCombiner != null && localValues != null) { + this.set(localCombiner.combine(localValues)); + } else { + Preconditions.checkState(this.isDone()); + } + } + + } + } + + newRemaining = this.remaining.decrementAndGet(); + Preconditions.checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { + localCombiner = this.combiner; + if (localCombiner != null && localValues != null) { + this.set(localCombiner.combine(localValues)); + } else { + Preconditions.checkState(this.isDone()); + } + + return; + } + + return; + } + + newRemaining = this.remaining.decrementAndGet(); + Preconditions.checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { + localCombiner = this.combiner; + if (localCombiner != null && localValues != null) { + this.set(localCombiner.combine(localValues)); + } else { + Preconditions.checkState(this.isDone()); + } + + return; + } + + return; + } + + newRemaining = this.remaining.decrementAndGet(); + Preconditions.checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { + localCombiner = this.combiner; + if (localCombiner != null && localValues != null) { + this.set(localCombiner.combine(localValues)); + } else { + Preconditions.checkState(this.isDone()); + } + + return; + } + + return; + } + + newRemaining = this.remaining.decrementAndGet(); + Preconditions.checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { + localCombiner = this.combiner; + if (localCombiner != null && localValues != null) { + this.set(localCombiner.combine(localValues)); + } else { + Preconditions.checkState(this.isDone()); + } + } + + } + } + + private interface FutureCombiner { + C combine(List> var1); + } + + private static class NonCancellationPropagatingFuture extends AbstractFuture { + NonCancellationPropagatingFuture(final ListenableFuture delegate) { + Preconditions.checkNotNull(delegate); + Futures.addCallback(delegate, new FutureCallback() { + public void onSuccess(V result) { + NonCancellationPropagatingFuture.this.set(result); + } + + public void onFailure(Throwable t) { + if (delegate.isCancelled()) { + NonCancellationPropagatingFuture.this.cancel(false); + } else { + NonCancellationPropagatingFuture.this.setException(t); + } + + } + }, MoreExecutors.directExecutor()); + } + } + + private static final class CombinerFuture extends ListenableFutureTask { + ImmutableList> futures; + + CombinerFuture(Callable callable, ImmutableList> futures) { + super(callable); + this.futures = futures; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + ImmutableList> futures = this.futures; + if (!super.cancel(mayInterruptIfRunning)) { + return false; + } else { + Iterator i$ = futures.iterator(); + + while(i$.hasNext()) { + ListenableFuture future = (ListenableFuture)i$.next(); + future.cancel(mayInterruptIfRunning); + } + + return true; + } + } + + protected void done() { + super.done(); + this.futures = null; + } + + protected void setException(Throwable t) { + super.setException(t); + } + } + + private static final class WrappedCombiner implements Callable { + final Callable delegate; + Futures.CombinerFuture outputFuture; + + WrappedCombiner(Callable delegate) { + this.delegate = (Callable)Preconditions.checkNotNull(delegate); + } + + public T call() throws Exception { + try { + return this.delegate.call(); + } catch (ExecutionException var2) { + this.outputFuture.setException(var2.getCause()); + } catch (CancellationException var3) { + this.outputFuture.cancel(false); + } + + return null; + } + } + + private static class ChainingListenableFuture extends AbstractFuture implements Runnable { + private AsyncFunction function; + private ListenableFuture inputFuture; + private volatile ListenableFuture outputFuture; + + private ChainingListenableFuture(AsyncFunction function, ListenableFuture inputFuture) { + this.function = (AsyncFunction)Preconditions.checkNotNull(function); + this.inputFuture = (ListenableFuture)Preconditions.checkNotNull(inputFuture); + } + + public boolean cancel(boolean mayInterruptIfRunning) { + if (super.cancel(mayInterruptIfRunning)) { + this.cancel(this.inputFuture, mayInterruptIfRunning); + this.cancel(this.outputFuture, mayInterruptIfRunning); + return true; + } else { + return false; + } + } + + private void cancel(@Nullable Future future, boolean mayInterruptIfRunning) { + if (future != null) { + future.cancel(mayInterruptIfRunning); + } + + } + + public void run() { + try { + Object sourceResult; + try { + sourceResult = Uninterruptibles.getUninterruptibly(this.inputFuture); + } catch (CancellationException var9) { + this.cancel(false); + return; + } catch (ExecutionException var10) { + this.setException(var10.getCause()); + return; + } + + final ListenableFuture outputFuture = this.outputFuture = (ListenableFuture)Preconditions.checkNotNull(this.function.apply(sourceResult), "AsyncFunction may not return null."); + if (!this.isCancelled()) { + outputFuture.addListener(new Runnable() { + public void run() { + try { + ChainingListenableFuture.this.set(Uninterruptibles.getUninterruptibly(outputFuture)); + return; + } catch (CancellationException var6) { + ChainingListenableFuture.this.cancel(false); + } catch (ExecutionException var7) { + ChainingListenableFuture.this.setException(var7.getCause()); + return; + } finally { + ChainingListenableFuture.this.outputFuture = null; + } + + } + }, MoreExecutors.directExecutor()); + return; + } + + outputFuture.cancel(this.wasInterrupted()); + this.outputFuture = null; + } catch (UndeclaredThrowableException var11) { + this.setException(var11.getCause()); + return; + } catch (Throwable var12) { + this.setException(var12); + return; + } finally { + this.function = null; + this.inputFuture = null; + } + + } + + // $FF: synthetic method + ChainingListenableFuture(AsyncFunction x0, ListenableFuture x1, Object x2) { + this(x0, x1); + } + } + + private static class FallbackFuture extends AbstractFuture { + private volatile ListenableFuture running; + + FallbackFuture(ListenableFuture input, final FutureFallback fallback, Executor executor) { + this.running = input; + Futures.addCallback(this.running, new FutureCallback() { + public void onSuccess(V value) { + FallbackFuture.this.set(value); + } + + public void onFailure(Throwable t) { + if (!FallbackFuture.this.isCancelled()) { + try { + FallbackFuture.this.running = fallback.create(t); + if (FallbackFuture.this.isCancelled()) { + FallbackFuture.this.running.cancel(FallbackFuture.this.wasInterrupted()); + return; + } + + Futures.addCallback(FallbackFuture.this.running, new FutureCallback() { + public void onSuccess(V value) { + FallbackFuture.this.set(value); + } + + public void onFailure(Throwable t) { + if (FallbackFuture.this.running.isCancelled()) { + FallbackFuture.this.cancel(false); + } else { + FallbackFuture.this.setException(t); + } + + } + }, MoreExecutors.directExecutor()); + } catch (Throwable var3) { + FallbackFuture.this.setException(var3); + } + + } + } + }, executor); + } + + public boolean cancel(boolean mayInterruptIfRunning) { + if (super.cancel(mayInterruptIfRunning)) { + this.running.cancel(mayInterruptIfRunning); + return true; + } else { + return false; + } + } + } + + private static class ImmediateFailedCheckedFuture extends Futures.ImmediateFuture implements CheckedFuture { + private final X thrown; + + ImmediateFailedCheckedFuture(X thrown) { + super(null); + this.thrown = thrown; + } + + public V get() throws ExecutionException { + throw new ExecutionException(this.thrown); + } + + public V checkedGet() throws X { + throw this.thrown; + } + + public V checkedGet(long timeout, TimeUnit unit) throws X { + Preconditions.checkNotNull(unit); + throw this.thrown; + } + } + + private static class ImmediateCancelledFuture extends Futures.ImmediateFuture { + private final CancellationException thrown = new CancellationException("Immediate cancelled future."); + + ImmediateCancelledFuture() { + super(null); + } + + public boolean isCancelled() { + return true; + } + + public V get() { + throw AbstractFuture.cancellationExceptionWithCause("Task was cancelled.", this.thrown); + } + } + + private static class ImmediateFailedFuture extends Futures.ImmediateFuture { + private final Throwable thrown; + + ImmediateFailedFuture(Throwable thrown) { + super(null); + this.thrown = thrown; + } + + public V get() throws ExecutionException { + throw new ExecutionException(this.thrown); + } + } + + private static class ImmediateSuccessfulCheckedFuture extends Futures.ImmediateFuture implements CheckedFuture { + @Nullable + private final V value; + + ImmediateSuccessfulCheckedFuture(@Nullable V value) { + super(null); + this.value = value; + } + + public V get() { + return this.value; + } + + public V checkedGet() { + return this.value; + } + + public V checkedGet(long timeout, TimeUnit unit) { + Preconditions.checkNotNull(unit); + return this.value; + } + } + + private static class ImmediateSuccessfulFuture extends Futures.ImmediateFuture { + @Nullable + private final V value; + + ImmediateSuccessfulFuture(@Nullable V value) { + super(null); + this.value = value; + } + + public V get() { + return this.value; + } + } + + private abstract static class ImmediateFuture implements ListenableFuture { + private static final Logger log = Logger.getLogger(Futures.ImmediateFuture.class.getName()); + + private ImmediateFuture() { + } + + public void addListener(Runnable listener, Executor executor) { + Preconditions.checkNotNull(listener, "Runnable was null."); + Preconditions.checkNotNull(executor, "Executor was null."); + + try { + executor.execute(listener); + } catch (RuntimeException var6) { + Logger var10000 = log; + Level var10001 = Level.SEVERE; + String var4 = String.valueOf(String.valueOf(listener)); + String var5 = String.valueOf(String.valueOf(executor)); + var10000.log(var10001, (new StringBuilder(57 + var4.length() + var5.length())).append("RuntimeException while executing runnable ").append(var4).append(" with executor ").append(var5).toString(), var6); + } + + } + + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + public abstract V get() throws ExecutionException; + + public V get(long timeout, TimeUnit unit) throws ExecutionException { + Preconditions.checkNotNull(unit); + return this.get(); + } + + public boolean isCancelled() { + return false; + } + + public boolean isDone() { + return true; + } + + // $FF: synthetic method + ImmediateFuture(Object x0) { + this(); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/JdkFutureAdapters.java b/src/main/com/google/common/util/concurrent/JdkFutureAdapters.java new file mode 100644 index 0000000..89f8918 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/JdkFutureAdapters.java @@ -0,0 +1,76 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; + +@Beta +public final class JdkFutureAdapters { + public static ListenableFuture listenInPoolThread(Future future) { + return (ListenableFuture)(future instanceof ListenableFuture ? (ListenableFuture)future : new JdkFutureAdapters.ListenableFutureAdapter(future)); + } + + public static ListenableFuture listenInPoolThread(Future future, Executor executor) { + Preconditions.checkNotNull(executor); + return (ListenableFuture)(future instanceof ListenableFuture ? (ListenableFuture)future : new JdkFutureAdapters.ListenableFutureAdapter(future, executor)); + } + + private JdkFutureAdapters() { + } + + private static class ListenableFutureAdapter extends ForwardingFuture implements ListenableFuture { + private static final ThreadFactory threadFactory = (new ThreadFactoryBuilder()).setDaemon(true).setNameFormat("ListenableFutureAdapter-thread-%d").build(); + private static final Executor defaultAdapterExecutor; + private final Executor adapterExecutor; + private final ExecutionList executionList; + private final AtomicBoolean hasListeners; + private final Future delegate; + + ListenableFutureAdapter(Future delegate) { + this(delegate, defaultAdapterExecutor); + } + + ListenableFutureAdapter(Future delegate, Executor adapterExecutor) { + this.executionList = new ExecutionList(); + this.hasListeners = new AtomicBoolean(false); + this.delegate = (Future)Preconditions.checkNotNull(delegate); + this.adapterExecutor = (Executor)Preconditions.checkNotNull(adapterExecutor); + } + + protected Future delegate() { + return this.delegate; + } + + public void addListener(Runnable listener, Executor exec) { + this.executionList.add(listener, exec); + if (this.hasListeners.compareAndSet(false, true)) { + if (this.delegate.isDone()) { + this.executionList.execute(); + return; + } + + this.adapterExecutor.execute(new Runnable() { + public void run() { + try { + Uninterruptibles.getUninterruptibly(ListenableFutureAdapter.this.delegate); + } catch (Error var2) { + throw var2; + } catch (Throwable var3) { + } + + ListenableFutureAdapter.this.executionList.execute(); + } + }); + } + + } + + static { + defaultAdapterExecutor = Executors.newCachedThreadPool(threadFactory); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/ListenableFuture.java b/src/main/com/google/common/util/concurrent/ListenableFuture.java new file mode 100644 index 0000000..3d96351 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ListenableFuture.java @@ -0,0 +1,8 @@ +package com.google.common.util.concurrent; + +import java.util.concurrent.Executor; +import java.util.concurrent.Future; + +public interface ListenableFuture extends Future { + void addListener(Runnable var1, Executor var2); +} diff --git a/src/main/com/google/common/util/concurrent/ListenableFutureTask.java b/src/main/com/google/common/util/concurrent/ListenableFutureTask.java new file mode 100644 index 0000000..553c996 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ListenableFutureTask.java @@ -0,0 +1,34 @@ +package com.google.common.util.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; +import javax.annotation.Nullable; + +public class ListenableFutureTask extends FutureTask implements ListenableFuture { + private final ExecutionList executionList = new ExecutionList(); + + public static ListenableFutureTask create(Callable callable) { + return new ListenableFutureTask(callable); + } + + public static ListenableFutureTask create(Runnable runnable, @Nullable V result) { + return new ListenableFutureTask(runnable, result); + } + + ListenableFutureTask(Callable callable) { + super(callable); + } + + ListenableFutureTask(Runnable runnable, @Nullable V result) { + super(runnable, result); + } + + public void addListener(Runnable listener, Executor exec) { + this.executionList.add(listener, exec); + } + + protected void done() { + this.executionList.execute(); + } +} diff --git a/src/main/com/google/common/util/concurrent/ListenableScheduledFuture.java b/src/main/com/google/common/util/concurrent/ListenableScheduledFuture.java new file mode 100644 index 0000000..3f2d68d --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ListenableScheduledFuture.java @@ -0,0 +1,8 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import java.util.concurrent.ScheduledFuture; + +@Beta +public interface ListenableScheduledFuture extends ScheduledFuture, ListenableFuture { +} diff --git a/src/main/com/google/common/util/concurrent/ListenerCallQueue.java b/src/main/com/google/common/util/concurrent/ListenerCallQueue.java new file mode 100644 index 0000000..ed3b5be --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ListenerCallQueue.java @@ -0,0 +1,126 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Queues; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.concurrent.GuardedBy; + +final class ListenerCallQueue implements Runnable { + private static final Logger logger = Logger.getLogger(ListenerCallQueue.class.getName()); + private final L listener; + private final Executor executor; + @GuardedBy("this") + private final Queue> waitQueue = Queues.newArrayDeque(); + @GuardedBy("this") + private boolean isThreadScheduled; + + ListenerCallQueue(L listener, Executor executor) { + this.listener = Preconditions.checkNotNull(listener); + this.executor = (Executor)Preconditions.checkNotNull(executor); + } + + synchronized void add(ListenerCallQueue.Callback callback) { + this.waitQueue.add(callback); + } + + void execute() { + boolean scheduleTaskRunner = false; + synchronized(this) { + if (!this.isThreadScheduled) { + this.isThreadScheduled = true; + scheduleTaskRunner = true; + } + } + + if (scheduleTaskRunner) { + try { + this.executor.execute(this); + } catch (RuntimeException var6) { + synchronized(this) { + this.isThreadScheduled = false; + } + + Logger var10000 = logger; + Level var10001 = Level.SEVERE; + String var3 = String.valueOf(String.valueOf(this.listener)); + String var4 = String.valueOf(String.valueOf(this.executor)); + var10000.log(var10001, (new StringBuilder(42 + var3.length() + var4.length())).append("Exception while running callbacks for ").append(var3).append(" on ").append(var4).toString(), var6); + throw var6; + } + } + + } + + public void run() { + boolean stillRunning = true; + + while(true) { + boolean var15 = false; + + try { + var15 = true; + ListenerCallQueue.Callback nextToRun; + synchronized(this) { + Preconditions.checkState(this.isThreadScheduled); + nextToRun = (ListenerCallQueue.Callback)this.waitQueue.poll(); + if (nextToRun == null) { + this.isThreadScheduled = false; + stillRunning = false; + var15 = false; + break; + } + } + + try { + nextToRun.call(this.listener); + } catch (RuntimeException var18) { + Logger var10000 = logger; + Level var10001 = Level.SEVERE; + String var4 = String.valueOf(String.valueOf(this.listener)); + String var5 = String.valueOf(String.valueOf(nextToRun.methodCall)); + var10000.log(var10001, (new StringBuilder(37 + var4.length() + var5.length())).append("Exception while executing callback: ").append(var4).append(".").append(var5).toString(), var18); + } + } finally { + if (var15) { + if (stillRunning) { + synchronized(this) { + this.isThreadScheduled = false; + } + } + + } + } + } + + if (stillRunning) { + synchronized(this) { + this.isThreadScheduled = false; + } + } + + } + + abstract static class Callback { + private final String methodCall; + + Callback(String methodCall) { + this.methodCall = methodCall; + } + + abstract void call(L var1); + + void enqueueOn(Iterable> queues) { + Iterator i$ = queues.iterator(); + + while(i$.hasNext()) { + ListenerCallQueue queue = (ListenerCallQueue)i$.next(); + queue.add(this); + } + + } + } +} diff --git a/src/main/com/google/common/util/concurrent/ListeningExecutorService.java b/src/main/com/google/common/util/concurrent/ListeningExecutorService.java new file mode 100644 index 0000000..9b03bb6 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ListeningExecutorService.java @@ -0,0 +1,20 @@ +package com.google.common.util.concurrent; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public interface ListeningExecutorService extends ExecutorService { + ListenableFuture submit(Callable var1); + + ListenableFuture submit(Runnable var1); + + ListenableFuture submit(Runnable var1, T var2); + + List> invokeAll(Collection> var1) throws InterruptedException; + + List> invokeAll(Collection> var1, long var2, TimeUnit var4) throws InterruptedException; +} diff --git a/src/main/com/google/common/util/concurrent/ListeningScheduledExecutorService.java b/src/main/com/google/common/util/concurrent/ListeningScheduledExecutorService.java new file mode 100644 index 0000000..674393d --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ListeningScheduledExecutorService.java @@ -0,0 +1,17 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Beta +public interface ListeningScheduledExecutorService extends ScheduledExecutorService, ListeningExecutorService { + ListenableScheduledFuture schedule(Runnable var1, long var2, TimeUnit var4); + + ListenableScheduledFuture schedule(Callable var1, long var2, TimeUnit var4); + + ListenableScheduledFuture scheduleAtFixedRate(Runnable var1, long var2, long var4, TimeUnit var6); + + ListenableScheduledFuture scheduleWithFixedDelay(Runnable var1, long var2, long var4, TimeUnit var6); +} diff --git a/src/main/com/google/common/util/concurrent/Monitor.java b/src/main/com/google/common/util/concurrent/Monitor.java new file mode 100644 index 0000000..7a9c211 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Monitor.java @@ -0,0 +1,607 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; +import javax.annotation.concurrent.GuardedBy; + +@Beta +public final class Monitor { + private final boolean fair; + private final ReentrantLock lock; + @GuardedBy("lock") + private Monitor.Guard activeGuards; + + public Monitor() { + this(false); + } + + public Monitor(boolean fair) { + this.activeGuards = null; + this.fair = fair; + this.lock = new ReentrantLock(fair); + } + + public void enter() { + this.lock.lock(); + } + + public void enterInterruptibly() throws InterruptedException { + this.lock.lockInterruptibly(); + } + + public boolean enter(long time, TimeUnit unit) { + long timeoutNanos = unit.toNanos(time); + ReentrantLock lock = this.lock; + if (!this.fair && lock.tryLock()) { + return true; + } else { + long deadline = System.nanoTime() + timeoutNanos; + boolean interrupted = Thread.interrupted(); + + try { + while(true) { + try { + boolean var10 = lock.tryLock(timeoutNanos, TimeUnit.NANOSECONDS); + return var10; + } catch (InterruptedException var14) { + interrupted = true; + timeoutNanos = deadline - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + } + + public boolean enterInterruptibly(long time, TimeUnit unit) throws InterruptedException { + return this.lock.tryLock(time, unit); + } + + public boolean tryEnter() { + return this.lock.tryLock(); + } + + public void enterWhen(Monitor.Guard guard) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); + lock.lockInterruptibly(); + boolean satisfied = false; + + try { + if (!guard.isSatisfied()) { + this.await(guard, signalBeforeWaiting); + } + + satisfied = true; + } finally { + if (!satisfied) { + this.leave(); + } + + } + + } + } + + public void enterWhenUninterruptibly(Monitor.Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); + lock.lock(); + boolean satisfied = false; + + try { + if (!guard.isSatisfied()) { + this.awaitUninterruptibly(guard, signalBeforeWaiting); + } + + satisfied = true; + } finally { + if (!satisfied) { + this.leave(); + } + + } + + } + } + + public boolean enterWhen(Monitor.Guard guard, long time, TimeUnit unit) throws InterruptedException { + long timeoutNanos = unit.toNanos(time); + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + boolean reentrant = lock.isHeldByCurrentThread(); + if (this.fair || !lock.tryLock()) { + long deadline = System.nanoTime() + timeoutNanos; + if (!lock.tryLock(time, unit)) { + return false; + } + + timeoutNanos = deadline - System.nanoTime(); + } + + boolean satisfied = false; + boolean threw = true; + + boolean var11; + try { + satisfied = guard.isSatisfied() || this.awaitNanos(guard, timeoutNanos, reentrant); + threw = false; + var11 = satisfied; + } finally { + if (!satisfied) { + try { + if (threw && !reentrant) { + this.signalNextWaiter(); + } + } finally { + lock.unlock(); + } + } + + } + + return var11; + } + } + + public boolean enterWhenUninterruptibly(Monitor.Guard guard, long time, TimeUnit unit) { + long timeoutNanos = unit.toNanos(time); + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + long deadline = System.nanoTime() + timeoutNanos; + boolean signalBeforeWaiting = lock.isHeldByCurrentThread(); + boolean interrupted = Thread.interrupted(); + + try { + boolean locked; + InterruptedException interrupt; + if (this.fair || !lock.tryLock()) { + locked = false; + + do { + try { + locked = lock.tryLock(timeoutNanos, TimeUnit.NANOSECONDS); + if (!locked) { + boolean var28 = false; + return var28; + } + } catch (InterruptedException var24) { + interrupt = var24; + interrupted = true; + } + + timeoutNanos = deadline - System.nanoTime(); + } while(!locked); + } + + locked = false; + + try { + while(true) { + try { + interrupt = locked = guard.isSatisfied() || this.awaitNanos(guard, timeoutNanos, signalBeforeWaiting); + return (boolean)interrupt; + } catch (InterruptedException var25) { + interrupted = true; + signalBeforeWaiting = false; + timeoutNanos = deadline - System.nanoTime(); + } + } + } finally { + if (!locked) { + lock.unlock(); + } + + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + } + + public boolean enterIf(Monitor.Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + lock.lock(); + boolean satisfied = false; + + boolean var4; + try { + var4 = satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + + } + + return var4; + } + } + + public boolean enterIfInterruptibly(Monitor.Guard guard) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + boolean satisfied = false; + + boolean var4; + try { + var4 = satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + + } + + return var4; + } + } + + public boolean enterIf(Monitor.Guard guard, long time, TimeUnit unit) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else if (!this.enter(time, unit)) { + return false; + } else { + boolean satisfied = false; + + boolean var6; + try { + var6 = satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + this.lock.unlock(); + } + + } + + return var6; + } + } + + public boolean enterIfInterruptibly(Monitor.Guard guard, long time, TimeUnit unit) throws InterruptedException { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + if (!lock.tryLock(time, unit)) { + return false; + } else { + boolean satisfied = false; + + boolean var7; + try { + var7 = satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + + } + + return var7; + } + } + } + + public boolean tryEnterIf(Monitor.Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + ReentrantLock lock = this.lock; + if (!lock.tryLock()) { + return false; + } else { + boolean satisfied = false; + + boolean var4; + try { + var4 = satisfied = guard.isSatisfied(); + } finally { + if (!satisfied) { + lock.unlock(); + } + + } + + return var4; + } + } + } + + public void waitFor(Monitor.Guard guard) throws InterruptedException { + if (!(guard.monitor == this & this.lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } else { + if (!guard.isSatisfied()) { + this.await(guard, true); + } + + } + } + + public void waitForUninterruptibly(Monitor.Guard guard) { + if (!(guard.monitor == this & this.lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } else { + if (!guard.isSatisfied()) { + this.awaitUninterruptibly(guard, true); + } + + } + } + + public boolean waitFor(Monitor.Guard guard, long time, TimeUnit unit) throws InterruptedException { + long timeoutNanos = unit.toNanos(time); + if (!(guard.monitor == this & this.lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } else { + return guard.isSatisfied() || this.awaitNanos(guard, timeoutNanos, true); + } + } + + public boolean waitForUninterruptibly(Monitor.Guard guard, long time, TimeUnit unit) { + long timeoutNanos = unit.toNanos(time); + if (!(guard.monitor == this & this.lock.isHeldByCurrentThread())) { + throw new IllegalMonitorStateException(); + } else if (guard.isSatisfied()) { + return true; + } else { + boolean signalBeforeWaiting = true; + long deadline = System.nanoTime() + timeoutNanos; + boolean interrupted = Thread.interrupted(); + + try { + while(true) { + try { + boolean var11 = this.awaitNanos(guard, timeoutNanos, signalBeforeWaiting); + return var11; + } catch (InterruptedException var16) { + interrupted = true; + if (!guard.isSatisfied()) { + signalBeforeWaiting = false; + timeoutNanos = deadline - System.nanoTime(); + } else { + boolean var12 = true; + return var12; + } + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + } + + public void leave() { + ReentrantLock lock = this.lock; + + try { + if (lock.getHoldCount() == 1) { + this.signalNextWaiter(); + } + } finally { + lock.unlock(); + } + + } + + public boolean isFair() { + return this.fair; + } + + public boolean isOccupied() { + return this.lock.isLocked(); + } + + public boolean isOccupiedByCurrentThread() { + return this.lock.isHeldByCurrentThread(); + } + + public int getOccupiedDepth() { + return this.lock.getHoldCount(); + } + + public int getQueueLength() { + return this.lock.getQueueLength(); + } + + public boolean hasQueuedThreads() { + return this.lock.hasQueuedThreads(); + } + + public boolean hasQueuedThread(Thread thread) { + return this.lock.hasQueuedThread(thread); + } + + public boolean hasWaiters(Monitor.Guard guard) { + return this.getWaitQueueLength(guard) > 0; + } + + public int getWaitQueueLength(Monitor.Guard guard) { + if (guard.monitor != this) { + throw new IllegalMonitorStateException(); + } else { + this.lock.lock(); + + int var2; + try { + var2 = guard.waiterCount; + } finally { + this.lock.unlock(); + } + + return var2; + } + } + + @GuardedBy("lock") + private void signalNextWaiter() { + for(Monitor.Guard guard = this.activeGuards; guard != null; guard = guard.next) { + if (this.isSatisfied(guard)) { + guard.condition.signal(); + break; + } + } + + } + + @GuardedBy("lock") + private boolean isSatisfied(Monitor.Guard guard) { + try { + return guard.isSatisfied(); + } catch (Throwable var3) { + this.signalAllWaiters(); + throw Throwables.propagate(var3); + } + } + + @GuardedBy("lock") + private void signalAllWaiters() { + for(Monitor.Guard guard = this.activeGuards; guard != null; guard = guard.next) { + guard.condition.signalAll(); + } + + } + + @GuardedBy("lock") + private void beginWaitingFor(Monitor.Guard guard) { + int waiters = guard.waiterCount++; + if (waiters == 0) { + guard.next = this.activeGuards; + this.activeGuards = guard; + } + + } + + @GuardedBy("lock") + private void endWaitingFor(Monitor.Guard guard) { + int waiters = --guard.waiterCount; + if (waiters == 0) { + Monitor.Guard p = this.activeGuards; + + Monitor.Guard pred; + for(pred = null; p != guard; p = p.next) { + pred = p; + } + + if (pred == null) { + this.activeGuards = p.next; + } else { + pred.next = p.next; + } + + p.next = null; + } + + } + + @GuardedBy("lock") + private void await(Monitor.Guard guard, boolean signalBeforeWaiting) throws InterruptedException { + if (signalBeforeWaiting) { + this.signalNextWaiter(); + } + + this.beginWaitingFor(guard); + + try { + do { + guard.condition.await(); + } while(!guard.isSatisfied()); + } finally { + this.endWaitingFor(guard); + } + + } + + @GuardedBy("lock") + private void awaitUninterruptibly(Monitor.Guard guard, boolean signalBeforeWaiting) { + if (signalBeforeWaiting) { + this.signalNextWaiter(); + } + + this.beginWaitingFor(guard); + + try { + do { + guard.condition.awaitUninterruptibly(); + } while(!guard.isSatisfied()); + } finally { + this.endWaitingFor(guard); + } + + } + + @GuardedBy("lock") + private boolean awaitNanos(Monitor.Guard guard, long nanos, boolean signalBeforeWaiting) throws InterruptedException { + if (signalBeforeWaiting) { + this.signalNextWaiter(); + } + + this.beginWaitingFor(guard); + + boolean var5; + try { + while(nanos >= 0L) { + nanos = guard.condition.awaitNanos(nanos); + if (guard.isSatisfied()) { + var5 = true; + return var5; + } + } + + var5 = false; + } finally { + this.endWaitingFor(guard); + } + + return var5; + } + + @Beta + public abstract static class Guard { + final Monitor monitor; + final Condition condition; + @GuardedBy("monitor.lock") + int waiterCount = 0; + @GuardedBy("monitor.lock") + Monitor.Guard next; + + protected Guard(Monitor monitor) { + this.monitor = (Monitor)Preconditions.checkNotNull(monitor, "monitor"); + this.condition = monitor.lock.newCondition(); + } + + public abstract boolean isSatisfied(); + } +} diff --git a/src/main/com/google/common/util/concurrent/MoreExecutors.java b/src/main/com/google/common/util/concurrent/MoreExecutors.java new file mode 100644 index 0000000..1d95687 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/MoreExecutors.java @@ -0,0 +1,578 @@ +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.Supplier; +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.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +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; + +public final class MoreExecutors { + private MoreExecutors() { + } + + @Beta + public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + return (new MoreExecutors.Application()).getExitingExecutorService(executor, terminationTimeout, timeUnit); + } + + @Beta + public static ScheduledExecutorService getExitingScheduledExecutorService(ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + return (new MoreExecutors.Application()).getExitingScheduledExecutorService(executor, terminationTimeout, timeUnit); + } + + @Beta + public static void addDelayedShutdownHook(ExecutorService service, long terminationTimeout, TimeUnit timeUnit) { + (new MoreExecutors.Application()).addDelayedShutdownHook(service, terminationTimeout, timeUnit); + } + + @Beta + public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { + return (new MoreExecutors.Application()).getExitingExecutorService(executor); + } + + @Beta + public static ScheduledExecutorService getExitingScheduledExecutorService(ScheduledThreadPoolExecutor executor) { + return (new MoreExecutors.Application()).getExitingScheduledExecutorService(executor); + } + + private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { + executor.setThreadFactory((new ThreadFactoryBuilder()).setDaemon(true).setThreadFactory(executor.getThreadFactory()).build()); + } + + /** @deprecated */ + @Deprecated + public static ListeningExecutorService sameThreadExecutor() { + return new MoreExecutors.DirectExecutorService(); + } + + public static ListeningExecutorService newDirectExecutorService() { + return new MoreExecutors.DirectExecutorService(); + } + + public static Executor directExecutor() { + return MoreExecutors.DirectExecutor.INSTANCE; + } + + public static ListeningExecutorService listeningDecorator(ExecutorService delegate) { + return (ListeningExecutorService)(delegate instanceof ListeningExecutorService ? (ListeningExecutorService)delegate : (delegate instanceof ScheduledExecutorService ? new MoreExecutors.ScheduledListeningDecorator((ScheduledExecutorService)delegate) : new MoreExecutors.ListeningDecorator(delegate))); + } + + public static ListeningScheduledExecutorService listeningDecorator(ScheduledExecutorService delegate) { + return (ListeningScheduledExecutorService)(delegate instanceof ListeningScheduledExecutorService ? (ListeningScheduledExecutorService)delegate : new MoreExecutors.ScheduledListeningDecorator(delegate)); + } + + static T invokeAnyImpl(ListeningExecutorService executorService, Collection> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException { + Preconditions.checkNotNull(executorService); + int ntasks = tasks.size(); + Preconditions.checkArgument(ntasks > 0); + List> futures = Lists.newArrayListWithCapacity(ntasks); + LinkedBlockingQueue futureQueue = Queues.newLinkedBlockingQueue(); + boolean var23 = false; + + Object var27; + try { + var23 = true; + ExecutionException ee = null; + long lastTime = timed ? System.nanoTime() : 0L; + Iterator> it = tasks.iterator(); + futures.add(submitAndAddQueueListener(executorService, (Callable)it.next(), futureQueue)); + --ntasks; + int active = 1; + + while(true) { + Future f = (Future)futureQueue.poll(); + if (f == null) { + if (ntasks > 0) { + --ntasks; + futures.add(submitAndAddQueueListener(executorService, (Callable)it.next(), futureQueue)); + ++active; + } else { + if (active == 0) { + if (ee == null) { + ee = new ExecutionException((Throwable)null); + } + + throw ee; + } + + if (timed) { + f = (Future)futureQueue.poll(nanos, TimeUnit.NANOSECONDS); + if (f == null) { + throw new TimeoutException(); + } + + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + } else { + f = (Future)futureQueue.take(); + } + } + } + + if (f != null) { + --active; + + try { + var27 = f.get(); + var23 = false; + break; + } catch (ExecutionException var24) { + ee = var24; + } catch (RuntimeException var25) { + ee = new ExecutionException(var25); + } + } + } + } finally { + if (var23) { + Iterator i$ = futures.iterator(); + + while(i$.hasNext()) { + Future f = (Future)i$.next(); + f.cancel(true); + } + + } + } + + Iterator i$ = futures.iterator(); + + while(i$.hasNext()) { + Future f = (Future)i$.next(); + f.cancel(true); + } + + return var27; + } + + private static ListenableFuture submitAndAddQueueListener(ListeningExecutorService executorService, Callable task, final BlockingQueue> queue) { + final ListenableFuture future = executorService.submit(task); + future.addListener(new Runnable() { + public void run() { + queue.add(future); + } + }, directExecutor()); + return future; + } + + @Beta + public static ThreadFactory platformThreadFactory() { + if (!isAppEngine()) { + return Executors.defaultThreadFactory(); + } else { + try { + return (ThreadFactory)Class.forName("com.google.appengine.api.ThreadManager").getMethod("currentRequestThreadFactory").invoke((Object)null); + } catch (IllegalAccessException var1) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", var1); + } catch (ClassNotFoundException var2) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", var2); + } catch (NoSuchMethodException var3) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", var3); + } catch (InvocationTargetException var4) { + throw Throwables.propagate(var4.getCause()); + } + } + } + + private static boolean isAppEngine() { + if (System.getProperty("com.google.appengine.runtime.environment") == null) { + return false; + } else { + try { + return Class.forName("com.google.apphosting.api.ApiProxy").getMethod("getCurrentEnvironment").invoke((Object)null) != null; + } catch (ClassNotFoundException var1) { + return false; + } catch (InvocationTargetException var2) { + return false; + } catch (IllegalAccessException var3) { + return false; + } catch (NoSuchMethodException var4) { + return false; + } + } + } + + static Thread newThread(String name, Runnable runnable) { + Preconditions.checkNotNull(name); + Preconditions.checkNotNull(runnable); + Thread result = platformThreadFactory().newThread(runnable); + + try { + result.setName(name); + } catch (SecurityException var4) { + } + + return result; + } + + static Executor renamingDecorator(final Executor executor, final Supplier nameSupplier) { + Preconditions.checkNotNull(executor); + Preconditions.checkNotNull(nameSupplier); + return isAppEngine() ? executor : new Executor() { + public void execute(Runnable command) { + executor.execute(Callables.threadRenaming(command, nameSupplier)); + } + }; + } + + static ExecutorService renamingDecorator(ExecutorService service, final Supplier nameSupplier) { + Preconditions.checkNotNull(service); + Preconditions.checkNotNull(nameSupplier); + return (ExecutorService)(isAppEngine() ? service : new WrappingExecutorService(service) { + protected Callable wrapTask(Callable callable) { + return Callables.threadRenaming(callable, nameSupplier); + } + + protected Runnable wrapTask(Runnable command) { + return Callables.threadRenaming(command, nameSupplier); + } + }); + } + + static ScheduledExecutorService renamingDecorator(ScheduledExecutorService service, final Supplier nameSupplier) { + Preconditions.checkNotNull(service); + Preconditions.checkNotNull(nameSupplier); + return (ScheduledExecutorService)(isAppEngine() ? service : new WrappingScheduledExecutorService(service) { + protected Callable wrapTask(Callable callable) { + return Callables.threadRenaming(callable, nameSupplier); + } + + protected Runnable wrapTask(Runnable command) { + return Callables.threadRenaming(command, nameSupplier); + } + }); + } + + @Beta + public static boolean shutdownAndAwaitTermination(ExecutorService service, long timeout, TimeUnit unit) { + Preconditions.checkNotNull(unit); + service.shutdown(); + + try { + long halfTimeoutNanos = TimeUnit.NANOSECONDS.convert(timeout, unit) / 2L; + if (!service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS)) { + service.shutdownNow(); + service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS); + } + } catch (InterruptedException var6) { + Thread.currentThread().interrupt(); + service.shutdownNow(); + } + + return service.isTerminated(); + } + + private static class ScheduledListeningDecorator extends MoreExecutors.ListeningDecorator implements ListeningScheduledExecutorService { + final ScheduledExecutorService delegate; + + ScheduledListeningDecorator(ScheduledExecutorService delegate) { + super(delegate); + this.delegate = (ScheduledExecutorService)Preconditions.checkNotNull(delegate); + } + + public ListenableScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + ListenableFutureTask task = ListenableFutureTask.create(command, (Object)null); + ScheduledFuture scheduled = this.delegate.schedule(task, delay, unit); + return new MoreExecutors.ScheduledListeningDecorator.ListenableScheduledTask(task, scheduled); + } + + public ListenableScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { + ListenableFutureTask task = ListenableFutureTask.create(callable); + ScheduledFuture scheduled = this.delegate.schedule(task, delay, unit); + return new MoreExecutors.ScheduledListeningDecorator.ListenableScheduledTask(task, scheduled); + } + + public ListenableScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + MoreExecutors.ScheduledListeningDecorator.NeverSuccessfulListenableFutureTask task = new MoreExecutors.ScheduledListeningDecorator.NeverSuccessfulListenableFutureTask(command); + ScheduledFuture scheduled = this.delegate.scheduleAtFixedRate(task, initialDelay, period, unit); + return new MoreExecutors.ScheduledListeningDecorator.ListenableScheduledTask(task, scheduled); + } + + public ListenableScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + MoreExecutors.ScheduledListeningDecorator.NeverSuccessfulListenableFutureTask task = new MoreExecutors.ScheduledListeningDecorator.NeverSuccessfulListenableFutureTask(command); + ScheduledFuture scheduled = this.delegate.scheduleWithFixedDelay(task, initialDelay, delay, unit); + return new MoreExecutors.ScheduledListeningDecorator.ListenableScheduledTask(task, scheduled); + } + + private static final class NeverSuccessfulListenableFutureTask extends AbstractFuture implements Runnable { + private final Runnable delegate; + + public NeverSuccessfulListenableFutureTask(Runnable delegate) { + this.delegate = (Runnable)Preconditions.checkNotNull(delegate); + } + + public void run() { + try { + this.delegate.run(); + } catch (Throwable var2) { + this.setException(var2); + throw Throwables.propagate(var2); + } + } + } + + private static final class ListenableScheduledTask extends ForwardingListenableFuture.SimpleForwardingListenableFuture implements ListenableScheduledFuture { + private final ScheduledFuture scheduledDelegate; + + public ListenableScheduledTask(ListenableFuture listenableDelegate, ScheduledFuture scheduledDelegate) { + super(listenableDelegate); + this.scheduledDelegate = scheduledDelegate; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + boolean cancelled = super.cancel(mayInterruptIfRunning); + if (cancelled) { + this.scheduledDelegate.cancel(mayInterruptIfRunning); + } + + return cancelled; + } + + public long getDelay(TimeUnit unit) { + return this.scheduledDelegate.getDelay(unit); + } + + public int compareTo(Delayed other) { + return this.scheduledDelegate.compareTo(other); + } + } + } + + private static class ListeningDecorator extends AbstractListeningExecutorService { + private final ExecutorService delegate; + + ListeningDecorator(ExecutorService delegate) { + this.delegate = (ExecutorService)Preconditions.checkNotNull(delegate); + } + + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate.awaitTermination(timeout, unit); + } + + public boolean isShutdown() { + return this.delegate.isShutdown(); + } + + public boolean isTerminated() { + return this.delegate.isTerminated(); + } + + public void shutdown() { + this.delegate.shutdown(); + } + + public List shutdownNow() { + return this.delegate.shutdownNow(); + } + + public void execute(Runnable command) { + this.delegate.execute(command); + } + } + + private static enum DirectExecutor implements Executor { + INSTANCE; + + public void execute(Runnable command) { + command.run(); + } + } + + private static class DirectExecutorService extends AbstractListeningExecutorService { + private final Lock lock; + private final Condition termination; + private int runningTasks; + private boolean shutdown; + + private DirectExecutorService() { + this.lock = new ReentrantLock(); + this.termination = this.lock.newCondition(); + this.runningTasks = 0; + this.shutdown = false; + } + + public void execute(Runnable command) { + this.startTask(); + + try { + command.run(); + } finally { + this.endTask(); + } + + } + + public boolean isShutdown() { + this.lock.lock(); + + boolean var1; + try { + var1 = this.shutdown; + } finally { + this.lock.unlock(); + } + + return var1; + } + + public void shutdown() { + this.lock.lock(); + + try { + this.shutdown = true; + } finally { + this.lock.unlock(); + } + + } + + public List shutdownNow() { + this.shutdown(); + return Collections.emptyList(); + } + + public boolean isTerminated() { + this.lock.lock(); + + boolean var1; + try { + var1 = this.shutdown && this.runningTasks == 0; + } finally { + this.lock.unlock(); + } + + return var1; + } + + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); + this.lock.lock(); + + boolean var6; + try { + while(!this.isTerminated()) { + if (nanos <= 0L) { + var6 = false; + return var6; + } + + nanos = this.termination.awaitNanos(nanos); + } + + var6 = true; + } finally { + this.lock.unlock(); + } + + return var6; + } + + private void startTask() { + this.lock.lock(); + + try { + if (this.isShutdown()) { + throw new RejectedExecutionException("Executor already shutdown"); + } + + ++this.runningTasks; + } finally { + this.lock.unlock(); + } + + } + + private void endTask() { + this.lock.lock(); + + try { + --this.runningTasks; + if (this.isTerminated()) { + this.termination.signalAll(); + } + } finally { + this.lock.unlock(); + } + + } + + // $FF: synthetic method + DirectExecutorService(Object x0) { + this(); + } + } + + @VisibleForTesting + static class Application { + final ExecutorService getExitingExecutorService(ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + MoreExecutors.useDaemonThreadFactory(executor); + ExecutorService service = Executors.unconfigurableExecutorService(executor); + this.addDelayedShutdownHook(service, terminationTimeout, timeUnit); + return service; + } + + final ScheduledExecutorService getExitingScheduledExecutorService(ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { + MoreExecutors.useDaemonThreadFactory(executor); + ScheduledExecutorService service = Executors.unconfigurableScheduledExecutorService(executor); + this.addDelayedShutdownHook(service, terminationTimeout, timeUnit); + return service; + } + + final void addDelayedShutdownHook(final ExecutorService service, final long terminationTimeout, final TimeUnit timeUnit) { + Preconditions.checkNotNull(service); + Preconditions.checkNotNull(timeUnit); + String var5 = String.valueOf(String.valueOf(service)); + this.addShutdownHook(MoreExecutors.newThread((new StringBuilder(24 + var5.length())).append("DelayedShutdownHook-for-").append(var5).toString(), new Runnable() { + public void run() { + try { + service.shutdown(); + service.awaitTermination(terminationTimeout, timeUnit); + } catch (InterruptedException var2) { + } + + } + })); + } + + final ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { + return this.getExitingExecutorService(executor, 120L, TimeUnit.SECONDS); + } + + final ScheduledExecutorService getExitingScheduledExecutorService(ScheduledThreadPoolExecutor executor) { + return this.getExitingScheduledExecutorService(executor, 120L, TimeUnit.SECONDS); + } + + @VisibleForTesting + void addShutdownHook(Thread hook) { + Runtime.getRuntime().addShutdownHook(hook); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/RateLimiter.java b/src/main/com/google/common/util/concurrent/RateLimiter.java new file mode 100644 index 0000000..0230bc0 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/RateLimiter.java @@ -0,0 +1,165 @@ +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.Stopwatch; +import java.util.concurrent.TimeUnit; +import javax.annotation.concurrent.ThreadSafe; + +@ThreadSafe +@Beta +public abstract class RateLimiter { + private final RateLimiter.SleepingStopwatch stopwatch; + private volatile Object mutexDoNotUseDirectly; + + public static RateLimiter create(double permitsPerSecond) { + return create(RateLimiter.SleepingStopwatch.createFromSystemTimer(), permitsPerSecond); + } + + @VisibleForTesting + static RateLimiter create(RateLimiter.SleepingStopwatch stopwatch, double permitsPerSecond) { + RateLimiter rateLimiter = new SmoothRateLimiter.SmoothBursty(stopwatch, 1.0D); + rateLimiter.setRate(permitsPerSecond); + return rateLimiter; + } + + public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) { + Preconditions.checkArgument(warmupPeriod >= 0L, "warmupPeriod must not be negative: %s", warmupPeriod); + return create(RateLimiter.SleepingStopwatch.createFromSystemTimer(), permitsPerSecond, warmupPeriod, unit); + } + + @VisibleForTesting + static RateLimiter create(RateLimiter.SleepingStopwatch stopwatch, double permitsPerSecond, long warmupPeriod, TimeUnit unit) { + RateLimiter rateLimiter = new SmoothRateLimiter.SmoothWarmingUp(stopwatch, warmupPeriod, unit); + rateLimiter.setRate(permitsPerSecond); + return rateLimiter; + } + + private Object mutex() { + Object mutex = this.mutexDoNotUseDirectly; + if (mutex == null) { + synchronized(this) { + mutex = this.mutexDoNotUseDirectly; + if (mutex == null) { + this.mutexDoNotUseDirectly = mutex = new Object(); + } + } + } + + return mutex; + } + + RateLimiter(RateLimiter.SleepingStopwatch stopwatch) { + this.stopwatch = (RateLimiter.SleepingStopwatch)Preconditions.checkNotNull(stopwatch); + } + + public final void setRate(double permitsPerSecond) { + Preconditions.checkArgument(permitsPerSecond > 0.0D && !Double.isNaN(permitsPerSecond), "rate must be positive"); + synchronized(this.mutex()) { + this.doSetRate(permitsPerSecond, this.stopwatch.readMicros()); + } + } + + abstract void doSetRate(double var1, long var3); + + public final double getRate() { + synchronized(this.mutex()) { + return this.doGetRate(); + } + } + + abstract double doGetRate(); + + public double acquire() { + return this.acquire(1); + } + + public double acquire(int permits) { + long microsToWait = this.reserve(permits); + this.stopwatch.sleepMicrosUninterruptibly(microsToWait); + return 1.0D * (double)microsToWait / (double)TimeUnit.SECONDS.toMicros(1L); + } + + final long reserve(int permits) { + checkPermits(permits); + synchronized(this.mutex()) { + return this.reserveAndGetWaitLength(permits, this.stopwatch.readMicros()); + } + } + + public boolean tryAcquire(long timeout, TimeUnit unit) { + return this.tryAcquire(1, timeout, unit); + } + + public boolean tryAcquire(int permits) { + return this.tryAcquire(permits, 0L, TimeUnit.MICROSECONDS); + } + + public boolean tryAcquire() { + return this.tryAcquire(1, 0L, TimeUnit.MICROSECONDS); + } + + public boolean tryAcquire(int permits, long timeout, TimeUnit unit) { + long timeoutMicros = Math.max(unit.toMicros(timeout), 0L); + checkPermits(permits); + long microsToWait; + synchronized(this.mutex()) { + long nowMicros = this.stopwatch.readMicros(); + if (!this.canAcquire(nowMicros, timeoutMicros)) { + return false; + } + + microsToWait = this.reserveAndGetWaitLength(permits, nowMicros); + } + + this.stopwatch.sleepMicrosUninterruptibly(microsToWait); + return true; + } + + private boolean canAcquire(long nowMicros, long timeoutMicros) { + return this.queryEarliestAvailable(nowMicros) - timeoutMicros <= nowMicros; + } + + final long reserveAndGetWaitLength(int permits, long nowMicros) { + long momentAvailable = this.reserveEarliestAvailable(permits, nowMicros); + return Math.max(momentAvailable - nowMicros, 0L); + } + + abstract long queryEarliestAvailable(long var1); + + abstract long reserveEarliestAvailable(int var1, long var2); + + public String toString() { + return String.format("RateLimiter[stableRate=%3.1fqps]", this.getRate()); + } + + private static int checkPermits(int permits) { + Preconditions.checkArgument(permits > 0, "Requested permits (%s) must be positive", permits); + return permits; + } + + @VisibleForTesting + abstract static class SleepingStopwatch { + abstract long readMicros(); + + abstract void sleepMicrosUninterruptibly(long var1); + + static final RateLimiter.SleepingStopwatch createFromSystemTimer() { + return new RateLimiter.SleepingStopwatch() { + final Stopwatch stopwatch = Stopwatch.createStarted(); + + long readMicros() { + return this.stopwatch.elapsed(TimeUnit.MICROSECONDS); + } + + void sleepMicrosUninterruptibly(long micros) { + if (micros > 0L) { + Uninterruptibles.sleepUninterruptibly(micros, TimeUnit.MICROSECONDS); + } + + } + }; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/Runnables.java b/src/main/com/google/common/util/concurrent/Runnables.java new file mode 100644 index 0000000..7bb1ef0 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Runnables.java @@ -0,0 +1,20 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; + +@Beta +@GwtCompatible +public final class Runnables { + private static final Runnable EMPTY_RUNNABLE = new Runnable() { + public void run() { + } + }; + + public static Runnable doNothing() { + return EMPTY_RUNNABLE; + } + + private Runnables() { + } +} diff --git a/src/main/com/google/common/util/concurrent/SerializingExecutor.java b/src/main/com/google/common/util/concurrent/SerializingExecutor.java new file mode 100644 index 0000000..99b946f --- /dev/null +++ b/src/main/com/google/common/util/concurrent/SerializingExecutor.java @@ -0,0 +1,137 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.concurrent.GuardedBy; + +final class SerializingExecutor implements Executor { + private static final Logger log = Logger.getLogger(SerializingExecutor.class.getName()); + private final Executor executor; + @GuardedBy("internalLock") + private final Queue waitQueue = new ArrayDeque(); + @GuardedBy("internalLock") + private boolean isThreadScheduled = false; + private final SerializingExecutor.TaskRunner taskRunner = new SerializingExecutor.TaskRunner(); + private final Object internalLock = new Object() { + public String toString() { + String var10001 = String.valueOf(super.toString()); + String var10000; + if (var10001.length() != 0) { + var10000 = "SerializingExecutor lock: ".concat(var10001); + } else { + String var10002 = new String; + var10000 = var10002; + var10002.("SerializingExecutor lock: "); + } + + return var10000; + } + }; + + public SerializingExecutor(Executor executor) { + Preconditions.checkNotNull(executor, "'executor' must not be null."); + this.executor = executor; + } + + public void execute(Runnable r) { + Preconditions.checkNotNull(r, "'r' must not be null."); + boolean scheduleTaskRunner = false; + synchronized(this.internalLock) { + this.waitQueue.add(r); + if (!this.isThreadScheduled) { + this.isThreadScheduled = true; + scheduleTaskRunner = true; + } + } + + if (scheduleTaskRunner) { + boolean threw = true; + boolean var13 = false; + + try { + var13 = true; + this.executor.execute(this.taskRunner); + threw = false; + var13 = false; + } finally { + if (var13) { + if (threw) { + synchronized(this.internalLock) { + this.isThreadScheduled = false; + } + } + + } + } + + if (threw) { + synchronized(this.internalLock) { + this.isThreadScheduled = false; + } + } + } + + } + + private class TaskRunner implements Runnable { + private TaskRunner() { + } + + public void run() { + boolean stillRunning = true; + + while(true) { + boolean var14 = false; + + try { + var14 = true; + Preconditions.checkState(SerializingExecutor.this.isThreadScheduled); + Runnable nextToRun; + synchronized(SerializingExecutor.this.internalLock) { + nextToRun = (Runnable)SerializingExecutor.this.waitQueue.poll(); + if (nextToRun == null) { + SerializingExecutor.this.isThreadScheduled = false; + stillRunning = false; + var14 = false; + break; + } + } + + try { + nextToRun.run(); + } catch (RuntimeException var17) { + Logger var10000 = SerializingExecutor.log; + Level var10001 = Level.SEVERE; + String var4 = String.valueOf(String.valueOf(nextToRun)); + var10000.log(var10001, (new StringBuilder(35 + var4.length())).append("Exception while executing runnable ").append(var4).toString(), var17); + } + } finally { + if (var14) { + if (stillRunning) { + synchronized(SerializingExecutor.this.internalLock) { + SerializingExecutor.this.isThreadScheduled = false; + } + } + + } + } + } + + if (stillRunning) { + synchronized(SerializingExecutor.this.internalLock) { + SerializingExecutor.this.isThreadScheduled = false; + } + } + + } + + // $FF: synthetic method + TaskRunner(Object x1) { + this(); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/Service.java b/src/main/com/google/common/util/concurrent/Service.java new file mode 100644 index 0000000..8b5cd1b --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Service.java @@ -0,0 +1,91 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Beta +public interface Service { + Service startAsync(); + + boolean isRunning(); + + Service.State state(); + + Service stopAsync(); + + void awaitRunning(); + + void awaitRunning(long var1, TimeUnit var3) throws TimeoutException; + + void awaitTerminated(); + + void awaitTerminated(long var1, TimeUnit var3) throws TimeoutException; + + Throwable failureCause(); + + void addListener(Service.Listener var1, Executor var2); + + @Beta + public abstract static class Listener { + public void starting() { + } + + public void running() { + } + + public void stopping(Service.State from) { + } + + public void terminated(Service.State from) { + } + + public void failed(Service.State from, Throwable failure) { + } + } + + @Beta + public static enum State { + NEW { + boolean isTerminal() { + return false; + } + }, + STARTING { + boolean isTerminal() { + return false; + } + }, + RUNNING { + boolean isTerminal() { + return false; + } + }, + STOPPING { + boolean isTerminal() { + return false; + } + }, + TERMINATED { + boolean isTerminal() { + return true; + } + }, + FAILED { + boolean isTerminal() { + return true; + } + }; + + private State() { + } + + abstract boolean isTerminal(); + + // $FF: synthetic method + State(Object x2) { + this(); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/ServiceManager.java b/src/main/com/google/common/util/concurrent/ServiceManager.java new file mode 100644 index 0000000..a6a9562 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ServiceManager.java @@ -0,0 +1,553 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicates; +import com.google.common.base.Stopwatch; +import com.google.common.base.Supplier; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimaps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Ordering; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +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; + +@Beta +public final class ServiceManager { + private static final Logger logger = Logger.getLogger(ServiceManager.class.getName()); + private static final ListenerCallQueue.Callback HEALTHY_CALLBACK = new ListenerCallQueue.Callback("healthy()") { + void call(ServiceManager.Listener listener) { + listener.healthy(); + } + }; + private static final ListenerCallQueue.Callback STOPPED_CALLBACK = new ListenerCallQueue.Callback("stopped()") { + void call(ServiceManager.Listener listener) { + listener.stopped(); + } + }; + private final ServiceManager.ServiceManagerState state; + private final ImmutableList services; + + public ServiceManager(Iterable services) { + ImmutableList copy = ImmutableList.copyOf(services); + if (copy.isEmpty()) { + logger.log(Level.WARNING, "ServiceManager configured with no services. Is your application configured properly?", new ServiceManager.EmptyServiceManagerWarning()); + copy = ImmutableList.of(new ServiceManager.NoOpService()); + } + + this.state = new ServiceManager.ServiceManagerState(copy); + this.services = copy; + WeakReference stateReference = new WeakReference(this.state); + Iterator i$ = copy.iterator(); + + while(i$.hasNext()) { + Service service = (Service)i$.next(); + service.addListener(new ServiceManager.ServiceListener(service, stateReference), MoreExecutors.directExecutor()); + Preconditions.checkArgument(service.state() == Service.State.NEW, "Can only manage NEW services, %s", service); + } + + this.state.markReady(); + } + + public void addListener(ServiceManager.Listener listener, Executor executor) { + this.state.addListener(listener, executor); + } + + public void addListener(ServiceManager.Listener listener) { + this.state.addListener(listener, MoreExecutors.directExecutor()); + } + + public ServiceManager startAsync() { + Iterator i$ = this.services.iterator(); + + Service service; + while(i$.hasNext()) { + service = (Service)i$.next(); + Service.State state = service.state(); + Preconditions.checkState(state == Service.State.NEW, "Service %s is %s, cannot start it.", service, state); + } + + i$ = this.services.iterator(); + + while(i$.hasNext()) { + service = (Service)i$.next(); + + try { + this.state.tryStartTiming(service); + service.startAsync(); + } catch (IllegalStateException var5) { + Logger var10000 = logger; + Level var10001 = Level.WARNING; + String var4 = String.valueOf(String.valueOf(service)); + var10000.log(var10001, (new StringBuilder(24 + var4.length())).append("Unable to start Service ").append(var4).toString(), var5); + } + } + + return this; + } + + public void awaitHealthy() { + this.state.awaitHealthy(); + } + + public void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException { + this.state.awaitHealthy(timeout, unit); + } + + public ServiceManager stopAsync() { + Iterator i$ = this.services.iterator(); + + while(i$.hasNext()) { + Service service = (Service)i$.next(); + service.stopAsync(); + } + + return this; + } + + public void awaitStopped() { + this.state.awaitStopped(); + } + + public void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException { + this.state.awaitStopped(timeout, unit); + } + + public boolean isHealthy() { + Iterator i$ = this.services.iterator(); + + Service service; + do { + if (!i$.hasNext()) { + return true; + } + + service = (Service)i$.next(); + } while(service.isRunning()); + + return false; + } + + public ImmutableMultimap servicesByState() { + return this.state.servicesByState(); + } + + public ImmutableMap startupTimes() { + return this.state.startupTimes(); + } + + public String toString() { + return MoreObjects.toStringHelper(ServiceManager.class).add("services", Collections2.filter(this.services, Predicates.not(Predicates.instanceOf(ServiceManager.NoOpService.class)))).toString(); + } + + private static final class EmptyServiceManagerWarning extends Throwable { + private EmptyServiceManagerWarning() { + } + + // $FF: synthetic method + EmptyServiceManagerWarning(Object x0) { + this(); + } + } + + private static final class NoOpService extends AbstractService { + private NoOpService() { + } + + protected void doStart() { + this.notifyStarted(); + } + + protected void doStop() { + this.notifyStopped(); + } + + // $FF: synthetic method + NoOpService(Object x0) { + this(); + } + } + + private static final class ServiceListener extends Service.Listener { + final Service service; + final WeakReference state; + + ServiceListener(Service service, WeakReference state) { + this.service = service; + this.state = state; + } + + public void starting() { + ServiceManager.ServiceManagerState state = (ServiceManager.ServiceManagerState)this.state.get(); + if (state != null) { + state.transitionService(this.service, Service.State.NEW, Service.State.STARTING); + if (!(this.service instanceof ServiceManager.NoOpService)) { + ServiceManager.logger.log(Level.FINE, "Starting {0}.", this.service); + } + } + + } + + public void running() { + ServiceManager.ServiceManagerState state = (ServiceManager.ServiceManagerState)this.state.get(); + if (state != null) { + state.transitionService(this.service, Service.State.STARTING, Service.State.RUNNING); + } + + } + + public void stopping(Service.State from) { + ServiceManager.ServiceManagerState state = (ServiceManager.ServiceManagerState)this.state.get(); + if (state != null) { + state.transitionService(this.service, from, Service.State.STOPPING); + } + + } + + public void terminated(Service.State from) { + ServiceManager.ServiceManagerState state = (ServiceManager.ServiceManagerState)this.state.get(); + if (state != null) { + if (!(this.service instanceof ServiceManager.NoOpService)) { + ServiceManager.logger.log(Level.FINE, "Service {0} has terminated. Previous state was: {1}", new Object[]{this.service, from}); + } + + state.transitionService(this.service, from, Service.State.TERMINATED); + } + + } + + public void failed(Service.State from, Throwable failure) { + ServiceManager.ServiceManagerState state = (ServiceManager.ServiceManagerState)this.state.get(); + if (state != null) { + if (!(this.service instanceof ServiceManager.NoOpService)) { + Logger var10000 = ServiceManager.logger; + Level var10001 = Level.SEVERE; + String var4 = String.valueOf(String.valueOf(this.service)); + String var5 = String.valueOf(String.valueOf(from)); + var10000.log(var10001, (new StringBuilder(34 + var4.length() + var5.length())).append("Service ").append(var4).append(" has failed in the ").append(var5).append(" state.").toString(), failure); + } + + state.transitionService(this.service, from, Service.State.FAILED); + } + + } + } + + private static final class ServiceManagerState { + final Monitor monitor = new Monitor(); + @GuardedBy("monitor") + final SetMultimap servicesByState = Multimaps.newSetMultimap(new EnumMap(Service.State.class), new Supplier>() { + public Set get() { + return Sets.newLinkedHashSet(); + } + }); + @GuardedBy("monitor") + final Multiset states; + @GuardedBy("monitor") + final Map startupTimers; + @GuardedBy("monitor") + boolean ready; + @GuardedBy("monitor") + boolean transitioned; + final int numberOfServices; + final Monitor.Guard awaitHealthGuard; + final Monitor.Guard stoppedGuard; + @GuardedBy("monitor") + final List> listeners; + + ServiceManagerState(ImmutableCollection services) { + this.states = this.servicesByState.keys(); + this.startupTimers = Maps.newIdentityHashMap(); + this.awaitHealthGuard = new Monitor.Guard(this.monitor) { + public boolean isSatisfied() { + return ServiceManagerState.this.states.count(Service.State.RUNNING) == ServiceManagerState.this.numberOfServices || ServiceManagerState.this.states.contains(Service.State.STOPPING) || ServiceManagerState.this.states.contains(Service.State.TERMINATED) || ServiceManagerState.this.states.contains(Service.State.FAILED); + } + }; + this.stoppedGuard = new Monitor.Guard(this.monitor) { + public boolean isSatisfied() { + return ServiceManagerState.this.states.count(Service.State.TERMINATED) + ServiceManagerState.this.states.count(Service.State.FAILED) == ServiceManagerState.this.numberOfServices; + } + }; + this.listeners = Collections.synchronizedList(new ArrayList()); + this.numberOfServices = services.size(); + this.servicesByState.putAll(Service.State.NEW, services); + } + + void tryStartTiming(Service service) { + this.monitor.enter(); + + try { + Stopwatch stopwatch = (Stopwatch)this.startupTimers.get(service); + if (stopwatch == null) { + this.startupTimers.put(service, Stopwatch.createStarted()); + } + } finally { + this.monitor.leave(); + } + + } + + void markReady() { + this.monitor.enter(); + + try { + if (this.transitioned) { + List servicesInBadStates = Lists.newArrayList(); + Iterator i$ = this.servicesByState().values().iterator(); + + while(i$.hasNext()) { + Service service = (Service)i$.next(); + if (service.state() != Service.State.NEW) { + servicesInBadStates.add(service); + } + } + + String var7 = String.valueOf(String.valueOf("Services started transitioning asynchronously before the ServiceManager was constructed: ")); + String var8 = String.valueOf(String.valueOf(servicesInBadStates)); + throw new IllegalArgumentException((new StringBuilder(0 + var7.length() + var8.length())).append(var7).append(var8).toString()); + } + + this.ready = true; + } finally { + this.monitor.leave(); + } + + } + + void addListener(ServiceManager.Listener listener, Executor executor) { + Preconditions.checkNotNull(listener, "listener"); + Preconditions.checkNotNull(executor, "executor"); + this.monitor.enter(); + + try { + if (!this.stoppedGuard.isSatisfied()) { + this.listeners.add(new ListenerCallQueue(listener, executor)); + } + } finally { + this.monitor.leave(); + } + + } + + void awaitHealthy() { + this.monitor.enterWhenUninterruptibly(this.awaitHealthGuard); + + try { + this.checkHealthy(); + } finally { + this.monitor.leave(); + } + + } + + void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException { + this.monitor.enter(); + + try { + if (!this.monitor.waitForUninterruptibly(this.awaitHealthGuard, timeout, unit)) { + String var4 = String.valueOf(String.valueOf("Timeout waiting for the services to become healthy. The following services have not started: ")); + String var5 = String.valueOf(String.valueOf(Multimaps.filterKeys(this.servicesByState, Predicates.in(ImmutableSet.of(Service.State.NEW, Service.State.STARTING))))); + throw new TimeoutException((new StringBuilder(0 + var4.length() + var5.length())).append(var4).append(var5).toString()); + } + + this.checkHealthy(); + } finally { + this.monitor.leave(); + } + + } + + void awaitStopped() { + this.monitor.enterWhenUninterruptibly(this.stoppedGuard); + this.monitor.leave(); + } + + void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException { + this.monitor.enter(); + + try { + if (!this.monitor.waitForUninterruptibly(this.stoppedGuard, timeout, unit)) { + String var4 = String.valueOf(String.valueOf("Timeout waiting for the services to stop. The following services have not stopped: ")); + String var5 = String.valueOf(String.valueOf(Multimaps.filterKeys(this.servicesByState, Predicates.not(Predicates.in(ImmutableSet.of(Service.State.TERMINATED, Service.State.FAILED)))))); + throw new TimeoutException((new StringBuilder(0 + var4.length() + var5.length())).append(var4).append(var5).toString()); + } + } finally { + this.monitor.leave(); + } + + } + + ImmutableMultimap servicesByState() { + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + this.monitor.enter(); + + try { + Iterator i$ = this.servicesByState.entries().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + if (!(entry.getValue() instanceof ServiceManager.NoOpService)) { + builder.put(entry.getKey(), entry.getValue()); + } + } + } finally { + this.monitor.leave(); + } + + return builder.build(); + } + + ImmutableMap startupTimes() { + this.monitor.enter(); + + ArrayList loadTimes; + try { + loadTimes = Lists.newArrayListWithCapacity(this.startupTimers.size()); + Iterator i$ = this.startupTimers.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + Service service = (Service)entry.getKey(); + Stopwatch stopWatch = (Stopwatch)entry.getValue(); + if (!stopWatch.isRunning() && !(service instanceof ServiceManager.NoOpService)) { + loadTimes.add(Maps.immutableEntry(service, stopWatch.elapsed(TimeUnit.MILLISECONDS))); + } + } + } finally { + this.monitor.leave(); + } + + Collections.sort(loadTimes, Ordering.natural().onResultOf(new Function, Long>() { + public Long apply(Entry input) { + return (Long)input.getValue(); + } + })); + ImmutableMap.Builder builder = ImmutableMap.builder(); + Iterator i$ = loadTimes.iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + builder.put(entry); + } + + return builder.build(); + } + + void transitionService(Service service, Service.State from, Service.State to) { + Preconditions.checkNotNull(service); + Preconditions.checkArgument(from != to); + this.monitor.enter(); + + try { + this.transitioned = true; + if (!this.ready) { + return; + } + + Preconditions.checkState(this.servicesByState.remove(from, service), "Service %s not at the expected location in the state map %s", service, from); + Preconditions.checkState(this.servicesByState.put(to, service), "Service %s in the state map unexpectedly at %s", service, to); + Stopwatch stopwatch = (Stopwatch)this.startupTimers.get(service); + if (stopwatch == null) { + stopwatch = Stopwatch.createStarted(); + this.startupTimers.put(service, stopwatch); + } + + if (to.compareTo(Service.State.RUNNING) >= 0 && stopwatch.isRunning()) { + stopwatch.stop(); + if (!(service instanceof ServiceManager.NoOpService)) { + ServiceManager.logger.log(Level.FINE, "Started {0} in {1}.", new Object[]{service, stopwatch}); + } + } + + if (to == Service.State.FAILED) { + this.fireFailedListeners(service); + } + + if (this.states.count(Service.State.RUNNING) == this.numberOfServices) { + this.fireHealthyListeners(); + } else if (this.states.count(Service.State.TERMINATED) + this.states.count(Service.State.FAILED) == this.numberOfServices) { + this.fireStoppedListeners(); + } + } finally { + this.monitor.leave(); + this.executeListeners(); + } + + } + + @GuardedBy("monitor") + void fireStoppedListeners() { + ServiceManager.STOPPED_CALLBACK.enqueueOn(this.listeners); + } + + @GuardedBy("monitor") + void fireHealthyListeners() { + ServiceManager.HEALTHY_CALLBACK.enqueueOn(this.listeners); + } + + @GuardedBy("monitor") + void fireFailedListeners(final Service service) { + String var2 = String.valueOf(String.valueOf(service)); + (new ListenerCallQueue.Callback((new StringBuilder(18 + var2.length())).append("failed({service=").append(var2).append("})").toString()) { + void call(ServiceManager.Listener listener) { + listener.failure(service); + } + }).enqueueOn(this.listeners); + } + + void executeListeners() { + Preconditions.checkState(!this.monitor.isOccupiedByCurrentThread(), "It is incorrect to execute listeners with the monitor held."); + + for(int i = 0; i < this.listeners.size(); ++i) { + ((ListenerCallQueue)this.listeners.get(i)).execute(); + } + + } + + @GuardedBy("monitor") + void checkHealthy() { + if (this.states.count(Service.State.RUNNING) != this.numberOfServices) { + String var2 = String.valueOf(String.valueOf(Multimaps.filterKeys(this.servicesByState, Predicates.not(Predicates.equalTo(Service.State.RUNNING))))); + IllegalStateException exception = new IllegalStateException((new StringBuilder(79 + var2.length())).append("Expected to be healthy after starting. The following services are not running: ").append(var2).toString()); + throw exception; + } + } + } + + @Beta + public abstract static class Listener { + public void healthy() { + } + + public void stopped() { + } + + public void failure(Service service) { + } + } +} diff --git a/src/main/com/google/common/util/concurrent/SettableFuture.java b/src/main/com/google/common/util/concurrent/SettableFuture.java new file mode 100644 index 0000000..36cb07f --- /dev/null +++ b/src/main/com/google/common/util/concurrent/SettableFuture.java @@ -0,0 +1,20 @@ +package com.google.common.util.concurrent; + +import javax.annotation.Nullable; + +public final class SettableFuture extends AbstractFuture { + public static SettableFuture create() { + return new SettableFuture(); + } + + private SettableFuture() { + } + + public boolean set(@Nullable V value) { + return super.set(value); + } + + public boolean setException(Throwable throwable) { + return super.setException(throwable); + } +} diff --git a/src/main/com/google/common/util/concurrent/SimpleTimeLimiter.java b/src/main/com/google/common/util/concurrent/SimpleTimeLimiter.java new file mode 100644 index 0000000..1ce82cb --- /dev/null +++ b/src/main/com/google/common/util/concurrent/SimpleTimeLimiter.java @@ -0,0 +1,135 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.collect.ObjectArrays; +import com.google.common.collect.Sets; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Set; +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.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Beta +public final class SimpleTimeLimiter implements TimeLimiter { + private final ExecutorService executor; + + public SimpleTimeLimiter(ExecutorService executor) { + this.executor = (ExecutorService)Preconditions.checkNotNull(executor); + } + + public SimpleTimeLimiter() { + this(Executors.newCachedThreadPool()); + } + + public T newProxy(final T target, Class interfaceType, final long timeoutDuration, final TimeUnit timeoutUnit) { + Preconditions.checkNotNull(target); + Preconditions.checkNotNull(interfaceType); + Preconditions.checkNotNull(timeoutUnit); + Preconditions.checkArgument(timeoutDuration > 0L, "bad timeout: %s", timeoutDuration); + Preconditions.checkArgument(interfaceType.isInterface(), "interfaceType must be an interface type"); + final Set interruptibleMethods = findInterruptibleMethods(interfaceType); + InvocationHandler handler = new InvocationHandler() { + public Object invoke(Object obj, final Method method, final Object[] args) throws Throwable { + Callable callable = new Callable() { + public Object call() throws Exception { + try { + return method.invoke(target, args); + } catch (InvocationTargetException var2) { + SimpleTimeLimiter.throwCause(var2, false); + throw new AssertionError("can't get here"); + } + } + }; + return SimpleTimeLimiter.this.callWithTimeout(callable, timeoutDuration, timeoutUnit, interruptibleMethods.contains(method)); + } + }; + return newProxy(interfaceType, handler); + } + + public T callWithTimeout(Callable callable, long timeoutDuration, TimeUnit timeoutUnit, boolean amInterruptible) throws Exception { + Preconditions.checkNotNull(callable); + Preconditions.checkNotNull(timeoutUnit); + Preconditions.checkArgument(timeoutDuration > 0L, "timeout must be positive: %s", timeoutDuration); + Future future = this.executor.submit(callable); + + try { + if (amInterruptible) { + try { + return future.get(timeoutDuration, timeoutUnit); + } catch (InterruptedException var8) { + future.cancel(true); + throw var8; + } + } else { + return Uninterruptibles.getUninterruptibly(future, timeoutDuration, timeoutUnit); + } + } catch (ExecutionException var9) { + throw throwCause(var9, true); + } catch (TimeoutException var10) { + future.cancel(true); + throw new UncheckedTimeoutException(var10); + } + } + + private static Exception throwCause(Exception e, boolean combineStackTraces) throws Exception { + Throwable cause = e.getCause(); + if (cause == null) { + throw e; + } else { + if (combineStackTraces) { + StackTraceElement[] combined = (StackTraceElement[])ObjectArrays.concat(cause.getStackTrace(), e.getStackTrace(), StackTraceElement.class); + cause.setStackTrace(combined); + } + + if (cause instanceof Exception) { + throw (Exception)cause; + } else if (cause instanceof Error) { + throw (Error)cause; + } else { + throw e; + } + } + } + + private static Set findInterruptibleMethods(Class interfaceType) { + Set set = Sets.newHashSet(); + Method[] arr$ = interfaceType.getMethods(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Method m = arr$[i$]; + if (declaresInterruptedEx(m)) { + set.add(m); + } + } + + return set; + } + + private static boolean declaresInterruptedEx(Method method) { + Class[] arr$ = method.getExceptionTypes(); + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Class exType = arr$[i$]; + if (exType == InterruptedException.class) { + return true; + } + } + + return false; + } + + private static T newProxy(Class interfaceType, InvocationHandler handler) { + Object object = Proxy.newProxyInstance(interfaceType.getClassLoader(), new Class[]{interfaceType}, handler); + return interfaceType.cast(object); + } +} diff --git a/src/main/com/google/common/util/concurrent/SmoothRateLimiter.java b/src/main/com/google/common/util/concurrent/SmoothRateLimiter.java new file mode 100644 index 0000000..99aeee8 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/SmoothRateLimiter.java @@ -0,0 +1,124 @@ +package com.google.common.util.concurrent; + +import java.util.concurrent.TimeUnit; + +abstract class SmoothRateLimiter extends RateLimiter { + double storedPermits; + double maxPermits; + double stableIntervalMicros; + private long nextFreeTicketMicros; + + private SmoothRateLimiter(RateLimiter.SleepingStopwatch stopwatch) { + super(stopwatch); + this.nextFreeTicketMicros = 0L; + } + + final void doSetRate(double permitsPerSecond, long nowMicros) { + this.resync(nowMicros); + double stableIntervalMicros = (double)TimeUnit.SECONDS.toMicros(1L) / permitsPerSecond; + this.stableIntervalMicros = stableIntervalMicros; + this.doSetRate(permitsPerSecond, stableIntervalMicros); + } + + abstract void doSetRate(double var1, double var3); + + final double doGetRate() { + return (double)TimeUnit.SECONDS.toMicros(1L) / this.stableIntervalMicros; + } + + final long queryEarliestAvailable(long nowMicros) { + return this.nextFreeTicketMicros; + } + + final long reserveEarliestAvailable(int requiredPermits, long nowMicros) { + this.resync(nowMicros); + long returnValue = this.nextFreeTicketMicros; + double storedPermitsToSpend = Math.min((double)requiredPermits, this.storedPermits); + double freshPermits = (double)requiredPermits - storedPermitsToSpend; + long waitMicros = this.storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend) + (long)(freshPermits * this.stableIntervalMicros); + this.nextFreeTicketMicros += waitMicros; + this.storedPermits -= storedPermitsToSpend; + return returnValue; + } + + abstract long storedPermitsToWaitTime(double var1, double var3); + + private void resync(long nowMicros) { + if (nowMicros > this.nextFreeTicketMicros) { + this.storedPermits = Math.min(this.maxPermits, this.storedPermits + (double)(nowMicros - this.nextFreeTicketMicros) / this.stableIntervalMicros); + this.nextFreeTicketMicros = nowMicros; + } + + } + + // $FF: synthetic method + SmoothRateLimiter(RateLimiter.SleepingStopwatch x0, Object x1) { + this(x0); + } + + static final class SmoothBursty extends SmoothRateLimiter { + final double maxBurstSeconds; + + SmoothBursty(RateLimiter.SleepingStopwatch stopwatch, double maxBurstSeconds) { + super(stopwatch, null); + this.maxBurstSeconds = maxBurstSeconds; + } + + void doSetRate(double permitsPerSecond, double stableIntervalMicros) { + double oldMaxPermits = this.maxPermits; + this.maxPermits = this.maxBurstSeconds * permitsPerSecond; + if (oldMaxPermits == Double.POSITIVE_INFINITY) { + this.storedPermits = this.maxPermits; + } else { + this.storedPermits = oldMaxPermits == 0.0D ? 0.0D : this.storedPermits * this.maxPermits / oldMaxPermits; + } + + } + + long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { + return 0L; + } + } + + static final class SmoothWarmingUp extends SmoothRateLimiter { + private final long warmupPeriodMicros; + private double slope; + private double halfPermits; + + SmoothWarmingUp(RateLimiter.SleepingStopwatch stopwatch, long warmupPeriod, TimeUnit timeUnit) { + super(stopwatch, null); + this.warmupPeriodMicros = timeUnit.toMicros(warmupPeriod); + } + + void doSetRate(double permitsPerSecond, double stableIntervalMicros) { + double oldMaxPermits = this.maxPermits; + this.maxPermits = (double)this.warmupPeriodMicros / stableIntervalMicros; + this.halfPermits = this.maxPermits / 2.0D; + double coldIntervalMicros = stableIntervalMicros * 3.0D; + this.slope = (coldIntervalMicros - stableIntervalMicros) / this.halfPermits; + if (oldMaxPermits == Double.POSITIVE_INFINITY) { + this.storedPermits = 0.0D; + } else { + this.storedPermits = oldMaxPermits == 0.0D ? this.maxPermits : this.storedPermits * this.maxPermits / oldMaxPermits; + } + + } + + long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { + double availablePermitsAboveHalf = storedPermits - this.halfPermits; + long micros = 0L; + if (availablePermitsAboveHalf > 0.0D) { + double permitsAboveHalfToTake = Math.min(availablePermitsAboveHalf, permitsToTake); + micros = (long)(permitsAboveHalfToTake * (this.permitsToTime(availablePermitsAboveHalf) + this.permitsToTime(availablePermitsAboveHalf - permitsAboveHalfToTake)) / 2.0D); + permitsToTake -= permitsAboveHalfToTake; + } + + micros = (long)((double)micros + this.stableIntervalMicros * permitsToTake); + return micros; + } + + private double permitsToTime(double permits) { + return this.stableIntervalMicros + permits * this.slope; + } + } +} diff --git a/src/main/com/google/common/util/concurrent/Striped.java b/src/main/com/google/common/util/concurrent/Striped.java new file mode 100644 index 0000000..7c4248a --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Striped.java @@ -0,0 +1,300 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.MapMaker; +import com.google.common.math.IntMath; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +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.atomic.AtomicReferenceArray; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +@Beta +public abstract class Striped { + private static final int LARGE_LAZY_CUTOFF = 1024; + private static final Supplier READ_WRITE_LOCK_SUPPLIER = new Supplier() { + public ReadWriteLock get() { + return new ReentrantReadWriteLock(); + } + }; + private static final int ALL_SET = -1; + + private Striped() { + } + + public abstract L get(Object var1); + + public abstract L getAt(int var1); + + abstract int indexFor(Object var1); + + public abstract int size(); + + public Iterable bulkGet(Iterable keys) { + Object[] array = Iterables.toArray(keys, Object.class); + if (array.length == 0) { + return ImmutableList.of(); + } else { + int[] stripes = new int[array.length]; + + int previousStripe; + for(previousStripe = 0; previousStripe < array.length; ++previousStripe) { + stripes[previousStripe] = this.indexFor(array[previousStripe]); + } + + Arrays.sort(stripes); + previousStripe = stripes[0]; + array[0] = this.getAt(previousStripe); + + for(int i = 1; i < array.length; ++i) { + int currentStripe = stripes[i]; + if (currentStripe == previousStripe) { + array[i] = array[i - 1]; + } else { + array[i] = this.getAt(currentStripe); + previousStripe = currentStripe; + } + } + + List asList = Arrays.asList(array); + return Collections.unmodifiableList(asList); + } + } + + public static Striped lock(int stripes) { + return new Striped.CompactStriped(stripes, new Supplier() { + public Lock get() { + return new Striped.PaddedLock(); + } + }); + } + + public static Striped lazyWeakLock(int stripes) { + return lazy(stripes, new Supplier() { + public Lock get() { + return new ReentrantLock(false); + } + }); + } + + private static Striped lazy(int stripes, Supplier supplier) { + return (Striped)(stripes < 1024 ? new Striped.SmallLazyStriped(stripes, supplier) : new Striped.LargeLazyStriped(stripes, supplier)); + } + + public static Striped semaphore(int stripes, final int permits) { + return new Striped.CompactStriped(stripes, new Supplier() { + public Semaphore get() { + return new Striped.PaddedSemaphore(permits); + } + }); + } + + public static Striped lazyWeakSemaphore(int stripes, final int permits) { + return lazy(stripes, new Supplier() { + public Semaphore get() { + return new Semaphore(permits, false); + } + }); + } + + public static Striped readWriteLock(int stripes) { + return new Striped.CompactStriped(stripes, READ_WRITE_LOCK_SUPPLIER); + } + + public static Striped lazyWeakReadWriteLock(int stripes) { + return lazy(stripes, READ_WRITE_LOCK_SUPPLIER); + } + + private static int ceilToPowerOfTwo(int x) { + return 1 << IntMath.log2(x, RoundingMode.CEILING); + } + + private static int smear(int hashCode) { + hashCode ^= hashCode >>> 20 ^ hashCode >>> 12; + return hashCode ^ hashCode >>> 7 ^ hashCode >>> 4; + } + + // $FF: synthetic method + Striped(Object x0) { + this(); + } + + private static class PaddedSemaphore extends Semaphore { + long q1; + long q2; + long q3; + + PaddedSemaphore(int permits) { + super(permits, false); + } + } + + private static class PaddedLock extends ReentrantLock { + long q1; + long q2; + long q3; + + PaddedLock() { + super(false); + } + } + + @VisibleForTesting + static class LargeLazyStriped extends Striped.PowerOfTwoStriped { + final ConcurrentMap locks; + final Supplier supplier; + final int size; + + LargeLazyStriped(int stripes, Supplier supplier) { + super(stripes); + this.size = this.mask == -1 ? Integer.MAX_VALUE : this.mask + 1; + this.supplier = supplier; + this.locks = (new MapMaker()).weakValues().makeMap(); + } + + public L getAt(int index) { + if (this.size != Integer.MAX_VALUE) { + Preconditions.checkElementIndex(index, this.size()); + } + + L existing = this.locks.get(index); + if (existing != null) { + return existing; + } else { + L created = this.supplier.get(); + existing = this.locks.putIfAbsent(index, created); + return MoreObjects.firstNonNull(existing, created); + } + } + + public int size() { + return this.size; + } + } + + @VisibleForTesting + static class SmallLazyStriped extends Striped.PowerOfTwoStriped { + final AtomicReferenceArray> locks; + final Supplier supplier; + final int size; + final ReferenceQueue queue = new ReferenceQueue(); + + SmallLazyStriped(int stripes, Supplier supplier) { + super(stripes); + this.size = this.mask == -1 ? Integer.MAX_VALUE : this.mask + 1; + this.locks = new AtomicReferenceArray(this.size); + this.supplier = supplier; + } + + public L getAt(int index) { + if (this.size != Integer.MAX_VALUE) { + Preconditions.checkElementIndex(index, this.size()); + } + + Striped.SmallLazyStriped.ArrayReference existingRef = (Striped.SmallLazyStriped.ArrayReference)this.locks.get(index); + L existing = existingRef == null ? null : existingRef.get(); + if (existing != null) { + return existing; + } else { + L created = this.supplier.get(); + Striped.SmallLazyStriped.ArrayReference newRef = new Striped.SmallLazyStriped.ArrayReference(created, index, this.queue); + + do { + if (this.locks.compareAndSet(index, existingRef, newRef)) { + this.drainQueue(); + return created; + } + + existingRef = (Striped.SmallLazyStriped.ArrayReference)this.locks.get(index); + existing = existingRef == null ? null : existingRef.get(); + } while(existing == null); + + return existing; + } + } + + private void drainQueue() { + Reference ref; + while((ref = this.queue.poll()) != null) { + Striped.SmallLazyStriped.ArrayReference arrayRef = (Striped.SmallLazyStriped.ArrayReference)ref; + this.locks.compareAndSet(arrayRef.index, arrayRef, (Object)null); + } + + } + + public int size() { + return this.size; + } + + private static final class ArrayReference extends WeakReference { + final int index; + + ArrayReference(L referent, int index, ReferenceQueue queue) { + super(referent, queue); + this.index = index; + } + } + } + + private static class CompactStriped extends Striped.PowerOfTwoStriped { + private final Object[] array; + + private CompactStriped(int stripes, Supplier supplier) { + super(stripes); + Preconditions.checkArgument(stripes <= 1073741824, "Stripes must be <= 2^30)"); + this.array = new Object[this.mask + 1]; + + for(int i = 0; i < this.array.length; ++i) { + this.array[i] = supplier.get(); + } + + } + + public L getAt(int index) { + return this.array[index]; + } + + public int size() { + return this.array.length; + } + + // $FF: synthetic method + CompactStriped(int x0, Supplier x1, Object x2) { + this(x0, x1); + } + } + + private abstract static class PowerOfTwoStriped extends Striped { + final int mask; + + PowerOfTwoStriped(int stripes) { + super(null); + Preconditions.checkArgument(stripes > 0, "Stripes must be positive"); + this.mask = stripes > 1073741824 ? -1 : Striped.ceilToPowerOfTwo(stripes) - 1; + } + + final int indexFor(Object key) { + int hash = Striped.smear(key.hashCode()); + return hash & this.mask; + } + + public final L get(Object key) { + return this.getAt(this.indexFor(key)); + } + } +} diff --git a/src/main/com/google/common/util/concurrent/ThreadFactoryBuilder.java b/src/main/com/google/common/util/concurrent/ThreadFactoryBuilder.java new file mode 100644 index 0000000..99c0d24 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/ThreadFactoryBuilder.java @@ -0,0 +1,78 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +public final class ThreadFactoryBuilder { + private String nameFormat = null; + private Boolean daemon = null; + private Integer priority = null; + private UncaughtExceptionHandler uncaughtExceptionHandler = null; + private ThreadFactory backingThreadFactory = null; + + public ThreadFactoryBuilder setNameFormat(String nameFormat) { + String.format(nameFormat, 0); + this.nameFormat = nameFormat; + return this; + } + + public ThreadFactoryBuilder setDaemon(boolean daemon) { + this.daemon = daemon; + return this; + } + + public ThreadFactoryBuilder setPriority(int priority) { + Preconditions.checkArgument(priority >= 1, "Thread priority (%s) must be >= %s", priority, 1); + Preconditions.checkArgument(priority <= 10, "Thread priority (%s) must be <= %s", priority, 10); + this.priority = priority; + return this; + } + + public ThreadFactoryBuilder setUncaughtExceptionHandler(UncaughtExceptionHandler uncaughtExceptionHandler) { + this.uncaughtExceptionHandler = (UncaughtExceptionHandler)Preconditions.checkNotNull(uncaughtExceptionHandler); + return this; + } + + public ThreadFactoryBuilder setThreadFactory(ThreadFactory backingThreadFactory) { + this.backingThreadFactory = (ThreadFactory)Preconditions.checkNotNull(backingThreadFactory); + return this; + } + + public ThreadFactory build() { + return build(this); + } + + private static ThreadFactory build(ThreadFactoryBuilder builder) { + final String nameFormat = builder.nameFormat; + final Boolean daemon = builder.daemon; + final Integer priority = builder.priority; + final UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler; + final ThreadFactory backingThreadFactory = builder.backingThreadFactory != null ? builder.backingThreadFactory : Executors.defaultThreadFactory(); + final AtomicLong count = nameFormat != null ? new AtomicLong(0L) : null; + return new ThreadFactory() { + public Thread newThread(Runnable runnable) { + Thread thread = backingThreadFactory.newThread(runnable); + if (nameFormat != null) { + thread.setName(String.format(nameFormat, count.getAndIncrement())); + } + + if (daemon != null) { + thread.setDaemon(daemon); + } + + if (priority != null) { + thread.setPriority(priority); + } + + if (uncaughtExceptionHandler != null) { + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); + } + + return thread; + } + }; + } +} diff --git a/src/main/com/google/common/util/concurrent/TimeLimiter.java b/src/main/com/google/common/util/concurrent/TimeLimiter.java new file mode 100644 index 0000000..a9c003b --- /dev/null +++ b/src/main/com/google/common/util/concurrent/TimeLimiter.java @@ -0,0 +1,12 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +@Beta +public interface TimeLimiter { + T newProxy(T var1, Class var2, long var3, TimeUnit var5); + + T callWithTimeout(Callable var1, long var2, TimeUnit var4, boolean var5) throws Exception; +} diff --git a/src/main/com/google/common/util/concurrent/UncaughtExceptionHandlers.java b/src/main/com/google/common/util/concurrent/UncaughtExceptionHandlers.java new file mode 100644 index 0000000..06c7889 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/UncaughtExceptionHandlers.java @@ -0,0 +1,37 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.VisibleForTesting; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.logging.Level; +import java.util.logging.Logger; + +public final class UncaughtExceptionHandlers { + private UncaughtExceptionHandlers() { + } + + public static UncaughtExceptionHandler systemExit() { + return new UncaughtExceptionHandlers.Exiter(Runtime.getRuntime()); + } + + @VisibleForTesting + static final class Exiter implements UncaughtExceptionHandler { + private static final Logger logger = Logger.getLogger(UncaughtExceptionHandlers.Exiter.class.getName()); + private final Runtime runtime; + + Exiter(Runtime runtime) { + this.runtime = runtime; + } + + public void uncaughtException(Thread t, Throwable e) { + try { + logger.log(Level.SEVERE, String.format("Caught an exception in %s. Shutting down.", t), e); + } catch (Throwable var7) { + System.err.println(e.getMessage()); + System.err.println(var7.getMessage()); + } finally { + this.runtime.exit(1); + } + + } + } +} diff --git a/src/main/com/google/common/util/concurrent/UncheckedExecutionException.java b/src/main/com/google/common/util/concurrent/UncheckedExecutionException.java new file mode 100644 index 0000000..2114ed2 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/UncheckedExecutionException.java @@ -0,0 +1,24 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.GwtCompatible; +import javax.annotation.Nullable; + +@GwtCompatible +public class UncheckedExecutionException extends RuntimeException { + private static final long serialVersionUID = 0L; + + protected UncheckedExecutionException() { + } + + protected UncheckedExecutionException(@Nullable String message) { + super(message); + } + + public UncheckedExecutionException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } + + public UncheckedExecutionException(@Nullable Throwable cause) { + super(cause); + } +} diff --git a/src/main/com/google/common/util/concurrent/UncheckedTimeoutException.java b/src/main/com/google/common/util/concurrent/UncheckedTimeoutException.java new file mode 100644 index 0000000..c27ea9f --- /dev/null +++ b/src/main/com/google/common/util/concurrent/UncheckedTimeoutException.java @@ -0,0 +1,22 @@ +package com.google.common.util.concurrent; + +import javax.annotation.Nullable; + +public class UncheckedTimeoutException extends RuntimeException { + private static final long serialVersionUID = 0L; + + public UncheckedTimeoutException() { + } + + public UncheckedTimeoutException(@Nullable String message) { + super(message); + } + + public UncheckedTimeoutException(@Nullable Throwable cause) { + super(cause); + } + + public UncheckedTimeoutException(@Nullable String message, @Nullable Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/com/google/common/util/concurrent/Uninterruptibles.java b/src/main/com/google/common/util/concurrent/Uninterruptibles.java new file mode 100644 index 0000000..9f1bbe3 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/Uninterruptibles.java @@ -0,0 +1,242 @@ +package com.google.common.util.concurrent; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Beta +public final class Uninterruptibles { + public static void awaitUninterruptibly(CountDownLatch latch) { + boolean interrupted = false; + + try { + while(true) { + try { + latch.await(); + return; + } catch (InterruptedException var6) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) { + boolean interrupted = false; + + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while(true) { + try { + boolean var9 = latch.await(remainingNanos, TimeUnit.NANOSECONDS); + return var9; + } catch (InterruptedException var13) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static void joinUninterruptibly(Thread toJoin) { + boolean interrupted = false; + + try { + while(true) { + try { + toJoin.join(); + return; + } catch (InterruptedException var6) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static V getUninterruptibly(Future future) throws ExecutionException { + boolean interrupted = false; + + try { + while(true) { + try { + Object var2 = future.get(); + return var2; + } catch (InterruptedException var6) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static V getUninterruptibly(Future future, long timeout, TimeUnit unit) throws ExecutionException, TimeoutException { + boolean interrupted = false; + + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while(true) { + try { + Object var9 = future.get(remainingNanos, TimeUnit.NANOSECONDS); + return var9; + } catch (InterruptedException var13) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit) { + Preconditions.checkNotNull(toJoin); + boolean interrupted = false; + + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while(true) { + try { + TimeUnit.NANOSECONDS.timedJoin(toJoin, remainingNanos); + return; + } catch (InterruptedException var13) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static E takeUninterruptibly(BlockingQueue queue) { + boolean interrupted = false; + + try { + while(true) { + try { + Object var2 = queue.take(); + return var2; + } catch (InterruptedException var6) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static void putUninterruptibly(BlockingQueue queue, E element) { + boolean interrupted = false; + + try { + while(true) { + try { + queue.put(element); + return; + } catch (InterruptedException var7) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) { + boolean interrupted = false; + + try { + long remainingNanos = unit.toNanos(sleepFor); + long end = System.nanoTime() + remainingNanos; + + while(true) { + try { + TimeUnit.NANOSECONDS.sleep(remainingNanos); + return; + } catch (InterruptedException var12) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + public static boolean tryAcquireUninterruptibly(Semaphore semaphore, long timeout, TimeUnit unit) { + return tryAcquireUninterruptibly(semaphore, 1, timeout, unit); + } + + public static boolean tryAcquireUninterruptibly(Semaphore semaphore, int permits, long timeout, TimeUnit unit) { + boolean interrupted = false; + + try { + long remainingNanos = unit.toNanos(timeout); + long end = System.nanoTime() + remainingNanos; + + while(true) { + try { + boolean var10 = semaphore.tryAcquire(permits, remainingNanos, TimeUnit.NANOSECONDS); + return var10; + } catch (InterruptedException var14) { + interrupted = true; + remainingNanos = end - System.nanoTime(); + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + + } + } + + private Uninterruptibles() { + } +} diff --git a/src/main/com/google/common/util/concurrent/WrappingExecutorService.java b/src/main/com/google/common/util/concurrent/WrappingExecutorService.java new file mode 100644 index 0000000..999ea32 --- /dev/null +++ b/src/main/com/google/common/util/concurrent/WrappingExecutorService.java @@ -0,0 +1,103 @@ +package com.google.common.util.concurrent; + +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +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.TimeUnit; +import java.util.concurrent.TimeoutException; + +abstract class WrappingExecutorService implements ExecutorService { + private final ExecutorService delegate; + + protected WrappingExecutorService(ExecutorService delegate) { + this.delegate = (ExecutorService)Preconditions.checkNotNull(delegate); + } + + protected abstract Callable wrapTask(Callable var1); + + protected Runnable wrapTask(Runnable command) { + final Callable wrapped = this.wrapTask(Executors.callable(command, (Object)null)); + return new Runnable() { + public void run() { + try { + wrapped.call(); + } catch (Exception var2) { + Throwables.propagate(var2); + } + + } + }; + } + + private final ImmutableList> wrapTasks(Collection> tasks) { + ImmutableList.Builder> builder = ImmutableList.builder(); + Iterator i$ = tasks.iterator(); + + while(i$.hasNext()) { + Callable task = (Callable)i$.next(); + builder.add((Object)this.wrapTask(task)); + } + + return builder.build(); + } + + public final void execute(Runnable command) { + this.delegate.execute(this.wrapTask(command)); + } + + public final Future submit(Callable task) { + return this.delegate.submit(this.wrapTask((Callable)Preconditions.checkNotNull(task))); + } + + public final Future submit(Runnable task) { + return this.delegate.submit(this.wrapTask(task)); + } + + public final Future submit(Runnable task, T result) { + return this.delegate.submit(this.wrapTask(task), result); + } + + public final List> invokeAll(Collection> tasks) throws InterruptedException { + return this.delegate.invokeAll(this.wrapTasks(tasks)); + } + + public final List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate.invokeAll(this.wrapTasks(tasks), timeout, unit); + } + + public final T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return this.delegate.invokeAny(this.wrapTasks(tasks)); + } + + public final T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return this.delegate.invokeAny(this.wrapTasks(tasks), timeout, unit); + } + + public final void shutdown() { + this.delegate.shutdown(); + } + + public final List shutdownNow() { + return this.delegate.shutdownNow(); + } + + public final boolean isShutdown() { + return this.delegate.isShutdown(); + } + + public final boolean isTerminated() { + return this.delegate.isTerminated(); + } + + public final boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return this.delegate.awaitTermination(timeout, unit); + } +} diff --git a/src/main/com/google/common/util/concurrent/WrappingScheduledExecutorService.java b/src/main/com/google/common/util/concurrent/WrappingScheduledExecutorService.java new file mode 100644 index 0000000..1fb710b --- /dev/null +++ b/src/main/com/google/common/util/concurrent/WrappingScheduledExecutorService.java @@ -0,0 +1,31 @@ +package com.google.common.util.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +abstract class WrappingScheduledExecutorService extends WrappingExecutorService implements ScheduledExecutorService { + final ScheduledExecutorService delegate; + + protected WrappingScheduledExecutorService(ScheduledExecutorService delegate) { + super(delegate); + this.delegate = delegate; + } + + public final ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { + return this.delegate.schedule(this.wrapTask(command), delay, unit); + } + + public final ScheduledFuture schedule(Callable task, long delay, TimeUnit unit) { + return this.delegate.schedule(this.wrapTask(task), delay, unit); + } + + public final ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { + return this.delegate.scheduleAtFixedRate(this.wrapTask(command), initialDelay, period, unit); + } + + public final ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + return this.delegate.scheduleWithFixedDelay(this.wrapTask(command), initialDelay, delay, unit); + } +} diff --git a/src/main/com/google/common/util/concurrent/package-info.java b/src/main/com/google/common/util/concurrent/package-info.java new file mode 100644 index 0000000..a05a7ea --- /dev/null +++ b/src/main/com/google/common/util/concurrent/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.util.concurrent; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/common/xml/XmlEscapers.java b/src/main/com/google/common/xml/XmlEscapers.java new file mode 100644 index 0000000..01aaffd --- /dev/null +++ b/src/main/com/google/common/xml/XmlEscapers.java @@ -0,0 +1,51 @@ +package com.google.common.xml; + +import com.google.common.annotations.Beta; +import com.google.common.annotations.GwtCompatible; +import com.google.common.escape.Escaper; +import com.google.common.escape.Escapers; + +@Beta +@GwtCompatible +public class XmlEscapers { + private static final char MIN_ASCII_CONTROL_CHAR = '\u0000'; + private static final char MAX_ASCII_CONTROL_CHAR = '\u001f'; + private static final Escaper XML_ESCAPER; + private static final Escaper XML_CONTENT_ESCAPER; + private static final Escaper XML_ATTRIBUTE_ESCAPER; + + private XmlEscapers() { + } + + public static Escaper xmlContentEscaper() { + return XML_CONTENT_ESCAPER; + } + + public static Escaper xmlAttributeEscaper() { + return XML_ATTRIBUTE_ESCAPER; + } + + static { + Escapers.Builder builder = Escapers.builder(); + builder.setSafeRange('\u0000', '�'); + builder.setUnsafeReplacement("�"); + + for(char c = 0; c <= 31; ++c) { + if (c != '\t' && c != '\n' && c != '\r') { + builder.addEscape(c, "�"); + } + } + + builder.addEscape('&', "&"); + builder.addEscape('<', "<"); + builder.addEscape('>', ">"); + XML_CONTENT_ESCAPER = builder.build(); + builder.addEscape('\'', "'"); + builder.addEscape('"', """); + XML_ESCAPER = builder.build(); + builder.addEscape('\t', " "); + builder.addEscape('\n', " "); + builder.addEscape('\r', " "); + XML_ATTRIBUTE_ESCAPER = builder.build(); + } +} diff --git a/src/main/com/google/common/xml/package-info.java b/src/main/com/google/common/xml/package-info.java new file mode 100644 index 0000000..db6a823 --- /dev/null +++ b/src/main/com/google/common/xml/package-info.java @@ -0,0 +1,8 @@ +package com.google.common.xml; + +import javax.annotation.ParametersAreNonnullByDefault; + +// $FF: synthetic class +@ParametersAreNonnullByDefault +interface package-info { +} diff --git a/src/main/com/google/gson/DefaultDateTypeAdapter.java b/src/main/com/google/gson/DefaultDateTypeAdapter.java new file mode 100644 index 0000000..147b668 --- /dev/null +++ b/src/main/com/google/gson/DefaultDateTypeAdapter.java @@ -0,0 +1,95 @@ +package com.google.gson; + +import java.lang.reflect.Type; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserializer { + private final DateFormat enUsFormat; + private final DateFormat localFormat; + private final DateFormat iso8601Format; + + DefaultDateTypeAdapter() { + this(DateFormat.getDateTimeInstance(2, 2, Locale.US), DateFormat.getDateTimeInstance(2, 2)); + } + + DefaultDateTypeAdapter(String datePattern) { + this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern)); + } + + DefaultDateTypeAdapter(int style) { + this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style)); + } + + public DefaultDateTypeAdapter(int dateStyle, int timeStyle) { + this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US), DateFormat.getDateTimeInstance(dateStyle, timeStyle)); + } + + DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) { + this.enUsFormat = enUsFormat; + this.localFormat = localFormat; + this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { + synchronized(this.localFormat) { + String dateFormatAsString = this.enUsFormat.format(src); + return new JsonPrimitive(dateFormatAsString); + } + } + + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + if (!(json instanceof JsonPrimitive)) { + throw new JsonParseException("The date should be a string value"); + } else { + Date date = this.deserializeToDate(json); + if (typeOfT == Date.class) { + return date; + } else if (typeOfT == Timestamp.class) { + return new Timestamp(date.getTime()); + } else if (typeOfT == java.sql.Date.class) { + return new java.sql.Date(date.getTime()); + } else { + throw new IllegalArgumentException(this.getClass() + " cannot deserialize to " + typeOfT); + } + } + } + + private Date deserializeToDate(JsonElement json) { + synchronized(this.localFormat) { + Date var10000; + try { + var10000 = this.localFormat.parse(json.getAsString()); + } catch (ParseException var7) { + try { + var10000 = this.enUsFormat.parse(json.getAsString()); + } catch (ParseException var6) { + try { + var10000 = this.iso8601Format.parse(json.getAsString()); + } catch (ParseException var5) { + throw new JsonSyntaxException(json.getAsString(), var5); + } + + return var10000; + } + + return var10000; + } + + return var10000; + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(DefaultDateTypeAdapter.class.getSimpleName()); + sb.append('(').append(this.localFormat.getClass().getSimpleName()).append(')'); + return sb.toString(); + } +} diff --git a/src/main/com/google/gson/ExclusionStrategy.java b/src/main/com/google/gson/ExclusionStrategy.java new file mode 100644 index 0000000..9a560bd --- /dev/null +++ b/src/main/com/google/gson/ExclusionStrategy.java @@ -0,0 +1,7 @@ +package com.google.gson; + +public interface ExclusionStrategy { + boolean shouldSkipField(FieldAttributes var1); + + boolean shouldSkipClass(Class var1); +} diff --git a/src/main/com/google/gson/FieldAttributes.java b/src/main/com/google/gson/FieldAttributes.java new file mode 100644 index 0000000..5c63c6e --- /dev/null +++ b/src/main/com/google/gson/FieldAttributes.java @@ -0,0 +1,53 @@ +package com.google.gson; + +import com.google.gson.internal.$Gson$Preconditions; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collection; + +public final class FieldAttributes { + private final Field field; + + public FieldAttributes(Field f) { + $Gson$Preconditions.checkNotNull(f); + this.field = f; + } + + public Class getDeclaringClass() { + return this.field.getDeclaringClass(); + } + + public String getName() { + return this.field.getName(); + } + + public Type getDeclaredType() { + return this.field.getGenericType(); + } + + public Class getDeclaredClass() { + return this.field.getType(); + } + + public T getAnnotation(Class annotation) { + return this.field.getAnnotation(annotation); + } + + public Collection getAnnotations() { + return Arrays.asList(this.field.getAnnotations()); + } + + public boolean hasModifier(int modifier) { + return (this.field.getModifiers() & modifier) != 0; + } + + Object get(Object instance) throws IllegalAccessException { + return this.field.get(instance); + } + + boolean isSynthetic() { + return this.field.isSynthetic(); + } +} diff --git a/src/main/com/google/gson/FieldNamingPolicy.java b/src/main/com/google/gson/FieldNamingPolicy.java new file mode 100644 index 0000000..9c59a04 --- /dev/null +++ b/src/main/com/google/gson/FieldNamingPolicy.java @@ -0,0 +1,80 @@ +package com.google.gson; + +import java.lang.reflect.Field; + +public enum FieldNamingPolicy implements FieldNamingStrategy { + IDENTITY { + public String translateName(Field f) { + return f.getName(); + } + }, + UPPER_CAMEL_CASE { + public String translateName(Field f) { + return FieldNamingPolicy.upperCaseFirstLetter(f.getName()); + } + }, + UPPER_CAMEL_CASE_WITH_SPACES { + public String translateName(Field f) { + return FieldNamingPolicy.upperCaseFirstLetter(FieldNamingPolicy.separateCamelCase(f.getName(), " ")); + } + }, + LOWER_CASE_WITH_UNDERSCORES { + public String translateName(Field f) { + return FieldNamingPolicy.separateCamelCase(f.getName(), "_").toLowerCase(); + } + }, + LOWER_CASE_WITH_DASHES { + public String translateName(Field f) { + return FieldNamingPolicy.separateCamelCase(f.getName(), "-").toLowerCase(); + } + }; + + private FieldNamingPolicy() { + } + + private static String separateCamelCase(String name, String separator) { + StringBuilder translation = new StringBuilder(); + + for(int i = 0; i < name.length(); ++i) { + char character = name.charAt(i); + if (Character.isUpperCase(character) && translation.length() != 0) { + translation.append(separator); + } + + translation.append(character); + } + + return translation.toString(); + } + + private static String upperCaseFirstLetter(String name) { + StringBuilder fieldNameBuilder = new StringBuilder(); + int index = 0; + + char firstCharacter; + for(firstCharacter = name.charAt(index); index < name.length() - 1 && !Character.isLetter(firstCharacter); firstCharacter = name.charAt(index)) { + fieldNameBuilder.append(firstCharacter); + ++index; + } + + if (index == name.length()) { + return fieldNameBuilder.toString(); + } else if (!Character.isUpperCase(firstCharacter)) { + char var10000 = Character.toUpperCase(firstCharacter); + ++index; + String modifiedTarget = modifyString(var10000, name, index); + return fieldNameBuilder.append(modifiedTarget).toString(); + } else { + return name; + } + } + + private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) { + return indexOfSubstring < srcString.length() ? firstCharacter + srcString.substring(indexOfSubstring) : String.valueOf(firstCharacter); + } + + // $FF: synthetic method + FieldNamingPolicy(Object x2) { + this(); + } +} diff --git a/src/main/com/google/gson/FieldNamingStrategy.java b/src/main/com/google/gson/FieldNamingStrategy.java new file mode 100644 index 0000000..3153ab9 --- /dev/null +++ b/src/main/com/google/gson/FieldNamingStrategy.java @@ -0,0 +1,7 @@ +package com.google.gson; + +import java.lang.reflect.Field; + +public interface FieldNamingStrategy { + String translateName(Field var1); +} diff --git a/src/main/com/google/gson/Gson.java b/src/main/com/google/gson/Gson.java new file mode 100644 index 0000000..8481da8 --- /dev/null +++ b/src/main/com/google/gson/Gson.java @@ -0,0 +1,483 @@ +package com.google.gson; + +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.Excluder; +import com.google.gson.internal.Primitives; +import com.google.gson.internal.Streams; +import com.google.gson.internal.bind.ArrayTypeAdapter; +import com.google.gson.internal.bind.CollectionTypeAdapterFactory; +import com.google.gson.internal.bind.DateTypeAdapter; +import com.google.gson.internal.bind.JsonTreeReader; +import com.google.gson.internal.bind.JsonTreeWriter; +import com.google.gson.internal.bind.MapTypeAdapterFactory; +import com.google.gson.internal.bind.ObjectTypeAdapter; +import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; +import com.google.gson.internal.bind.SqlDateTypeAdapter; +import com.google.gson.internal.bind.TimeTypeAdapter; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.google.gson.stream.MalformedJsonException; +import java.io.EOFException; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public final class Gson { + static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; + private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; + private final ThreadLocal, Gson.FutureTypeAdapter>> calls; + private final Map, TypeAdapter> typeTokenCache; + private final List factories; + private final ConstructorConstructor constructorConstructor; + private final boolean serializeNulls; + private final boolean htmlSafe; + private final boolean generateNonExecutableJson; + private final boolean prettyPrinting; + final JsonDeserializationContext deserializationContext; + final JsonSerializationContext serializationContext; + + public Gson() { + this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, Collections.emptyMap(), false, false, false, true, false, false, LongSerializationPolicy.DEFAULT, Collections.emptyList()); + } + + Gson(Excluder excluder, FieldNamingStrategy fieldNamingPolicy, Map> instanceCreators, boolean serializeNulls, boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting, boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy, List typeAdapterFactories) { + this.calls = new ThreadLocal(); + this.typeTokenCache = Collections.synchronizedMap(new HashMap()); + this.deserializationContext = new JsonDeserializationContext() { + public T deserialize(JsonElement json, Type typeOfT) throws JsonParseException { + return Gson.this.fromJson(json, typeOfT); + } + }; + this.serializationContext = new JsonSerializationContext() { + public JsonElement serialize(Object src) { + return Gson.this.toJsonTree(src); + } + + public JsonElement serialize(Object src, Type typeOfSrc) { + return Gson.this.toJsonTree(src, typeOfSrc); + } + }; + this.constructorConstructor = new ConstructorConstructor(instanceCreators); + this.serializeNulls = serializeNulls; + this.generateNonExecutableJson = generateNonExecutableGson; + this.htmlSafe = htmlSafe; + this.prettyPrinting = prettyPrinting; + List factories = new ArrayList(); + factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); + factories.add(ObjectTypeAdapter.FACTORY); + factories.add(excluder); + factories.addAll(typeAdapterFactories); + factories.add(TypeAdapters.STRING_FACTORY); + factories.add(TypeAdapters.INTEGER_FACTORY); + factories.add(TypeAdapters.BOOLEAN_FACTORY); + factories.add(TypeAdapters.BYTE_FACTORY); + factories.add(TypeAdapters.SHORT_FACTORY); + factories.add(TypeAdapters.newFactory(Long.TYPE, Long.class, this.longAdapter(longSerializationPolicy))); + factories.add(TypeAdapters.newFactory(Double.TYPE, Double.class, this.doubleAdapter(serializeSpecialFloatingPointValues))); + factories.add(TypeAdapters.newFactory(Float.TYPE, Float.class, this.floatAdapter(serializeSpecialFloatingPointValues))); + factories.add(TypeAdapters.NUMBER_FACTORY); + factories.add(TypeAdapters.CHARACTER_FACTORY); + factories.add(TypeAdapters.STRING_BUILDER_FACTORY); + factories.add(TypeAdapters.STRING_BUFFER_FACTORY); + factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL)); + factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER)); + factories.add(TypeAdapters.URL_FACTORY); + factories.add(TypeAdapters.URI_FACTORY); + factories.add(TypeAdapters.UUID_FACTORY); + factories.add(TypeAdapters.LOCALE_FACTORY); + factories.add(TypeAdapters.INET_ADDRESS_FACTORY); + factories.add(TypeAdapters.BIT_SET_FACTORY); + factories.add(DateTypeAdapter.FACTORY); + factories.add(TypeAdapters.CALENDAR_FACTORY); + factories.add(TimeTypeAdapter.FACTORY); + factories.add(SqlDateTypeAdapter.FACTORY); + factories.add(TypeAdapters.TIMESTAMP_FACTORY); + factories.add(ArrayTypeAdapter.FACTORY); + factories.add(TypeAdapters.ENUM_FACTORY); + factories.add(TypeAdapters.CLASS_FACTORY); + factories.add(new CollectionTypeAdapterFactory(this.constructorConstructor)); + factories.add(new MapTypeAdapterFactory(this.constructorConstructor, complexMapKeySerialization)); + factories.add(new ReflectiveTypeAdapterFactory(this.constructorConstructor, fieldNamingPolicy, excluder)); + this.factories = Collections.unmodifiableList(factories); + } + + private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointValues) { + return serializeSpecialFloatingPointValues ? TypeAdapters.DOUBLE : new TypeAdapter() { + public Double read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + return in.nextDouble(); + } + } + + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + double doubleValue = value.doubleValue(); + Gson.this.checkValidFloatingPoint(doubleValue); + out.value(value); + } + } + }; + } + + private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointValues) { + return serializeSpecialFloatingPointValues ? TypeAdapters.FLOAT : new TypeAdapter() { + public Float read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + return (float)in.nextDouble(); + } + } + + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + float floatValue = value.floatValue(); + Gson.this.checkValidFloatingPoint((double)floatValue); + out.value(value); + } + } + }; + } + + private void checkValidFloatingPoint(double value) { + if (Double.isNaN(value) || Double.isInfinite(value)) { + throw new IllegalArgumentException(value + " is not a valid double value as per JSON specification. To override this" + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."); + } + } + + private TypeAdapter longAdapter(LongSerializationPolicy longSerializationPolicy) { + return longSerializationPolicy == LongSerializationPolicy.DEFAULT ? TypeAdapters.LONG : new TypeAdapter() { + public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + return in.nextLong(); + } + } + + public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(value.toString()); + } + } + }; + } + + public TypeAdapter getAdapter(TypeToken type) { + TypeAdapter cached = (TypeAdapter)this.typeTokenCache.get(type); + if (cached != null) { + return cached; + } else { + Map, Gson.FutureTypeAdapter> threadCalls = (Map)this.calls.get(); + boolean requiresThreadLocalCleanup = false; + if (threadCalls == null) { + threadCalls = new HashMap(); + this.calls.set(threadCalls); + requiresThreadLocalCleanup = true; + } + + Gson.FutureTypeAdapter ongoingCall = (Gson.FutureTypeAdapter)((Map)threadCalls).get(type); + if (ongoingCall != null) { + return ongoingCall; + } else { + try { + Gson.FutureTypeAdapter call = new Gson.FutureTypeAdapter(); + ((Map)threadCalls).put(type, call); + Iterator i$ = this.factories.iterator(); + + TypeAdapter candidate; + do { + if (!i$.hasNext()) { + throw new IllegalArgumentException("GSON cannot handle " + type); + } + + TypeAdapterFactory factory = (TypeAdapterFactory)i$.next(); + candidate = factory.create(this, type); + } while(candidate == null); + + call.setDelegate(candidate); + this.typeTokenCache.put(type, candidate); + TypeAdapter var10 = candidate; + return var10; + } finally { + ((Map)threadCalls).remove(type); + if (requiresThreadLocalCleanup) { + this.calls.remove(); + } + + } + } + } + } + + public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken type) { + boolean skipPastFound = false; + Iterator i$ = this.factories.iterator(); + + while(i$.hasNext()) { + TypeAdapterFactory factory = (TypeAdapterFactory)i$.next(); + if (!skipPastFound) { + if (factory == skipPast) { + skipPastFound = true; + } + } else { + TypeAdapter candidate = factory.create(this, type); + if (candidate != null) { + return candidate; + } + } + } + + throw new IllegalArgumentException("GSON cannot serialize " + type); + } + + public TypeAdapter getAdapter(Class type) { + return this.getAdapter(TypeToken.get(type)); + } + + public JsonElement toJsonTree(Object src) { + return (JsonElement)(src == null ? JsonNull.INSTANCE : this.toJsonTree(src, src.getClass())); + } + + public JsonElement toJsonTree(Object src, Type typeOfSrc) { + JsonTreeWriter writer = new JsonTreeWriter(); + this.toJson(src, typeOfSrc, (JsonWriter)writer); + return writer.get(); + } + + public String toJson(Object src) { + return src == null ? this.toJson((JsonElement)JsonNull.INSTANCE) : this.toJson((Object)src, (Type)src.getClass()); + } + + public String toJson(Object src, Type typeOfSrc) { + StringWriter writer = new StringWriter(); + this.toJson(src, typeOfSrc, (Appendable)writer); + return writer.toString(); + } + + public void toJson(Object src, Appendable writer) throws JsonIOException { + if (src != null) { + this.toJson(src, src.getClass(), (Appendable)writer); + } else { + this.toJson((JsonElement)JsonNull.INSTANCE, (Appendable)writer); + } + + } + + public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException { + try { + JsonWriter jsonWriter = this.newJsonWriter(Streams.writerForAppendable(writer)); + this.toJson(src, typeOfSrc, jsonWriter); + } catch (IOException var5) { + throw new JsonIOException(var5); + } + } + + public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { + TypeAdapter adapter = this.getAdapter(TypeToken.get(typeOfSrc)); + boolean oldLenient = writer.isLenient(); + writer.setLenient(true); + boolean oldHtmlSafe = writer.isHtmlSafe(); + writer.setHtmlSafe(this.htmlSafe); + boolean oldSerializeNulls = writer.getSerializeNulls(); + writer.setSerializeNulls(this.serializeNulls); + + try { + adapter.write(writer, src); + } catch (IOException var12) { + throw new JsonIOException(var12); + } finally { + writer.setLenient(oldLenient); + writer.setHtmlSafe(oldHtmlSafe); + writer.setSerializeNulls(oldSerializeNulls); + } + + } + + public String toJson(JsonElement jsonElement) { + StringWriter writer = new StringWriter(); + this.toJson((JsonElement)jsonElement, (Appendable)writer); + return writer.toString(); + } + + public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOException { + try { + JsonWriter jsonWriter = this.newJsonWriter(Streams.writerForAppendable(writer)); + this.toJson(jsonElement, jsonWriter); + } catch (IOException var4) { + throw new RuntimeException(var4); + } + } + + private JsonWriter newJsonWriter(Writer writer) throws IOException { + if (this.generateNonExecutableJson) { + writer.write(")]}'\n"); + } + + JsonWriter jsonWriter = new JsonWriter(writer); + if (this.prettyPrinting) { + jsonWriter.setIndent(" "); + } + + jsonWriter.setSerializeNulls(this.serializeNulls); + return jsonWriter; + } + + public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException { + boolean oldLenient = writer.isLenient(); + writer.setLenient(true); + boolean oldHtmlSafe = writer.isHtmlSafe(); + writer.setHtmlSafe(this.htmlSafe); + boolean oldSerializeNulls = writer.getSerializeNulls(); + writer.setSerializeNulls(this.serializeNulls); + + try { + Streams.write(jsonElement, writer); + } catch (IOException var10) { + throw new JsonIOException(var10); + } finally { + writer.setLenient(oldLenient); + writer.setHtmlSafe(oldHtmlSafe); + writer.setSerializeNulls(oldSerializeNulls); + } + + } + + public T fromJson(String json, Class classOfT) throws JsonSyntaxException { + Object object = this.fromJson((String)json, (Type)classOfT); + return Primitives.wrap(classOfT).cast(object); + } + + public T fromJson(String json, Type typeOfT) throws JsonSyntaxException { + if (json == null) { + return null; + } else { + StringReader reader = new StringReader(json); + T target = this.fromJson((Reader)reader, (Type)typeOfT); + return target; + } + } + + public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException { + JsonReader jsonReader = new JsonReader(json); + Object object = this.fromJson((JsonReader)jsonReader, (Type)classOfT); + assertFullConsumption(object, jsonReader); + return Primitives.wrap(classOfT).cast(object); + } + + public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { + JsonReader jsonReader = new JsonReader(json); + T object = this.fromJson(jsonReader, typeOfT); + assertFullConsumption(object, jsonReader); + return object; + } + + private static void assertFullConsumption(Object obj, JsonReader reader) { + try { + if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonIOException("JSON document was not fully consumed."); + } + } catch (MalformedJsonException var3) { + throw new JsonSyntaxException(var3); + } catch (IOException var4) { + throw new JsonIOException(var4); + } + } + + public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { + boolean isEmpty = true; + boolean oldLenient = reader.isLenient(); + reader.setLenient(true); + + TypeAdapter typeAdapter; + try { + reader.peek(); + isEmpty = false; + TypeToken typeToken = TypeToken.get(typeOfT); + typeAdapter = this.getAdapter(typeToken); + T object = typeAdapter.read(reader); + Object var8 = object; + return var8; + } catch (EOFException var14) { + if (!isEmpty) { + throw new JsonSyntaxException(var14); + } + + typeAdapter = null; + } catch (IllegalStateException var15) { + throw new JsonSyntaxException(var15); + } catch (IOException var16) { + throw new JsonSyntaxException(var16); + } finally { + reader.setLenient(oldLenient); + } + + return typeAdapter; + } + + public T fromJson(JsonElement json, Class classOfT) throws JsonSyntaxException { + Object object = this.fromJson((JsonElement)json, (Type)classOfT); + return Primitives.wrap(classOfT).cast(object); + } + + public T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException { + return json == null ? null : this.fromJson((JsonReader)(new JsonTreeReader(json)), (Type)typeOfT); + } + + public String toString() { + return "{serializeNulls:" + this.serializeNulls + "factories:" + this.factories + ",instanceCreators:" + this.constructorConstructor + "}"; + } + + static class FutureTypeAdapter extends TypeAdapter { + private TypeAdapter delegate; + + public void setDelegate(TypeAdapter typeAdapter) { + if (this.delegate != null) { + throw new AssertionError(); + } else { + this.delegate = typeAdapter; + } + } + + public T read(JsonReader in) throws IOException { + if (this.delegate == null) { + throw new IllegalStateException(); + } else { + return this.delegate.read(in); + } + } + + public void write(JsonWriter out, T value) throws IOException { + if (this.delegate == null) { + throw new IllegalStateException(); + } else { + this.delegate.write(out, value); + } + } + } +} diff --git a/src/main/com/google/gson/GsonBuilder.java b/src/main/com/google/gson/GsonBuilder.java new file mode 100644 index 0000000..7c68d8d --- /dev/null +++ b/src/main/com/google/gson/GsonBuilder.java @@ -0,0 +1,211 @@ +package com.google.gson; + +import com.google.gson.internal.$Gson$Preconditions; +import com.google.gson.internal.Excluder; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class GsonBuilder { + private Excluder excluder; + private LongSerializationPolicy longSerializationPolicy; + private FieldNamingStrategy fieldNamingPolicy; + private final Map> instanceCreators; + private final List factories; + private final List hierarchyFactories; + private boolean serializeNulls; + private String datePattern; + private int dateStyle; + private int timeStyle; + private boolean complexMapKeySerialization; + private boolean serializeSpecialFloatingPointValues; + private boolean escapeHtmlChars; + private boolean prettyPrinting; + private boolean generateNonExecutableJson; + + public GsonBuilder() { + this.excluder = Excluder.DEFAULT; + this.longSerializationPolicy = LongSerializationPolicy.DEFAULT; + this.fieldNamingPolicy = FieldNamingPolicy.IDENTITY; + this.instanceCreators = new HashMap(); + this.factories = new ArrayList(); + this.hierarchyFactories = new ArrayList(); + this.dateStyle = 2; + this.timeStyle = 2; + this.escapeHtmlChars = true; + } + + public GsonBuilder setVersion(double ignoreVersionsAfter) { + this.excluder = this.excluder.withVersion(ignoreVersionsAfter); + return this; + } + + public GsonBuilder excludeFieldsWithModifiers(int... modifiers) { + this.excluder = this.excluder.withModifiers(modifiers); + return this; + } + + public GsonBuilder generateNonExecutableJson() { + this.generateNonExecutableJson = true; + return this; + } + + public GsonBuilder excludeFieldsWithoutExposeAnnotation() { + this.excluder = this.excluder.excludeFieldsWithoutExposeAnnotation(); + return this; + } + + public GsonBuilder serializeNulls() { + this.serializeNulls = true; + return this; + } + + public GsonBuilder enableComplexMapKeySerialization() { + this.complexMapKeySerialization = true; + return this; + } + + public GsonBuilder disableInnerClassSerialization() { + this.excluder = this.excluder.disableInnerClassSerialization(); + return this; + } + + public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) { + this.longSerializationPolicy = serializationPolicy; + return this; + } + + public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) { + this.fieldNamingPolicy = namingConvention; + return this; + } + + public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) { + this.fieldNamingPolicy = fieldNamingStrategy; + return this; + } + + public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) { + ExclusionStrategy[] arr$ = strategies; + int len$ = strategies.length; + + for(int i$ = 0; i$ < len$; ++i$) { + ExclusionStrategy strategy = arr$[i$]; + this.excluder = this.excluder.withExclusionStrategy(strategy, true, true); + } + + return this; + } + + public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) { + this.excluder = this.excluder.withExclusionStrategy(strategy, true, false); + return this; + } + + public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) { + this.excluder = this.excluder.withExclusionStrategy(strategy, false, true); + return this; + } + + public GsonBuilder setPrettyPrinting() { + this.prettyPrinting = true; + return this; + } + + public GsonBuilder disableHtmlEscaping() { + this.escapeHtmlChars = false; + return this; + } + + public GsonBuilder setDateFormat(String pattern) { + this.datePattern = pattern; + return this; + } + + public GsonBuilder setDateFormat(int style) { + this.dateStyle = style; + this.datePattern = null; + return this; + } + + public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { + this.dateStyle = dateStyle; + this.timeStyle = timeStyle; + this.datePattern = null; + return this; + } + + public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { + $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof InstanceCreator || typeAdapter instanceof TypeAdapter); + if (typeAdapter instanceof InstanceCreator) { + this.instanceCreators.put(type, (InstanceCreator)typeAdapter); + } + + if (typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer) { + TypeToken typeToken = TypeToken.get(type); + this.factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter)); + } + + if (typeAdapter instanceof TypeAdapter) { + this.factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter)); + } + + return this; + } + + public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) { + this.factories.add(factory); + return this; + } + + public GsonBuilder registerTypeHierarchyAdapter(Class baseType, Object typeAdapter) { + $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer || typeAdapter instanceof JsonDeserializer || typeAdapter instanceof TypeAdapter); + if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) { + this.hierarchyFactories.add(0, TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter)); + } + + if (typeAdapter instanceof TypeAdapter) { + this.factories.add(TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter)typeAdapter)); + } + + return this; + } + + public GsonBuilder serializeSpecialFloatingPointValues() { + this.serializeSpecialFloatingPointValues = true; + return this; + } + + public Gson create() { + List factories = new ArrayList(); + factories.addAll(this.factories); + Collections.reverse(factories); + factories.addAll(this.hierarchyFactories); + this.addTypeAdaptersForDate(this.datePattern, this.dateStyle, this.timeStyle, factories); + return new Gson(this.excluder, this.fieldNamingPolicy, this.instanceCreators, this.serializeNulls, this.complexMapKeySerialization, this.generateNonExecutableJson, this.escapeHtmlChars, this.prettyPrinting, this.serializeSpecialFloatingPointValues, this.longSerializationPolicy, factories); + } + + private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, List factories) { + DefaultDateTypeAdapter dateTypeAdapter; + if (datePattern != null && !"".equals(datePattern.trim())) { + dateTypeAdapter = new DefaultDateTypeAdapter(datePattern); + } else { + if (dateStyle == 2 || timeStyle == 2) { + return; + } + + dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle); + } + + factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter)); + factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter)); + factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter)); + } +} diff --git a/src/main/com/google/gson/InstanceCreator.java b/src/main/com/google/gson/InstanceCreator.java new file mode 100644 index 0000000..0378b40 --- /dev/null +++ b/src/main/com/google/gson/InstanceCreator.java @@ -0,0 +1,7 @@ +package com.google.gson; + +import java.lang.reflect.Type; + +public interface InstanceCreator { + T createInstance(Type var1); +} diff --git a/src/main/com/google/gson/JsonArray.java b/src/main/com/google/gson/JsonArray.java new file mode 100644 index 0000000..6d9368c --- /dev/null +++ b/src/main/com/google/gson/JsonArray.java @@ -0,0 +1,151 @@ +package com.google.gson; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public final class JsonArray extends JsonElement implements Iterable { + private final List elements = new ArrayList(); + + JsonArray deepCopy() { + JsonArray result = new JsonArray(); + Iterator i$ = this.elements.iterator(); + + while(i$.hasNext()) { + JsonElement element = (JsonElement)i$.next(); + result.add(element.deepCopy()); + } + + return result; + } + + public void add(JsonElement element) { + if (element == null) { + element = JsonNull.INSTANCE; + } + + this.elements.add(element); + } + + public void addAll(JsonArray array) { + this.elements.addAll(array.elements); + } + + public int size() { + return this.elements.size(); + } + + public Iterator iterator() { + return this.elements.iterator(); + } + + public JsonElement get(int i) { + return (JsonElement)this.elements.get(i); + } + + public Number getAsNumber() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsNumber(); + } else { + throw new IllegalStateException(); + } + } + + public String getAsString() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsString(); + } else { + throw new IllegalStateException(); + } + } + + public double getAsDouble() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsDouble(); + } else { + throw new IllegalStateException(); + } + } + + public BigDecimal getAsBigDecimal() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsBigDecimal(); + } else { + throw new IllegalStateException(); + } + } + + public BigInteger getAsBigInteger() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsBigInteger(); + } else { + throw new IllegalStateException(); + } + } + + public float getAsFloat() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsFloat(); + } else { + throw new IllegalStateException(); + } + } + + public long getAsLong() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsLong(); + } else { + throw new IllegalStateException(); + } + } + + public int getAsInt() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsInt(); + } else { + throw new IllegalStateException(); + } + } + + public byte getAsByte() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsByte(); + } else { + throw new IllegalStateException(); + } + } + + public char getAsCharacter() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsCharacter(); + } else { + throw new IllegalStateException(); + } + } + + public short getAsShort() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsShort(); + } else { + throw new IllegalStateException(); + } + } + + public boolean getAsBoolean() { + if (this.elements.size() == 1) { + return ((JsonElement)this.elements.get(0)).getAsBoolean(); + } else { + throw new IllegalStateException(); + } + } + + public boolean equals(Object o) { + return o == this || o instanceof JsonArray && ((JsonArray)o).elements.equals(this.elements); + } + + public int hashCode() { + return this.elements.hashCode(); + } +} diff --git a/src/main/com/google/gson/JsonDeserializationContext.java b/src/main/com/google/gson/JsonDeserializationContext.java new file mode 100644 index 0000000..fee6142 --- /dev/null +++ b/src/main/com/google/gson/JsonDeserializationContext.java @@ -0,0 +1,7 @@ +package com.google.gson; + +import java.lang.reflect.Type; + +public interface JsonDeserializationContext { + T deserialize(JsonElement var1, Type var2) throws JsonParseException; +} diff --git a/src/main/com/google/gson/JsonDeserializer.java b/src/main/com/google/gson/JsonDeserializer.java new file mode 100644 index 0000000..4eee6c6 --- /dev/null +++ b/src/main/com/google/gson/JsonDeserializer.java @@ -0,0 +1,7 @@ +package com.google.gson; + +import java.lang.reflect.Type; + +public interface JsonDeserializer { + T deserialize(JsonElement var1, Type var2, JsonDeserializationContext var3) throws JsonParseException; +} diff --git a/src/main/com/google/gson/JsonElement.java b/src/main/com/google/gson/JsonElement.java new file mode 100644 index 0000000..2d18131 --- /dev/null +++ b/src/main/com/google/gson/JsonElement.java @@ -0,0 +1,124 @@ +package com.google.gson; + +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.math.BigInteger; + +public abstract class JsonElement { + abstract JsonElement deepCopy(); + + public boolean isJsonArray() { + return this instanceof JsonArray; + } + + public boolean isJsonObject() { + return this instanceof JsonObject; + } + + public boolean isJsonPrimitive() { + return this instanceof JsonPrimitive; + } + + public boolean isJsonNull() { + return this instanceof JsonNull; + } + + public JsonObject getAsJsonObject() { + if (this.isJsonObject()) { + return (JsonObject)this; + } else { + throw new IllegalStateException("Not a JSON Object: " + this); + } + } + + public JsonArray getAsJsonArray() { + if (this.isJsonArray()) { + return (JsonArray)this; + } else { + throw new IllegalStateException("This is not a JSON Array."); + } + } + + public JsonPrimitive getAsJsonPrimitive() { + if (this.isJsonPrimitive()) { + return (JsonPrimitive)this; + } else { + throw new IllegalStateException("This is not a JSON Primitive."); + } + } + + public JsonNull getAsJsonNull() { + if (this.isJsonNull()) { + return (JsonNull)this; + } else { + throw new IllegalStateException("This is not a JSON Null."); + } + } + + public boolean getAsBoolean() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + Boolean getAsBooleanWrapper() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public Number getAsNumber() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public String getAsString() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public double getAsDouble() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public float getAsFloat() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public long getAsLong() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public int getAsInt() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public byte getAsByte() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public char getAsCharacter() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public BigDecimal getAsBigDecimal() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public BigInteger getAsBigInteger() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public short getAsShort() { + throw new UnsupportedOperationException(this.getClass().getSimpleName()); + } + + public String toString() { + try { + StringWriter stringWriter = new StringWriter(); + JsonWriter jsonWriter = new JsonWriter(stringWriter); + jsonWriter.setLenient(true); + Streams.write(this, jsonWriter); + return stringWriter.toString(); + } catch (IOException var3) { + throw new AssertionError(var3); + } + } +} diff --git a/src/main/com/google/gson/JsonIOException.java b/src/main/com/google/gson/JsonIOException.java new file mode 100644 index 0000000..07e6dd8 --- /dev/null +++ b/src/main/com/google/gson/JsonIOException.java @@ -0,0 +1,17 @@ +package com.google.gson; + +public final class JsonIOException extends JsonParseException { + private static final long serialVersionUID = 1L; + + public JsonIOException(String msg) { + super(msg); + } + + public JsonIOException(String msg, Throwable cause) { + super(msg, cause); + } + + public JsonIOException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/com/google/gson/JsonNull.java b/src/main/com/google/gson/JsonNull.java new file mode 100644 index 0000000..4f316af --- /dev/null +++ b/src/main/com/google/gson/JsonNull.java @@ -0,0 +1,17 @@ +package com.google.gson; + +public final class JsonNull extends JsonElement { + public static final JsonNull INSTANCE = new JsonNull(); + + JsonNull deepCopy() { + return INSTANCE; + } + + public int hashCode() { + return JsonNull.class.hashCode(); + } + + public boolean equals(Object other) { + return this == other || other instanceof JsonNull; + } +} diff --git a/src/main/com/google/gson/JsonObject.java b/src/main/com/google/gson/JsonObject.java new file mode 100644 index 0000000..5fcca70 --- /dev/null +++ b/src/main/com/google/gson/JsonObject.java @@ -0,0 +1,86 @@ +package com.google.gson; + +import com.google.gson.internal.LinkedTreeMap; +import java.util.Iterator; +import java.util.Set; +import java.util.Map.Entry; + +public final class JsonObject extends JsonElement { + private final LinkedTreeMap members = new LinkedTreeMap(); + + JsonObject deepCopy() { + JsonObject result = new JsonObject(); + Iterator i$ = this.members.entrySet().iterator(); + + while(i$.hasNext()) { + Entry entry = (Entry)i$.next(); + result.add((String)entry.getKey(), ((JsonElement)entry.getValue()).deepCopy()); + } + + return result; + } + + public void add(String property, JsonElement value) { + if (value == null) { + value = JsonNull.INSTANCE; + } + + this.members.put(property, value); + } + + public JsonElement remove(String property) { + return (JsonElement)this.members.remove(property); + } + + public void addProperty(String property, String value) { + this.add(property, this.createJsonElement(value)); + } + + public void addProperty(String property, Number value) { + this.add(property, this.createJsonElement(value)); + } + + public void addProperty(String property, Boolean value) { + this.add(property, this.createJsonElement(value)); + } + + public void addProperty(String property, Character value) { + this.add(property, this.createJsonElement(value)); + } + + private JsonElement createJsonElement(Object value) { + return (JsonElement)(value == null ? JsonNull.INSTANCE : new JsonPrimitive(value)); + } + + public Set> entrySet() { + return this.members.entrySet(); + } + + public boolean has(String memberName) { + return this.members.containsKey(memberName); + } + + public JsonElement get(String memberName) { + return (JsonElement)this.members.get(memberName); + } + + public JsonPrimitive getAsJsonPrimitive(String memberName) { + return (JsonPrimitive)this.members.get(memberName); + } + + public JsonArray getAsJsonArray(String memberName) { + return (JsonArray)this.members.get(memberName); + } + + public JsonObject getAsJsonObject(String memberName) { + return (JsonObject)this.members.get(memberName); + } + + public boolean equals(Object o) { + return o == this || o instanceof JsonObject && ((JsonObject)o).members.equals(this.members); + } + + public int hashCode() { + return this.members.hashCode(); + } +} diff --git a/src/main/com/google/gson/JsonParseException.java b/src/main/com/google/gson/JsonParseException.java new file mode 100644 index 0000000..26b306a --- /dev/null +++ b/src/main/com/google/gson/JsonParseException.java @@ -0,0 +1,17 @@ +package com.google.gson; + +public class JsonParseException extends RuntimeException { + static final long serialVersionUID = -4086729973971783390L; + + public JsonParseException(String msg) { + super(msg); + } + + public JsonParseException(String msg, Throwable cause) { + super(msg, cause); + } + + public JsonParseException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/com/google/gson/JsonParser.java b/src/main/com/google/gson/JsonParser.java new file mode 100644 index 0000000..6112131 --- /dev/null +++ b/src/main/com/google/gson/JsonParser.java @@ -0,0 +1,51 @@ +package com.google.gson; + +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.MalformedJsonException; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +public final class JsonParser { + public JsonElement parse(String json) throws JsonSyntaxException { + return this.parse((Reader)(new StringReader(json))); + } + + public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException { + try { + JsonReader jsonReader = new JsonReader(json); + JsonElement element = this.parse(jsonReader); + if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonSyntaxException("Did not consume the entire document."); + } else { + return element; + } + } catch (MalformedJsonException var4) { + throw new JsonSyntaxException(var4); + } catch (IOException var5) { + throw new JsonIOException(var5); + } catch (NumberFormatException var6) { + throw new JsonSyntaxException(var6); + } + } + + public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException { + boolean lenient = json.isLenient(); + json.setLenient(true); + + JsonElement var3; + try { + var3 = Streams.parse(json); + } catch (StackOverflowError var8) { + throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", var8); + } catch (OutOfMemoryError var9) { + throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", var9); + } finally { + json.setLenient(lenient); + } + + return var3; + } +} diff --git a/src/main/com/google/gson/JsonPrimitive.java b/src/main/com/google/gson/JsonPrimitive.java new file mode 100644 index 0000000..0743d73 --- /dev/null +++ b/src/main/com/google/gson/JsonPrimitive.java @@ -0,0 +1,184 @@ +package com.google.gson; + +import com.google.gson.internal.$Gson$Preconditions; +import com.google.gson.internal.LazilyParsedNumber; +import java.math.BigDecimal; +import java.math.BigInteger; + +public final class JsonPrimitive extends JsonElement { + private static final Class[] PRIMITIVE_TYPES; + private Object value; + + public JsonPrimitive(Boolean bool) { + this.setValue(bool); + } + + public JsonPrimitive(Number number) { + this.setValue(number); + } + + public JsonPrimitive(String string) { + this.setValue(string); + } + + public JsonPrimitive(Character c) { + this.setValue(c); + } + + JsonPrimitive(Object primitive) { + this.setValue(primitive); + } + + JsonPrimitive deepCopy() { + return this; + } + + void setValue(Object primitive) { + if (primitive instanceof Character) { + char c = (Character)primitive; + this.value = String.valueOf(c); + } else { + $Gson$Preconditions.checkArgument(primitive instanceof Number || isPrimitiveOrString(primitive)); + this.value = primitive; + } + + } + + public boolean isBoolean() { + return this.value instanceof Boolean; + } + + Boolean getAsBooleanWrapper() { + return (Boolean)this.value; + } + + public boolean getAsBoolean() { + return this.isBoolean() ? this.getAsBooleanWrapper() : Boolean.parseBoolean(this.getAsString()); + } + + public boolean isNumber() { + return this.value instanceof Number; + } + + public Number getAsNumber() { + return (Number)(this.value instanceof String ? new LazilyParsedNumber((String)this.value) : (Number)this.value); + } + + public boolean isString() { + return this.value instanceof String; + } + + public String getAsString() { + if (this.isNumber()) { + return this.getAsNumber().toString(); + } else { + return this.isBoolean() ? this.getAsBooleanWrapper().toString() : (String)this.value; + } + } + + public double getAsDouble() { + return this.isNumber() ? this.getAsNumber().doubleValue() : Double.parseDouble(this.getAsString()); + } + + public BigDecimal getAsBigDecimal() { + return this.value instanceof BigDecimal ? (BigDecimal)this.value : new BigDecimal(this.value.toString()); + } + + public BigInteger getAsBigInteger() { + return this.value instanceof BigInteger ? (BigInteger)this.value : new BigInteger(this.value.toString()); + } + + public float getAsFloat() { + return this.isNumber() ? this.getAsNumber().floatValue() : Float.parseFloat(this.getAsString()); + } + + public long getAsLong() { + return this.isNumber() ? this.getAsNumber().longValue() : Long.parseLong(this.getAsString()); + } + + public short getAsShort() { + return this.isNumber() ? this.getAsNumber().shortValue() : Short.parseShort(this.getAsString()); + } + + public int getAsInt() { + return this.isNumber() ? this.getAsNumber().intValue() : Integer.parseInt(this.getAsString()); + } + + public byte getAsByte() { + return this.isNumber() ? this.getAsNumber().byteValue() : Byte.parseByte(this.getAsString()); + } + + public char getAsCharacter() { + return this.getAsString().charAt(0); + } + + private static boolean isPrimitiveOrString(Object target) { + if (target instanceof String) { + return true; + } else { + Class classOfPrimitive = target.getClass(); + Class[] arr$ = PRIMITIVE_TYPES; + int len$ = arr$.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Class standardPrimitive = arr$[i$]; + if (standardPrimitive.isAssignableFrom(classOfPrimitive)) { + return true; + } + } + + return false; + } + } + + public int hashCode() { + if (this.value == null) { + return 31; + } else { + long value; + if (isIntegral(this)) { + value = this.getAsNumber().longValue(); + return (int)(value ^ value >>> 32); + } else if (this.value instanceof Number) { + value = Double.doubleToLongBits(this.getAsNumber().doubleValue()); + return (int)(value ^ value >>> 32); + } else { + return this.value.hashCode(); + } + } + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj != null && this.getClass() == obj.getClass()) { + JsonPrimitive other = (JsonPrimitive)obj; + if (this.value == null) { + return other.value == null; + } else if (isIntegral(this) && isIntegral(other)) { + return this.getAsNumber().longValue() == other.getAsNumber().longValue(); + } else if (this.value instanceof Number && other.value instanceof Number) { + double a = this.getAsNumber().doubleValue(); + double b = other.getAsNumber().doubleValue(); + return a == b || Double.isNaN(a) && Double.isNaN(b); + } else { + return this.value.equals(other.value); + } + } else { + return false; + } + } + + private static boolean isIntegral(JsonPrimitive primitive) { + if (!(primitive.value instanceof Number)) { + return false; + } else { + Number number = (Number)primitive.value; + return number instanceof BigInteger || number instanceof Long || number instanceof Integer || number instanceof Short || number instanceof Byte; + } + } + + static { + PRIMITIVE_TYPES = new Class[]{Integer.TYPE, Long.TYPE, Short.TYPE, Float.TYPE, Double.TYPE, Byte.TYPE, Boolean.TYPE, Character.TYPE, Integer.class, Long.class, Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class}; + } +} diff --git a/src/main/com/google/gson/JsonSerializationContext.java b/src/main/com/google/gson/JsonSerializationContext.java new file mode 100644 index 0000000..3d56eb5 --- /dev/null +++ b/src/main/com/google/gson/JsonSerializationContext.java @@ -0,0 +1,9 @@ +package com.google.gson; + +import java.lang.reflect.Type; + +public interface JsonSerializationContext { + JsonElement serialize(Object var1); + + JsonElement serialize(Object var1, Type var2); +} diff --git a/src/main/com/google/gson/JsonSerializer.java b/src/main/com/google/gson/JsonSerializer.java new file mode 100644 index 0000000..2da630c --- /dev/null +++ b/src/main/com/google/gson/JsonSerializer.java @@ -0,0 +1,7 @@ +package com.google.gson; + +import java.lang.reflect.Type; + +public interface JsonSerializer { + JsonElement serialize(T var1, Type var2, JsonSerializationContext var3); +} diff --git a/src/main/com/google/gson/JsonStreamParser.java b/src/main/com/google/gson/JsonStreamParser.java new file mode 100644 index 0000000..54c8e32 --- /dev/null +++ b/src/main/com/google/gson/JsonStreamParser.java @@ -0,0 +1,62 @@ +package com.google.gson; + +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.MalformedJsonException; +import java.io.EOFException; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public final class JsonStreamParser implements Iterator { + private final JsonReader parser; + private final Object lock; + + public JsonStreamParser(String json) { + this((Reader)(new StringReader(json))); + } + + public JsonStreamParser(Reader reader) { + this.parser = new JsonReader(reader); + this.parser.setLenient(true); + this.lock = new Object(); + } + + public JsonElement next() throws JsonParseException { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } else { + try { + return Streams.parse(this.parser); + } catch (StackOverflowError var2) { + throw new JsonParseException("Failed parsing JSON source to Json", var2); + } catch (OutOfMemoryError var3) { + throw new JsonParseException("Failed parsing JSON source to Json", var3); + } catch (JsonParseException var4) { + throw var4.getCause() instanceof EOFException ? new NoSuchElementException() : var4; + } + } + } + + public boolean hasNext() { + synchronized(this.lock) { + boolean var10000; + try { + var10000 = this.parser.peek() != JsonToken.END_DOCUMENT; + } catch (MalformedJsonException var4) { + throw new JsonSyntaxException(var4); + } catch (IOException var5) { + throw new JsonIOException(var5); + } + + return var10000; + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/com/google/gson/JsonSyntaxException.java b/src/main/com/google/gson/JsonSyntaxException.java new file mode 100644 index 0000000..e51c28c --- /dev/null +++ b/src/main/com/google/gson/JsonSyntaxException.java @@ -0,0 +1,17 @@ +package com.google.gson; + +public final class JsonSyntaxException extends JsonParseException { + private static final long serialVersionUID = 1L; + + public JsonSyntaxException(String msg) { + super(msg); + } + + public JsonSyntaxException(String msg, Throwable cause) { + super(msg, cause); + } + + public JsonSyntaxException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/com/google/gson/LongSerializationPolicy.java b/src/main/com/google/gson/LongSerializationPolicy.java new file mode 100644 index 0000000..d444c85 --- /dev/null +++ b/src/main/com/google/gson/LongSerializationPolicy.java @@ -0,0 +1,24 @@ +package com.google.gson; + +public enum LongSerializationPolicy { + DEFAULT { + public JsonElement serialize(Long value) { + return new JsonPrimitive(value); + } + }, + STRING { + public JsonElement serialize(Long value) { + return new JsonPrimitive(String.valueOf(value)); + } + }; + + private LongSerializationPolicy() { + } + + public abstract JsonElement serialize(Long var1); + + // $FF: synthetic method + LongSerializationPolicy(Object x2) { + this(); + } +} diff --git a/src/main/com/google/gson/TreeTypeAdapter.java b/src/main/com/google/gson/TreeTypeAdapter.java new file mode 100644 index 0000000..7005238 --- /dev/null +++ b/src/main/com/google/gson/TreeTypeAdapter.java @@ -0,0 +1,95 @@ +package com.google.gson; + +import com.google.gson.internal.$Gson$Preconditions; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; + +final class TreeTypeAdapter extends TypeAdapter { + private final JsonSerializer serializer; + private final JsonDeserializer deserializer; + private final Gson gson; + private final TypeToken typeToken; + private final TypeAdapterFactory skipPast; + private TypeAdapter delegate; + + private TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deserializer, Gson gson, TypeToken typeToken, TypeAdapterFactory skipPast) { + this.serializer = serializer; + this.deserializer = deserializer; + this.gson = gson; + this.typeToken = typeToken; + this.skipPast = skipPast; + } + + public T read(JsonReader in) throws IOException { + if (this.deserializer == null) { + return this.delegate().read(in); + } else { + JsonElement value = Streams.parse(in); + return value.isJsonNull() ? null : this.deserializer.deserialize(value, this.typeToken.getType(), this.gson.deserializationContext); + } + } + + public void write(JsonWriter out, T value) throws IOException { + if (this.serializer == null) { + this.delegate().write(out, value); + } else if (value == null) { + out.nullValue(); + } else { + JsonElement tree = this.serializer.serialize(value, this.typeToken.getType(), this.gson.serializationContext); + Streams.write(tree, out); + } + } + + private TypeAdapter delegate() { + TypeAdapter d = this.delegate; + return d != null ? d : (this.delegate = this.gson.getDelegateAdapter(this.skipPast, this.typeToken)); + } + + public static TypeAdapterFactory newFactory(TypeToken exactType, Object typeAdapter) { + return new TreeTypeAdapter.SingleTypeFactory(typeAdapter, exactType, false, (Class)null); + } + + public static TypeAdapterFactory newFactoryWithMatchRawType(TypeToken exactType, Object typeAdapter) { + boolean matchRawType = exactType.getType() == exactType.getRawType(); + return new TreeTypeAdapter.SingleTypeFactory(typeAdapter, exactType, matchRawType, (Class)null); + } + + public static TypeAdapterFactory newTypeHierarchyFactory(Class hierarchyType, Object typeAdapter) { + return new TreeTypeAdapter.SingleTypeFactory(typeAdapter, (TypeToken)null, false, hierarchyType); + } + + // $FF: synthetic method + TreeTypeAdapter(JsonSerializer x0, JsonDeserializer x1, Gson x2, TypeToken x3, TypeAdapterFactory x4, Object x5) { + this(x0, x1, x2, x3, x4); + } + + private static class SingleTypeFactory implements TypeAdapterFactory { + private final TypeToken exactType; + private final boolean matchRawType; + private final Class hierarchyType; + private final JsonSerializer serializer; + private final JsonDeserializer deserializer; + + private SingleTypeFactory(Object typeAdapter, TypeToken exactType, boolean matchRawType, Class hierarchyType) { + this.serializer = typeAdapter instanceof JsonSerializer ? (JsonSerializer)typeAdapter : null; + this.deserializer = typeAdapter instanceof JsonDeserializer ? (JsonDeserializer)typeAdapter : null; + $Gson$Preconditions.checkArgument(this.serializer != null || this.deserializer != null); + this.exactType = exactType; + this.matchRawType = matchRawType; + this.hierarchyType = hierarchyType; + } + + public TypeAdapter create(Gson gson, TypeToken type) { + boolean matches = this.exactType != null ? this.exactType.equals(type) || this.matchRawType && this.exactType.getType() == type.getRawType() : this.hierarchyType.isAssignableFrom(type.getRawType()); + return matches ? new TreeTypeAdapter(this.serializer, this.deserializer, gson, type, this) : null; + } + + // $FF: synthetic method + SingleTypeFactory(Object x0, TypeToken x1, boolean x2, Class x3, Object x4) { + this(x0, x1, x2, x3); + } + } +} diff --git a/src/main/com/google/gson/TypeAdapter.java b/src/main/com/google/gson/TypeAdapter.java new file mode 100644 index 0000000..4730de0 --- /dev/null +++ b/src/main/com/google/gson/TypeAdapter.java @@ -0,0 +1,79 @@ +package com.google.gson; + +import com.google.gson.internal.bind.JsonTreeReader; +import com.google.gson.internal.bind.JsonTreeWriter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; + +public abstract class TypeAdapter { + public abstract void write(JsonWriter var1, T var2) throws IOException; + + public final void toJson(Writer out, T value) throws IOException { + JsonWriter writer = new JsonWriter(out); + this.write(writer, value); + } + + public final TypeAdapter nullSafe() { + return new TypeAdapter() { + public void write(JsonWriter out, T value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + TypeAdapter.this.write(out, value); + } + + } + + public T read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } else { + return TypeAdapter.this.read(reader); + } + } + }; + } + + public final String toJson(T value) throws IOException { + StringWriter stringWriter = new StringWriter(); + this.toJson(stringWriter, value); + return stringWriter.toString(); + } + + public final JsonElement toJsonTree(T value) { + try { + JsonTreeWriter jsonWriter = new JsonTreeWriter(); + this.write(jsonWriter, value); + return jsonWriter.get(); + } catch (IOException var3) { + throw new JsonIOException(var3); + } + } + + public abstract T read(JsonReader var1) throws IOException; + + public final T fromJson(Reader in) throws IOException { + JsonReader reader = new JsonReader(in); + return this.read(reader); + } + + public final T fromJson(String json) throws IOException { + return this.fromJson((Reader)(new StringReader(json))); + } + + public final T fromJsonTree(JsonElement jsonTree) { + try { + JsonReader jsonReader = new JsonTreeReader(jsonTree); + return this.read(jsonReader); + } catch (IOException var3) { + throw new JsonIOException(var3); + } + } +} diff --git a/src/main/com/google/gson/TypeAdapterFactory.java b/src/main/com/google/gson/TypeAdapterFactory.java new file mode 100644 index 0000000..6d30cab --- /dev/null +++ b/src/main/com/google/gson/TypeAdapterFactory.java @@ -0,0 +1,7 @@ +package com.google.gson; + +import com.google.gson.reflect.TypeToken; + +public interface TypeAdapterFactory { + TypeAdapter create(Gson var1, TypeToken var2); +} diff --git a/src/main/com/google/gson/annotations/Expose.java b/src/main/com/google/gson/annotations/Expose.java new file mode 100644 index 0000000..3f9dc6b --- /dev/null +++ b/src/main/com/google/gson/annotations/Expose.java @@ -0,0 +1,14 @@ +package com.google.gson.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface Expose { + boolean serialize() default true; + + boolean deserialize() default true; +} diff --git a/src/main/com/google/gson/annotations/SerializedName.java b/src/main/com/google/gson/annotations/SerializedName.java new file mode 100644 index 0000000..0a6ae73 --- /dev/null +++ b/src/main/com/google/gson/annotations/SerializedName.java @@ -0,0 +1,12 @@ +package com.google.gson.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface SerializedName { + String value(); +} diff --git a/src/main/com/google/gson/annotations/Since.java b/src/main/com/google/gson/annotations/Since.java new file mode 100644 index 0000000..36f5960 --- /dev/null +++ b/src/main/com/google/gson/annotations/Since.java @@ -0,0 +1,12 @@ +package com.google.gson.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.TYPE}) +public @interface Since { + double value(); +} diff --git a/src/main/com/google/gson/annotations/Until.java b/src/main/com/google/gson/annotations/Until.java new file mode 100644 index 0000000..c17eedb --- /dev/null +++ b/src/main/com/google/gson/annotations/Until.java @@ -0,0 +1,12 @@ +package com.google.gson.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.TYPE}) +public @interface Until { + double value(); +} diff --git a/src/main/com/google/gson/internal/$Gson$Preconditions.java b/src/main/com/google/gson/internal/$Gson$Preconditions.java new file mode 100644 index 0000000..92a433e --- /dev/null +++ b/src/main/com/google/gson/internal/$Gson$Preconditions.java @@ -0,0 +1,17 @@ +package com.google.gson.internal; + +public final class $Gson$Preconditions { + public static T checkNotNull(T obj) { + if (obj == null) { + throw new NullPointerException(); + } else { + return obj; + } + } + + public static void checkArgument(boolean condition) { + if (!condition) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/com/google/gson/internal/$Gson$Types.java b/src/main/com/google/gson/internal/$Gson$Types.java new file mode 100644 index 0000000..d2a2126 --- /dev/null +++ b/src/main/com/google/gson/internal/$Gson$Types.java @@ -0,0 +1,445 @@ +package com.google.gson.internal; + +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 java.util.Map; +import java.util.NoSuchElementException; +import java.util.Properties; + +public final class $Gson$Types { + static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; + + private $Gson$Types() { + } + + public static ParameterizedType newParameterizedTypeWithOwner(Type ownerType, Type rawType, Type... typeArguments) { + return new $Gson$Types.ParameterizedTypeImpl(ownerType, rawType, typeArguments); + } + + public static GenericArrayType arrayOf(Type componentType) { + return new $Gson$Types.GenericArrayTypeImpl(componentType); + } + + public static WildcardType subtypeOf(Type bound) { + return new $Gson$Types.WildcardTypeImpl(new Type[]{bound}, EMPTY_TYPE_ARRAY); + } + + public static WildcardType supertypeOf(Type bound) { + return new $Gson$Types.WildcardTypeImpl(new Type[]{Object.class}, new Type[]{bound}); + } + + public static Type canonicalize(Type type) { + if (type instanceof Class) { + Class c = (Class)type; + return (Type)(c.isArray() ? new $Gson$Types.GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c); + } else if (type instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType)type; + return new $Gson$Types.ParameterizedTypeImpl(p.getOwnerType(), p.getRawType(), p.getActualTypeArguments()); + } else if (type instanceof GenericArrayType) { + GenericArrayType g = (GenericArrayType)type; + return new $Gson$Types.GenericArrayTypeImpl(g.getGenericComponentType()); + } else if (type instanceof WildcardType) { + WildcardType w = (WildcardType)type; + return new $Gson$Types.WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds()); + } else { + return type; + } + } + + public static Class getRawType(Type type) { + if (type instanceof Class) { + return (Class)type; + } else if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)type; + Type rawType = parameterizedType.getRawType(); + $Gson$Preconditions.checkArgument(rawType instanceof Class); + return (Class)rawType; + } else if (type instanceof GenericArrayType) { + Type componentType = ((GenericArrayType)type).getGenericComponentType(); + return Array.newInstance(getRawType(componentType), 0).getClass(); + } else if (type instanceof TypeVariable) { + return Object.class; + } else if (type instanceof WildcardType) { + return getRawType(((WildcardType)type).getUpperBounds()[0]); + } else { + String className = type == null ? "null" : type.getClass().getName(); + throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but <" + type + "> is of type " + className); + } + } + + static boolean equal(Object a, Object b) { + return a == b || a != null && a.equals(b); + } + + public static boolean equals(Type a, Type b) { + if (a == b) { + return true; + } else if (a instanceof Class) { + return a.equals(b); + } else if (a instanceof ParameterizedType) { + if (!(b instanceof ParameterizedType)) { + return false; + } else { + ParameterizedType pa = (ParameterizedType)a; + ParameterizedType pb = (ParameterizedType)b; + return equal(pa.getOwnerType(), pb.getOwnerType()) && pa.getRawType().equals(pb.getRawType()) && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); + } + } else if (a instanceof GenericArrayType) { + if (!(b instanceof GenericArrayType)) { + return false; + } else { + GenericArrayType ga = (GenericArrayType)a; + GenericArrayType gb = (GenericArrayType)b; + return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); + } + } else if (a instanceof WildcardType) { + if (!(b instanceof WildcardType)) { + return false; + } else { + WildcardType wa = (WildcardType)a; + WildcardType wb = (WildcardType)b; + return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); + } + } else if (a instanceof TypeVariable) { + if (!(b instanceof TypeVariable)) { + return false; + } else { + TypeVariable va = (TypeVariable)a; + TypeVariable vb = (TypeVariable)b; + return va.getGenericDeclaration() == vb.getGenericDeclaration() && va.getName().equals(vb.getName()); + } + } else { + return false; + } + } + + private static int hashCodeOrZero(Object o) { + return o != null ? o.hashCode() : 0; + } + + public static String typeToString(Type type) { + return type instanceof Class ? ((Class)type).getName() : type.toString(); + } + + static Type getGenericSupertype(Type context, Class rawType, Class toResolve) { + if (toResolve == rawType) { + return context; + } else { + if (toResolve.isInterface()) { + Class[] interfaces = rawType.getInterfaces(); + int i = 0; + + for(int length = interfaces.length; i < length; ++i) { + if (interfaces[i] == toResolve) { + return rawType.getGenericInterfaces()[i]; + } + + if (toResolve.isAssignableFrom(interfaces[i])) { + return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve); + } + } + } + + if (!rawType.isInterface()) { + while(rawType != Object.class) { + Class rawSupertype = rawType.getSuperclass(); + if (rawSupertype == toResolve) { + return rawType.getGenericSuperclass(); + } + + if (toResolve.isAssignableFrom(rawSupertype)) { + return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve); + } + + rawType = rawSupertype; + } + } + + return toResolve; + } + } + + static Type getSupertype(Type context, Class contextRawType, Class supertype) { + $Gson$Preconditions.checkArgument(supertype.isAssignableFrom(contextRawType)); + return resolve(context, contextRawType, getGenericSupertype(context, contextRawType, supertype)); + } + + public static Type getArrayComponentType(Type array) { + return (Type)(array instanceof GenericArrayType ? ((GenericArrayType)array).getGenericComponentType() : ((Class)array).getComponentType()); + } + + public static Type getCollectionElementType(Type context, Class contextRawType) { + Type collectionType = getSupertype(context, contextRawType, Collection.class); + if (collectionType instanceof WildcardType) { + collectionType = ((WildcardType)collectionType).getUpperBounds()[0]; + } + + return (Type)(collectionType instanceof ParameterizedType ? ((ParameterizedType)collectionType).getActualTypeArguments()[0] : Object.class); + } + + public static Type[] getMapKeyAndValueTypes(Type context, Class contextRawType) { + if (context == Properties.class) { + return new Type[]{String.class, String.class}; + } else { + Type mapType = getSupertype(context, contextRawType, Map.class); + if (mapType instanceof ParameterizedType) { + ParameterizedType mapParameterizedType = (ParameterizedType)mapType; + return mapParameterizedType.getActualTypeArguments(); + } else { + return new Type[]{Object.class, Object.class}; + } + } + } + + public static Type resolve(Type context, Class contextRawType, Type toResolve) { + while(true) { + if (toResolve instanceof TypeVariable) { + TypeVariable typeVariable = (TypeVariable)toResolve; + toResolve = resolveTypeVariable(context, contextRawType, typeVariable); + if (toResolve != typeVariable) { + continue; + } + + return toResolve; + } + + Type newOwnerType; + if (toResolve instanceof Class && ((Class)toResolve).isArray()) { + Class original = (Class)toResolve; + Type componentType = original.getComponentType(); + newOwnerType = resolve(context, contextRawType, componentType); + return (Type)(componentType == newOwnerType ? original : arrayOf(newOwnerType)); + } + + Type ownerType; + if (toResolve instanceof GenericArrayType) { + GenericArrayType original = (GenericArrayType)toResolve; + ownerType = original.getGenericComponentType(); + newOwnerType = resolve(context, contextRawType, ownerType); + return ownerType == newOwnerType ? original : arrayOf(newOwnerType); + } + + if (toResolve instanceof ParameterizedType) { + ParameterizedType original = (ParameterizedType)toResolve; + ownerType = original.getOwnerType(); + newOwnerType = resolve(context, contextRawType, ownerType); + boolean changed = newOwnerType != ownerType; + Type[] args = original.getActualTypeArguments(); + int t = 0; + + for(int length = args.length; t < length; ++t) { + Type resolvedTypeArgument = resolve(context, contextRawType, args[t]); + if (resolvedTypeArgument != args[t]) { + if (!changed) { + args = (Type[])args.clone(); + changed = true; + } + + args[t] = resolvedTypeArgument; + } + } + + return changed ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args) : original; + } + + if (toResolve instanceof WildcardType) { + WildcardType original = (WildcardType)toResolve; + Type[] originalLowerBound = original.getLowerBounds(); + Type[] originalUpperBound = original.getUpperBounds(); + Type upperBound; + if (originalLowerBound.length == 1) { + upperBound = resolve(context, contextRawType, originalLowerBound[0]); + if (upperBound != originalLowerBound[0]) { + return supertypeOf(upperBound); + } + } else if (originalUpperBound.length == 1) { + upperBound = resolve(context, contextRawType, originalUpperBound[0]); + if (upperBound != originalUpperBound[0]) { + return subtypeOf(upperBound); + } + } + + return original; + } + + return toResolve; + } + } + + static Type resolveTypeVariable(Type context, Class contextRawType, TypeVariable unknown) { + Class declaredByRaw = declaringClassOf(unknown); + if (declaredByRaw == null) { + return unknown; + } else { + Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw); + if (declaredBy instanceof ParameterizedType) { + int index = indexOf(declaredByRaw.getTypeParameters(), unknown); + return ((ParameterizedType)declaredBy).getActualTypeArguments()[index]; + } else { + return unknown; + } + } + } + + private static int indexOf(Object[] array, Object toFind) { + for(int i = 0; i < array.length; ++i) { + if (toFind.equals(array[i])) { + return i; + } + } + + throw new NoSuchElementException(); + } + + private static Class declaringClassOf(TypeVariable typeVariable) { + GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); + return genericDeclaration instanceof Class ? (Class)genericDeclaration : null; + } + + private static void checkNotPrimitive(Type type) { + $Gson$Preconditions.checkArgument(!(type instanceof Class) || !((Class)type).isPrimitive()); + } + + private static final class WildcardTypeImpl implements WildcardType, Serializable { + private final Type upperBound; + private final Type lowerBound; + private static final long serialVersionUID = 0L; + + public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { + $Gson$Preconditions.checkArgument(lowerBounds.length <= 1); + $Gson$Preconditions.checkArgument(upperBounds.length == 1); + if (lowerBounds.length == 1) { + $Gson$Preconditions.checkNotNull(lowerBounds[0]); + $Gson$Types.checkNotPrimitive(lowerBounds[0]); + $Gson$Preconditions.checkArgument(upperBounds[0] == Object.class); + this.lowerBound = $Gson$Types.canonicalize(lowerBounds[0]); + this.upperBound = Object.class; + } else { + $Gson$Preconditions.checkNotNull(upperBounds[0]); + $Gson$Types.checkNotPrimitive(upperBounds[0]); + this.lowerBound = null; + this.upperBound = $Gson$Types.canonicalize(upperBounds[0]); + } + + } + + public Type[] getUpperBounds() { + return new Type[]{this.upperBound}; + } + + public Type[] getLowerBounds() { + return this.lowerBound != null ? new Type[]{this.lowerBound} : $Gson$Types.EMPTY_TYPE_ARRAY; + } + + public boolean equals(Object other) { + return other instanceof WildcardType && $Gson$Types.equals(this, (WildcardType)other); + } + + public int hashCode() { + return (this.lowerBound != null ? 31 + this.lowerBound.hashCode() : 1) ^ 31 + this.upperBound.hashCode(); + } + + public String toString() { + if (this.lowerBound != null) { + return "? super " + $Gson$Types.typeToString(this.lowerBound); + } else { + return this.upperBound == Object.class ? "?" : "? extends " + $Gson$Types.typeToString(this.upperBound); + } + } + } + + private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable { + private final Type componentType; + private static final long serialVersionUID = 0L; + + public GenericArrayTypeImpl(Type componentType) { + this.componentType = $Gson$Types.canonicalize(componentType); + } + + public Type getGenericComponentType() { + return this.componentType; + } + + public boolean equals(Object o) { + return o instanceof GenericArrayType && $Gson$Types.equals(this, (GenericArrayType)o); + } + + public int hashCode() { + return this.componentType.hashCode(); + } + + public String toString() { + return $Gson$Types.typeToString(this.componentType) + "[]"; + } + } + + private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { + private final Type ownerType; + private final Type rawType; + private final Type[] typeArguments; + private static final long serialVersionUID = 0L; + + public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { + if (rawType instanceof Class) { + Class rawTypeAsClass = (Class)rawType; + $Gson$Preconditions.checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null); + $Gson$Preconditions.checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null); + } + + this.ownerType = ownerType == null ? null : $Gson$Types.canonicalize(ownerType); + this.rawType = $Gson$Types.canonicalize(rawType); + this.typeArguments = (Type[])typeArguments.clone(); + + for(int t = 0; t < this.typeArguments.length; ++t) { + $Gson$Preconditions.checkNotNull(this.typeArguments[t]); + $Gson$Types.checkNotPrimitive(this.typeArguments[t]); + this.typeArguments[t] = $Gson$Types.canonicalize(this.typeArguments[t]); + } + + } + + public Type[] getActualTypeArguments() { + return (Type[])this.typeArguments.clone(); + } + + public Type getRawType() { + return this.rawType; + } + + public Type getOwnerType() { + return this.ownerType; + } + + public boolean equals(Object other) { + return other instanceof ParameterizedType && $Gson$Types.equals(this, (ParameterizedType)other); + } + + public int hashCode() { + return Arrays.hashCode(this.typeArguments) ^ this.rawType.hashCode() ^ $Gson$Types.hashCodeOrZero(this.ownerType); + } + + public String toString() { + StringBuilder stringBuilder = new StringBuilder(30 * (this.typeArguments.length + 1)); + stringBuilder.append($Gson$Types.typeToString(this.rawType)); + if (this.typeArguments.length == 0) { + return stringBuilder.toString(); + } else { + stringBuilder.append("<").append($Gson$Types.typeToString(this.typeArguments[0])); + + for(int i = 1; i < this.typeArguments.length; ++i) { + stringBuilder.append(", ").append($Gson$Types.typeToString(this.typeArguments[i])); + } + + return stringBuilder.append(">").toString(); + } + } + } +} diff --git a/src/main/com/google/gson/internal/ConstructorConstructor.java b/src/main/com/google/gson/internal/ConstructorConstructor.java new file mode 100644 index 0000000..86fdd17 --- /dev/null +++ b/src/main/com/google/gson/internal/ConstructorConstructor.java @@ -0,0 +1,168 @@ +package com.google.gson.internal; + +import com.google.gson.InstanceCreator; +import com.google.gson.JsonIOException; +import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +public final class ConstructorConstructor { + private final Map> instanceCreators; + + public ConstructorConstructor(Map> instanceCreators) { + this.instanceCreators = instanceCreators; + } + + public ObjectConstructor get(TypeToken typeToken) { + final Type type = typeToken.getType(); + Class rawType = typeToken.getRawType(); + final InstanceCreator typeCreator = (InstanceCreator)this.instanceCreators.get(type); + if (typeCreator != null) { + return new ObjectConstructor() { + public T construct() { + return typeCreator.createInstance(type); + } + }; + } else { + final InstanceCreator rawTypeCreator = (InstanceCreator)this.instanceCreators.get(rawType); + if (rawTypeCreator != null) { + return new ObjectConstructor() { + public T construct() { + return rawTypeCreator.createInstance(type); + } + }; + } else { + ObjectConstructor defaultConstructor = this.newDefaultConstructor(rawType); + if (defaultConstructor != null) { + return defaultConstructor; + } else { + ObjectConstructor defaultImplementation = this.newDefaultImplementationConstructor(type, rawType); + return defaultImplementation != null ? defaultImplementation : this.newUnsafeAllocator(type, rawType); + } + } + } + } + + private ObjectConstructor newDefaultConstructor(Class rawType) { + try { + final Constructor constructor = rawType.getDeclaredConstructor(); + if (!constructor.isAccessible()) { + constructor.setAccessible(true); + } + + return new ObjectConstructor() { + public T construct() { + try { + Object[] args = null; + return constructor.newInstance((Object[])args); + } catch (InstantiationException var2) { + throw new RuntimeException("Failed to invoke " + constructor + " with no args", var2); + } catch (InvocationTargetException var3) { + throw new RuntimeException("Failed to invoke " + constructor + " with no args", var3.getTargetException()); + } catch (IllegalAccessException var4) { + throw new AssertionError(var4); + } + } + }; + } catch (NoSuchMethodException var3) { + return null; + } + } + + private ObjectConstructor newDefaultImplementationConstructor(final Type type, Class rawType) { + if (Collection.class.isAssignableFrom(rawType)) { + if (SortedSet.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + public T construct() { + return new TreeSet(); + } + }; + } else if (EnumSet.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + public T construct() { + if (type instanceof ParameterizedType) { + Type elementType = ((ParameterizedType)type).getActualTypeArguments()[0]; + if (elementType instanceof Class) { + return EnumSet.noneOf((Class)elementType); + } else { + throw new JsonIOException("Invalid EnumSet type: " + type.toString()); + } + } else { + throw new JsonIOException("Invalid EnumSet type: " + type.toString()); + } + } + }; + } else if (Set.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + public T construct() { + return new LinkedHashSet(); + } + }; + } else { + return Queue.class.isAssignableFrom(rawType) ? new ObjectConstructor() { + public T construct() { + return new LinkedList(); + } + } : new ObjectConstructor() { + public T construct() { + return new ArrayList(); + } + }; + } + } else if (Map.class.isAssignableFrom(rawType)) { + if (SortedMap.class.isAssignableFrom(rawType)) { + return new ObjectConstructor() { + public T construct() { + return new TreeMap(); + } + }; + } else { + return type instanceof ParameterizedType && !String.class.isAssignableFrom(TypeToken.get(((ParameterizedType)type).getActualTypeArguments()[0]).getRawType()) ? new ObjectConstructor() { + public T construct() { + return new LinkedHashMap(); + } + } : new ObjectConstructor() { + public T construct() { + return new LinkedTreeMap(); + } + }; + } + } else { + return null; + } + } + + private ObjectConstructor newUnsafeAllocator(final Type type, final Class rawType) { + return new ObjectConstructor() { + private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create(); + + public T construct() { + try { + Object newInstance = this.unsafeAllocator.newInstance(rawType); + return newInstance; + } catch (Exception var2) { + throw new RuntimeException("Unable to invoke no-args constructor for " + type + ". " + "Register an InstanceCreator with Gson for this type may fix this problem.", var2); + } + } + }; + } + + public String toString() { + return this.instanceCreators.toString(); + } +} diff --git a/src/main/com/google/gson/internal/Excluder.java b/src/main/com/google/gson/internal/Excluder.java new file mode 100644 index 0000000..591b9de --- /dev/null +++ b/src/main/com/google/gson/internal/Excluder.java @@ -0,0 +1,226 @@ +package com.google.gson.internal; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.Since; +import com.google.gson.annotations.Until; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +public final class Excluder implements TypeAdapterFactory, Cloneable { + private static final double IGNORE_VERSIONS = -1.0D; + public static final Excluder DEFAULT = new Excluder(); + private double version = -1.0D; + private int modifiers = 136; + private boolean serializeInnerClasses = true; + private boolean requireExpose; + private List serializationStrategies = Collections.emptyList(); + private List deserializationStrategies = Collections.emptyList(); + + protected Excluder clone() { + try { + return (Excluder)super.clone(); + } catch (CloneNotSupportedException var2) { + throw new AssertionError(); + } + } + + public Excluder withVersion(double ignoreVersionsAfter) { + Excluder result = this.clone(); + result.version = ignoreVersionsAfter; + return result; + } + + public Excluder withModifiers(int... modifiers) { + Excluder result = this.clone(); + result.modifiers = 0; + int[] arr$ = modifiers; + int len$ = modifiers.length; + + for(int i$ = 0; i$ < len$; ++i$) { + int modifier = arr$[i$]; + result.modifiers |= modifier; + } + + return result; + } + + public Excluder disableInnerClassSerialization() { + Excluder result = this.clone(); + result.serializeInnerClasses = false; + return result; + } + + public Excluder excludeFieldsWithoutExposeAnnotation() { + Excluder result = this.clone(); + result.requireExpose = true; + return result; + } + + public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, boolean serialization, boolean deserialization) { + Excluder result = this.clone(); + if (serialization) { + result.serializationStrategies = new ArrayList(this.serializationStrategies); + result.serializationStrategies.add(exclusionStrategy); + } + + if (deserialization) { + result.deserializationStrategies = new ArrayList(this.deserializationStrategies); + result.deserializationStrategies.add(exclusionStrategy); + } + + return result; + } + + public TypeAdapter create(final Gson gson, final TypeToken type) { + Class rawType = type.getRawType(); + final boolean skipSerialize = this.excludeClass(rawType, true); + final boolean skipDeserialize = this.excludeClass(rawType, false); + return !skipSerialize && !skipDeserialize ? null : new TypeAdapter() { + private TypeAdapter delegate; + + public T read(JsonReader in) throws IOException { + if (skipDeserialize) { + in.skipValue(); + return null; + } else { + return this.delegate().read(in); + } + } + + public void write(JsonWriter out, T value) throws IOException { + if (skipSerialize) { + out.nullValue(); + } else { + this.delegate().write(out, value); + } + } + + private TypeAdapter delegate() { + TypeAdapter d = this.delegate; + return d != null ? d : (this.delegate = gson.getDelegateAdapter(Excluder.this, type)); + } + }; + } + + public boolean excludeField(Field field, boolean serialize) { + if ((this.modifiers & field.getModifiers()) != 0) { + return true; + } else if (this.version != -1.0D && !this.isValidVersion((Since)field.getAnnotation(Since.class), (Until)field.getAnnotation(Until.class))) { + return true; + } else if (field.isSynthetic()) { + return true; + } else { + if (this.requireExpose) { + label69: { + Expose annotation = (Expose)field.getAnnotation(Expose.class); + if (annotation != null) { + if (serialize) { + if (annotation.serialize()) { + break label69; + } + } else if (annotation.deserialize()) { + break label69; + } + } + + return true; + } + } + + if (!this.serializeInnerClasses && this.isInnerClass(field.getType())) { + return true; + } else if (this.isAnonymousOrLocal(field.getType())) { + return true; + } else { + List list = serialize ? this.serializationStrategies : this.deserializationStrategies; + if (!list.isEmpty()) { + FieldAttributes fieldAttributes = new FieldAttributes(field); + Iterator i$ = list.iterator(); + + while(i$.hasNext()) { + ExclusionStrategy exclusionStrategy = (ExclusionStrategy)i$.next(); + if (exclusionStrategy.shouldSkipField(fieldAttributes)) { + return true; + } + } + } + + return false; + } + } + } + + public boolean excludeClass(Class clazz, boolean serialize) { + if (this.version != -1.0D && !this.isValidVersion((Since)clazz.getAnnotation(Since.class), (Until)clazz.getAnnotation(Until.class))) { + return true; + } else if (!this.serializeInnerClasses && this.isInnerClass(clazz)) { + return true; + } else if (this.isAnonymousOrLocal(clazz)) { + return true; + } else { + List list = serialize ? this.serializationStrategies : this.deserializationStrategies; + Iterator i$ = list.iterator(); + + ExclusionStrategy exclusionStrategy; + do { + if (!i$.hasNext()) { + return false; + } + + exclusionStrategy = (ExclusionStrategy)i$.next(); + } while(!exclusionStrategy.shouldSkipClass(clazz)); + + return true; + } + } + + private boolean isAnonymousOrLocal(Class clazz) { + return !Enum.class.isAssignableFrom(clazz) && (clazz.isAnonymousClass() || clazz.isLocalClass()); + } + + private boolean isInnerClass(Class clazz) { + return clazz.isMemberClass() && !this.isStatic(clazz); + } + + private boolean isStatic(Class clazz) { + return (clazz.getModifiers() & 8) != 0; + } + + private boolean isValidVersion(Since since, Until until) { + return this.isValidSince(since) && this.isValidUntil(until); + } + + private boolean isValidSince(Since annotation) { + if (annotation != null) { + double annotationVersion = annotation.value(); + if (annotationVersion > this.version) { + return false; + } + } + + return true; + } + + private boolean isValidUntil(Until annotation) { + if (annotation != null) { + double annotationVersion = annotation.value(); + if (annotationVersion <= this.version) { + return false; + } + } + + return true; + } +} diff --git a/src/main/com/google/gson/internal/JsonReaderInternalAccess.java b/src/main/com/google/gson/internal/JsonReaderInternalAccess.java new file mode 100644 index 0000000..592cbdb --- /dev/null +++ b/src/main/com/google/gson/internal/JsonReaderInternalAccess.java @@ -0,0 +1,10 @@ +package com.google.gson.internal; + +import com.google.gson.stream.JsonReader; +import java.io.IOException; + +public abstract class JsonReaderInternalAccess { + public static JsonReaderInternalAccess INSTANCE; + + public abstract void promoteNameToValue(JsonReader var1) throws IOException; +} diff --git a/src/main/com/google/gson/internal/LazilyParsedNumber.java b/src/main/com/google/gson/internal/LazilyParsedNumber.java new file mode 100644 index 0000000..f5c3fcb --- /dev/null +++ b/src/main/com/google/gson/internal/LazilyParsedNumber.java @@ -0,0 +1,48 @@ +package com.google.gson.internal; + +import java.io.ObjectStreamException; +import java.math.BigDecimal; + +public final class LazilyParsedNumber extends Number { + private final String value; + + public LazilyParsedNumber(String value) { + this.value = value; + } + + public int intValue() { + try { + return Integer.parseInt(this.value); + } catch (NumberFormatException var4) { + try { + return (int)Long.parseLong(this.value); + } catch (NumberFormatException var3) { + return (new BigDecimal(this.value)).intValue(); + } + } + } + + public long longValue() { + try { + return Long.parseLong(this.value); + } catch (NumberFormatException var2) { + return (new BigDecimal(this.value)).longValue(); + } + } + + public float floatValue() { + return Float.parseFloat(this.value); + } + + public double doubleValue() { + return Double.parseDouble(this.value); + } + + public String toString() { + return this.value; + } + + private Object writeReplace() throws ObjectStreamException { + return new BigDecimal(this.value); + } +} diff --git a/src/main/com/google/gson/internal/LinkedTreeMap.java b/src/main/com/google/gson/internal/LinkedTreeMap.java new file mode 100644 index 0000000..c025def --- /dev/null +++ b/src/main/com/google/gson/internal/LinkedTreeMap.java @@ -0,0 +1,544 @@ +package com.google.gson.internal; + +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Map.Entry; + +public final class LinkedTreeMap extends AbstractMap implements Serializable { + private static final Comparator NATURAL_ORDER = new Comparator() { + public int compare(Comparable a, Comparable b) { + return a.compareTo(b); + } + }; + Comparator comparator; + LinkedTreeMap.Node root; + int size; + int modCount; + final LinkedTreeMap.Node header; + private LinkedTreeMap.EntrySet entrySet; + private LinkedTreeMap.KeySet keySet; + + public LinkedTreeMap() { + this(NATURAL_ORDER); + } + + public LinkedTreeMap(Comparator comparator) { + this.size = 0; + this.modCount = 0; + this.header = new LinkedTreeMap.Node(); + this.comparator = comparator != null ? comparator : NATURAL_ORDER; + } + + public int size() { + return this.size; + } + + public V get(Object key) { + LinkedTreeMap.Node node = this.findByObject(key); + return node != null ? node.value : null; + } + + public boolean containsKey(Object key) { + return this.findByObject(key) != null; + } + + public V put(K key, V value) { + if (key == null) { + throw new NullPointerException("key == null"); + } else { + LinkedTreeMap.Node created = this.find(key, true); + V result = created.value; + created.value = value; + return result; + } + } + + public void clear() { + this.root = null; + this.size = 0; + ++this.modCount; + LinkedTreeMap.Node header = this.header; + header.next = header.prev = header; + } + + public V remove(Object key) { + LinkedTreeMap.Node node = this.removeInternalByKey(key); + return node != null ? node.value : null; + } + + LinkedTreeMap.Node find(K key, boolean create) { + Comparator comparator = this.comparator; + LinkedTreeMap.Node nearest = this.root; + int comparison = 0; + LinkedTreeMap.Node created; + if (nearest != null) { + Comparable comparableKey = comparator == NATURAL_ORDER ? (Comparable)key : null; + + while(true) { + comparison = comparableKey != null ? comparableKey.compareTo(nearest.key) : comparator.compare(key, nearest.key); + if (comparison == 0) { + return nearest; + } + + created = comparison < 0 ? nearest.left : nearest.right; + if (created == null) { + break; + } + + nearest = created; + } + } + + if (!create) { + return null; + } else { + LinkedTreeMap.Node header = this.header; + if (nearest == null) { + if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) { + throw new ClassCastException(key.getClass().getName() + " is not Comparable"); + } + + created = new LinkedTreeMap.Node(nearest, key, header, header.prev); + this.root = created; + } else { + created = new LinkedTreeMap.Node(nearest, key, header, header.prev); + if (comparison < 0) { + nearest.left = created; + } else { + nearest.right = created; + } + + this.rebalance(nearest, true); + } + + ++this.size; + ++this.modCount; + return created; + } + } + + LinkedTreeMap.Node findByObject(Object key) { + try { + return key != null ? this.find(key, false) : null; + } catch (ClassCastException var3) { + return null; + } + } + + LinkedTreeMap.Node findByEntry(Entry entry) { + LinkedTreeMap.Node mine = this.findByObject(entry.getKey()); + boolean valuesEqual = mine != null && this.equal(mine.value, entry.getValue()); + return valuesEqual ? mine : null; + } + + private boolean equal(Object a, Object b) { + return a == b || a != null && a.equals(b); + } + + void removeInternal(LinkedTreeMap.Node node, boolean unlink) { + if (unlink) { + node.prev.next = node.next; + node.next.prev = node.prev; + } + + LinkedTreeMap.Node left = node.left; + LinkedTreeMap.Node right = node.right; + LinkedTreeMap.Node originalParent = node.parent; + if (left != null && right != null) { + LinkedTreeMap.Node adjacent = left.height > right.height ? left.last() : right.first(); + this.removeInternal(adjacent, false); + int leftHeight = 0; + left = node.left; + if (left != null) { + leftHeight = left.height; + adjacent.left = left; + left.parent = adjacent; + node.left = null; + } + + int rightHeight = 0; + right = node.right; + if (right != null) { + rightHeight = right.height; + adjacent.right = right; + right.parent = adjacent; + node.right = null; + } + + adjacent.height = Math.max(leftHeight, rightHeight) + 1; + this.replaceInParent(node, adjacent); + } else { + if (left != null) { + this.replaceInParent(node, left); + node.left = null; + } else if (right != null) { + this.replaceInParent(node, right); + node.right = null; + } else { + this.replaceInParent(node, (LinkedTreeMap.Node)null); + } + + this.rebalance(originalParent, false); + --this.size; + ++this.modCount; + } + } + + LinkedTreeMap.Node removeInternalByKey(Object key) { + LinkedTreeMap.Node node = this.findByObject(key); + if (node != null) { + this.removeInternal(node, true); + } + + return node; + } + + private void replaceInParent(LinkedTreeMap.Node node, LinkedTreeMap.Node replacement) { + LinkedTreeMap.Node parent = node.parent; + node.parent = null; + if (replacement != null) { + replacement.parent = parent; + } + + if (parent != null) { + if (parent.left == node) { + parent.left = replacement; + } else { + assert parent.right == node; + + parent.right = replacement; + } + } else { + this.root = replacement; + } + + } + + private void rebalance(LinkedTreeMap.Node unbalanced, boolean insert) { + for(LinkedTreeMap.Node node = unbalanced; node != null; node = node.parent) { + LinkedTreeMap.Node left = node.left; + LinkedTreeMap.Node right = node.right; + int leftHeight = left != null ? left.height : 0; + int rightHeight = right != null ? right.height : 0; + int delta = leftHeight - rightHeight; + LinkedTreeMap.Node leftLeft; + LinkedTreeMap.Node leftRight; + int leftRightHeight; + int leftLeftHeight; + int leftDelta; + if (delta == -2) { + leftLeft = right.left; + leftRight = right.right; + leftRightHeight = leftRight != null ? leftRight.height : 0; + leftLeftHeight = leftLeft != null ? leftLeft.height : 0; + leftDelta = leftLeftHeight - leftRightHeight; + if (leftDelta != -1 && (leftDelta != 0 || insert)) { + assert leftDelta == 1; + + this.rotateRight(right); + this.rotateLeft(node); + } else { + this.rotateLeft(node); + } + + if (insert) { + break; + } + } else if (delta == 2) { + leftLeft = left.left; + leftRight = left.right; + leftRightHeight = leftRight != null ? leftRight.height : 0; + leftLeftHeight = leftLeft != null ? leftLeft.height : 0; + leftDelta = leftLeftHeight - leftRightHeight; + if (leftDelta == 1 || leftDelta == 0 && !insert) { + this.rotateRight(node); + } else { + assert leftDelta == -1; + + this.rotateLeft(left); + this.rotateRight(node); + } + + if (insert) { + break; + } + } else if (delta == 0) { + node.height = leftHeight + 1; + if (insert) { + break; + } + } else { + assert delta == -1 || delta == 1; + + node.height = Math.max(leftHeight, rightHeight) + 1; + if (!insert) { + break; + } + } + } + + } + + private void rotateLeft(LinkedTreeMap.Node root) { + LinkedTreeMap.Node left = root.left; + LinkedTreeMap.Node pivot = root.right; + LinkedTreeMap.Node pivotLeft = pivot.left; + LinkedTreeMap.Node pivotRight = pivot.right; + root.right = pivotLeft; + if (pivotLeft != null) { + pivotLeft.parent = root; + } + + this.replaceInParent(root, pivot); + pivot.left = root; + root.parent = pivot; + root.height = Math.max(left != null ? left.height : 0, pivotLeft != null ? pivotLeft.height : 0) + 1; + pivot.height = Math.max(root.height, pivotRight != null ? pivotRight.height : 0) + 1; + } + + private void rotateRight(LinkedTreeMap.Node root) { + LinkedTreeMap.Node pivot = root.left; + LinkedTreeMap.Node right = root.right; + LinkedTreeMap.Node pivotLeft = pivot.left; + LinkedTreeMap.Node pivotRight = pivot.right; + root.left = pivotRight; + if (pivotRight != null) { + pivotRight.parent = root; + } + + this.replaceInParent(root, pivot); + pivot.right = root; + root.parent = pivot; + root.height = Math.max(right != null ? right.height : 0, pivotRight != null ? pivotRight.height : 0) + 1; + pivot.height = Math.max(root.height, pivotLeft != null ? pivotLeft.height : 0) + 1; + } + + public Set> entrySet() { + LinkedTreeMap.EntrySet result = this.entrySet; + return result != null ? result : (this.entrySet = new LinkedTreeMap.EntrySet()); + } + + public Set keySet() { + LinkedTreeMap.KeySet result = this.keySet; + return result != null ? result : (this.keySet = new LinkedTreeMap.KeySet()); + } + + private Object writeReplace() throws ObjectStreamException { + return new LinkedHashMap(this); + } + + class KeySet extends AbstractSet { + public int size() { + return LinkedTreeMap.this.size; + } + + public Iterator iterator() { + return new LinkedTreeMap.LinkedTreeMapIterator() { + public K next() { + return this.nextNode().key; + } + }; + } + + public boolean contains(Object o) { + return LinkedTreeMap.this.containsKey(o); + } + + public boolean remove(Object key) { + return LinkedTreeMap.this.removeInternalByKey(key) != null; + } + + public void clear() { + LinkedTreeMap.this.clear(); + } + } + + class EntrySet extends AbstractSet> { + public int size() { + return LinkedTreeMap.this.size; + } + + public Iterator> iterator() { + return new LinkedTreeMap.LinkedTreeMapIterator>() { + public Entry next() { + return this.nextNode(); + } + }; + } + + public boolean contains(Object o) { + return o instanceof Entry && LinkedTreeMap.this.findByEntry((Entry)o) != null; + } + + public boolean remove(Object o) { + if (!(o instanceof Entry)) { + return false; + } else { + LinkedTreeMap.Node node = LinkedTreeMap.this.findByEntry((Entry)o); + if (node == null) { + return false; + } else { + LinkedTreeMap.this.removeInternal(node, true); + return true; + } + } + } + + public void clear() { + LinkedTreeMap.this.clear(); + } + } + + private abstract class LinkedTreeMapIterator implements Iterator { + LinkedTreeMap.Node next; + LinkedTreeMap.Node lastReturned; + int expectedModCount; + + private LinkedTreeMapIterator() { + this.next = LinkedTreeMap.this.header.next; + this.lastReturned = null; + this.expectedModCount = LinkedTreeMap.this.modCount; + } + + public final boolean hasNext() { + return this.next != LinkedTreeMap.this.header; + } + + final LinkedTreeMap.Node nextNode() { + LinkedTreeMap.Node e = this.next; + if (e == LinkedTreeMap.this.header) { + throw new NoSuchElementException(); + } else if (LinkedTreeMap.this.modCount != this.expectedModCount) { + throw new ConcurrentModificationException(); + } else { + this.next = e.next; + return this.lastReturned = e; + } + } + + public final void remove() { + if (this.lastReturned == null) { + throw new IllegalStateException(); + } else { + LinkedTreeMap.this.removeInternal(this.lastReturned, true); + this.lastReturned = null; + this.expectedModCount = LinkedTreeMap.this.modCount; + } + } + + // $FF: synthetic method + LinkedTreeMapIterator(Object x1) { + this(); + } + } + + static final class Node implements Entry { + LinkedTreeMap.Node parent; + LinkedTreeMap.Node left; + LinkedTreeMap.Node right; + LinkedTreeMap.Node next; + LinkedTreeMap.Node prev; + final K key; + V value; + int height; + + Node() { + this.key = null; + this.next = this.prev = this; + } + + Node(LinkedTreeMap.Node parent, K key, LinkedTreeMap.Node next, LinkedTreeMap.Node prev) { + this.parent = parent; + this.key = key; + this.height = 1; + this.next = next; + this.prev = prev; + prev.next = this; + next.prev = this; + } + + public K getKey() { + return this.key; + } + + public V getValue() { + return this.value; + } + + public V setValue(V value) { + V oldValue = this.value; + this.value = value; + return oldValue; + } + + public boolean equals(Object o) { + if (!(o instanceof Entry)) { + return false; + } else { + boolean var10000; + label38: { + label27: { + Entry other = (Entry)o; + if (this.key == null) { + if (other.getKey() != null) { + break label27; + } + } else if (!this.key.equals(other.getKey())) { + break label27; + } + + if (this.value == null) { + if (other.getValue() == null) { + break label38; + } + } else if (this.value.equals(other.getValue())) { + break label38; + } + } + + var10000 = false; + return var10000; + } + + var10000 = true; + return var10000; + } + } + + public int hashCode() { + return (this.key == null ? 0 : this.key.hashCode()) ^ (this.value == null ? 0 : this.value.hashCode()); + } + + public String toString() { + return this.key + "=" + this.value; + } + + public LinkedTreeMap.Node first() { + LinkedTreeMap.Node node = this; + + for(LinkedTreeMap.Node child = this.left; child != null; child = child.left) { + node = child; + } + + return node; + } + + public LinkedTreeMap.Node last() { + LinkedTreeMap.Node node = this; + + for(LinkedTreeMap.Node child = this.right; child != null; child = child.right) { + node = child; + } + + return node; + } + } +} diff --git a/src/main/com/google/gson/internal/ObjectConstructor.java b/src/main/com/google/gson/internal/ObjectConstructor.java new file mode 100644 index 0000000..40b0ca7 --- /dev/null +++ b/src/main/com/google/gson/internal/ObjectConstructor.java @@ -0,0 +1,5 @@ +package com.google.gson.internal; + +public interface ObjectConstructor { + T construct(); +} diff --git a/src/main/com/google/gson/internal/Primitives.java b/src/main/com/google/gson/internal/Primitives.java new file mode 100644 index 0000000..0ec6ae5 --- /dev/null +++ b/src/main/com/google/gson/internal/Primitives.java @@ -0,0 +1,53 @@ +package com.google.gson.internal; + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public final class Primitives { + private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; + private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPE; + + private Primitives() { + } + + private static void add(Map, Class> forward, Map, Class> backward, Class key, Class value) { + forward.put(key, value); + backward.put(value, key); + } + + public static boolean isPrimitive(Type type) { + return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type); + } + + public static boolean isWrapperType(Type type) { + return WRAPPER_TO_PRIMITIVE_TYPE.containsKey($Gson$Preconditions.checkNotNull(type)); + } + + public static Class wrap(Class type) { + Class wrapped = (Class)PRIMITIVE_TO_WRAPPER_TYPE.get($Gson$Preconditions.checkNotNull(type)); + return wrapped == null ? type : wrapped; + } + + public static Class unwrap(Class type) { + Class unwrapped = (Class)WRAPPER_TO_PRIMITIVE_TYPE.get($Gson$Preconditions.checkNotNull(type)); + return unwrapped == null ? type : unwrapped; + } + + static { + Map, Class> primToWrap = new HashMap(16); + Map, Class> wrapToPrim = new HashMap(16); + add(primToWrap, wrapToPrim, Boolean.TYPE, Boolean.class); + add(primToWrap, wrapToPrim, Byte.TYPE, Byte.class); + add(primToWrap, wrapToPrim, Character.TYPE, Character.class); + add(primToWrap, wrapToPrim, Double.TYPE, Double.class); + add(primToWrap, wrapToPrim, Float.TYPE, Float.class); + add(primToWrap, wrapToPrim, Integer.TYPE, Integer.class); + add(primToWrap, wrapToPrim, Long.TYPE, Long.class); + add(primToWrap, wrapToPrim, Short.TYPE, Short.class); + add(primToWrap, wrapToPrim, Void.TYPE, Void.class); + PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap); + WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim); + } +} diff --git a/src/main/com/google/gson/internal/Streams.java b/src/main/com/google/gson/internal/Streams.java new file mode 100644 index 0000000..d697f11 --- /dev/null +++ b/src/main/com/google/gson/internal/Streams.java @@ -0,0 +1,92 @@ +package com.google.gson.internal; + +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.google.gson.stream.MalformedJsonException; +import java.io.EOFException; +import java.io.IOException; +import java.io.Writer; + +public final class Streams { + public static JsonElement parse(JsonReader reader) throws JsonParseException { + boolean isEmpty = true; + + try { + reader.peek(); + isEmpty = false; + return (JsonElement)TypeAdapters.JSON_ELEMENT.read(reader); + } catch (EOFException var3) { + if (isEmpty) { + return JsonNull.INSTANCE; + } else { + throw new JsonSyntaxException(var3); + } + } catch (MalformedJsonException var4) { + throw new JsonSyntaxException(var4); + } catch (IOException var5) { + throw new JsonIOException(var5); + } catch (NumberFormatException var6) { + throw new JsonSyntaxException(var6); + } + } + + public static void write(JsonElement element, JsonWriter writer) throws IOException { + TypeAdapters.JSON_ELEMENT.write(writer, element); + } + + public static Writer writerForAppendable(Appendable appendable) { + return (Writer)(appendable instanceof Writer ? (Writer)appendable : new Streams.AppendableWriter(appendable)); + } + + private static final class AppendableWriter extends Writer { + private final Appendable appendable; + private final Streams.AppendableWriter.CurrentWrite currentWrite; + + private AppendableWriter(Appendable appendable) { + this.currentWrite = new Streams.AppendableWriter.CurrentWrite(); + this.appendable = appendable; + } + + public void write(char[] chars, int offset, int length) throws IOException { + this.currentWrite.chars = chars; + this.appendable.append(this.currentWrite, offset, offset + length); + } + + public void write(int i) throws IOException { + this.appendable.append((char)i); + } + + public void flush() { + } + + public void close() { + } + + // $FF: synthetic method + AppendableWriter(Appendable x0, Object x1) { + this(x0); + } + + static class CurrentWrite implements CharSequence { + char[] chars; + + public int length() { + return this.chars.length; + } + + public char charAt(int i) { + return this.chars[i]; + } + + public CharSequence subSequence(int start, int end) { + return new String(this.chars, start, end - start); + } + } + } +} diff --git a/src/main/com/google/gson/internal/UnsafeAllocator.java b/src/main/com/google/gson/internal/UnsafeAllocator.java new file mode 100644 index 0000000..fd082c6 --- /dev/null +++ b/src/main/com/google/gson/internal/UnsafeAllocator.java @@ -0,0 +1,55 @@ +package com.google.gson.internal; + +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public abstract class UnsafeAllocator { + public abstract T newInstance(Class var1) throws Exception; + + public static UnsafeAllocator create() { + try { + Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + final Object unsafe = f.get((Object)null); + final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class); + return new UnsafeAllocator() { + public T newInstance(Class c) throws Exception { + return allocateInstance.invoke(unsafe, c); + } + }; + } catch (Exception var6) { + final Method getConstructorId; + try { + getConstructorId = ObjectInputStream.class.getDeclaredMethod("newInstance", Class.class, Class.class); + getConstructorId.setAccessible(true); + return new UnsafeAllocator() { + public T newInstance(Class c) throws Exception { + return getConstructorId.invoke((Object)null, c, Object.class); + } + }; + } catch (Exception var5) { + try { + getConstructorId = ObjectStreamClass.class.getDeclaredMethod("getConstructorId", Class.class); + getConstructorId.setAccessible(true); + final int constructorId = (Integer)getConstructorId.invoke((Object)null, Object.class); + final Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance", Class.class, Integer.TYPE); + newInstance.setAccessible(true); + return new UnsafeAllocator() { + public T newInstance(Class c) throws Exception { + return newInstance.invoke((Object)null, c, constructorId); + } + }; + } catch (Exception var4) { + return new UnsafeAllocator() { + public T newInstance(Class c) { + throw new UnsupportedOperationException("Cannot allocate " + c); + } + }; + } + } + } + } +} diff --git a/src/main/com/google/gson/internal/bind/ArrayTypeAdapter.java b/src/main/com/google/gson/internal/bind/ArrayTypeAdapter.java new file mode 100644 index 0000000..124943d --- /dev/null +++ b/src/main/com/google/gson/internal/bind/ArrayTypeAdapter.java @@ -0,0 +1,79 @@ +package com.google.gson.internal.bind; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.$Gson$Types; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public final class ArrayTypeAdapter extends TypeAdapter { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { + public TypeAdapter create(Gson gson, TypeToken typeToken) { + Type type = typeToken.getType(); + if (type instanceof GenericArrayType || type instanceof Class && ((Class)type).isArray()) { + Type componentType = $Gson$Types.getArrayComponentType(type); + TypeAdapter componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType)); + return new ArrayTypeAdapter(gson, componentTypeAdapter, $Gson$Types.getRawType(componentType)); + } else { + return null; + } + } + }; + private final Class componentType; + private final TypeAdapter componentTypeAdapter; + + public ArrayTypeAdapter(Gson context, TypeAdapter componentTypeAdapter, Class componentType) { + this.componentTypeAdapter = new TypeAdapterRuntimeTypeWrapper(context, componentTypeAdapter, componentType); + this.componentType = componentType; + } + + public Object read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + List list = new ArrayList(); + in.beginArray(); + + Object array; + while(in.hasNext()) { + array = this.componentTypeAdapter.read(in); + list.add(array); + } + + in.endArray(); + array = Array.newInstance(this.componentType, list.size()); + + for(int i = 0; i < list.size(); ++i) { + Array.set(array, i, list.get(i)); + } + + return array; + } + } + + public void write(JsonWriter out, Object array) throws IOException { + if (array == null) { + out.nullValue(); + } else { + out.beginArray(); + int i = 0; + + for(int length = Array.getLength(array); i < length; ++i) { + E value = Array.get(array, i); + this.componentTypeAdapter.write(out, value); + } + + out.endArray(); + } + } +} diff --git a/src/main/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/src/main/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java new file mode 100644 index 0000000..44abb63 --- /dev/null +++ b/src/main/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java @@ -0,0 +1,82 @@ +package com.google.gson.internal.bind; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.ObjectConstructor; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Iterator; + +public final class CollectionTypeAdapterFactory implements TypeAdapterFactory { + private final ConstructorConstructor constructorConstructor; + + public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) { + this.constructorConstructor = constructorConstructor; + } + + public TypeAdapter create(Gson gson, TypeToken typeToken) { + Type type = typeToken.getType(); + Class rawType = typeToken.getRawType(); + if (!Collection.class.isAssignableFrom(rawType)) { + return null; + } else { + Type elementType = $Gson$Types.getCollectionElementType(type, rawType); + TypeAdapter elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType)); + ObjectConstructor constructor = this.constructorConstructor.get(typeToken); + TypeAdapter result = new CollectionTypeAdapterFactory.Adapter(gson, elementType, elementTypeAdapter, constructor); + return result; + } + } + + private static final class Adapter extends TypeAdapter> { + private final TypeAdapter elementTypeAdapter; + private final ObjectConstructor> constructor; + + public Adapter(Gson context, Type elementType, TypeAdapter elementTypeAdapter, ObjectConstructor> constructor) { + this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper(context, elementTypeAdapter, elementType); + this.constructor = constructor; + } + + public Collection read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + Collection collection = (Collection)this.constructor.construct(); + in.beginArray(); + + while(in.hasNext()) { + E instance = this.elementTypeAdapter.read(in); + collection.add(instance); + } + + in.endArray(); + return collection; + } + } + + public void write(JsonWriter out, Collection collection) throws IOException { + if (collection == null) { + out.nullValue(); + } else { + out.beginArray(); + Iterator i$ = collection.iterator(); + + while(i$.hasNext()) { + E element = i$.next(); + this.elementTypeAdapter.write(out, element); + } + + out.endArray(); + } + } + } +} diff --git a/src/main/com/google/gson/internal/bind/DateTypeAdapter.java b/src/main/com/google/gson/internal/bind/DateTypeAdapter.java new file mode 100644 index 0000000..03fc33d --- /dev/null +++ b/src/main/com/google/gson/internal/bind/DateTypeAdapter.java @@ -0,0 +1,74 @@ +package com.google.gson.internal.bind; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +public final class DateTypeAdapter extends TypeAdapter { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { + public TypeAdapter create(Gson gson, TypeToken typeToken) { + return typeToken.getRawType() == Date.class ? new DateTypeAdapter() : null; + } + }; + private final DateFormat enUsFormat; + private final DateFormat localFormat; + private final DateFormat iso8601Format; + + public DateTypeAdapter() { + this.enUsFormat = DateFormat.getDateTimeInstance(2, 2, Locale.US); + this.localFormat = DateFormat.getDateTimeInstance(2, 2); + this.iso8601Format = buildIso8601Format(); + } + + private static DateFormat buildIso8601Format() { + DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC")); + return iso8601Format; + } + + public Date read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + return this.deserializeToDate(in.nextString()); + } + } + + private synchronized Date deserializeToDate(String json) { + try { + return this.localFormat.parse(json); + } catch (ParseException var5) { + try { + return this.enUsFormat.parse(json); + } catch (ParseException var4) { + try { + return this.iso8601Format.parse(json); + } catch (ParseException var3) { + throw new JsonSyntaxException(json, var3); + } + } + } + } + + public synchronized void write(JsonWriter out, Date value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + String dateFormatAsString = this.enUsFormat.format(value); + out.value(dateFormatAsString); + } + } +} diff --git a/src/main/com/google/gson/internal/bind/JsonTreeReader.java b/src/main/com/google/gson/internal/bind/JsonTreeReader.java new file mode 100644 index 0000000..3fddc79 --- /dev/null +++ b/src/main/com/google/gson/internal/bind/JsonTreeReader.java @@ -0,0 +1,210 @@ +package com.google.gson.internal.bind; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +public final class JsonTreeReader extends JsonReader { + private static final Reader UNREADABLE_READER = new Reader() { + public int read(char[] buffer, int offset, int count) throws IOException { + throw new AssertionError(); + } + + public void close() throws IOException { + throw new AssertionError(); + } + }; + private static final Object SENTINEL_CLOSED = new Object(); + private final List stack = new ArrayList(); + + public JsonTreeReader(JsonElement element) { + super(UNREADABLE_READER); + this.stack.add(element); + } + + public void beginArray() throws IOException { + this.expect(JsonToken.BEGIN_ARRAY); + JsonArray array = (JsonArray)this.peekStack(); + this.stack.add(array.iterator()); + } + + public void endArray() throws IOException { + this.expect(JsonToken.END_ARRAY); + this.popStack(); + this.popStack(); + } + + public void beginObject() throws IOException { + this.expect(JsonToken.BEGIN_OBJECT); + JsonObject object = (JsonObject)this.peekStack(); + this.stack.add(object.entrySet().iterator()); + } + + public void endObject() throws IOException { + this.expect(JsonToken.END_OBJECT); + this.popStack(); + this.popStack(); + } + + public boolean hasNext() throws IOException { + JsonToken token = this.peek(); + return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY; + } + + public JsonToken peek() throws IOException { + if (this.stack.isEmpty()) { + return JsonToken.END_DOCUMENT; + } else { + Object o = this.peekStack(); + if (o instanceof Iterator) { + boolean isObject = this.stack.get(this.stack.size() - 2) instanceof JsonObject; + Iterator iterator = (Iterator)o; + if (iterator.hasNext()) { + if (isObject) { + return JsonToken.NAME; + } else { + this.stack.add(iterator.next()); + return this.peek(); + } + } else { + return isObject ? JsonToken.END_OBJECT : JsonToken.END_ARRAY; + } + } else if (o instanceof JsonObject) { + return JsonToken.BEGIN_OBJECT; + } else if (o instanceof JsonArray) { + return JsonToken.BEGIN_ARRAY; + } else if (o instanceof JsonPrimitive) { + JsonPrimitive primitive = (JsonPrimitive)o; + if (primitive.isString()) { + return JsonToken.STRING; + } else if (primitive.isBoolean()) { + return JsonToken.BOOLEAN; + } else if (primitive.isNumber()) { + return JsonToken.NUMBER; + } else { + throw new AssertionError(); + } + } else if (o instanceof JsonNull) { + return JsonToken.NULL; + } else if (o == SENTINEL_CLOSED) { + throw new IllegalStateException("JsonReader is closed"); + } else { + throw new AssertionError(); + } + } + } + + private Object peekStack() { + return this.stack.get(this.stack.size() - 1); + } + + private Object popStack() { + return this.stack.remove(this.stack.size() - 1); + } + + private void expect(JsonToken expected) throws IOException { + if (this.peek() != expected) { + throw new IllegalStateException("Expected " + expected + " but was " + this.peek()); + } + } + + public String nextName() throws IOException { + this.expect(JsonToken.NAME); + Iterator i = (Iterator)this.peekStack(); + Entry entry = (Entry)i.next(); + this.stack.add(entry.getValue()); + return (String)entry.getKey(); + } + + public String nextString() throws IOException { + JsonToken token = this.peek(); + if (token != JsonToken.STRING && token != JsonToken.NUMBER) { + throw new IllegalStateException("Expected " + JsonToken.STRING + " but was " + token); + } else { + return ((JsonPrimitive)this.popStack()).getAsString(); + } + } + + public boolean nextBoolean() throws IOException { + this.expect(JsonToken.BOOLEAN); + return ((JsonPrimitive)this.popStack()).getAsBoolean(); + } + + public void nextNull() throws IOException { + this.expect(JsonToken.NULL); + this.popStack(); + } + + public double nextDouble() throws IOException { + JsonToken token = this.peek(); + if (token != JsonToken.NUMBER && token != JsonToken.STRING) { + throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token); + } else { + double result = ((JsonPrimitive)this.peekStack()).getAsDouble(); + if (this.isLenient() || !Double.isNaN(result) && !Double.isInfinite(result)) { + this.popStack(); + return result; + } else { + throw new NumberFormatException("JSON forbids NaN and infinities: " + result); + } + } + } + + public long nextLong() throws IOException { + JsonToken token = this.peek(); + if (token != JsonToken.NUMBER && token != JsonToken.STRING) { + throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token); + } else { + long result = ((JsonPrimitive)this.peekStack()).getAsLong(); + this.popStack(); + return result; + } + } + + public int nextInt() throws IOException { + JsonToken token = this.peek(); + if (token != JsonToken.NUMBER && token != JsonToken.STRING) { + throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token); + } else { + int result = ((JsonPrimitive)this.peekStack()).getAsInt(); + this.popStack(); + return result; + } + } + + public void close() throws IOException { + this.stack.clear(); + this.stack.add(SENTINEL_CLOSED); + } + + public void skipValue() throws IOException { + if (this.peek() == JsonToken.NAME) { + this.nextName(); + } else { + this.popStack(); + } + + } + + public String toString() { + return this.getClass().getSimpleName(); + } + + public void promoteNameToValue() throws IOException { + this.expect(JsonToken.NAME); + Iterator i = (Iterator)this.peekStack(); + Entry entry = (Entry)i.next(); + this.stack.add(entry.getValue()); + this.stack.add(new JsonPrimitive((String)entry.getKey())); + } +} diff --git a/src/main/com/google/gson/internal/bind/JsonTreeWriter.java b/src/main/com/google/gson/internal/bind/JsonTreeWriter.java new file mode 100644 index 0000000..98e83ec --- /dev/null +++ b/src/main/com/google/gson/internal/bind/JsonTreeWriter.java @@ -0,0 +1,186 @@ +package com.google.gson.internal.bind; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +public final class JsonTreeWriter extends JsonWriter { + private static final Writer UNWRITABLE_WRITER = new Writer() { + public void write(char[] buffer, int offset, int counter) { + throw new AssertionError(); + } + + public void flush() throws IOException { + throw new AssertionError(); + } + + public void close() throws IOException { + throw new AssertionError(); + } + }; + private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed"); + private final List stack = new ArrayList(); + private String pendingName; + private JsonElement product; + + public JsonTreeWriter() { + super(UNWRITABLE_WRITER); + this.product = JsonNull.INSTANCE; + } + + public JsonElement get() { + if (!this.stack.isEmpty()) { + throw new IllegalStateException("Expected one JSON element but was " + this.stack); + } else { + return this.product; + } + } + + private JsonElement peek() { + return (JsonElement)this.stack.get(this.stack.size() - 1); + } + + private void put(JsonElement value) { + if (this.pendingName != null) { + if (!value.isJsonNull() || this.getSerializeNulls()) { + JsonObject object = (JsonObject)this.peek(); + object.add(this.pendingName, value); + } + + this.pendingName = null; + } else if (this.stack.isEmpty()) { + this.product = value; + } else { + JsonElement element = this.peek(); + if (!(element instanceof JsonArray)) { + throw new IllegalStateException(); + } + + ((JsonArray)element).add(value); + } + + } + + public JsonWriter beginArray() throws IOException { + JsonArray array = new JsonArray(); + this.put(array); + this.stack.add(array); + return this; + } + + public JsonWriter endArray() throws IOException { + if (!this.stack.isEmpty() && this.pendingName == null) { + JsonElement element = this.peek(); + if (element instanceof JsonArray) { + this.stack.remove(this.stack.size() - 1); + return this; + } else { + throw new IllegalStateException(); + } + } else { + throw new IllegalStateException(); + } + } + + public JsonWriter beginObject() throws IOException { + JsonObject object = new JsonObject(); + this.put(object); + this.stack.add(object); + return this; + } + + public JsonWriter endObject() throws IOException { + if (!this.stack.isEmpty() && this.pendingName == null) { + JsonElement element = this.peek(); + if (element instanceof JsonObject) { + this.stack.remove(this.stack.size() - 1); + return this; + } else { + throw new IllegalStateException(); + } + } else { + throw new IllegalStateException(); + } + } + + public JsonWriter name(String name) throws IOException { + if (!this.stack.isEmpty() && this.pendingName == null) { + JsonElement element = this.peek(); + if (element instanceof JsonObject) { + this.pendingName = name; + return this; + } else { + throw new IllegalStateException(); + } + } else { + throw new IllegalStateException(); + } + } + + public JsonWriter value(String value) throws IOException { + if (value == null) { + return this.nullValue(); + } else { + this.put(new JsonPrimitive(value)); + return this; + } + } + + public JsonWriter nullValue() throws IOException { + this.put(JsonNull.INSTANCE); + return this; + } + + public JsonWriter value(boolean value) throws IOException { + this.put(new JsonPrimitive(value)); + return this; + } + + public JsonWriter value(double value) throws IOException { + if (this.isLenient() || !Double.isNaN(value) && !Double.isInfinite(value)) { + this.put(new JsonPrimitive(value)); + return this; + } else { + throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value); + } + } + + public JsonWriter value(long value) throws IOException { + this.put(new JsonPrimitive(value)); + return this; + } + + public JsonWriter value(Number value) throws IOException { + if (value == null) { + return this.nullValue(); + } else { + if (!this.isLenient()) { + double d = value.doubleValue(); + if (Double.isNaN(d) || Double.isInfinite(d)) { + throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value); + } + } + + this.put(new JsonPrimitive(value)); + return this; + } + } + + public void flush() throws IOException { + } + + public void close() throws IOException { + if (!this.stack.isEmpty()) { + throw new IOException("Incomplete document"); + } else { + this.stack.add(SENTINEL_CLOSED); + } + } +} diff --git a/src/main/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/src/main/com/google/gson/internal/bind/MapTypeAdapterFactory.java new file mode 100644 index 0000000..f721608 --- /dev/null +++ b/src/main/com/google/gson/internal/bind/MapTypeAdapterFactory.java @@ -0,0 +1,185 @@ +package com.google.gson.internal.bind; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.JsonReaderInternalAccess; +import com.google.gson.internal.ObjectConstructor; +import com.google.gson.internal.Streams; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public final class MapTypeAdapterFactory implements TypeAdapterFactory { + private final ConstructorConstructor constructorConstructor; + private final boolean complexMapKeySerialization; + + public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, boolean complexMapKeySerialization) { + this.constructorConstructor = constructorConstructor; + this.complexMapKeySerialization = complexMapKeySerialization; + } + + public TypeAdapter create(Gson gson, TypeToken typeToken) { + Type type = typeToken.getType(); + Class rawType = typeToken.getRawType(); + if (!Map.class.isAssignableFrom(rawType)) { + return null; + } else { + Class rawTypeOfSrc = $Gson$Types.getRawType(type); + Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc); + TypeAdapter keyAdapter = this.getKeyAdapter(gson, keyAndValueTypes[0]); + TypeAdapter valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1])); + ObjectConstructor constructor = this.constructorConstructor.get(typeToken); + TypeAdapter result = new MapTypeAdapterFactory.Adapter(gson, keyAndValueTypes[0], keyAdapter, keyAndValueTypes[1], valueAdapter, constructor); + return result; + } + } + + private TypeAdapter getKeyAdapter(Gson context, Type keyType) { + return keyType != Boolean.TYPE && keyType != Boolean.class ? context.getAdapter(TypeToken.get(keyType)) : TypeAdapters.BOOLEAN_AS_STRING; + } + + private final class Adapter extends TypeAdapter> { + private final TypeAdapter keyTypeAdapter; + private final TypeAdapter valueTypeAdapter; + private final ObjectConstructor> constructor; + + public Adapter(Gson context, Type keyType, TypeAdapter keyTypeAdapter, Type valueType, TypeAdapter valueTypeAdapter, ObjectConstructor> constructor) { + this.keyTypeAdapter = new TypeAdapterRuntimeTypeWrapper(context, keyTypeAdapter, keyType); + this.valueTypeAdapter = new TypeAdapterRuntimeTypeWrapper(context, valueTypeAdapter, valueType); + this.constructor = constructor; + } + + public Map read(JsonReader in) throws IOException { + JsonToken peek = in.peek(); + if (peek == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + Map map = (Map)this.constructor.construct(); + Object key; + Object value; + Object replaced; + if (peek == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + + while(in.hasNext()) { + in.beginArray(); + key = this.keyTypeAdapter.read(in); + value = this.valueTypeAdapter.read(in); + replaced = map.put(key, value); + if (replaced != null) { + throw new JsonSyntaxException("duplicate key: " + key); + } + + in.endArray(); + } + + in.endArray(); + } else { + in.beginObject(); + + while(in.hasNext()) { + JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in); + key = this.keyTypeAdapter.read(in); + value = this.valueTypeAdapter.read(in); + replaced = map.put(key, value); + if (replaced != null) { + throw new JsonSyntaxException("duplicate key: " + key); + } + } + + in.endObject(); + } + + return map; + } + } + + public void write(JsonWriter out, Map map) throws IOException { + if (map == null) { + out.nullValue(); + } else if (!MapTypeAdapterFactory.this.complexMapKeySerialization) { + out.beginObject(); + Iterator i$x = map.entrySet().iterator(); + + while(i$x.hasNext()) { + Entry entryx = (Entry)i$x.next(); + out.name(String.valueOf(entryx.getKey())); + this.valueTypeAdapter.write(out, entryx.getValue()); + } + + out.endObject(); + } else { + boolean hasComplexKeys = false; + List keys = new ArrayList(map.size()); + List values = new ArrayList(map.size()); + + JsonElement keyElementx; + for(Iterator i$ = map.entrySet().iterator(); i$.hasNext(); hasComplexKeys |= keyElementx.isJsonArray() || keyElementx.isJsonObject()) { + Entry entry = (Entry)i$.next(); + keyElementx = this.keyTypeAdapter.toJsonTree(entry.getKey()); + keys.add(keyElementx); + values.add(entry.getValue()); + } + + int i; + if (hasComplexKeys) { + out.beginArray(); + + for(i = 0; i < keys.size(); ++i) { + out.beginArray(); + Streams.write((JsonElement)keys.get(i), out); + this.valueTypeAdapter.write(out, values.get(i)); + out.endArray(); + } + + out.endArray(); + } else { + out.beginObject(); + + for(i = 0; i < keys.size(); ++i) { + JsonElement keyElement = (JsonElement)keys.get(i); + out.name(this.keyToString(keyElement)); + this.valueTypeAdapter.write(out, values.get(i)); + } + + out.endObject(); + } + + } + } + + private String keyToString(JsonElement keyElement) { + if (keyElement.isJsonPrimitive()) { + JsonPrimitive primitive = keyElement.getAsJsonPrimitive(); + if (primitive.isNumber()) { + return String.valueOf(primitive.getAsNumber()); + } else if (primitive.isBoolean()) { + return Boolean.toString(primitive.getAsBoolean()); + } else if (primitive.isString()) { + return primitive.getAsString(); + } else { + throw new AssertionError(); + } + } else if (keyElement.isJsonNull()) { + return "null"; + } else { + throw new AssertionError(); + } + } + } +} diff --git a/src/main/com/google/gson/internal/bind/ObjectTypeAdapter.java b/src/main/com/google/gson/internal/bind/ObjectTypeAdapter.java new file mode 100644 index 0000000..e10b136 --- /dev/null +++ b/src/main/com/google/gson/internal/bind/ObjectTypeAdapter.java @@ -0,0 +1,83 @@ +package com.google.gson.internal.bind; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.LinkedTreeMap; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public final class ObjectTypeAdapter extends TypeAdapter { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { + public TypeAdapter create(Gson gson, TypeToken type) { + return type.getRawType() == Object.class ? new ObjectTypeAdapter(gson) : null; + } + }; + private final Gson gson; + + private ObjectTypeAdapter(Gson gson) { + this.gson = gson; + } + + public Object read(JsonReader in) throws IOException { + JsonToken token = in.peek(); + switch(token) { + case BEGIN_ARRAY: + List list = new ArrayList(); + in.beginArray(); + + while(in.hasNext()) { + list.add(this.read(in)); + } + + in.endArray(); + return list; + case BEGIN_OBJECT: + Map map = new LinkedTreeMap(); + in.beginObject(); + + while(in.hasNext()) { + map.put(in.nextName(), this.read(in)); + } + + in.endObject(); + return map; + case STRING: + return in.nextString(); + case NUMBER: + return in.nextDouble(); + case BOOLEAN: + return in.nextBoolean(); + case NULL: + in.nextNull(); + return null; + default: + throw new IllegalStateException(); + } + } + + public void write(JsonWriter out, Object value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + TypeAdapter typeAdapter = this.gson.getAdapter(value.getClass()); + if (typeAdapter instanceof ObjectTypeAdapter) { + out.beginObject(); + out.endObject(); + } else { + typeAdapter.write(out, value); + } + } + } + + // $FF: synthetic method + ObjectTypeAdapter(Gson x0, Object x1) { + this(x0); + } +} diff --git a/src/main/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/src/main/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java new file mode 100644 index 0000000..0fe6eda --- /dev/null +++ b/src/main/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -0,0 +1,192 @@ +package com.google.gson.internal.bind; + +import com.google.gson.FieldNamingStrategy; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.annotations.SerializedName; +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.ConstructorConstructor; +import com.google.gson.internal.Excluder; +import com.google.gson.internal.ObjectConstructor; +import com.google.gson.internal.Primitives; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { + private final ConstructorConstructor constructorConstructor; + private final FieldNamingStrategy fieldNamingPolicy; + private final Excluder excluder; + + public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, FieldNamingStrategy fieldNamingPolicy, Excluder excluder) { + this.constructorConstructor = constructorConstructor; + this.fieldNamingPolicy = fieldNamingPolicy; + this.excluder = excluder; + } + + public boolean excludeField(Field f, boolean serialize) { + return !this.excluder.excludeClass(f.getType(), serialize) && !this.excluder.excludeField(f, serialize); + } + + private String getFieldName(Field f) { + SerializedName serializedName = (SerializedName)f.getAnnotation(SerializedName.class); + return serializedName == null ? this.fieldNamingPolicy.translateName(f) : serializedName.value(); + } + + public TypeAdapter create(Gson gson, TypeToken type) { + Class raw = type.getRawType(); + if (!Object.class.isAssignableFrom(raw)) { + return null; + } else { + ObjectConstructor constructor = this.constructorConstructor.get(type); + return new ReflectiveTypeAdapterFactory.Adapter(constructor, this.getBoundFields(gson, type, raw)); + } + } + + private ReflectiveTypeAdapterFactory.BoundField createBoundField(final Gson context, final Field field, String name, final TypeToken fieldType, boolean serialize, boolean deserialize) { + final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); + return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) { + final TypeAdapter typeAdapter = context.getAdapter(fieldType); + + void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException { + Object fieldValue = field.get(value); + TypeAdapter t = new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType()); + t.write(writer, fieldValue); + } + + void read(JsonReader reader, Object value) throws IOException, IllegalAccessException { + Object fieldValue = this.typeAdapter.read(reader); + if (fieldValue != null || !isPrimitive) { + field.set(value, fieldValue); + } + + } + }; + } + + private Map getBoundFields(Gson context, TypeToken type, Class raw) { + Map result = new LinkedHashMap(); + if (raw.isInterface()) { + return result; + } else { + for(Type declaredType = type.getType(); raw != Object.class; raw = type.getRawType()) { + Field[] fields = raw.getDeclaredFields(); + Field[] arr$ = fields; + int len$ = fields.length; + + for(int i$ = 0; i$ < len$; ++i$) { + Field field = arr$[i$]; + boolean serialize = this.excludeField(field, true); + boolean deserialize = this.excludeField(field, false); + if (serialize || deserialize) { + field.setAccessible(true); + Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType()); + ReflectiveTypeAdapterFactory.BoundField boundField = this.createBoundField(context, field, this.getFieldName(field), TypeToken.get(fieldType), serialize, deserialize); + ReflectiveTypeAdapterFactory.BoundField previous = (ReflectiveTypeAdapterFactory.BoundField)result.put(boundField.name, boundField); + if (previous != null) { + throw new IllegalArgumentException(declaredType + " declares multiple JSON fields named " + previous.name); + } + } + } + + type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass())); + } + + return result; + } + } + + public static final class Adapter extends TypeAdapter { + private final ObjectConstructor constructor; + private final Map boundFields; + + private Adapter(ObjectConstructor constructor, Map boundFields) { + this.constructor = constructor; + this.boundFields = boundFields; + } + + public T read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + Object instance = this.constructor.construct(); + + try { + in.beginObject(); + + while(in.hasNext()) { + String name = in.nextName(); + ReflectiveTypeAdapterFactory.BoundField field = (ReflectiveTypeAdapterFactory.BoundField)this.boundFields.get(name); + if (field != null && field.deserialized) { + field.read(in, instance); + } else { + in.skipValue(); + } + } + } catch (IllegalStateException var5) { + throw new JsonSyntaxException(var5); + } catch (IllegalAccessException var6) { + throw new AssertionError(var6); + } + + in.endObject(); + return instance; + } + } + + public void write(JsonWriter out, T value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.beginObject(); + + try { + Iterator i$ = this.boundFields.values().iterator(); + + while(i$.hasNext()) { + ReflectiveTypeAdapterFactory.BoundField boundField = (ReflectiveTypeAdapterFactory.BoundField)i$.next(); + if (boundField.serialized) { + out.name(boundField.name); + boundField.write(out, value); + } + } + } catch (IllegalAccessException var5) { + throw new AssertionError(); + } + + out.endObject(); + } + } + + // $FF: synthetic method + Adapter(ObjectConstructor x0, Map x1, Object x2) { + this(x0, x1); + } + } + + abstract static class BoundField { + final String name; + final boolean serialized; + final boolean deserialized; + + protected BoundField(String name, boolean serialized, boolean deserialized) { + this.name = name; + this.serialized = serialized; + this.deserialized = deserialized; + } + + abstract void write(JsonWriter var1, Object var2) throws IOException, IllegalAccessException; + + abstract void read(JsonReader var1, Object var2) throws IOException, IllegalAccessException; + } +} diff --git a/src/main/com/google/gson/internal/bind/SqlDateTypeAdapter.java b/src/main/com/google/gson/internal/bind/SqlDateTypeAdapter.java new file mode 100644 index 0000000..3abbf2c --- /dev/null +++ b/src/main/com/google/gson/internal/bind/SqlDateTypeAdapter.java @@ -0,0 +1,42 @@ +package com.google.gson.internal.bind; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.sql.Date; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; + +public final class SqlDateTypeAdapter extends TypeAdapter { + public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { + public TypeAdapter create(Gson gson, TypeToken typeToken) { + return typeToken.getRawType() == Date.class ? new SqlDateTypeAdapter() : null; + } + }; + private final DateFormat format = new SimpleDateFormat("MMM d, yyyy"); + + public synchronized Date read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } else { + try { + long utilDate = this.format.parse(in.nextString()).getTime(); + return new Date(utilDate); + } catch (ParseException var4) { + throw new JsonSyntaxException(var4); + } + } + } + + public synchronized void write(JsonWriter out, Date value) throws IOException { + out.value(value == null ? null : this.format.format(value)); + } +} diff --git a/src/main/com/google/gson/internal/bind/TimeTypeAdapter.java b/src/main/com/google/gson/internal/bind/TimeTypeAdapter.java new file mode 100644 index 0000000..bc9c8ad --- /dev/null +++ b/src/main/com/google/gson/internal/bind/TimeTypeAdapter.java @@ -0,0 +1,43 @@ +package com.google.gson.internal.bind; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.sql.Time; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public final class TimeTypeAdapter extends TypeAdapter