From 920405d8c5607ea5bbc76ffee5db3b4738b6d96e Mon Sep 17 00:00:00 2001 From: Sen Date: Tue, 25 Mar 2025 11:31:48 +0100 Subject: [PATCH] remove Windows support, revert new guava to small version --- java/src/game/Game.java | 10 +- java/src/game/Server.java | 12 +- java/src/game/ai/EntityAIAvoidEntity.java | 6 +- java/src/game/ai/EntityAIEatGrass.java | 8 +- .../game/ai/EntityAIFindEntityNearest.java | 4 +- .../game/ai/EntityAIMoveThroughVillage.java | 2 +- .../ai/EntityAINearestAttackableTarget.java | 6 +- java/src/game/ai/EntityAITakePlace.java | 2 +- java/src/game/ai/EntityAITargetNonTamed.java | 2 +- java/src/game/ai/EntityAITasks.java | 2 +- java/src/game/ai/EntitySenses.java | 2 +- java/src/game/audio/AudioInterface.java | 2 +- java/src/game/audio/SoundManager.java | 8 +- java/src/game/biome/Biome.java | 4 +- java/src/game/block/Block.java | 14 +- java/src/game/block/BlockButton.java | 2 +- java/src/game/block/BlockDoor.java | 2 +- java/src/game/block/BlockFence.java | 2 +- java/src/game/block/BlockFire.java | 2 +- java/src/game/block/BlockFloorPortal.java | 2 +- java/src/game/block/BlockFlower.java | 12 +- java/src/game/block/BlockHopper.java | 4 +- java/src/game/block/BlockLeaves.java | 2 +- java/src/game/block/BlockPistonBase.java | 2 +- java/src/game/block/BlockPumpkin.java | 2 +- java/src/game/block/BlockRailBase.java | 2 +- java/src/game/block/BlockRailDetector.java | 6 +- java/src/game/block/BlockRailPowered.java | 4 +- .../game/block/BlockRedstoneComparator.java | 2 +- java/src/game/block/BlockRedstoneTorch.java | 4 +- java/src/game/block/BlockRedstoneWire.java | 4 +- java/src/game/block/BlockSapling.java | 2 +- java/src/game/block/BlockSlab.java | 2 +- java/src/game/block/BlockStem.java | 4 +- java/src/game/block/BlockTorch.java | 4 +- java/src/game/clipboard/ClipboardPlacer.java | 2 +- java/src/game/clipboard/Rotation.java | 4 +- java/src/game/collect/AbstractBiMap.java | 399 ++ .../collect/AbstractIndexedListIterator.java | 106 + java/src/game/collect/AbstractIterator.java | 173 + java/src/game/collect/AbstractMapEntry.java | 62 + java/src/game/collect/AbstractTable.java | 223 + java/src/game/collect/BiMap.java | 106 + .../game/collect/CollectPreconditions.java | 49 + .../src/game/collect/DenseImmutableTable.java | 273 ++ java/src/game/collect/EmptyImmutableMap.java | 304 ++ java/src/game/collect/EmptyImmutableSet.java | 93 + java/src/game/collect/Filter.java | 659 +++ .../game/collect/ForwardingCollection.java | 250 ++ java/src/game/collect/ForwardingMap.java | 302 ++ java/src/game/collect/ForwardingMapEntry.java | 122 + java/src/game/collect/ForwardingObject.java | 72 + java/src/game/collect/ForwardingSet.java | 96 + java/src/game/collect/HashBiMap.java | 658 +++ java/src/game/collect/Hashing.java | 67 + java/src/game/collect/ImmutableAsList.java | 81 + .../src/game/collect/ImmutableCollection.java | 360 ++ java/src/game/collect/ImmutableEntry.java | 48 + java/src/game/collect/ImmutableEnumMap.java | 146 + java/src/game/collect/ImmutableEnumSet.java | 117 + java/src/game/collect/ImmutableList.java | 743 ++++ java/src/game/collect/ImmutableMap.java | 548 +++ java/src/game/collect/ImmutableMapEntry.java | 68 + .../game/collect/ImmutableMapEntrySet.java | 71 + java/src/game/collect/ImmutableMapKeySet.java | 90 + java/src/game/collect/ImmutableMapValues.java | 88 + java/src/game/collect/ImmutableSet.java | 533 +++ java/src/game/collect/ImmutableTable.java | 436 ++ java/src/game/collect/Iterables.java | 1029 +++++ java/src/game/collect/Iterators.java | 1296 ++++++ java/src/game/collect/Lists.java | 49 + java/src/game/collect/Maps.java | 3953 +++++++++++++++++ java/src/game/collect/ObjectArrays.java | 244 + java/src/game/collect/Preconditions.java | 350 ++ .../game/collect/RegularImmutableAsList.java | 65 + .../game/collect/RegularImmutableList.java | 106 + .../src/game/collect/RegularImmutableMap.java | 205 + .../src/game/collect/RegularImmutableSet.java | 92 + .../game/collect/RegularImmutableTable.java | 175 + java/src/game/collect/Sets.java | 1607 +++++++ .../game/collect/SparseImmutableTable.java | 108 + java/src/game/collect/StandardTable.java | 891 ++++ java/src/game/collect/Table.java | 291 ++ .../src/game/collect/TransformedIterator.java | 53 + .../game/collect/UnmodifiableIterator.java | 43 + .../collect/UnmodifiableListIterator.java | 53 + java/src/game/color/DyeColor.java | 2 +- java/src/game/command/ArgumentParser.java | 2 +- java/src/game/command/CachedExecutable.java | 4 +- java/src/game/command/EnumParser.java | 2 +- java/src/game/command/ScriptArgs.java | 6 +- java/src/game/command/ScriptEnvironment.java | 4 +- java/src/game/command/ScriptExecutable.java | 4 +- java/src/game/command/WorldParser.java | 2 +- .../game/command/commands/CommandHelp.java | 2 +- .../game/command/commands/CommandSpawn.java | 2 +- java/src/game/dimension/DimType.java | 2 +- java/src/game/dimension/Dimension.java | 6 +- java/src/game/dimension/Domain.java | 2 +- java/src/game/dimension/Galaxy.java | 2 +- java/src/game/dimension/Planet.java | 2 +- java/src/game/dimension/Sector.java | 2 +- java/src/game/dimension/Star.java | 2 +- java/src/game/enchantment/Enchantment.java | 4 +- .../game/enchantment/EnchantmentHelper.java | 4 +- java/src/game/entity/DataWatcher.java | 4 +- java/src/game/entity/EntityTrackerEntry.java | 2 +- java/src/game/entity/animal/EntityDragon.java | 2 +- java/src/game/entity/animal/EntityHorse.java | 4 +- java/src/game/entity/animal/EntityOcelot.java | 6 +- java/src/game/entity/animal/EntityRabbit.java | 4 +- java/src/game/entity/animal/EntitySheep.java | 2 +- java/src/game/entity/animal/EntityWolf.java | 4 +- .../src/game/entity/attributes/Attribute.java | 2 +- .../entity/attributes/AttributeInstance.java | 6 +- .../game/entity/attributes/AttributeMap.java | 4 +- .../entity/attributes/LowerStringMap.java | 2 +- java/src/game/entity/item/EntityFalling.java | 2 +- .../game/entity/item/EntityHopperCart.java | 4 +- java/src/game/entity/item/EntityXp.java | 2 +- java/src/game/entity/npc/Alignment.java | 2 +- java/src/game/entity/npc/ClassInfo.java | 2 +- .../game/entity/npc/EntityChaosMarine.java | 2 +- java/src/game/entity/npc/EntityMobNPC.java | 2 +- java/src/game/entity/npc/EntityNPC.java | 8 +- java/src/game/entity/npc/EntityPrimarch.java | 2 +- .../game/entity/npc/EntitySpaceMarine.java | 2 +- java/src/game/entity/npc/EntityZombie.java | 4 +- java/src/game/entity/npc/SpeciesInfo.java | 8 +- java/src/game/entity/types/EntityAnimal.java | 2 +- java/src/game/entity/types/EntityLiving.java | 8 +- java/src/game/future/AbstractFuture.java | 392 ++ java/src/game/future/ExecutionError.java | 60 + java/src/game/future/ExecutionList.java | 170 + java/src/game/future/FutureCallback.java | 43 + java/src/game/future/Futures.java | 1723 +++++++ java/src/game/future/ListenableFuture.java | 132 + .../src/game/future/ListenableFutureTask.java | 91 + java/src/game/future/MoreExecutors.java | 889 ++++ .../src/game/future/ThreadFactoryBuilder.java | 176 + .../future/UncheckedExecutionException.java | 65 + java/src/game/gui/Gui.java | 2 +- java/src/game/gui/GuiConsole.java | 2 +- java/src/game/gui/GuiInfo.java | 4 +- java/src/game/gui/container/GuiContainer.java | 4 +- java/src/game/gui/element/GuiList.java | 2 +- java/src/game/gui/world/GuiEdit.java | 4 +- java/src/game/gui/world/GuiWorlds.java | 4 +- java/src/game/init/CraftingRegistry.java | 4 +- java/src/game/init/EntityRegistry.java | 2 +- java/src/game/init/FluidRegistry.java | 4 +- java/src/game/init/ItemRegistry.java | 4 +- java/src/game/init/ObjectIntIdentityMap.java | 6 +- java/src/game/init/RegistryNamespaced.java | 4 +- java/src/game/init/RegistrySimple.java | 2 +- java/src/game/init/RotationRegistry.java | 8 +- java/src/game/init/SmeltingRegistry.java | 2 +- java/src/game/init/SpeciesRegistry.java | 6 +- java/src/game/init/TileRegistry.java | 2 +- java/src/game/init/ToolMaterial.java | 2 +- java/src/game/init/UniverseRegistry.java | 6 +- java/src/game/inventory/Container.java | 4 +- .../game/inventory/ContainerLocalMenu.java | 2 +- java/src/game/inventory/ContainerPlayer.java | 2 +- java/src/game/inventory/InventoryBasic.java | 2 +- java/src/game/item/Item.java | 4 +- java/src/game/item/ItemArmor.java | 6 +- java/src/game/item/ItemBucket.java | 2 +- java/src/game/item/ItemBucketMilk.java | 2 +- java/src/game/item/ItemFirework.java | 2 +- java/src/game/item/ItemFishFood.java | 2 +- java/src/game/item/ItemMagnet.java | 4 +- java/src/game/item/ItemMetal.java | 2 +- java/src/game/item/ItemMetalBlock.java | 2 +- java/src/game/item/ItemPotion.java | 4 +- java/src/game/item/ItemStack.java | 4 +- java/src/game/item/ItemSword.java | 2 +- java/src/game/item/ItemTool.java | 2 +- java/src/game/log/Log.java | 2 +- java/src/game/model/BakedModel.java | 2 +- java/src/game/model/ModelBakery.java | 6 +- java/src/game/model/ModelManager.java | 2 +- java/src/game/model/ModelRotation.java | 2 +- java/src/game/nbt/NBTParser.java | 2 +- java/src/game/nbt/NBTTagCompound.java | 2 +- java/src/game/nbt/NBTTagList.java | 2 +- java/src/game/network/IThreadListener.java | 2 +- java/src/game/network/NetConnection.java | 2 +- .../game/network/NetHandlerPlayClient.java | 2 +- .../game/network/NetHandlerPlayServer.java | 8 +- java/src/game/network/PacketRegistry.java | 6 +- .../packet/S20PacketEntityProperties.java | 2 +- java/src/game/packet/S27PacketExplosion.java | 2 +- .../game/packet/S38PacketPlayerListItem.java | 2 +- java/src/game/packet/SPacketChunkData.java | 2 +- java/src/game/pattern/BlockStateHelper.java | 8 +- java/src/game/potion/Potion.java | 2 +- java/src/game/potion/PotionHelper.java | 4 +- java/src/game/properties/PropertyBool.java | 2 +- .../game/properties/PropertyDirection.java | 10 +- java/src/game/properties/PropertyEnum.java | 14 +- java/src/game/properties/PropertyInteger.java | 4 +- java/src/game/renderer/BlockRenderer.java | 2 +- java/src/game/renderer/EntityRenderer.java | 4 +- java/src/game/renderer/ItemModelMesher.java | 4 +- java/src/game/renderer/RenderGlobal.java | 6 +- java/src/game/renderer/VertexFormat.java | 2 +- .../game/renderer/blockmodel/ModelBlock.java | 4 +- .../renderer/blockmodel/ModelGenerator.java | 4 +- .../renderer/blockmodel/MultiStateMap.java | 4 +- .../game/renderer/blockmodel/StateMap.java | 2 +- .../chunk/ChunkCompileTaskGenerator.java | 2 +- .../renderer/chunk/ChunkRenderDispatcher.java | 10 +- .../renderer/chunk/ChunkRenderWorker.java | 11 +- .../game/renderer/chunk/CompiledChunk.java | 2 +- java/src/game/renderer/chunk/RenderChunk.java | 4 +- .../src/game/renderer/entity/RenderHorse.java | 2 +- .../game/renderer/entity/RenderManager.java | 2 +- .../renderer/entity/RendererLivingEntity.java | 2 +- java/src/game/renderer/layers/LayerExtra.java | 2 +- java/src/game/renderer/model/ModelBase.java | 4 +- .../game/renderer/model/ModelRenderer.java | 2 +- .../renderer/particle/EffectRenderer.java | 4 +- .../game/renderer/particle/ParticleType.java | 4 +- .../renderer/texture/EntityTexManager.java | 6 +- .../game/renderer/texture/LayeredTexture.java | 2 +- java/src/game/renderer/texture/Stitcher.java | 4 +- .../renderer/texture/TextureAtlasSprite.java | 2 +- .../game/renderer/texture/TextureManager.java | 2 +- .../src/game/renderer/texture/TextureMap.java | 4 +- .../tileentity/TileEntityBannerRenderer.java | 4 +- .../TileEntityRendererDispatcher.java | 2 +- java/src/game/rng/WeightedList.java | 2 +- .../src/game/tileentity/TileEntityBanner.java | 2 +- .../src/game/tileentity/TileEntityHopper.java | 6 +- .../src/game/tileentity/TileEntityPiston.java | 2 +- java/src/game/util/Predicates.java | 645 +++ java/src/game/village/Village.java | 2 +- java/src/game/village/VillageCollection.java | 2 +- java/src/game/world/BlockPos.java | 2 +- java/src/game/world/Chunk.java | 10 +- .../game/world/ClassInheritanceMultiMap.java | 6 +- java/src/game/world/Converter.java | 2 +- java/src/game/world/EmptyChunk.java | 2 +- java/src/game/world/Explosion.java | 6 +- java/src/game/world/Facing.java | 10 +- java/src/game/world/Region.java | 4 +- java/src/game/world/Spawner.java | 2 +- java/src/game/world/State.java | 16 +- java/src/game/world/Weather.java | 2 +- java/src/game/world/World.java | 8 +- java/src/game/world/WorldClient.java | 4 +- java/src/game/world/WorldServer.java | 10 +- java/src/game/worldgen/BiomeGenLayered.java | 2 +- java/src/game/worldgen/GeneratorDebug.java | 2 +- .../worldgen/feature/WorldGenDesertWells.java | 4 +- java/src/game/worldgen/layer/IntCache.java | 2 +- .../worldgen/structure/MapGenStructure.java | 2 +- .../worldgen/structure/MapGenStructureIO.java | 2 +- .../worldgen/structure/MapGenVillage.java | 2 +- .../worldgen/structure/StructureBridge.java | 2 +- .../structure/StructureStronghold.java | 4 +- .../worldgen/structure/StructureVillage.java | 2 +- .../game/worldgen/tree/WorldGenBigTree.java | 2 +- 264 files changed, 22715 insertions(+), 377 deletions(-) create mode 100644 java/src/game/collect/AbstractBiMap.java create mode 100644 java/src/game/collect/AbstractIndexedListIterator.java create mode 100644 java/src/game/collect/AbstractIterator.java create mode 100644 java/src/game/collect/AbstractMapEntry.java create mode 100644 java/src/game/collect/AbstractTable.java create mode 100644 java/src/game/collect/BiMap.java create mode 100644 java/src/game/collect/CollectPreconditions.java create mode 100644 java/src/game/collect/DenseImmutableTable.java create mode 100644 java/src/game/collect/EmptyImmutableMap.java create mode 100644 java/src/game/collect/EmptyImmutableSet.java create mode 100644 java/src/game/collect/Filter.java create mode 100644 java/src/game/collect/ForwardingCollection.java create mode 100644 java/src/game/collect/ForwardingMap.java create mode 100644 java/src/game/collect/ForwardingMapEntry.java create mode 100644 java/src/game/collect/ForwardingObject.java create mode 100644 java/src/game/collect/ForwardingSet.java create mode 100644 java/src/game/collect/HashBiMap.java create mode 100644 java/src/game/collect/Hashing.java create mode 100644 java/src/game/collect/ImmutableAsList.java create mode 100644 java/src/game/collect/ImmutableCollection.java create mode 100644 java/src/game/collect/ImmutableEntry.java create mode 100644 java/src/game/collect/ImmutableEnumMap.java create mode 100644 java/src/game/collect/ImmutableEnumSet.java create mode 100644 java/src/game/collect/ImmutableList.java create mode 100644 java/src/game/collect/ImmutableMap.java create mode 100644 java/src/game/collect/ImmutableMapEntry.java create mode 100644 java/src/game/collect/ImmutableMapEntrySet.java create mode 100644 java/src/game/collect/ImmutableMapKeySet.java create mode 100644 java/src/game/collect/ImmutableMapValues.java create mode 100644 java/src/game/collect/ImmutableSet.java create mode 100644 java/src/game/collect/ImmutableTable.java create mode 100644 java/src/game/collect/Iterables.java create mode 100644 java/src/game/collect/Iterators.java create mode 100644 java/src/game/collect/Lists.java create mode 100644 java/src/game/collect/Maps.java create mode 100644 java/src/game/collect/ObjectArrays.java create mode 100644 java/src/game/collect/Preconditions.java create mode 100644 java/src/game/collect/RegularImmutableAsList.java create mode 100644 java/src/game/collect/RegularImmutableList.java create mode 100644 java/src/game/collect/RegularImmutableMap.java create mode 100644 java/src/game/collect/RegularImmutableSet.java create mode 100644 java/src/game/collect/RegularImmutableTable.java create mode 100644 java/src/game/collect/Sets.java create mode 100644 java/src/game/collect/SparseImmutableTable.java create mode 100644 java/src/game/collect/StandardTable.java create mode 100644 java/src/game/collect/Table.java create mode 100644 java/src/game/collect/TransformedIterator.java create mode 100644 java/src/game/collect/UnmodifiableIterator.java create mode 100644 java/src/game/collect/UnmodifiableListIterator.java create mode 100644 java/src/game/future/AbstractFuture.java create mode 100644 java/src/game/future/ExecutionError.java create mode 100644 java/src/game/future/ExecutionList.java create mode 100644 java/src/game/future/FutureCallback.java create mode 100644 java/src/game/future/Futures.java create mode 100644 java/src/game/future/ListenableFuture.java create mode 100644 java/src/game/future/ListenableFutureTask.java create mode 100644 java/src/game/future/MoreExecutors.java create mode 100644 java/src/game/future/ThreadFactoryBuilder.java create mode 100644 java/src/game/future/UncheckedExecutionException.java create mode 100644 java/src/game/util/Predicates.java diff --git a/java/src/game/Game.java b/java/src/game/Game.java index f62bde8..084715a 100755 --- a/java/src/game/Game.java +++ b/java/src/game/Game.java @@ -43,11 +43,11 @@ import javax.imageio.ImageIO; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFutureTask; +import game.collect.Lists; +import game.collect.Maps; +import game.future.Futures; +import game.future.ListenableFuture; +import game.future.ListenableFutureTask; import game.audio.AudioInterface; import game.audio.SoundManager; diff --git a/java/src/game/Server.java b/java/src/game/Server.java index 9da17ca..599333e 100755 --- a/java/src/game/Server.java +++ b/java/src/game/Server.java @@ -16,12 +16,12 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFutureTask; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import game.collect.Lists; +import game.collect.Maps; +import game.future.Futures; +import game.future.ListenableFuture; +import game.future.ListenableFutureTask; +import game.future.ThreadFactoryBuilder; import game.color.TextColor; import game.command.ScriptEnvironment; diff --git a/java/src/game/ai/EntityAIAvoidEntity.java b/java/src/game/ai/EntityAIAvoidEntity.java index edffc96..79e15c4 100755 --- a/java/src/game/ai/EntityAIAvoidEntity.java +++ b/java/src/game/ai/EntityAIAvoidEntity.java @@ -2,8 +2,8 @@ package game.ai; import java.util.List; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; +import java.util.function.Predicate; +import game.util.Predicates; import game.entity.Entity; import game.entity.types.EntityLiving; @@ -39,7 +39,7 @@ public class EntityAIAvoidEntity extends EntityAIBase { this.canBeSeenSelector = new Predicate() { - public boolean apply(Entity p_apply_1_) + public boolean test(Entity p_apply_1_) { return p_apply_1_.isEntityAlive() && EntityAIAvoidEntity.this.theEntity.getEntitySenses().canSee(p_apply_1_); } diff --git a/java/src/game/ai/EntityAIEatGrass.java b/java/src/game/ai/EntityAIEatGrass.java index 5fa87eb..9886e91 100755 --- a/java/src/game/ai/EntityAIEatGrass.java +++ b/java/src/game/ai/EntityAIEatGrass.java @@ -1,7 +1,7 @@ package game.ai; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; +import java.util.function.Predicate; +import game.util.Predicates; import game.block.BlockTallGrass; import game.entity.animal.EntitySheep; @@ -40,7 +40,7 @@ public class EntityAIEatGrass extends EntityAIBase else { BlockPos blockpos = new BlockPos(this.grassEaterEntity.posX, this.grassEaterEntity.posY, this.grassEaterEntity.posZ); - return field_179505_b.apply(this.entityWorld.getState(blockpos)) ? true : this.entityWorld.getState(blockpos.down()).getBlock() == Blocks.grass; + return field_179505_b.test(this.entityWorld.getState(blockpos)) ? true : this.entityWorld.getState(blockpos.down()).getBlock() == Blocks.grass; } } @@ -89,7 +89,7 @@ public class EntityAIEatGrass extends EntityAIBase { BlockPos blockpos = new BlockPos(this.grassEaterEntity.posX, this.grassEaterEntity.posY, this.grassEaterEntity.posZ); - if (field_179505_b.apply(this.entityWorld.getState(blockpos))) + if (field_179505_b.test(this.entityWorld.getState(blockpos))) { if (Config.mobGrief) { diff --git a/java/src/game/ai/EntityAIFindEntityNearest.java b/java/src/game/ai/EntityAIFindEntityNearest.java index e4366d5..99ee83e 100755 --- a/java/src/game/ai/EntityAIFindEntityNearest.java +++ b/java/src/game/ai/EntityAIFindEntityNearest.java @@ -3,7 +3,7 @@ package game.ai; import java.util.Collections; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.attributes.AttributeInstance; import game.entity.attributes.Attributes; @@ -29,7 +29,7 @@ public class EntityAIFindEntityNearest extends EntityAIBase this.field_179443_c = new Predicate() { - public boolean apply(EntityLiving p_apply_1_) + public boolean test(EntityLiving p_apply_1_) { double d0 = EntityAIFindEntityNearest.this.getFollowRange(); diff --git a/java/src/game/ai/EntityAIMoveThroughVillage.java b/java/src/game/ai/EntityAIMoveThroughVillage.java index 4e9a794..2085b57 100755 --- a/java/src/game/ai/EntityAIMoveThroughVillage.java +++ b/java/src/game/ai/EntityAIMoveThroughVillage.java @@ -2,7 +2,7 @@ package game.ai; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.types.EntityLiving; import game.pathfinding.PathEntity; diff --git a/java/src/game/ai/EntityAINearestAttackableTarget.java b/java/src/game/ai/EntityAINearestAttackableTarget.java index a377701..1ffbcf6 100755 --- a/java/src/game/ai/EntityAINearestAttackableTarget.java +++ b/java/src/game/ai/EntityAINearestAttackableTarget.java @@ -4,7 +4,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.Entity; import game.entity.types.EntityLiving; @@ -38,9 +38,9 @@ public class EntityAINearestAttackableTarget extends Ent this.setMutexBits(1); this.targetEntitySelector = new Predicate() { - public boolean apply(T p_apply_1_) + public boolean test(T p_apply_1_) { - if (targetSelector != null && !targetSelector.apply(p_apply_1_)) + if (targetSelector != null && !targetSelector.test(p_apply_1_)) { return false; } diff --git a/java/src/game/ai/EntityAITakePlace.java b/java/src/game/ai/EntityAITakePlace.java index f2447ed..564c713 100755 --- a/java/src/game/ai/EntityAITakePlace.java +++ b/java/src/game/ai/EntityAITakePlace.java @@ -2,7 +2,7 @@ package game.ai; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.block.Block; import game.entity.npc.EntityNPC; diff --git a/java/src/game/ai/EntityAITargetNonTamed.java b/java/src/game/ai/EntityAITargetNonTamed.java index 26f986c..4b9fd5b 100755 --- a/java/src/game/ai/EntityAITargetNonTamed.java +++ b/java/src/game/ai/EntityAITargetNonTamed.java @@ -1,6 +1,6 @@ package game.ai; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.types.EntityLiving; import game.entity.types.EntityTameable; diff --git a/java/src/game/ai/EntityAITasks.java b/java/src/game/ai/EntityAITasks.java index eb26780..3db2a95 100755 --- a/java/src/game/ai/EntityAITasks.java +++ b/java/src/game/ai/EntityAITasks.java @@ -3,7 +3,7 @@ package game.ai; import java.util.Iterator; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; public class EntityAITasks { diff --git a/java/src/game/ai/EntitySenses.java b/java/src/game/ai/EntitySenses.java index a1ae1d9..e9c257d 100755 --- a/java/src/game/ai/EntitySenses.java +++ b/java/src/game/ai/EntitySenses.java @@ -2,7 +2,7 @@ package game.ai; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.Entity; import game.entity.types.EntityLiving; diff --git a/java/src/game/audio/AudioInterface.java b/java/src/game/audio/AudioInterface.java index 4448f8e..1631cb7 100644 --- a/java/src/game/audio/AudioInterface.java +++ b/java/src/game/audio/AudioInterface.java @@ -10,7 +10,7 @@ import javax.sound.sampled.DataLine.Info; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.log.Log; diff --git a/java/src/game/audio/SoundManager.java b/java/src/game/audio/SoundManager.java index 198a16f..d6d3f86 100755 --- a/java/src/game/audio/SoundManager.java +++ b/java/src/game/audio/SoundManager.java @@ -6,10 +6,10 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.BiMap; +import game.collect.HashBiMap; +import game.collect.Lists; +import game.collect.Maps; import game.Game; import game.entity.npc.EntityNPC; diff --git a/java/src/game/biome/Biome.java b/java/src/game/biome/Biome.java index d64c4b0..07cc861 100755 --- a/java/src/game/biome/Biome.java +++ b/java/src/game/biome/Biome.java @@ -3,8 +3,8 @@ package game.biome; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.block.BlockColored; diff --git a/java/src/game/block/Block.java b/java/src/game/block/Block.java index 651173d..78aab88 100755 --- a/java/src/game/block/Block.java +++ b/java/src/game/block/Block.java @@ -10,13 +10,13 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; -import com.google.common.base.Function; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.UnmodifiableIterator; +import java.util.function.Function; +import game.collect.ImmutableList; +import game.collect.ImmutableMap; +import game.collect.Iterables; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.UnmodifiableIterator; import game.audio.SoundType; import game.enchantment.EnchantmentHelper; diff --git a/java/src/game/block/BlockButton.java b/java/src/game/block/BlockButton.java index 0dd01a7..f98c869 100755 --- a/java/src/game/block/BlockButton.java +++ b/java/src/game/block/BlockButton.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.Entity; import game.entity.npc.EntityNPC; diff --git a/java/src/game/block/BlockDoor.java b/java/src/game/block/BlockDoor.java index 267a128..64f33c7 100755 --- a/java/src/game/block/BlockDoor.java +++ b/java/src/game/block/BlockDoor.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.npc.EntityNPC; import game.init.Blocks; diff --git a/java/src/game/block/BlockFence.java b/java/src/game/block/BlockFence.java index 8a92095..8b8530a 100755 --- a/java/src/game/block/BlockFence.java +++ b/java/src/game/block/BlockFence.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.Entity; import game.entity.npc.EntityNPC; diff --git a/java/src/game/block/BlockFire.java b/java/src/game/block/BlockFire.java index 201cef6..f517e84 100755 --- a/java/src/game/block/BlockFire.java +++ b/java/src/game/block/BlockFire.java @@ -2,7 +2,7 @@ package game.block; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.init.Blocks; import game.init.Config; diff --git a/java/src/game/block/BlockFloorPortal.java b/java/src/game/block/BlockFloorPortal.java index bf79ea9..4eb4c3f 100755 --- a/java/src/game/block/BlockFloorPortal.java +++ b/java/src/game/block/BlockFloorPortal.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.entity.Entity; import game.item.Item; diff --git a/java/src/game/block/BlockFlower.java b/java/src/game/block/BlockFlower.java index 3cb4836..7479965 100755 --- a/java/src/game/block/BlockFlower.java +++ b/java/src/game/block/BlockFlower.java @@ -3,9 +3,9 @@ package game.block; import java.util.Collection; import java.util.List; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; +import java.util.function.Predicate; +import game.collect.Filter; +import game.collect.Lists; import game.init.Blocks; import game.init.Config; @@ -81,7 +81,7 @@ public abstract class BlockFlower extends BlockBush { this.type = PropertyEnum.create("type", BlockFlower.EnumFlowerType.class, new Predicate() { - public boolean apply(BlockFlower.EnumFlowerType p_apply_1_) + public boolean test(BlockFlower.EnumFlowerType p_apply_1_) { return p_apply_1_.getBlockType() == BlockFlower.this.getBlockType(); } @@ -202,9 +202,9 @@ public abstract class BlockFlower extends BlockBush static { for (final BlockFlower.EnumFlowerColor blockflower$enumflowercolor : BlockFlower.EnumFlowerColor.values()) { - Collection collection = Collections2.filter(Lists.newArrayList(values()), new Predicate() + Collection collection = Filter.filter(Lists.newArrayList(values()), new Predicate() { - public boolean apply(BlockFlower.EnumFlowerType p_apply_1_) + public boolean test(BlockFlower.EnumFlowerType p_apply_1_) { return p_apply_1_.getBlockType() == blockflower$enumflowercolor; } diff --git a/java/src/game/block/BlockHopper.java b/java/src/game/block/BlockHopper.java index 9391e1b..6b3c0f7 100755 --- a/java/src/game/block/BlockHopper.java +++ b/java/src/game/block/BlockHopper.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.Entity; import game.entity.npc.EntityNPC; @@ -84,7 +84,7 @@ public class BlockHopper extends BlockContainer public static final PropertyDirection FACING = PropertyDirection.create("facing", new Predicate() { - public boolean apply(Facing p_apply_1_) + public boolean test(Facing p_apply_1_) { return p_apply_1_ != Facing.UP; } diff --git a/java/src/game/block/BlockLeaves.java b/java/src/game/block/BlockLeaves.java index 86835e3..96a20bf 100755 --- a/java/src/game/block/BlockLeaves.java +++ b/java/src/game/block/BlockLeaves.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.audio.SoundType; import game.color.Colorizer; diff --git a/java/src/game/block/BlockPistonBase.java b/java/src/game/block/BlockPistonBase.java index cfc5dfe..04c6fd6 100755 --- a/java/src/game/block/BlockPistonBase.java +++ b/java/src/game/block/BlockPistonBase.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.audio.SoundType; import game.entity.Entity; diff --git a/java/src/game/block/BlockPumpkin.java b/java/src/game/block/BlockPumpkin.java index 55cafd3..c955c33 100755 --- a/java/src/game/block/BlockPumpkin.java +++ b/java/src/game/block/BlockPumpkin.java @@ -19,7 +19,7 @@ public class BlockPumpkin extends BlockDirectional // private BlockPattern golemPattern; // private static final Predicate field_181085_Q = new Predicate() // { -// public boolean apply(IBlockState p_apply_1_) +// public boolean test(IBlockState p_apply_1_) // { // return p_apply_1_ != null && (p_apply_1_.getBlock() == Blocks.pumpkin || p_apply_1_.getBlock() == Blocks.lit_pumpkin); // } diff --git a/java/src/game/block/BlockRailBase.java b/java/src/game/block/BlockRailBase.java index ef09fd8..44c9cee 100755 --- a/java/src/game/block/BlockRailBase.java +++ b/java/src/game/block/BlockRailBase.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.init.Blocks; import game.item.CheatTab; diff --git a/java/src/game/block/BlockRailDetector.java b/java/src/game/block/BlockRailDetector.java index 022c6ab..976b079 100755 --- a/java/src/game/block/BlockRailDetector.java +++ b/java/src/game/block/BlockRailDetector.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.Entity; import game.entity.item.EntityCart; @@ -25,7 +25,7 @@ public class BlockRailDetector extends BlockRailBase { public static final PropertyEnum SHAPE = PropertyEnum.create("shape", BlockRailBase.EnumRailDirection.class, new Predicate() { - public boolean apply(BlockRailBase.EnumRailDirection p_apply_1_) + public boolean test(BlockRailBase.EnumRailDirection p_apply_1_) { return p_apply_1_ != BlockRailBase.EnumRailDirection.NORTH_EAST && p_apply_1_ != BlockRailBase.EnumRailDirection.NORTH_WEST && p_apply_1_ != BlockRailBase.EnumRailDirection.SOUTH_EAST && p_apply_1_ != BlockRailBase.EnumRailDirection.SOUTH_WEST; } @@ -157,7 +157,7 @@ public class BlockRailDetector extends BlockRailBase // } List list1 = this.findMinecarts(worldIn, pos, EntityCart.class, new Predicate() { - public boolean apply(EntityCart entity) { + public boolean test(EntityCart entity) { return entity instanceof IInventory && entity.isEntityAlive(); } }); diff --git a/java/src/game/block/BlockRailPowered.java b/java/src/game/block/BlockRailPowered.java index 97ce08c..7782ed8 100755 --- a/java/src/game/block/BlockRailPowered.java +++ b/java/src/game/block/BlockRailPowered.java @@ -1,6 +1,6 @@ package game.block; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.properties.IProperty; import game.properties.PropertyBool; @@ -14,7 +14,7 @@ public class BlockRailPowered extends BlockRailBase { public static final PropertyEnum SHAPE = PropertyEnum.create("shape", BlockRailBase.EnumRailDirection.class, new Predicate() { - public boolean apply(BlockRailBase.EnumRailDirection p_apply_1_) + public boolean test(BlockRailBase.EnumRailDirection p_apply_1_) { return p_apply_1_ != BlockRailBase.EnumRailDirection.NORTH_EAST && p_apply_1_ != BlockRailBase.EnumRailDirection.NORTH_WEST && p_apply_1_ != BlockRailBase.EnumRailDirection.SOUTH_EAST && p_apply_1_ != BlockRailBase.EnumRailDirection.SOUTH_WEST; } diff --git a/java/src/game/block/BlockRedstoneComparator.java b/java/src/game/block/BlockRedstoneComparator.java index 6cead08..1e79399 100755 --- a/java/src/game/block/BlockRedstoneComparator.java +++ b/java/src/game/block/BlockRedstoneComparator.java @@ -149,7 +149,7 @@ public class BlockRedstoneComparator extends BlockRedstoneDiode implements ITile // { // List list = worldIn.getEntitiesWithinAABB(EntityFrame.class, new BoundingBox((double)pos.getX(), (double)pos.getY(), (double)pos.getZ(), (double)(pos.getX() + 1), (double)(pos.getY() + 1), (double)(pos.getZ() + 1)), new Predicate() // { -// public boolean apply(Entity p_apply_1_) +// public boolean test(Entity p_apply_1_) // { // return p_apply_1_ != null && p_apply_1_.getHorizontalFacing() == facing; // } diff --git a/java/src/game/block/BlockRedstoneTorch.java b/java/src/game/block/BlockRedstoneTorch.java index 81a79f3..1ad83e1 100755 --- a/java/src/game/block/BlockRedstoneTorch.java +++ b/java/src/game/block/BlockRedstoneTorch.java @@ -3,8 +3,8 @@ package game.block; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.init.Blocks; import game.init.ItemRegistry; diff --git a/java/src/game/block/BlockRedstoneWire.java b/java/src/game/block/BlockRedstoneWire.java index d531b0f..ef33571 100755 --- a/java/src/game/block/BlockRedstoneWire.java +++ b/java/src/game/block/BlockRedstoneWire.java @@ -4,8 +4,8 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Sets; import game.init.Blocks; import game.init.Items; diff --git a/java/src/game/block/BlockSapling.java b/java/src/game/block/BlockSapling.java index 2fc5162..e829bc7 100755 --- a/java/src/game/block/BlockSapling.java +++ b/java/src/game/block/BlockSapling.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.init.Blocks; import game.init.Config; diff --git a/java/src/game/block/BlockSlab.java b/java/src/game/block/BlockSlab.java index 46fc27b..caebe48 100755 --- a/java/src/game/block/BlockSlab.java +++ b/java/src/game/block/BlockSlab.java @@ -2,7 +2,7 @@ package game.block; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.Entity; import game.entity.types.EntityLiving; diff --git a/java/src/game/block/BlockStem.java b/java/src/game/block/BlockStem.java index 8bd40de..f8b3364 100755 --- a/java/src/game/block/BlockStem.java +++ b/java/src/game/block/BlockStem.java @@ -1,6 +1,6 @@ package game.block; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.init.Blocks; import game.init.Config; @@ -27,7 +27,7 @@ public class BlockStem extends BlockBush implements IGrowable public static final PropertyInteger AGE = PropertyInteger.create("age", 0, 7); public static final PropertyDirection FACING = PropertyDirection.create("facing", new Predicate() { - public boolean apply(Facing p_apply_1_) + public boolean test(Facing p_apply_1_) { return p_apply_1_ != Facing.DOWN; } diff --git a/java/src/game/block/BlockTorch.java b/java/src/game/block/BlockTorch.java index 3f3df31..5aff3b1 100755 --- a/java/src/game/block/BlockTorch.java +++ b/java/src/game/block/BlockTorch.java @@ -1,6 +1,6 @@ package game.block; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.types.EntityLiving; import game.init.Blocks; @@ -28,7 +28,7 @@ public class BlockTorch extends Block { public static final PropertyDirection FACING = PropertyDirection.create("facing", new Predicate() { - public boolean apply(Facing p_apply_1_) + public boolean test(Facing p_apply_1_) { return p_apply_1_ != Facing.DOWN; } diff --git a/java/src/game/clipboard/ClipboardPlacer.java b/java/src/game/clipboard/ClipboardPlacer.java index bedb874..02dd51b 100755 --- a/java/src/game/clipboard/ClipboardPlacer.java +++ b/java/src/game/clipboard/ClipboardPlacer.java @@ -8,7 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.block.Block; import game.block.BlockDoor; diff --git a/java/src/game/clipboard/Rotation.java b/java/src/game/clipboard/Rotation.java index 3de3cea..f70cb46 100755 --- a/java/src/game/clipboard/Rotation.java +++ b/java/src/game/clipboard/Rotation.java @@ -1,6 +1,6 @@ package game.clipboard; -import com.google.common.base.Predicate; +import java.util.function.Predicate; public class Rotation { private final RotationValue[] values; @@ -9,7 +9,7 @@ public class Rotation { public Rotation(RotationValue[] values, Predicate predicate) { this.values = values; for(int z = 0; z < 16; z++) { - if(predicate != null && !predicate.apply(z)) { + if(predicate != null && !predicate.test(z)) { this.dirFlags[z] = false; continue; } diff --git a/java/src/game/collect/AbstractBiMap.java b/java/src/game/collect/AbstractBiMap.java new file mode 100644 index 0000000..d2c3336 --- /dev/null +++ b/java/src/game/collect/AbstractBiMap.java @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkRemove; +import static game.collect.Preconditions.checkArgument; +import static game.collect.Preconditions.checkState; + +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; + +/** + * A general-purpose bimap implementation using any two backing {@code Map} + * instances. + * + *

Note that this class contains {@code equals()} calls that keep it from + * supporting {@code IdentityHashMap} backing maps. + * + * @author Kevin Bourrillion + * @author Mike Bostock + */ + +abstract class AbstractBiMap extends ForwardingMap + implements BiMap, Serializable { + + private transient Map delegate; + transient AbstractBiMap inverse; + + /** Package-private constructor for creating a map-backed bimap. */ + AbstractBiMap(Map forward, Map backward) { + setDelegates(forward, backward); + } + + /** Private constructor for inverse bimap. */ + private AbstractBiMap(Map backward, AbstractBiMap forward) { + delegate = backward; + inverse = forward; + } + + @Override protected Map delegate() { + return delegate; + } + + /** + * Returns its input, or throws an exception if this is not a valid key. + */ + K checkKey(K key) { + return key; + } + + /** + * Returns its input, or throws an exception if this is not a valid value. + */ + V checkValue(V value) { + return value; + } + + /** + * Specifies the delegate maps going in each direction. Called by the + * constructor and by subclasses during deserialization. + */ + void setDelegates(Map forward, Map backward) { + checkState(delegate == null); + checkState(inverse == null); + checkArgument(forward.isEmpty()); + checkArgument(backward.isEmpty()); + checkArgument(forward != backward); + delegate = forward; + inverse = new Inverse(backward, this); + } + + void setInverse(AbstractBiMap inverse) { + this.inverse = inverse; + } + + // Query Operations (optimizations) + + @Override public boolean containsValue(Object value) { + return inverse.containsKey(value); + } + + // Modification Operations + + @Override public V put(K key, V value) { + return putInBothMaps(key, value, false); + } + + @Override + public V forcePut(K key, V value) { + return putInBothMaps(key, value, true); + } + + private V putInBothMaps(K key, V value, boolean force) { + checkKey(key); + checkValue(value); + boolean containedKey = containsKey(key); + if (containedKey && Preconditions.equal(value, get(key))) { + return value; + } + if (force) { + inverse().remove(value); + } else { + checkArgument(!containsValue(value), "value already present: %s", value); + } + V oldValue = delegate.put(key, value); + updateInverseMap(key, containedKey, oldValue, value); + return oldValue; + } + + private void updateInverseMap( + K key, boolean containedKey, V oldValue, V newValue) { + if (containedKey) { + removeFromInverseMap(oldValue); + } + inverse.delegate.put(newValue, key); + } + + @Override public V remove(Object key) { + return containsKey(key) ? removeFromBothMaps(key) : null; + } + + private V removeFromBothMaps(Object key) { + V oldValue = delegate.remove(key); + removeFromInverseMap(oldValue); + return oldValue; + } + + private void removeFromInverseMap(V oldValue) { + inverse.delegate.remove(oldValue); + } + + // Bulk Operations + + @Override public void putAll(Map map) { + for (Entry entry : map.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override public void clear() { + delegate.clear(); + inverse.delegate.clear(); + } + + // Views + + @Override + public BiMap inverse() { + return inverse; + } + + private transient Set keySet; + + @Override public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = new KeySet() : result; + } + + private class KeySet extends ForwardingSet { + @Override protected Set delegate() { + return delegate.keySet(); + } + + @Override public void clear() { + AbstractBiMap.this.clear(); + } + + @Override public boolean remove(Object key) { + if (!contains(key)) { + return false; + } + removeFromBothMaps(key); + return true; + } + + @Override public boolean removeAll(Collection keysToRemove) { + return standardRemoveAll(keysToRemove); + } + + @Override public boolean retainAll(Collection keysToRetain) { + return standardRetainAll(keysToRetain); + } + + @Override public Iterator iterator() { + return Maps.keyIterator(entrySet().iterator()); + } + } + + private transient Set valueSet; + + @Override public Set values() { + /* + * We can almost reuse the inverse's keySet, except we have to fix the + * iteration order so that it is consistent with the forward map. + */ + Set result = valueSet; + return (result == null) ? valueSet = new ValueSet() : result; + } + + private class ValueSet extends ForwardingSet { + final Set valuesDelegate = inverse.keySet(); + + @Override protected Set delegate() { + return valuesDelegate; + } + + @Override public Iterator iterator() { + return Maps.valueIterator(entrySet().iterator()); + } + + @Override public Object[] toArray() { + return standardToArray(); + } + + @Override public T[] toArray(T[] array) { + return standardToArray(array); + } + +// @Override public String toString() { +// return standardToString(); +// } + } + + private transient Set> entrySet; + + @Override public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = new EntrySet() : result; + } + + private class EntrySet extends ForwardingSet> { + final Set> esDelegate = delegate.entrySet(); + + @Override protected Set> delegate() { + return esDelegate; + } + + @Override public void clear() { + AbstractBiMap.this.clear(); + } + + @Override public boolean remove(Object object) { + if (!esDelegate.contains(object)) { + return false; + } + + // safe because esDelgate.contains(object). + Entry entry = (Entry) object; + inverse.delegate.remove(entry.getValue()); + /* + * Remove the mapping in inverse before removing from esDelegate because + * if entry is part of esDelegate, entry might be invalidated after the + * mapping is removed from esDelegate. + */ + esDelegate.remove(entry); + return true; + } + + @Override public Iterator> iterator() { + final Iterator> iterator = esDelegate.iterator(); + return new Iterator>() { + Entry entry; + + @Override public boolean hasNext() { + return iterator.hasNext(); + } + + @Override public Entry next() { + entry = iterator.next(); + final Entry finalEntry = entry; + + return new ForwardingMapEntry() { + @Override protected Entry delegate() { + return finalEntry; + } + + @Override public V setValue(V value) { + // Preconditions keep the map and inverse consistent. + checkState(contains(this), "entry no longer in map"); + // similar to putInBothMaps, but set via entry + if (Preconditions.equal(value, getValue())) { + return value; + } + checkArgument(!containsValue(value), + "value already present: %s", value); + V oldValue = finalEntry.setValue(value); + checkState(Preconditions.equal(value, get(getKey())), + "entry no longer in map"); + updateInverseMap(getKey(), true, oldValue, value); + return oldValue; + } + }; + } + + @Override public void remove() { + checkRemove(entry != null); + V value = entry.getValue(); + iterator.remove(); + removeFromInverseMap(value); + } + }; + } + + // See java.util.Collections.CheckedEntrySet for details on attacks. + + @Override public Object[] toArray() { + return standardToArray(); + } + @Override public T[] toArray(T[] array) { + return standardToArray(array); + } + @Override public boolean contains(Object o) { + return Maps.containsEntryImpl(delegate(), o); + } + @Override public boolean containsAll(Collection c) { + return standardContainsAll(c); + } + @Override public boolean removeAll(Collection c) { + return standardRemoveAll(c); + } + @Override public boolean retainAll(Collection c) { + return standardRetainAll(c); + } + } + + /** The inverse of any other {@code AbstractBiMap} subclass. */ + private static class Inverse extends AbstractBiMap { + private Inverse(Map backward, AbstractBiMap forward) { + super(backward, forward); + } + + /* + * Serialization stores the forward bimap, the inverse of this inverse. + * Deserialization calls inverse() on the forward bimap and returns that + * inverse. + * + * If a bimap and its inverse are serialized together, the deserialized + * instances have inverse() methods that return the other. + */ + + @Override + K checkKey(K key) { + return inverse.checkValue(key); + } + + @Override + V checkValue(V value) { + return inverse.checkKey(value); + } + + /** + * @serialData the forward bimap + */ + + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.defaultWriteObject(); + stream.writeObject(inverse()); + } + + + // reading data stored by writeObject + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + setInverse((AbstractBiMap) stream.readObject()); + } + + + Object readResolve() { + return inverse().inverse(); + } + + + private static final long serialVersionUID = 0; + } + + + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/collect/AbstractIndexedListIterator.java b/java/src/game/collect/AbstractIndexedListIterator.java new file mode 100644 index 0000000..a2056ae --- /dev/null +++ b/java/src/game/collect/AbstractIndexedListIterator.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkPositionIndex; + +import java.util.NoSuchElementException; + +/** + * This class provides a skeletal implementation of the {@link ListIterator} + * interface across a fixed number of elements that may be retrieved by + * position. It does not support {@link #remove}, {@link #set}, or {@link #add}. + * + * @author Jared Levy + */ + +abstract class AbstractIndexedListIterator + extends UnmodifiableListIterator { + private final int size; + private int position; + + /** + * Returns the element with the specified index. This method is called by + * {@link #next()}. + */ + protected abstract E get(int index); + + /** + * Constructs an iterator across a sequence of the given size whose initial + * position is 0. That is, the first call to {@link #next()} will return the + * first element (or throw {@link NoSuchElementException} if {@code size} is + * zero). + * + * @throws IllegalArgumentException if {@code size} is negative + */ + protected AbstractIndexedListIterator(int size) { + this(size, 0); + } + + /** + * Constructs an iterator across a sequence of the given size with the given + * initial position. That is, the first call to {@link #nextIndex()} will + * return {@code position}, and the first call to {@link #next()} will return + * the element at that index, if available. Calls to {@link #previous()} can + * retrieve the preceding {@code position} elements. + * + * @throws IndexOutOfBoundsException if {@code position} is negative or is + * greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + protected AbstractIndexedListIterator(int size, int position) { + checkPositionIndex(position, size); + this.size = size; + this.position = position; + } + + @Override + public final boolean hasNext() { + return position < size; + } + + @Override + public final E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return get(position++); + } + + @Override + public final int nextIndex() { + return position; + } + + @Override + public final boolean hasPrevious() { + return position > 0; + } + + @Override + public final E previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + return get(--position); + } + + @Override + public final int previousIndex() { + return position - 1; + } +} diff --git a/java/src/game/collect/AbstractIterator.java b/java/src/game/collect/AbstractIterator.java new file mode 100644 index 0000000..7ba15e0 --- /dev/null +++ b/java/src/game/collect/AbstractIterator.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkState; + +import java.util.NoSuchElementException; + +/** + * This class provides a skeletal implementation of the {@code Iterator} + * interface, to make this interface easier to implement for certain types of + * data sources. + * + *

{@code Iterator} requires its implementations to support querying the + * end-of-data status without changing the iterator's state, using the {@link + * #hasNext} method. But many data sources, such as {@link + * java.io.Reader#read()}, do not expose this information; the only way to + * discover whether there is any data left is by trying to retrieve it. These + * types of data sources are ordinarily difficult to write iterators for. But + * using this class, one must implement only the {@link #computeNext} method, + * and invoke the {@link #endOfData} method when appropriate. + * + *

Another example is an iterator that skips over null elements in a backing + * iterator. This could be implemented as:

   {@code
+ *
+ *   public static Iterator skipNulls(final Iterator in) {
+ *     return new AbstractIterator() {
+ *       protected String computeNext() {
+ *         while (in.hasNext()) {
+ *           String s = in.next();
+ *           if (s != null) {
+ *             return s;
+ *           }
+ *         }
+ *         return endOfData();
+ *       }
+ *     };
+ *   }}
+ * + *

This class supports iterators that include null elements. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ +// When making changes to this class, please also update the copy at +// com.google.common.base.AbstractIterator + +public abstract class AbstractIterator extends UnmodifiableIterator { + private State state = State.NOT_READY; + + /** Constructor for use by subclasses. */ + protected AbstractIterator() {} + + private enum State { + /** We have computed the next element and haven't returned it yet. */ + READY, + + /** We haven't yet computed or have already returned the element. */ + NOT_READY, + + /** We have reached the end of the data and are finished. */ + DONE, + + /** We've suffered an exception and are kaput. */ + FAILED, + } + + private T next; + + /** + * Returns the next element. Note: the implementation must call {@link + * #endOfData()} when there are no elements left in the iteration. Failure to + * do so could result in an infinite loop. + * + *

The initial invocation of {@link #hasNext()} or {@link #next()} calls + * this method, as does the first invocation of {@code hasNext} or {@code + * next} following each successful call to {@code next}. Once the + * implementation either invokes {@code endOfData} or throws an exception, + * {@code computeNext} is guaranteed to never be called again. + * + *

If this method throws an exception, it will propagate outward to the + * {@code hasNext} or {@code next} invocation that invoked this method. Any + * further attempts to use the iterator will result in an {@link + * IllegalStateException}. + * + *

The implementation of this method may not invoke the {@code hasNext}, + * {@code next}, or {@link #peek()} methods on this instance; if it does, an + * {@code IllegalStateException} will result. + * + * @return the next element if there was one. If {@code endOfData} was called + * during execution, the return value will be ignored. + * @throws RuntimeException if any unrecoverable error happens. This exception + * will propagate outward to the {@code hasNext()}, {@code next()}, or + * {@code peek()} invocation that invoked this method. Any further + * attempts to use the iterator will result in an + * {@link IllegalStateException}. + */ + protected abstract T computeNext(); + + /** + * Implementations of {@link #computeNext} must invoke this method when + * there are no elements left in the iteration. + * + * @return {@code null}; a convenience so your {@code computeNext} + * implementation can use the simple statement {@code return endOfData();} + */ + protected final T endOfData() { + state = State.DONE; + return null; + } + + @Override + public final boolean hasNext() { + checkState(state != State.FAILED); + switch (state) { + case DONE: + return false; + case READY: + return true; + default: + } + return tryToComputeNext(); + } + + private boolean tryToComputeNext() { + state = State.FAILED; // temporary pessimism + next = computeNext(); + if (state != State.DONE) { + state = State.READY; + return true; + } + return false; + } + + @Override + public final T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + state = State.NOT_READY; + T result = next; + next = null; + return result; + } + + /** + * Returns the next element in the iteration without advancing the iteration, + * according to the contract of {@link PeekingIterator#peek()}. + * + *

Implementations of {@code AbstractIterator} that wish to expose this + * functionality should implement {@code PeekingIterator}. + */ + public final T peek() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return next; + } +} diff --git a/java/src/game/collect/AbstractMapEntry.java b/java/src/game/collect/AbstractMapEntry.java new file mode 100644 index 0000000..4c89223 --- /dev/null +++ b/java/src/game/collect/AbstractMapEntry.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Map.Entry; + +/** + * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} + * methods of {@code Entry}. + * + * @author Jared Levy + */ + +abstract class AbstractMapEntry implements Entry { + + @Override + public abstract K getKey(); + + @Override + public abstract V getValue(); + + @Override + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + + @Override public boolean equals(Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Preconditions.equal(this.getKey(), that.getKey()) + && Preconditions.equal(this.getValue(), that.getValue()); + } + return false; + } + + @Override public int hashCode() { + K k = getKey(); + V v = getValue(); + return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); + } + + /** + * Returns a string representation of the form {@code {key}={value}}. + */ + @Override public String toString() { + return getKey() + "=" + getValue(); + } +} diff --git a/java/src/game/collect/AbstractTable.java b/java/src/game/collect/AbstractTable.java new file mode 100644 index 0000000..b19ada4 --- /dev/null +++ b/java/src/game/collect/AbstractTable.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package game.collect; + +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Skeletal, implementation-agnostic implementation of the {@link Table} interface. + * + * @author Louis Wasserman + */ + +abstract class AbstractTable implements Table { + + @Override + public boolean containsRow(Object rowKey) { + return Maps.safeContainsKey(rowMap(), rowKey); + } + + @Override + public boolean containsColumn(Object columnKey) { + return Maps.safeContainsKey(columnMap(), columnKey); + } + + @Override + public Set rowKeySet() { + return rowMap().keySet(); + } + + @Override + public Set columnKeySet() { + return columnMap().keySet(); + } + + @Override + public boolean containsValue(Object value) { + for (Map row : rowMap().values()) { + if (row.containsValue(value)) { + return true; + } + } + return false; + } + + @Override + public boolean contains(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return row != null && Maps.safeContainsKey(row, columnKey); + } + + @Override + public V get(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return (row == null) ? null : Maps.safeGet(row, columnKey); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public void clear() { + Iterators.clear(cellSet().iterator()); + } + + @Override + public V remove(Object rowKey, Object columnKey) { + Map row = Maps.safeGet(rowMap(), rowKey); + return (row == null) ? null : Maps.safeRemove(row, columnKey); + } + + @Override + public V put(R rowKey, C columnKey, V value) { + return row(rowKey).put(columnKey, value); + } + + @Override + public void putAll(Table table) { + for (Table.Cell cell : table.cellSet()) { + put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + } + + private transient Set> cellSet; + + @Override + public Set> cellSet() { + Set> result = cellSet; + return (result == null) ? cellSet = createCellSet() : result; + } + + Set> createCellSet() { + return new CellSet(); + } + + abstract Iterator> cellIterator(); + + class CellSet extends AbstractSet> { + @Override + public boolean contains(Object o) { + if (o instanceof Cell) { + Cell cell = (Cell) o; + Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + return row != null && Filter.safeContains( + row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof Cell) { + Cell cell = (Cell) o; + Map row = Maps.safeGet(rowMap(), cell.getRowKey()); + return row != null && Filter.safeRemove( + row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue())); + } + return false; + } + + @Override + public void clear() { + AbstractTable.this.clear(); + } + + @Override + public Iterator> iterator() { + return cellIterator(); + } + + @Override + public int size() { + return AbstractTable.this.size(); + } + } + + private transient Collection values; + + @Override + public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + Collection createValues() { + return new Values(); + } + + Iterator valuesIterator() { + return new TransformedIterator, V>(cellSet().iterator()) { + @Override + V transform(Cell cell) { + return cell.getValue(); + } + }; + } + + class Values extends AbstractCollection { + @Override + public Iterator iterator() { + return valuesIterator(); + } + + @Override + public boolean contains(Object o) { + return containsValue(o); + } + + @Override + public void clear() { + AbstractTable.this.clear(); + } + + @Override + public int size() { + return AbstractTable.this.size(); + } + } + + @Override public boolean equals(Object obj) { + return equalsImpl(this, obj); + } + + static boolean equalsImpl(Table table, 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; + } + } + + @Override public int hashCode() { + return cellSet().hashCode(); + } + + /** + * Returns the string representation {@code rowMap().toString()}. + */ + @Override public String toString() { + return rowMap().toString(); + } +} diff --git a/java/src/game/collect/BiMap.java b/java/src/game/collect/BiMap.java new file mode 100644 index 0000000..3b920df --- /dev/null +++ b/java/src/game/collect/BiMap.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Map; +import java.util.Set; + +/** + * A bimap (or "bidirectional map") is a map that preserves the uniqueness of + * its values as well as that of its keys. This constraint enables bimaps to + * support an "inverse view", which is another bimap containing the same entries + * as this bimap but with reversed keys and values. + * + *

See the Guava User Guide article on + * {@code BiMap}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public interface BiMap extends Map { + // Modification Operations + + /** + * {@inheritDoc} + * + * @throws IllegalArgumentException if the given value is already bound to a + * different key in this bimap. The bimap will remain unmodified in this + * event. To avoid this exception, call {@link #forcePut} instead. + */ + @Override + V put(K key, V value); + + /** + * An alternate form of {@code put} that silently removes any existing entry + * with the value {@code value} before proceeding with the {@link #put} + * operation. If the bimap previously contained the provided key-value + * mapping, this method has no effect. + * + *

Note that a successful call to this method could cause the size of the + * bimap to increase by one, stay the same, or even decrease by one. + * + *

Warning: If an existing entry with this value is removed, the key + * for that entry is discarded and not returned. + * + * @param key the key with which the specified value is to be associated + * @param value the value to be associated with the specified key + * @return the value which was previously associated with the key, which may + * be {@code null}, or {@code null} if there was no previous entry + */ + V forcePut(K key, V value); + + // Bulk Operations + + /** + * {@inheritDoc} + * + *

Warning: the results of calling this method may vary depending on + * the iteration order of {@code map}. + * + * @throws IllegalArgumentException if an attempt to {@code put} any + * entry fails. Note that some map entries may have been added to the + * bimap before the exception was thrown. + */ + @Override + void putAll(Map map); + + // Views + + /** + * {@inheritDoc} + * + *

Because a bimap has unique values, this method returns a {@link Set}, + * instead of the {@link java.util.Collection} specified in the {@link Map} + * interface. + */ + @Override + Set values(); + + /** + * Returns the inverse view of this bimap, which maps each of this bimap's + * values to its associated key. The two bimaps are backed by the same data; + * any changes to one will appear in the other. + * + *

Note:There is no guaranteed correspondence between the iteration + * order of a bimap and that of its inverse. + * + * @return the inverse view of this bimap + */ + BiMap inverse(); +} diff --git a/java/src/game/collect/CollectPreconditions.java b/java/src/game/collect/CollectPreconditions.java new file mode 100644 index 0000000..520d5d2 --- /dev/null +++ b/java/src/game/collect/CollectPreconditions.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkState; + +/** + * Precondition checks useful in collection implementations. + */ + +final class CollectPreconditions { + + static void checkEntryNotNull(Object key, Object value) { + if (key == null) { + throw new NullPointerException("null key in entry: null=" + value); + } else if (value == null) { + throw new NullPointerException("null value in entry: " + key + "=null"); + } + } + + static int checkNonnegative(int value, String name) { + if (value < 0) { + throw new IllegalArgumentException(name + " cannot be negative but was: " + value); + } + return value; + } + + /** + * Precondition tester for {@code Iterator.remove()} that throws an exception with a consistent + * error message. + */ + static void checkRemove(boolean canRemove) { + checkState(canRemove, "no calls to next() since the last call to remove()"); + } +} diff --git a/java/src/game/collect/DenseImmutableTable.java b/java/src/game/collect/DenseImmutableTable.java new file mode 100644 index 0000000..5afd429 --- /dev/null +++ b/java/src/game/collect/DenseImmutableTable.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkArgument; + +import java.util.Map; + +/** + * A {@code RegularImmutableTable} optimized for dense data. + */ + +//@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 (E key : set) { + indexBuilder.put(key, i); + i++; + } + return indexBuilder.build(); + } + + DenseImmutableTable(ImmutableList> cellList, + ImmutableSet rowSpace, ImmutableSet columnSpace) { + + V[][] array = (V[][]) new Object[rowSpace.size()][columnSpace.size()]; + this.values = array; + this.rowKeyToIndex = makeIndex(rowSpace); + this.columnKeyToIndex = makeIndex(columnSpace); + rowCounts = new int[rowKeyToIndex.size()]; + columnCounts = new int[columnKeyToIndex.size()]; + int[] iterationOrderRow = new int[cellList.size()]; + int[] iterationOrderColumn = new int[cellList.size()]; + for (int i = 0; i < cellList.size(); i++) { + Cell cell = cellList.get(i); + R rowKey = cell.getRowKey(); + C columnKey = cell.getColumnKey(); + int rowIndex = rowKeyToIndex.get(rowKey); + int columnIndex = columnKeyToIndex.get(columnKey); + V existingValue = values[rowIndex][columnIndex]; + checkArgument(existingValue == null, "duplicate key: (%s, %s)", rowKey, columnKey); + values[rowIndex][columnIndex] = cell.getValue(); + rowCounts[rowIndex]++; + columnCounts[columnIndex]++; + iterationOrderRow[i] = rowIndex; + iterationOrderColumn[i] = columnIndex; + } + this.iterationOrderRow = iterationOrderRow; + this.iterationOrderColumn = iterationOrderColumn; + this.rowMap = new RowMap(); + this.columnMap = new ColumnMap(); + } + + /** + * An immutable map implementation backed by an indexed nullable array. + */ + private abstract static class ImmutableArrayMap extends ImmutableMap { + private final int size; + + ImmutableArrayMap(int size) { + this.size = size; + } + + abstract ImmutableMap keyToIndex(); + + // True if getValue never returns null. + private boolean isFull() { + return size == keyToIndex().size(); + } + + K getKey(int index) { + return keyToIndex().keySet().asList().get(index); + } + + abstract V getValue(int keyIndex); + + @Override + ImmutableSet createKeySet() { + return isFull() ? keyToIndex().keySet() : super.createKeySet(); + } + + @Override + public int size() { + return size; + } + + @Override + public V get(Object key) { + Integer keyIndex = keyToIndex().get(key); + return (keyIndex == null) ? null : getValue(keyIndex); + } + + @Override + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet() { + @Override ImmutableMap map() { + return ImmutableArrayMap.this; + } + + @Override + public UnmodifiableIterator> iterator() { + return new AbstractIterator>() { + private int index = -1; + private final int maxIndex = keyToIndex().size(); + + @Override + protected Entry computeNext() { + for (index++; index < maxIndex; index++) { + V value = getValue(index); + if (value != null) { + return Maps.immutableEntry(getKey(index), value); + } + } + return endOfData(); + } + }; + } + }; + } + } + + private final class Row extends ImmutableArrayMap { + private final int rowIndex; + + Row(int rowIndex) { + super(rowCounts[rowIndex]); + this.rowIndex = rowIndex; + } + + @Override + ImmutableMap keyToIndex() { + return columnKeyToIndex; + } + + @Override + V getValue(int keyIndex) { + return values[rowIndex][keyIndex]; + } + + @Override + boolean isPartialView() { + return true; + } + } + + private final class Column extends ImmutableArrayMap { + private final int columnIndex; + + Column(int columnIndex) { + super(columnCounts[columnIndex]); + this.columnIndex = columnIndex; + } + + @Override + ImmutableMap keyToIndex() { + return rowKeyToIndex; + } + + @Override + V getValue(int keyIndex) { + return values[keyIndex][columnIndex]; + } + + @Override + boolean isPartialView() { + return true; + } + } + + private final class RowMap extends ImmutableArrayMap> { + private RowMap() { + super(rowCounts.length); + } + + @Override + ImmutableMap keyToIndex() { + return rowKeyToIndex; + } + + @Override + Map getValue(int keyIndex) { + return new Row(keyIndex); + } + + @Override + boolean isPartialView() { + return false; + } + } + + private final class ColumnMap extends ImmutableArrayMap> { + private ColumnMap() { + super(columnCounts.length); + } + + @Override + ImmutableMap keyToIndex() { + return columnKeyToIndex; + } + + @Override + Map getValue(int keyIndex) { + return new Column(keyIndex); + } + + @Override + boolean isPartialView() { + return false; + } + } + + @Override public ImmutableMap> columnMap() { + return columnMap; + } + + @Override + public ImmutableMap> rowMap() { + return rowMap; + } + + @Override public V get(Object rowKey, + Object columnKey) { + Integer rowIndex = rowKeyToIndex.get(rowKey); + Integer columnIndex = columnKeyToIndex.get(columnKey); + return ((rowIndex == null) || (columnIndex == null)) ? null + : values[rowIndex][columnIndex]; + } + + @Override + public int size() { + return iterationOrderRow.length; + } + + @Override + Cell getCell(int index) { + int rowIndex = iterationOrderRow[index]; + int columnIndex = iterationOrderColumn[index]; + R rowKey = rowKeySet().asList().get(rowIndex); + C columnKey = columnKeySet().asList().get(columnIndex); + V value = values[rowIndex][columnIndex]; + return cellOf(rowKey, columnKey, value); + } + + @Override + V getValue(int index) { + return values[iterationOrderRow[index]][iterationOrderColumn[index]]; + } +} diff --git a/java/src/game/collect/EmptyImmutableMap.java b/java/src/game/collect/EmptyImmutableMap.java new file mode 100644 index 0000000..866868a --- /dev/null +++ b/java/src/game/collect/EmptyImmutableMap.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +/** + * Bimap with no mappings. + * + * @author Jared Levy + */ + +// // uses writeReplace(), not default serialization +final class EmptyImmutableMap extends ImmutableMap { + static final EmptyImmutableMap INSTANCE = new EmptyImmutableMap(); + + private EmptyImmutableMap() {} + +// @Override public ImmutableBiMap inverse() { +// return this; +// } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public ImmutableSet> entrySet() { + return ImmutableSet.of(); + } + + @Override + ImmutableSet> createEntrySet() { + throw new AssertionError("should never be called"); + } + +// @Override +// public ImmutableSetMultimap asMultimap() { +// return ImmutableSetMultimap.of(); +// } + + @Override + public ImmutableSet keySet() { + return ImmutableSet.of(); + } + + @Override + boolean isPartialView() { + return false; + } + +// Object readResolve() { +// return INSTANCE; // preserve singleton property +// } + /** + * Returns the empty bimap. + */ + // Casting to any type is safe because the set will never hold any elements. +// +// public static ImmutableBiMap of() { +// return (ImmutableBiMap) EmptyImmutableBiMap.INSTANCE; +// } + + /** + * Returns an immutable bimap containing a single entry. + */ +// public static ImmutableBiMap of(K k1, V v1) { +// return new SingletonImmutableBiMap(k1, v1); +// } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys or values are added + */ +// public static ImmutableBiMap of(K k1, V v1, K k2, V v2) { +// return new RegularImmutableBiMap(entryOf(k1, v1), entryOf(k2, v2)); +// } +// +// /** +// * Returns an immutable map containing the given entries, in order. +// * +// * @throws IllegalArgumentException if duplicate keys or values are added +// */ +// public static ImmutableBiMap of( +// K k1, V v1, K k2, V v2, K k3, V v3) { +// return new RegularImmutableBiMap(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); +// } +// +// /** +// * Returns an immutable map containing the given entries, in order. +// * +// * @throws IllegalArgumentException if duplicate keys or values are added +// */ +// public static ImmutableBiMap of( +// K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { +// return new RegularImmutableBiMap(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), +// entryOf(k4, v4)); +// } +// +// /** +// * Returns an immutable map containing the given entries, in order. +// * +// * @throws IllegalArgumentException if duplicate keys or values are added +// */ +// 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(entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), +// entryOf(k4, v4), entryOf(k5, v5)); +// } + + // looking for of() with > 5 entries? Use the builder instead. + + /** + * Returns a new builder. The generated builder is equivalent to the builder + * created by the {@link Builder} constructor. + */ +// public static Builder builder() { +// return new Builder(); +// } + + /** + * A builder for creating immutable bimap instances, especially {@code public + * static final} bimaps ("constant bimaps"). Example:
   {@code
+   *
+   *   static final ImmutableBiMap WORD_TO_INT =
+   *       new ImmutableBiMap.Builder()
+   *           .put("one", 1)
+   *           .put("two", 2)
+   *           .put("three", 3)
+   *           .build();}
+ * + *

For small immutable bimaps, the {@code ImmutableBiMap.of()} methods + * are even more convenient. + * + *

Builder instances can be reused - it is safe to call {@link #build} + * multiple times to build multiple bimaps in series. Each bimap is a superset + * of the bimaps created before it. + * + * @since 2.0 (imported from Google Collections Library) + */ +// public static final class Builder extends ImmutableMap.Builder { +// +// /** +// * Creates a new builder. The returned builder is equivalent to the builder +// * generated by {@link ImmutableBiMap#builder}. +// */ +// public Builder() {} +// +// /** +// * Associates {@code key} with {@code value} in the built bimap. Duplicate +// * keys or values are not allowed, and will cause {@link #build} to fail. +// */ +// @Override public Builder put(K key, V value) { +// super.put(key, value); +// return this; +// } +// +// /** +// * Associates all of the given map's keys and values in the built bimap. +// * Duplicate keys or values are not allowed, and will cause {@link #build} +// * to fail. +// * +// * @throws NullPointerException if any key or value in {@code map} is null +// */ +// @Override public Builder putAll(Map map) { +// super.putAll(map); +// return this; +// } +// +// /** +// * Returns a newly-created immutable bimap. +// * +// * @throws IllegalArgumentException if duplicate keys or values were added +// */ +// @Override public ImmutableBiMap build() { +// switch (size) { +// case 0: +// return of(); +// case 1: +// return of(entries[0].getKey(), entries[0].getValue()); +// default: +// return new RegularImmutableBiMap(size, entries); +// } +// } +// } + + /** + * Returns an immutable bimap containing the same entries as {@code map}. If + * {@code map} somehow contains entries with duplicate keys (for example, if + * it is a {@code SortedMap} whose comparator is not consistent with + * equals), the results of this method are undefined. + * + *

Despite the method name, this method attempts to avoid actually copying + * the data when it is safe to do so. The exact circumstances under which a + * copy will or will not be performed are undocumented and subject to change. + * + * @throws IllegalArgumentException if two keys have the same value + * @throws NullPointerException if any key or value in {@code map} is null + */ +// public static ImmutableBiMap copyOf( +// Map map) { +// if (map instanceof ImmutableBiMap) { +// // safe since map is not writable +// ImmutableBiMap bimap = (ImmutableBiMap) map; +// // TODO(user): if we need to make a copy of a BiMap because the +// // forward map is a view, don't make a copy of the non-view delegate map +// if (!bimap.isPartialView()) { +// return bimap; +// } +// } +// Entry[] entries = map.entrySet().toArray(EMPTY_ENTRY_ARRAY); +// switch (entries.length) { +// case 0: +// return of(); +// case 1: +// // safe covariant cast in this context +// Entry entry = (Entry) entries[0]; +// return of(entry.getKey(), entry.getValue()); +// default: +// return new RegularImmutableBiMap(entries); +// } +// } + +// private static final Entry[] EMPTY_ENTRY_ARRAY = new Entry[0]; + +// ImmutableBiMap() {} + + /** + * {@inheritDoc} + * + *

The inverse of an {@code ImmutableBiMap} is another + * {@code ImmutableBiMap}. + */ +// @Override +// public abstract ImmutableBiMap inverse(); + + /** + * Returns an immutable set of the values in this map. The values are in the + * same order as the parameters used to build this map. + */ + @Override public ImmutableSet values() { + return ImmutableSet.of(); + } + + /** + * Guaranteed to throw an exception and leave the bimap unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ +// @Deprecated +// @Override +// public V forcePut(K key, V value) { +// throw new UnsupportedOperationException(); +// } + + /** + * Serialized type for all ImmutableBiMap instances. It captures the logical + * contents and they are reconstructed using public factory methods. This + * ensures that the implementation types remain as implementation details. + * + * Since the bimap is immutable, ImmutableBiMap doesn't require special logic + * for keeping the bimap and its inverse in sync during serialization, the way + * AbstractBiMap does. + */ +// private static class SerializedForm extends ImmutableMap.SerializedForm { +// SerializedForm(ImmutableBiMap bimap) { +// super(bimap); +// } +// @Override Object readResolve() { +// Builder builder = new Builder(); +// return createMap(builder); +// } +// private static final long serialVersionUID = 0; +// } +// +// @Override Object writeReplace() { +// return new SerializedForm(this); +// } +} diff --git a/java/src/game/collect/EmptyImmutableSet.java b/java/src/game/collect/EmptyImmutableSet.java new file mode 100644 index 0000000..f86b50f --- /dev/null +++ b/java/src/game/collect/EmptyImmutableSet.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Collection; +import java.util.Set; + +/** + * An empty immutable set. + * + * @author Kevin Bourrillion + */ + +final class EmptyImmutableSet extends ImmutableSet { + static final EmptyImmutableSet INSTANCE = new EmptyImmutableSet(); + + private EmptyImmutableSet() {} + + @Override + public int size() { + return 0; + } + + @Override public boolean isEmpty() { + return true; + } + + @Override public boolean contains(Object target) { + return false; + } + + @Override public boolean containsAll(Collection targets) { + return targets.isEmpty(); + } + + @Override public UnmodifiableIterator iterator() { + return Iterators.emptyIterator(); + } + + @Override boolean isPartialView() { + return false; + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + return offset; + } + + @Override + public ImmutableList asList() { + return ImmutableList.of(); + } + + @Override public boolean equals(Object object) { + if (object instanceof Set) { + Set that = (Set) object; + return that.isEmpty(); + } + return false; + } + + @Override public final int hashCode() { + return 0; + } + + @Override boolean isHashCodeFast() { + return true; + } + + @Override public String toString() { + return "[]"; + } + + Object readResolve() { + return INSTANCE; // preserve singleton property + } + + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/collect/Filter.java b/java/src/game/collect/Filter.java new file mode 100644 index 0000000..3d45f77 --- /dev/null +++ b/java/src/game/collect/Filter.java @@ -0,0 +1,659 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkArgument; +import static game.collect.Preconditions.checkNotNull; +import static game.util.Predicates.and; +import static game.util.Predicates.in; +import static game.util.Predicates.not; + +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Predicate; + +import game.util.Predicates; + +/** + * Provides static methods for working with {@code Collection} instances. + * + * @author Chris Povirk + * @author Mike Bostock + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Filter { + private Filter() {} + + /** + * Returns the elements of {@code unfiltered} that satisfy a predicate. The + * returned collection is a live view of {@code unfiltered}; changes to one + * affect the other. + * + *

The resulting collection's iterator does not support {@code remove()}, + * but all other collection methods are supported. When given an element that + * doesn't satisfy the predicate, the collection's {@code add()} and {@code + * addAll()} methods throw an {@link IllegalArgumentException}. When methods + * such as {@code removeAll()} and {@code clear()} are called on the filtered + * collection, only elements that satisfy the filter will be removed from the + * underlying collection. + * + *

The returned collection isn't threadsafe or serializable, even if + * {@code unfiltered} is. + * + *

Many of the filtered collection's methods, such as {@code size()}, + * iterate across every element in the underlying collection and determine + * which elements satisfy the filter. When a live view is not needed, + * it may be faster to copy {@code Iterables.filter(unfiltered, predicate)} + * and use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, + * as documented at {@link Predicate#apply}. Do not provide a predicate such + * as {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent + * with equals. (See {@link Iterables#filter(Iterable, Class)} for related + * functionality.) + */ + // TODO(kevinb): how can we omit that Iterables link when building gwt + // javadoc? + public static Collection filter( + Collection unfiltered, Predicate predicate) { + if (unfiltered instanceof FilteredCollection) { + // Support clear(), removeAll(), and retainAll() when filtering a filtered + // collection. + return ((FilteredCollection) unfiltered).createCombined(predicate); + } + + return new FilteredCollection( + checkNotNull(unfiltered), checkNotNull(predicate)); + } + + /** + * Delegates to {@link Collection#contains}. Returns {@code false} if the + * {@code contains} method throws a {@code ClassCastException} or + * {@code NullPointerException}. + */ + static boolean safeContains( + Collection collection, Object object) { + checkNotNull(collection); + try { + return collection.contains(object); + } catch (ClassCastException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Delegates to {@link Collection#remove}. Returns {@code false} if the + * {@code remove} method throws a {@code ClassCastException} or + * {@code NullPointerException}. + */ + static boolean safeRemove(Collection collection, Object object) { + checkNotNull(collection); + try { + return collection.remove(object); + } catch (ClassCastException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + static class FilteredCollection extends AbstractCollection { + final Collection unfiltered; + final Predicate predicate; + + FilteredCollection(Collection unfiltered, + Predicate predicate) { + this.unfiltered = unfiltered; + this.predicate = predicate; + } + + FilteredCollection createCombined(Predicate newPredicate) { + return new FilteredCollection(unfiltered, + Predicates.and(predicate, newPredicate)); + // . above needed to compile in JDK 5 + } + + @Override + public boolean add(E element) { + checkArgument(predicate.test(element)); + return unfiltered.add(element); + } + + @Override + public boolean addAll(Collection collection) { + for (E element : collection) { + checkArgument(predicate.test(element)); + } + return unfiltered.addAll(collection); + } + + @Override + public void clear() { + Iterables.removeIf(unfiltered, predicate); + } + + @Override + public boolean contains(Object element) { + if (safeContains(unfiltered, element)) { + // element is in unfiltered, so it must be an E + E e = (E) element; + return predicate.test(e); + } + return false; + } + + @Override + public boolean containsAll(Collection collection) { + return containsAllImpl(this, collection); + } + + @Override + public boolean isEmpty() { + return !Iterables.any(unfiltered, predicate); + } + + @Override + public Iterator iterator() { + return Iterators.filter(unfiltered.iterator(), predicate); + } + + @Override + public boolean remove(Object element) { + return contains(element) && unfiltered.remove(element); + } + + @Override + public boolean removeAll(final Collection collection) { + return Iterables.removeIf(unfiltered, and(predicate, in(collection))); + } + + @Override + public boolean retainAll(final Collection collection) { + return Iterables.removeIf(unfiltered, and(predicate, not(in(collection)))); + } + + @Override + public int size() { + return Iterators.size(iterator()); + } + + @Override + public Object[] toArray() { + // creating an ArrayList so filtering happens once + return Lists.newArrayList(iterator()).toArray(); + } + + @Override + public T[] toArray(T[] array) { + return Lists.newArrayList(iterator()).toArray(array); + } + } + + /** + * Returns a collection that applies {@code function} to each element of + * {@code fromCollection}. The returned collection is a live view of {@code + * fromCollection}; changes to one affect the other. + * + *

The returned collection's {@code add()} and {@code addAll()} methods + * throw an {@link UnsupportedOperationException}. All other collection + * methods are supported, as long as {@code fromCollection} supports them. + * + *

The returned collection isn't threadsafe or serializable, even if + * {@code fromCollection} is. + * + *

When a live view is not needed, it may be faster to copy the + * transformed collection and use the copy. + * + *

If the input {@code Collection} is known to be a {@code List}, consider + * {@link Lists#transform}. If only an {@code Iterable} is available, use + * {@link Iterables#transform}. + */ +// public static Collection transform(Collection fromCollection, +// Function function) { +// return new TransformedCollection(fromCollection, function); +// } +// +// static class TransformedCollection extends AbstractCollection { +// final Collection fromCollection; +// final Function function; +// +// TransformedCollection(Collection fromCollection, +// Function function) { +// this.fromCollection = checkNotNull(fromCollection); +// this.function = checkNotNull(function); +// } +// +// @Override public void clear() { +// fromCollection.clear(); +// } +// +// @Override public boolean isEmpty() { +// return fromCollection.isEmpty(); +// } +// +// @Override public Iterator iterator() { +// return Iterators.transform(fromCollection.iterator(), function); +// } +// +// @Override public int size() { +// return fromCollection.size(); +// } +// } + + /** + * Returns {@code true} if the collection {@code self} contains all of the + * elements in the collection {@code c}. + * + *

This method iterates over the specified collection {@code c}, checking + * each element returned by the iterator in turn to see if it is contained in + * the specified collection {@code self}. If all elements are so contained, + * {@code true} is returned, otherwise {@code false}. + * + * @param self a collection which might contain all elements in {@code c} + * @param c a collection whose elements might be contained by {@code self} + */ + static boolean containsAllImpl(Collection self, Collection c) { + return Iterables.all(c, Predicates.in(self)); + } + + /** + * An implementation of {@link Collection#toString()}. + */ +// static String toStringImpl(final Collection collection) { +// StringBuilder sb +// = newStringBuilderForCollection(collection.size()).append('['); +// STANDARD_JOINER.appendTo( +// sb, Iterables.transform(collection, new Function() { +// @Override public Object apply(Object input) { +// return input == collection ? "(this Collection)" : input; +// } +// })); +// return sb.append(']').toString(); +// } +// +// /** +// * Returns best-effort-sized StringBuilder based on the given collection size. +// */ +// static StringBuilder newStringBuilderForCollection(int size) { +// checkNonnegative(size, "size"); +// return new StringBuilder((int) Math.min(size * 8L, Ints.MAX_POWER_OF_TWO)); +// } + + /** + * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 + */ + static Collection cast(Iterable iterable) { + return (Collection) iterable; + } + +// static final Joiner STANDARD_JOINER = Joiner.on(", ").useForNull("null"); + + /** + * Returns a {@link Collection} of all the permutations of the specified + * {@link Iterable}. + * + *

Notes: This is an implementation of the algorithm for + * Lexicographical Permutations Generation, described in Knuth's "The Art of + * Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The + * iteration order follows the lexicographical order. This means that + * the first permutation will be in ascending order, and the last will be in + * descending order. + * + *

Duplicate elements are considered equal. For example, the list [1, 1] + * will have only one permutation, instead of two. This is why the elements + * have to implement {@link Comparable}. + * + *

An empty iterable has only one permutation, which is an empty list. + * + *

This method is equivalent to + * {@code Filter.orderedPermutations(list, Ordering.natural())}. + * + * @param elements the original iterable whose elements have to be permuted. + * @return an immutable {@link Collection} containing all the different + * permutations of the original iterable. + * @throws NullPointerException if the specified iterable is null or has any + * null elements. + * @since 12.0 + */ +// @Beta public static > +// Collection> orderedPermutations(Iterable elements) { +// return orderedPermutations(elements, Ordering.natural()); +// } + + /** + * Returns a {@link Collection} of all the permutations of the specified + * {@link Iterable} using the specified {@link Comparator} for establishing + * the lexicographical ordering. + * + *

Examples:

   {@code
+   *
+   *   for (List perm : orderedPermutations(asList("b", "c", "a"))) {
+   *     println(perm);
+   *   }
+   *   // -> ["a", "b", "c"]
+   *   // -> ["a", "c", "b"]
+   *   // -> ["b", "a", "c"]
+   *   // -> ["b", "c", "a"]
+   *   // -> ["c", "a", "b"]
+   *   // -> ["c", "b", "a"]
+   *
+   *   for (List perm : orderedPermutations(asList(1, 2, 2, 1))) {
+   *     println(perm);
+   *   }
+   *   // -> [1, 1, 2, 2]
+   *   // -> [1, 2, 1, 2]
+   *   // -> [1, 2, 2, 1]
+   *   // -> [2, 1, 1, 2]
+   *   // -> [2, 1, 2, 1]
+   *   // -> [2, 2, 1, 1]}
+ * + *

Notes: This is an implementation of the algorithm for + * Lexicographical Permutations Generation, described in Knuth's "The Art of + * Computer Programming", Volume 4, Chapter 7, Section 7.2.1.2. The + * iteration order follows the lexicographical order. This means that + * the first permutation will be in ascending order, and the last will be in + * descending order. + * + *

Elements that compare equal are considered equal and no new permutations + * are created by swapping them. + * + *

An empty iterable has only one permutation, which is an empty list. + * + * @param elements the original iterable whose elements have to be permuted. + * @param comparator a comparator for the iterable's elements. + * @return an immutable {@link Collection} containing all the different + * permutations of the original iterable. + * @throws NullPointerException If the specified iterable is null, has any + * null elements, or if the specified comparator is null. + * @since 12.0 + */ +// @Beta public static Collection> orderedPermutations( +// Iterable elements, Comparator comparator) { +// return new OrderedPermutationCollection(elements, comparator); +// } + +// 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(inputList, comparator); +// } +// +// /** +// * The number of permutations with repeated elements is calculated as +// * follows: +// *

    +// *
  • For an empty list, it is 1 (base case).
  • +// *
  • When r numbers are added to a list of n-r elements, the number of +// * permutations is increased by a factor of (n choose r).
  • +// *
+// */ +// private static int calculateSize( +// List sortedInputList, Comparator comparator) { +// long permutations = 1; +// int n = 1; +// int r = 1; +// while (n < sortedInputList.size()) { +// int comparison = comparator.compare( +// sortedInputList.get(n - 1), sortedInputList.get(n)); +// if (comparison < 0) { +// // We move to the next non-repeated element. +// permutations *= binomial(n, r); +// r = 0; +// if (!isPositiveInt(permutations)) { +// return Integer.MAX_VALUE; +// } +// } +// n++; +// r++; +// } +// permutations *= binomial(n, r); +// if (!isPositiveInt(permutations)) { +// return Integer.MAX_VALUE; +// } +// return (int) permutations; +// } +// +// @Override public int size() { +// return size; +// } +// +// @Override public boolean isEmpty() { +// return false; +// } +// +// @Override public Iterator> iterator() { +// return new OrderedPermutationIterator(inputList, comparator); +// } +// +// @Override public boolean contains(Object obj) { +// if (obj instanceof List) { +// List list = (List) obj; +// return isPermutation(inputList, list); +// } +// return false; +// } +// +// @Override public String toString() { +// return "orderedPermutationCollection(" + inputList + ")"; +// } +// } +// +// private static final class OrderedPermutationIterator +// extends AbstractIterator> { +// +// List nextPermutation; +// final Comparator comparator; +// +// OrderedPermutationIterator(List list, +// Comparator comparator) { +// this.nextPermutation = Lists.newArrayList(list); +// this.comparator = comparator; +// } +// +// @Override protected List computeNext() { +// if (nextPermutation == null) { +// return endOfData(); +// } +// ImmutableList next = ImmutableList.copyOf(nextPermutation); +// calculateNextPermutation(); +// return next; +// } +// +// void calculateNextPermutation() { +// int j = findNextJ(); +// if (j == -1) { +// nextPermutation = null; +// return; +// } +// +// int l = findNextL(j); +// Collections.swap(nextPermutation, j, l); +// int n = nextPermutation.size(); +// Collections.reverse(nextPermutation.subList(j + 1, n)); +// } +// +// int findNextJ() { +// for (int k = nextPermutation.size() - 2; k >= 0; k--) { +// if (comparator.compare(nextPermutation.get(k), +// nextPermutation.get(k + 1)) < 0) { +// return k; +// } +// } +// return -1; +// } +// +// int findNextL(int j) { +// E ak = nextPermutation.get(j); +// for (int l = nextPermutation.size() - 1; l > j; l--) { +// if (comparator.compare(ak, nextPermutation.get(l)) < 0) { +// return l; +// } +// } +// throw new AssertionError("this statement should be unreachable"); +// } +// } + + /** + * Returns a {@link Collection} of all the permutations of the specified + * {@link Collection}. + * + *

Notes: This is an implementation of the Plain Changes algorithm + * for permutations generation, described in Knuth's "The Art of Computer + * Programming", Volume 4, Chapter 7, Section 7.2.1.2. + * + *

If the input list contains equal elements, some of the generated + * permutations will be equal. + * + *

An empty collection has only one permutation, which is an empty list. + * + * @param elements the original collection whose elements have to be permuted. + * @return an immutable {@link Collection} containing all the different + * permutations of the original collection. + * @throws NullPointerException if the specified collection is null or has any + * null elements. + * @since 12.0 + */ +// @Beta public static Collection> permutations( +// Collection elements) { +// return new PermutationCollection(ImmutableList.copyOf(elements)); +// } +// +// private static final class PermutationCollection +// extends AbstractCollection> { +// final ImmutableList inputList; +// +// PermutationCollection(ImmutableList input) { +// this.inputList = input; +// } +// +// @Override public int size() { +// return IntMath.factorial(inputList.size()); +// } +// +// @Override public boolean isEmpty() { +// return false; +// } +// +// @Override public Iterator> iterator() { +// return new PermutationIterator(inputList); +// } +// +// @Override public boolean contains(Object obj) { +// if (obj instanceof List) { +// List list = (List) obj; +// return isPermutation(inputList, list); +// } +// return false; +// } +// +// @Override public String toString() { +// return "permutations(" + inputList + ")"; +// } +// } + +// private static class PermutationIterator +// 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(); +// c = new int[n]; +// o = new int[n]; +// Arrays.fill(c, 0); +// Arrays.fill(o, 1); +// j = Integer.MAX_VALUE; +// } +// +// @Override protected List computeNext() { +// if (j <= 0) { +// return endOfData(); +// } +// ImmutableList next = ImmutableList.copyOf(list); +// calculateNextPermutation(); +// return next; +// } +// +// void calculateNextPermutation() { +// j = list.size() - 1; +// int s = 0; +// +// // Handle the special case of an empty list. Skip the calculation of the +// // next permutation. +// if (j == -1) { +// return; +// } +// +// while (true) { +// int q = c[j] + o[j]; +// if (q < 0) { +// switchDirection(); +// continue; +// } +// if (q == j + 1) { +// if (j == 0) { +// break; +// } +// s++; +// switchDirection(); +// continue; +// } +// +// Collections.swap(list, j - c[j] + s, j - q + s); +// c[j] = q; +// break; +// } +// } +// +// void switchDirection() { +// o[j] = -o[j]; +// j--; +// } +// } +// +// /** +// * Returns {@code true} if the second list is a permutation of the first. +// */ +// private static boolean isPermutation(List first, +// List second) { +// if (first.size() != second.size()) { +// return false; +// } +// Multiset firstMultiset = HashMultiset.create(first); +// Multiset secondMultiset = HashMultiset.create(second); +// return firstMultiset.equals(secondMultiset); +// } + +// private static boolean isPositiveInt(long n) { +// return n >= 0 && n <= Integer.MAX_VALUE; +// } +} diff --git a/java/src/game/collect/ForwardingCollection.java b/java/src/game/collect/ForwardingCollection.java new file mode 100644 index 0000000..3d1733a --- /dev/null +++ b/java/src/game/collect/ForwardingCollection.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A collection which forwards all its method calls to another collection. + * Subclasses should override one or more methods to modify the behavior of the + * backing collection as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingCollection} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #add} alone will not change the behavior of {@link + * #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or + * delegating to the provided {@code standardAddAll} method. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even + * when all of the methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ + +abstract class ForwardingCollection extends ForwardingObject + implements Collection { + // TODO(user): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingCollection() {} + + @Override protected abstract Collection delegate(); + + @Override + public Iterator iterator() { + return delegate().iterator(); + } + + @Override + public int size() { + return delegate().size(); + } + + @Override + public boolean removeAll(Collection collection) { + return delegate().removeAll(collection); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public boolean contains(Object object) { + return delegate().contains(object); + } + + @Override + public boolean add(E element) { + return delegate().add(element); + } + + @Override + public boolean remove(Object object) { + return delegate().remove(object); + } + + @Override + public boolean containsAll(Collection collection) { + return delegate().containsAll(collection); + } + + @Override + public boolean addAll(Collection collection) { + return delegate().addAll(collection); + } + + @Override + public boolean retainAll(Collection collection) { + return delegate().retainAll(collection); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public Object[] toArray() { + return delegate().toArray(); + } + + @Override + public T[] toArray(T[] array) { + return delegate().toArray(array); + } + + /** + * A sensible definition of {@link #contains} in terms of {@link #iterator}. + * If you override {@link #iterator}, you may wish to override {@link + * #contains} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardContains(Object object) { + return Iterators.contains(iterator(), object); + } + + /** + * A sensible definition of {@link #containsAll} in terms of {@link #contains} + * . If you override {@link #contains}, you may wish to override {@link + * #containsAll} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardContainsAll(Collection collection) { + return Filter.containsAllImpl(this, collection); + } + + /** + * A sensible definition of {@link #addAll} in terms of {@link #add}. If you + * override {@link #add}, you may wish to override {@link #addAll} to forward + * to this implementation. + * + * @since 7.0 + */ + protected boolean standardAddAll(Collection collection) { + return Iterators.addAll(this, collection.iterator()); + } + + /** + * A sensible definition of {@link #remove} in terms of {@link #iterator}, + * using the iterator's {@code remove} method. If you override {@link + * #iterator}, you may wish to override {@link #remove} to forward to this + * implementation. + * + * @since 7.0 + */ + protected boolean standardRemove(Object object) { + Iterator iterator = iterator(); + while (iterator.hasNext()) { + if (Preconditions.equal(iterator.next(), object)) { + iterator.remove(); + return true; + } + } + return false; + } + + /** + * A sensible definition of {@link #removeAll} in terms of {@link #iterator}, + * using the iterator's {@code remove} method. If you override {@link + * #iterator}, you may wish to override {@link #removeAll} to forward to this + * implementation. + * + * @since 7.0 + */ + protected boolean standardRemoveAll(Collection collection) { + return Iterators.removeAll(iterator(), collection); + } + + /** + * A sensible definition of {@link #retainAll} in terms of {@link #iterator}, + * using the iterator's {@code remove} method. If you override {@link + * #iterator}, you may wish to override {@link #retainAll} to forward to this + * implementation. + * + * @since 7.0 + */ + protected boolean standardRetainAll(Collection collection) { + return Iterators.retainAll(iterator(), collection); + } + + /** + * A sensible definition of {@link #clear} in terms of {@link #iterator}, + * using the iterator's {@code remove} method. If you override {@link + * #iterator}, you may wish to override {@link #clear} to forward to this + * implementation. + * + * @since 7.0 + */ + protected void standardClear() { + Iterators.clear(iterator()); + } + + /** + * A sensible definition of {@link #isEmpty} as {@code !iterator().hasNext}. + * If you override {@link #isEmpty}, you may wish to override {@link #isEmpty} + * to forward to this implementation. Alternately, it may be more efficient to + * implement {@code isEmpty} as {@code size() == 0}. + * + * @since 7.0 + */ + protected boolean standardIsEmpty() { + return !iterator().hasNext(); + } + + /** + * A sensible definition of {@link #toString} in terms of {@link #iterator}. + * If you override {@link #iterator}, you may wish to override {@link + * #toString} to forward to this implementation. + * + * @since 7.0 + */ +// protected String standardToString() { +// return Filter.toStringImpl(this); +// } + + /** + * A sensible definition of {@link #toArray()} in terms of {@link + * #toArray(Object[])}. If you override {@link #toArray(Object[])}, you may + * wish to override {@link #toArray} to forward to this implementation. + * + * @since 7.0 + */ + protected Object[] standardToArray() { + Object[] newArray = new Object[size()]; + return toArray(newArray); + } + + /** + * A sensible definition of {@link #toArray(Object[])} in terms of {@link + * #size} and {@link #iterator}. If you override either of these methods, you + * may wish to override {@link #toArray} to forward to this implementation. + * + * @since 7.0 + */ + protected T[] standardToArray(T[] array) { + return ObjectArrays.toArrayImpl(this, array); + } +} diff --git a/java/src/game/collect/ForwardingMap.java b/java/src/game/collect/ForwardingMap.java new file mode 100644 index 0000000..b810066 --- /dev/null +++ b/java/src/game/collect/ForwardingMap.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A map which forwards all its method calls to another map. Subclasses should + * override one or more methods to modify the behavior of the backing map as + * desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingMap} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #put} alone will not change the behavior of {@link + * #putAll}, which can lead to unexpected behavior. In this case, you should + * override {@code putAll} as well, either providing your own implementation, or + * delegating to the provided {@code standardPutAll} method. + * + *

Each of the {@code standard} methods, where appropriate, use {@link + * Objects#equal} to test equality for both keys and values. This may not be + * the desired behavior for map implementations that use non-standard notions of + * key equality, such as a {@code SortedMap} whose comparator is not consistent + * with {@code equals}. + * + *

The {@code standard} methods and the collection views they return are not + * guaranteed to be thread-safe, even when all of the methods that they depend + * on are thread-safe. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ + +abstract class ForwardingMap extends ForwardingObject + implements Map { + // TODO(user): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingMap() {} + + @Override protected abstract Map delegate(); + + @Override + public int size() { + return delegate().size(); + } + + @Override + public boolean isEmpty() { + return delegate().isEmpty(); + } + + @Override + public V remove(Object object) { + return delegate().remove(object); + } + + @Override + public void clear() { + delegate().clear(); + } + + @Override + public boolean containsKey(Object key) { + return delegate().containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return delegate().containsValue(value); + } + + @Override + public V get(Object key) { + return delegate().get(key); + } + + @Override + public V put(K key, V value) { + return delegate().put(key, value); + } + + @Override + public void putAll(Map map) { + delegate().putAll(map); + } + + @Override + public Set keySet() { + return delegate().keySet(); + } + + @Override + public Collection values() { + return delegate().values(); + } + + @Override + public Set> entrySet() { + return delegate().entrySet(); + } + + @Override public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible definition of {@link #putAll(Map)} in terms of {@link + * #put(Object, Object)}. If you override {@link #put(Object, Object)}, you + * may wish to override {@link #putAll(Map)} to forward to this + * implementation. + * + * @since 7.0 + */ +// protected void standardPutAll(Map map) { +// Maps.putAllImpl(this, map); +// } + + /** + * A sensible, albeit inefficient, definition of {@link #remove} in terms of + * the {@code iterator} method of {@link #entrySet}. If you override {@link + * #entrySet}, you may wish to override {@link #remove} to forward to this + * implementation. + * + *

Alternately, you may wish to override {@link #remove} with {@code + * keySet().remove}, assuming that approach would not lead to an infinite + * loop. + * + * @since 7.0 + */ +// @Beta protected V standardRemove(Object key) { +// Iterator> entryIterator = entrySet().iterator(); +// while (entryIterator.hasNext()) { +// Entry entry = entryIterator.next(); +// if (Objects.equal(entry.getKey(), key)) { +// V value = entry.getValue(); +// entryIterator.remove(); +// return value; +// } +// } +// return null; +// } + + /** + * A sensible definition of {@link #clear} in terms of the {@code iterator} + * method of {@link #entrySet}. In many cases, you may wish to override + * {@link #clear} to forward to this implementation. + * + * @since 7.0 + */ +// protected void standardClear() { +// Iterators.clear(entrySet().iterator()); +// } + + /** + * A sensible implementation of {@link Map#keySet} in terms of the following + * methods: {@link ForwardingMap#clear}, {@link ForwardingMap#containsKey}, + * {@link ForwardingMap#isEmpty}, {@link ForwardingMap#remove}, {@link + * ForwardingMap#size}, and the {@link Set#iterator} method of {@link + * ForwardingMap#entrySet}. In many cases, you may wish to override {@link + * ForwardingMap#keySet} to forward to this implementation or a subclass + * thereof. + * + * @since 10.0 + */ +// @Beta +// protected class StandardKeySet extends Maps.KeySet { +// /** Constructor for use by subclasses. */ +// public StandardKeySet() { +// super(ForwardingMap.this); +// } +// } + + /** + * A sensible, albeit inefficient, definition of {@link #containsKey} in terms + * of the {@code iterator} method of {@link #entrySet}. If you override {@link + * #entrySet}, you may wish to override {@link #containsKey} to forward to + * this implementation. + * + * @since 7.0 + */ +// @Beta protected boolean standardContainsKey(Object key) { +// return Maps.containsKeyImpl(this, key); +// } + + /** + * A sensible implementation of {@link Map#values} in terms of the following + * methods: {@link ForwardingMap#clear}, {@link ForwardingMap#containsValue}, + * {@link ForwardingMap#isEmpty}, {@link ForwardingMap#size}, and the {@link + * Set#iterator} method of {@link ForwardingMap#entrySet}. In many cases, you + * may wish to override {@link ForwardingMap#values} to forward to this + * implementation or a subclass thereof. + * + * @since 10.0 + */ +// @Beta +// protected class StandardValues extends Maps.Values { +// /** Constructor for use by subclasses. */ +// public StandardValues() { +// super(ForwardingMap.this); +// } +// } + + /** + * A sensible definition of {@link #containsValue} in terms of the {@code + * iterator} method of {@link #entrySet}. If you override {@link #entrySet}, + * you may wish to override {@link #containsValue} to forward to this + * implementation. + * + * @since 7.0 + */ +// protected boolean standardContainsValue(Object value) { +// return Maps.containsValueImpl(this, value); +// } + + /** + * A sensible implementation of {@link Map#entrySet} in terms of the following + * methods: {@link ForwardingMap#clear}, {@link ForwardingMap#containsKey}, + * {@link ForwardingMap#get}, {@link ForwardingMap#isEmpty}, {@link + * ForwardingMap#remove}, and {@link ForwardingMap#size}. In many cases, you + * may wish to override {@link #entrySet} to forward to this implementation + * or a subclass thereof. + * + * @since 10.0 + */ +// @Beta +// protected abstract class StandardEntrySet extends Maps.EntrySet { +// /** Constructor for use by subclasses. */ +// public StandardEntrySet() {} +// +// @Override +// Map map() { +// return ForwardingMap.this; +// } +// } + + /** + * A sensible definition of {@link #isEmpty} in terms of the {@code iterator} + * method of {@link #entrySet}. If you override {@link #entrySet}, you may + * wish to override {@link #isEmpty} to forward to this implementation. + * + * @since 7.0 + */ +// protected boolean standardIsEmpty() { +// return !entrySet().iterator().hasNext(); +// } + + /** + * A sensible definition of {@link #equals} in terms of the {@code equals} + * method of {@link #entrySet}. If you override {@link #entrySet}, you may + * wish to override {@link #equals} to forward to this implementation. + * + * @since 7.0 + */ +// protected boolean standardEquals(Object object) { +// return Maps.equalsImpl(this, object); +// } + + /** + * A sensible definition of {@link #hashCode} in terms of the {@code iterator} + * method of {@link #entrySet}. If you override {@link #entrySet}, you may + * wish to override {@link #hashCode} to forward to this implementation. + * + * @since 7.0 + */ +// protected int standardHashCode() { +// return Sets.hashCodeImpl(entrySet()); +// } + + /** + * A sensible definition of {@link #toString} in terms of the {@code iterator} + * method of {@link #entrySet}. If you override {@link #entrySet}, you may + * wish to override {@link #toString} to forward to this implementation. + * + * @since 7.0 + */ +// protected String standardToString() { +// return Maps.toStringImpl(this); +// } +} diff --git a/java/src/game/collect/ForwardingMapEntry.java b/java/src/game/collect/ForwardingMapEntry.java new file mode 100644 index 0000000..a4f4d03 --- /dev/null +++ b/java/src/game/collect/ForwardingMapEntry.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Map; +import java.util.Map.Entry; + +/** + * A map entry which forwards all its method calls to another map entry. + * Subclasses should override one or more methods to modify the behavior of the + * backing map entry as desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingMapEntry} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #getValue} alone will not change the behavior of + * {@link #equals}, which can lead to unexpected behavior. In this case, you + * should override {@code equals} as well, either providing your own + * implementation, or delegating to the provided {@code standardEquals} method. + * + *

Each of the {@code standard} methods, where appropriate, use {@link + * Objects#equal} to test equality for both keys and values. This may not be + * the desired behavior for map implementations that use non-standard notions of + * key equality, such as the entry of a {@code SortedMap} whose comparator is + * not consistent with {@code equals}. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even + * when all of the methods that they depend on are thread-safe. + * + * @author Mike Bostock + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ + +abstract class ForwardingMapEntry + extends ForwardingObject implements Map.Entry { + // TODO(user): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingMapEntry() {} + + @Override protected abstract Map.Entry delegate(); + + @Override + public K getKey() { + return delegate().getKey(); + } + + @Override + public V getValue() { + return delegate().getValue(); + } + + @Override + public V setValue(V value) { + return delegate().setValue(value); + } + + @Override public boolean equals(Object object) { + return delegate().equals(object); + } + + @Override public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible definition of {@link #equals(Object)} in terms of {@link + * #getKey()} and {@link #getValue()}. If you override either of these + * methods, you may wish to override {@link #equals(Object)} to forward to + * this implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(Object object) { + if (object instanceof Entry) { + Entry that = (Entry) object; + return Preconditions.equal(this.getKey(), that.getKey()) + && Preconditions.equal(this.getValue(), that.getValue()); + } + return false; + } + + /** + * A sensible definition of {@link #hashCode()} in terms of {@link #getKey()} + * and {@link #getValue()}. If you override either of these methods, you may + * wish to override {@link #hashCode()} to forward to this implementation. + * + * @since 7.0 + */ +// protected int standardHashCode() { +// K k = getKey(); +// V v = getValue(); +// return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode()); +// } + + /** + * A sensible definition of {@link #toString} in terms of {@link + * #getKey} and {@link #getValue}. If you override either of these + * methods, you may wish to override {@link #equals} to forward to this + * implementation. + * + * @since 7.0 + */ +// @Beta protected String standardToString() { +// return getKey() + "=" + getValue(); +// } +} diff --git a/java/src/game/collect/ForwardingObject.java b/java/src/game/collect/ForwardingObject.java new file mode 100644 index 0000000..d5b64e8 --- /dev/null +++ b/java/src/game/collect/ForwardingObject.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +/** + * An abstract base class for implementing the decorator pattern. + * The {@link #delegate()} method must be overridden to return the instance + * being decorated. + * + *

This class does not forward the {@code hashCode} and {@code equals} + * methods through to the backing object, but relies on {@code Object}'s + * implementation. This is necessary to preserve the symmetry of {@code equals}. + * Custom definitions of equality are usually based on an interface, such as + * {@code Set} or {@code List}, so that the implementation of {@code equals} can + * cast the object being tested for equality to the custom interface. {@code + * ForwardingObject} implements no such custom interfaces directly; they + * are implemented only in subclasses. Therefore, forwarding {@code equals} + * would break symmetry, as the forwarding object might consider itself equal to + * the object being tested, but the reverse could not be true. This behavior is + * consistent with the JDK's collection wrappers, such as + * {@link java.util.Collections#unmodifiableCollection}. Use an + * interface-specific subclass of {@code ForwardingObject}, such as {@link + * ForwardingList}, to preserve equality behavior, or override {@code equals} + * directly. + * + *

The {@code toString} method is forwarded to the delegate. Although this + * class does not implement {@link Serializable}, a serializable subclass may be + * created since this class has a parameter-less constructor. + * + * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) + */ + +abstract class ForwardingObject { + + /** Constructor for use by subclasses. */ + protected ForwardingObject() {} + + /** + * Returns the backing delegate instance that methods are forwarded to. + * Abstract subclasses generally override this method with an abstract method + * that has a more specific return type, such as {@link + * ForwardingSet#delegate}. Concrete subclasses override this method to supply + * the instance being decorated. + */ + protected abstract Object delegate(); + + /** + * Returns the string representation generated by the delegate's + * {@code toString} method. + */ + @Override public String toString() { + return delegate().toString(); + } + + /* No equals or hashCode. See class comments for details. */ +} diff --git a/java/src/game/collect/ForwardingSet.java b/java/src/game/collect/ForwardingSet.java new file mode 100644 index 0000000..39e87db --- /dev/null +++ b/java/src/game/collect/ForwardingSet.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkNotNull; + +import java.util.Collection; +import java.util.Set; + +/** + * A set which forwards all its method calls to another set. Subclasses should + * override one or more methods to modify the behavior of the backing set as + * desired per the decorator pattern. + * + *

Warning: The methods of {@code ForwardingSet} forward + * indiscriminately to the methods of the delegate. For example, + * overriding {@link #add} alone will not change the behavior of {@link + * #addAll}, which can lead to unexpected behavior. In this case, you should + * override {@code addAll} as well, either providing your own implementation, or + * delegating to the provided {@code standardAddAll} method. + * + *

The {@code standard} methods are not guaranteed to be thread-safe, even + * when all of the methods that they depend on are thread-safe. + * + * @author Kevin Bourrillion + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ + +abstract class ForwardingSet extends ForwardingCollection + implements Set { + // TODO(user): identify places where thread safety is actually lost + + /** Constructor for use by subclasses. */ + protected ForwardingSet() {} + + @Override protected abstract Set delegate(); + + @Override public boolean equals(Object object) { + return object == this || delegate().equals(object); + } + + @Override public int hashCode() { + return delegate().hashCode(); + } + + /** + * A sensible definition of {@link #removeAll} in terms of {@link #iterator} + * and {@link #remove}. If you override {@code iterator} or {@code remove}, + * you may wish to override {@link #removeAll} to forward to this + * implementation. + * + * @since 7.0 (this version overrides the {@code ForwardingCollection} version as of 12.0) + */ + @Override + protected boolean standardRemoveAll(Collection collection) { + return Sets.removeAllImpl(this, checkNotNull(collection)); // for GWT + } + + /** + * A sensible definition of {@link #equals} in terms of {@link #size} and + * {@link #containsAll}. If you override either of those methods, you may wish + * to override {@link #equals} to forward to this implementation. + * + * @since 7.0 + */ + protected boolean standardEquals(Object object) { + return Sets.equalsImpl(this, object); + } + + /** + * A sensible definition of {@link #hashCode} in terms of {@link #iterator}. + * If you override {@link #iterator}, you may wish to override {@link #equals} + * to forward to this implementation. + * + * @since 7.0 + */ + protected int standardHashCode() { + return Sets.hashCodeImpl(this); + } +} diff --git a/java/src/game/collect/HashBiMap.java b/java/src/game/collect/HashBiMap.java new file mode 100644 index 0000000..e816a22 --- /dev/null +++ b/java/src/game/collect/HashBiMap.java @@ -0,0 +1,658 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkNonnegative; +import static game.collect.CollectPreconditions.checkRemove; +import static game.collect.Preconditions.checkArgument; + +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; + +/** + * A {@link BiMap} backed by two hash tables. This implementation allows null keys and values. A + * {@code HashBiMap} and its inverse are both serializable. + * + *

See the Guava User Guide article on {@code BiMap} + * . + * + * @author Louis Wasserman + * @author Mike Bostock + * @since 2.0 (imported from Google Collections Library) + */ + +public final class HashBiMap extends AbstractMap implements BiMap, Serializable { + + /** + * Returns a new, empty {@code HashBiMap} with the default initial capacity (16). + */ + public static HashBiMap create() { + return create(16); + } + + /** + * Constructs a new, empty bimap with the specified expected size. + * + * @param expectedSize the expected number of entries + * @throws IllegalArgumentException if the specified expected size is negative + */ + public static HashBiMap create(int expectedSize) { + return new HashBiMap(expectedSize); + } + + /** + * Constructs a new bimap containing initial values from {@code map}. The bimap is created with an + * initial capacity sufficient to hold the mappings in the specified map. + */ + public static HashBiMap create(Map map) { + HashBiMap bimap = create(map.size()); + bimap.putAll(map); + return bimap; + } + + private static final class BiEntry extends ImmutableEntry { + final int keyHash; + final int valueHash; + + + BiEntry nextInKToVBucket; + + + BiEntry nextInVToKBucket; + + BiEntry(K key, int keyHash, V value, int valueHash) { + super(key, value); + this.keyHash = keyHash; + this.valueHash = valueHash; + } + } + + private static final double LOAD_FACTOR = 1.0; + + private transient BiEntry[] hashTableKToV; + private transient BiEntry[] hashTableVToK; + private transient int size; + private transient int mask; + private transient int modCount; + + private HashBiMap(int expectedSize) { + init(expectedSize); + } + + private void init(int expectedSize) { + checkNonnegative(expectedSize, "expectedSize"); + int tableSize = Hashing.closedTableSize(expectedSize, LOAD_FACTOR); + this.hashTableKToV = createTable(tableSize); + this.hashTableVToK = createTable(tableSize); + this.mask = tableSize - 1; + this.modCount = 0; + this.size = 0; + } + + /** + * Finds and removes {@code entry} from the bucket linked lists in both the + * key-to-value direction and the value-to-key direction. + */ + private void delete(BiEntry entry) { + int keyBucket = entry.keyHash & mask; + BiEntry prevBucketEntry = null; + for (BiEntry bucketEntry = hashTableKToV[keyBucket]; true; + bucketEntry = bucketEntry.nextInKToVBucket) { + if (bucketEntry == entry) { + if (prevBucketEntry == null) { + hashTableKToV[keyBucket] = entry.nextInKToVBucket; + } else { + prevBucketEntry.nextInKToVBucket = entry.nextInKToVBucket; + } + break; + } + prevBucketEntry = bucketEntry; + } + + int valueBucket = entry.valueHash & mask; + prevBucketEntry = null; + for (BiEntry bucketEntry = hashTableVToK[valueBucket];; + bucketEntry = bucketEntry.nextInVToKBucket) { + if (bucketEntry == entry) { + if (prevBucketEntry == null) { + hashTableVToK[valueBucket] = entry.nextInVToKBucket; + } else { + prevBucketEntry.nextInVToKBucket = entry.nextInVToKBucket; + } + break; + } + prevBucketEntry = bucketEntry; + } + + size--; + modCount++; + } + + private void insert(BiEntry entry) { + int keyBucket = entry.keyHash & mask; + entry.nextInKToVBucket = hashTableKToV[keyBucket]; + hashTableKToV[keyBucket] = entry; + + int valueBucket = entry.valueHash & mask; + entry.nextInVToKBucket = hashTableVToK[valueBucket]; + hashTableVToK[valueBucket] = entry; + + size++; + modCount++; + } + + private static int hash(Object o) { + return Hashing.smear((o == null) ? 0 : o.hashCode()); + } + + private BiEntry seekByKey(Object key, int keyHash) { + for (BiEntry entry = hashTableKToV[keyHash & mask]; entry != null; + entry = entry.nextInKToVBucket) { + if (keyHash == entry.keyHash && Preconditions.equal(key, entry.key)) { + return entry; + } + } + return null; + } + + private BiEntry seekByValue(Object value, int valueHash) { + for (BiEntry entry = hashTableVToK[valueHash & mask]; entry != null; + entry = entry.nextInVToKBucket) { + if (valueHash == entry.valueHash && Preconditions.equal(value, entry.value)) { + return entry; + } + } + return null; + } + + @Override + public boolean containsKey(Object key) { + return seekByKey(key, hash(key)) != null; + } + + @Override + public boolean containsValue(Object value) { + return seekByValue(value, hash(value)) != null; + } + + + @Override + public V get(Object key) { + BiEntry entry = seekByKey(key, hash(key)); + return (entry == null) ? null : entry.value; + } + + @Override + public V put(K key, V value) { + return put(key, value, false); + } + + @Override + public V forcePut(K key, V value) { + return put(key, value, true); + } + + private V put(K key, V value, boolean force) { + int keyHash = hash(key); + int valueHash = hash(value); + + BiEntry oldEntryForKey = seekByKey(key, keyHash); + if (oldEntryForKey != null && valueHash == oldEntryForKey.valueHash + && Preconditions.equal(value, oldEntryForKey.value)) { + return value; + } + + BiEntry oldEntryForValue = seekByValue(value, valueHash); + if (oldEntryForValue != null) { + if (force) { + delete(oldEntryForValue); + } else { + throw new IllegalArgumentException("value already present: " + value); + } + } + + if (oldEntryForKey != null) { + delete(oldEntryForKey); + } + BiEntry newEntry = new BiEntry(key, keyHash, value, valueHash); + insert(newEntry); + rehashIfNecessary(); + return (oldEntryForKey == null) ? null : oldEntryForKey.value; + } + + + private K putInverse(V value, K key, boolean force) { + int valueHash = hash(value); + int keyHash = hash(key); + + BiEntry oldEntryForValue = seekByValue(value, valueHash); + if (oldEntryForValue != null && keyHash == oldEntryForValue.keyHash + && Preconditions.equal(key, oldEntryForValue.key)) { + return key; + } + + BiEntry oldEntryForKey = seekByKey(key, keyHash); + if (oldEntryForKey != null) { + if (force) { + delete(oldEntryForKey); + } else { + throw new IllegalArgumentException("value already present: " + key); + } + } + + if (oldEntryForValue != null) { + delete(oldEntryForValue); + } + BiEntry newEntry = new BiEntry(key, keyHash, value, valueHash); + insert(newEntry); + rehashIfNecessary(); + return (oldEntryForValue == null) ? null : oldEntryForValue.key; + } + + private void rehashIfNecessary() { + BiEntry[] oldKToV = hashTableKToV; + if (Hashing.needsResizing(size, oldKToV.length, LOAD_FACTOR)) { + int newTableSize = oldKToV.length * 2; + + this.hashTableKToV = createTable(newTableSize); + this.hashTableVToK = createTable(newTableSize); + this.mask = newTableSize - 1; + this.size = 0; + + for (int bucket = 0; bucket < oldKToV.length; bucket++) { + BiEntry entry = oldKToV[bucket]; + while (entry != null) { + BiEntry nextEntry = entry.nextInKToVBucket; + insert(entry); + entry = nextEntry; + } + } + this.modCount++; + } + } + + + private BiEntry[] createTable(int length) { + return new BiEntry[length]; + } + + @Override + public V remove(Object key) { + BiEntry entry = seekByKey(key, hash(key)); + if (entry == null) { + return null; + } else { + delete(entry); + return entry.value; + } + } + + @Override + public void clear() { + size = 0; + Arrays.fill(hashTableKToV, null); + Arrays.fill(hashTableVToK, null); + modCount++; + } + + @Override + public int size() { + return size; + } + + abstract class Itr implements Iterator { + int nextBucket = 0; + BiEntry next = null; + BiEntry toRemove = null; + int expectedModCount = modCount; + + private void checkForConcurrentModification() { + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean hasNext() { + checkForConcurrentModification(); + if (next != null) { + return true; + } + while (nextBucket < hashTableKToV.length) { + if (hashTableKToV[nextBucket] != null) { + next = hashTableKToV[nextBucket++]; + return true; + } + nextBucket++; + } + return false; + } + + @Override + public T next() { + checkForConcurrentModification(); + if (!hasNext()) { + throw new NoSuchElementException(); + } + + BiEntry entry = next; + next = entry.nextInKToVBucket; + toRemove = entry; + return output(entry); + } + + @Override + public void remove() { + checkForConcurrentModification(); + checkRemove(toRemove != null); + delete(toRemove); + expectedModCount = modCount; + toRemove = null; + } + + abstract T output(BiEntry entry); + } + + @Override + public Set keySet() { + return new KeySet(); + } + + private final class KeySet extends Maps.KeySet { + KeySet() { + super(HashBiMap.this); + } + + @Override + public Iterator iterator() { + return new Itr() { + @Override + K output(BiEntry entry) { + return entry.key; + } + }; + } + + @Override + public boolean remove(Object o) { + BiEntry entry = seekByKey(o, hash(o)); + if (entry == null) { + return false; + } else { + delete(entry); + return true; + } + } + } + + @Override + public Set values() { + return inverse().keySet(); + } + + @Override + public Set> entrySet() { + return new EntrySet(); + } + + private final class EntrySet extends Maps.EntrySet { + @Override + Map map() { + return HashBiMap.this; + } + + @Override + public Iterator> iterator() { + return new Itr>() { + @Override + Entry output(BiEntry entry) { + return new MapEntry(entry); + } + + class MapEntry extends AbstractMapEntry { + BiEntry delegate; + + MapEntry(BiEntry entry) { + this.delegate = entry; + } + + @Override public K getKey() { + return delegate.key; + } + + @Override public V getValue() { + return delegate.value; + } + + @Override public V setValue(V value) { + V oldValue = delegate.value; + int valueHash = hash(value); + if (valueHash == delegate.valueHash && Preconditions.equal(value, oldValue)) { + return value; + } + checkArgument( + seekByValue(value, valueHash) == null, "value already present: %s", value); + delete(delegate); + BiEntry newEntry = + new BiEntry(delegate.key, delegate.keyHash, value, valueHash); + insert(newEntry); + expectedModCount = modCount; + if (toRemove == delegate) { + toRemove = newEntry; + } + delegate = newEntry; + return oldValue; + } + } + }; + } + } + + private transient BiMap inverse; + + @Override + public BiMap inverse() { + return (inverse == null) ? inverse = new Inverse() : inverse; + } + + private final class Inverse extends AbstractMap implements BiMap, Serializable { + BiMap forward() { + return HashBiMap.this; + } + + @Override + public int size() { + return size; + } + + @Override + public void clear() { + forward().clear(); + } + + @Override + public boolean containsKey(Object value) { + return forward().containsValue(value); + } + + @Override + public K get(Object value) { + BiEntry entry = seekByValue(value, hash(value)); + return (entry == null) ? null : entry.key; + } + + @Override + public K put(V value, K key) { + return putInverse(value, key, false); + } + + @Override + public K forcePut(V value, K key) { + return putInverse(value, key, true); + } + + @Override + public K remove(Object value) { + BiEntry entry = seekByValue(value, hash(value)); + if (entry == null) { + return null; + } else { + delete(entry); + return entry.key; + } + } + + @Override + public BiMap inverse() { + return forward(); + } + + @Override + public Set keySet() { + return new InverseKeySet(); + } + + private final class InverseKeySet extends Maps.KeySet { + InverseKeySet() { + super(Inverse.this); + } + + @Override + public boolean remove(Object o) { + BiEntry entry = seekByValue(o, hash(o)); + if (entry == null) { + return false; + } else { + delete(entry); + return true; + } + } + + @Override + public Iterator iterator() { + return new Itr() { + @Override V output(BiEntry entry) { + return entry.value; + } + }; + } + } + + @Override + public Set values() { + return forward().keySet(); + } + + @Override + public Set> entrySet() { + return new Maps.EntrySet() { + + @Override + Map map() { + return Inverse.this; + } + + @Override + public Iterator> iterator() { + return new Itr>() { + @Override + Entry output(BiEntry entry) { + return new InverseEntry(entry); + } + + class InverseEntry extends AbstractMapEntry { + BiEntry delegate; + + InverseEntry(BiEntry entry) { + this.delegate = entry; + } + + @Override + public V getKey() { + return delegate.value; + } + + @Override + public K getValue() { + return delegate.key; + } + + @Override + public K setValue(K key) { + K oldKey = delegate.key; + int keyHash = hash(key); + if (keyHash == delegate.keyHash && Preconditions.equal(key, oldKey)) { + return key; + } + checkArgument(seekByKey(key, keyHash) == null, "value already present: %s", key); + delete(delegate); + BiEntry newEntry = + new BiEntry(key, keyHash, delegate.value, delegate.valueHash); + insert(newEntry); + expectedModCount = modCount; + // This is safe because entries can only get bumped up to earlier in the iteration, + // so they can't get revisited. + return oldKey; + } + } + }; + } + }; + } + + Object writeReplace() { + return new InverseSerializedForm(HashBiMap.this); + } + } + + private static final class InverseSerializedForm implements Serializable { + private final HashBiMap bimap; + + InverseSerializedForm(HashBiMap bimap) { + this.bimap = bimap; + } + + Object readResolve() { + return bimap.inverse(); + } + } + + /** + * @serialData the number of entries, first key, first value, second key, second value, and so on. + */ +// +// private void writeObject(ObjectOutputStream stream) throws IOException { +// stream.defaultWriteObject(); +// Serialization.writeMap(this, stream); +// } +// +// +// private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { +// stream.defaultReadObject(); +// int size = Serialization.readCount(stream); +// init(size); +// Serialization.populateMap(this, stream, size); +// } + + + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/collect/Hashing.java b/java/src/game/collect/Hashing.java new file mode 100644 index 0000000..0ce6f8d --- /dev/null +++ b/java/src/game/collect/Hashing.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +/** + * Static methods for implementing hash-based collections. + * + * @author Kevin Bourrillion + * @author Jesse Wilson + * @author Austin Appleby + */ + +final class Hashing { + private Hashing() {} + + private static final int C1 = 0xcc9e2d51; + private static final int C2 = 0x1b873593; + + /* + * This method was rewritten in Java from an intermediate step of the Murmur hash function in + * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp, which contained the + * following header: + * + * MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author + * hereby disclaims copyright to this source code. + */ + static int smear(int hashCode) { + return C2 * Integer.rotateLeft(hashCode * C1, 15); + } + + static int smearedHash(Object o) { + return smear((o == null) ? 0 : o.hashCode()); + } + + private static int MAX_TABLE_SIZE = 1 << (Integer.SIZE - 2); + + static int closedTableSize(int expectedEntries, double loadFactor) { + // Get the recommended table size. + // Round down to the nearest power of 2. + expectedEntries = Math.max(expectedEntries, 2); + int tableSize = Integer.highestOneBit(expectedEntries); + // Check to make sure that we will not exceed the maximum load factor. + if (expectedEntries > (int) (loadFactor * tableSize)) { + tableSize <<= 1; + return (tableSize > 0) ? tableSize : MAX_TABLE_SIZE; + } + return tableSize; + } + + static boolean needsResizing(int size, int tableSize, double loadFactor) { + return size > loadFactor * tableSize && tableSize < MAX_TABLE_SIZE; + } +} diff --git a/java/src/game/collect/ImmutableAsList.java b/java/src/game/collect/ImmutableAsList.java new file mode 100644 index 0000000..c65f53e --- /dev/null +++ b/java/src/game/collect/ImmutableAsList.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; + +/** + * List returned by {@link ImmutableCollection#asList} that delegates {@code contains} checks + * to the backing collection. + * + * @author Jared Levy + * @author Louis Wasserman + */ + + +abstract class ImmutableAsList extends ImmutableList { + abstract ImmutableCollection delegateCollection(); + + @Override public boolean contains(Object target) { + // The collection's contains() is at least as fast as ImmutableList's + // and is often faster. + return delegateCollection().contains(target); + } + + @Override + public int size() { + return delegateCollection().size(); + } + + @Override + public boolean isEmpty() { + return delegateCollection().isEmpty(); + } + + @Override + boolean isPartialView() { + return delegateCollection().isPartialView(); + } + + /** + * Serialized form that leads to the same performance as the original list. + */ + + static class SerializedForm implements Serializable { + final ImmutableCollection collection; + SerializedForm(ImmutableCollection collection) { + this.collection = collection; + } + Object readResolve() { + return collection.asList(); + } + private static final long serialVersionUID = 0; + } + + + private void readObject(ObjectInputStream stream) + throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + + @Override Object writeReplace() { + return new SerializedForm(delegateCollection()); + } +} diff --git a/java/src/game/collect/ImmutableCollection.java b/java/src/game/collect/ImmutableCollection.java new file mode 100644 index 0000000..f8e4278 --- /dev/null +++ b/java/src/game/collect/ImmutableCollection.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkNonnegative; +import static game.collect.ObjectArrays.checkElementsNotNull; +import static game.collect.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.AbstractCollection; +import java.util.Collection; +import java.util.Iterator; + +/** + * An immutable collection. Does not permit null elements. + * + *

In addition to the {@link Collection} methods, this class has an {@link + * #asList()} method, which returns a list view of the collection's elements. + * + *

Note: Although this class is not final, it cannot be subclassed + * outside of this package as it has no public or protected constructors. Thus, + * instances of this type are guaranteed to be immutable. + * + * @author Jesse Wilson + * @since 2.0 (imported from Google Collections Library) + */ + + // we're overriding default serialization +abstract class ImmutableCollection extends AbstractCollection + implements Serializable { + + ImmutableCollection() {} + + /** + * Returns an unmodifiable iterator across the elements in this collection. + */ + @Override + public abstract UnmodifiableIterator iterator(); + + @Override + public final Object[] toArray() { + int size = size(); + if (size == 0) { + return ObjectArrays.EMPTY_ARRAY; + } + Object[] result = new Object[size()]; + copyIntoArray(result, 0); + return result; + } + + @Override + public final T[] toArray(T[] other) { + checkNotNull(other); + int size = size(); + if (other.length < size) { + other = ObjectArrays.newArray(other, size); + } else if (other.length > size) { + other[size] = null; + } + copyIntoArray(other, 0); + return other; + } + + @Override + public boolean contains(Object object) { + return object != null && super.contains(object); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean add(E e) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean remove(Object object) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean addAll(Collection newElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean removeAll(Collection oldElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean retainAll(Collection elementsToKeep) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + /* + * TODO(kevinb): Restructure code so ImmutableList doesn't contain this + * variable, which it doesn't use. + */ + private transient ImmutableList asList; + + /** + * Returns a list view of the collection. + * + * @since 2.0 + */ + public ImmutableList asList() { + ImmutableList list = asList; + return (list == null) ? (asList = createAsList()) : list; + } + + ImmutableList createAsList() { + switch (size()) { + case 0: + return ImmutableList.of(); +// case 1: +// return ImmutableList.of(iterator().next()); + default: + return new RegularImmutableAsList(this, toArray()); + } + } + + /** + * Returns {@code true} if this immutable collection's implementation contains references to + * user-created objects that aren't accessible via this collection's methods. This is generally + * used to determine whether {@code copyOf} implementations should make an explicit copy to avoid + * memory leaks. + */ + abstract boolean isPartialView(); + + /** + * Copies the contents of this immutable collection into the specified array at the specified + * offset. Returns {@code offset + size()}. + */ + int copyIntoArray(Object[] dst, int offset) { + for (E e : this) { + dst[offset++] = e; + } + return offset; + } + + Object writeReplace() { + // We serialize by default to ImmutableList, the simplest thing that works. + return new ImmutableList.SerializedForm(toArray()); + } + + /** + * Abstract base class for builders of {@link ImmutableCollection} types. + * + * @since 10.0 + */ + 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"); + } + // careful of overflow! + int newCapacity = oldCapacity + (oldCapacity >> 1) + 1; + if (newCapacity < minCapacity) { + newCapacity = Integer.highestOneBit(minCapacity - 1) << 1; + } + if (newCapacity < 0) { + newCapacity = Integer.MAX_VALUE; + // guaranteed to be >= newCapacity + } + return newCapacity; + } + + Builder() { + } + + /** + * Adds {@code element} to the {@code ImmutableCollection} being built. + * + *

Note that each builder class covariantly returns its own type from + * this method. + * + * @param element the element to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code element} is null + */ + public abstract Builder add(E element); + + /** + * Adds each element of {@code elements} to the {@code ImmutableCollection} + * being built. + * + *

Note that each builder class overrides this method in order to + * covariantly return its own type. + * + * @param elements the elements to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + public Builder add(E... elements) { + for (E element : elements) { + add(element); + } + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableCollection} + * being built. + * + *

Note that each builder class overrides this method in order to + * covariantly return its own type. + * + * @param elements the elements to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + public Builder addAll(Iterable elements) { + for (E element : elements) { + add(element); + } + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableCollection} + * being built. + * + *

Note that each builder class overrides this method in order to + * covariantly return its own type. + * + * @param elements the elements to add + * @return this {@code Builder} instance + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + public Builder addAll(Iterator elements) { + while (elements.hasNext()) { + add(elements.next()); + } + return this; + } + + /** + * Returns a newly-created {@code ImmutableCollection} of the appropriate + * type, containing the elements provided to this builder. + * + *

Note that each builder class covariantly returns the appropriate type + * of {@code ImmutableCollection} from this method. + */ + public abstract ImmutableCollection build(); + } + + abstract static class ArrayBasedBuilder extends ImmutableCollection.Builder { + Object[] contents; + int size; + + ArrayBasedBuilder(int initialCapacity) { + checkNonnegative(initialCapacity, "initialCapacity"); + this.contents = new Object[initialCapacity]; + this.size = 0; + } + + /** + * Expand the absolute capacity of the builder so it can accept at least + * the specified number of elements without being resized. + */ + private void ensureCapacity(int minCapacity) { + if (contents.length < minCapacity) { + this.contents = ObjectArrays.arraysCopyOf( + this.contents, expandedCapacity(contents.length, minCapacity)); + } + } + + @Override + public ArrayBasedBuilder add(E element) { + checkNotNull(element); + ensureCapacity(size + 1); + contents[size++] = element; + return this; + } + + @Override + public Builder add(E... elements) { + checkElementsNotNull(elements); + ensureCapacity(size + elements.length); + System.arraycopy(elements, 0, contents, size, elements.length); + size += elements.length; + return this; + } + + @Override + public Builder addAll(Iterable elements) { + if (elements instanceof Collection) { + Collection collection = (Collection) elements; + ensureCapacity(size + collection.size()); + } + super.addAll(elements); + return this; + } + } +} diff --git a/java/src/game/collect/ImmutableEntry.java b/java/src/game/collect/ImmutableEntry.java new file mode 100644 index 0000000..9629968 --- /dev/null +++ b/java/src/game/collect/ImmutableEntry.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.io.Serializable; + +/** + * @see game.collect.Maps#immutableEntry(Object, Object) + */ + +class ImmutableEntry extends AbstractMapEntry + implements Serializable { + final K key; + final V value; + + ImmutableEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override public final K getKey() { + return key; + } + + @Override public final V getValue() { + return value; + } + + @Override public final V setValue(V value) { + throw new UnsupportedOperationException(); + } + + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/collect/ImmutableEnumMap.java b/java/src/game/collect/ImmutableEnumMap.java new file mode 100644 index 0000000..163cf97 --- /dev/null +++ b/java/src/game/collect/ImmutableEnumMap.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package game.collect; + +import static game.collect.Preconditions.checkArgument; + +import java.util.EnumMap; +import java.util.Iterator; + +/** + * Implementation of {@link ImmutableMap} backed by a non-empty {@link + * java.util.EnumMap}. + * + * @author Louis Wasserman + */ + + // we're overriding default serialization +final class ImmutableEnumMap, V> extends ImmutableMap { + static , V> ImmutableMap asImmutable(EnumMap map) { + switch (map.size()) { + case 0: + return ImmutableMap.of(); +// case 1: { +// Entry entry = Iterables.getOnlyElement(map.entrySet()); +// return ImmutableMap.of(entry.getKey(), entry.getValue()); +// } + default: + return new ImmutableEnumMap(map); + } + } + + private transient final EnumMap delegate; + + private ImmutableEnumMap(EnumMap delegate) { + this.delegate = delegate; + checkArgument(!delegate.isEmpty()); + } + + @Override + ImmutableSet createKeySet() { + return new ImmutableSet() { + + @Override + public boolean contains(Object object) { + return delegate.containsKey(object); + } + + @Override + public int size() { + return ImmutableEnumMap.this.size(); + } + + @Override + public UnmodifiableIterator iterator() { + return Iterators.unmodifiableIterator(delegate.keySet().iterator()); + } + + @Override + boolean isPartialView() { + return true; + } + }; + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean containsKey(Object key) { + return delegate.containsKey(key); + } + + @Override + public V get(Object key) { + return delegate.get(key); + } + + @Override + ImmutableSet> createEntrySet() { + return new ImmutableMapEntrySet() { + + @Override + ImmutableMap map() { + return ImmutableEnumMap.this; + } + + @Override + public UnmodifiableIterator> iterator() { + return new UnmodifiableIterator>() { + private final Iterator> backingIterator = delegate.entrySet().iterator(); + + @Override + public boolean hasNext() { + return backingIterator.hasNext(); + } + + @Override + public Entry next() { + Entry entry = backingIterator.next(); + return Maps.immutableEntry(entry.getKey(), entry.getValue()); + } + }; + } + }; + } + + @Override + boolean isPartialView() { + return false; + } + + // All callers of the constructor are restricted to >. +// @Override Object writeReplace() { +// return new EnumSerializedForm(delegate); +// } +// +// /* +// * This class is used to serialize ImmutableEnumSet instances. +// */ +// private static class EnumSerializedForm, V> +// implements Serializable { +// final EnumMap delegate; +// EnumSerializedForm(EnumMap delegate) { +// this.delegate = delegate; +// } +// Object readResolve() { +// return new ImmutableEnumMap(delegate); +// } +// private static final long serialVersionUID = 0; +// } +} diff --git a/java/src/game/collect/ImmutableEnumSet.java b/java/src/game/collect/ImmutableEnumSet.java new file mode 100644 index 0000000..8f49f32 --- /dev/null +++ b/java/src/game/collect/ImmutableEnumSet.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.io.Serializable; +import java.util.Collection; +import java.util.EnumSet; + +/** + * Implementation of {@link ImmutableSet} backed by a non-empty {@link + * java.util.EnumSet}. + * + * @author Jared Levy + */ + + // we're overriding default serialization +final class ImmutableEnumSet> extends ImmutableSet { + 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); + } + } + + /* + * Notes on EnumSet and >: + * + * This class isn't an arbitrary ForwardingImmutableSet because we need to + * know that calling {@code clone()} during deserialization will return an + * object that no one else has a reference to, allowing us to guarantee + * immutability. Hence, we support only {@link EnumSet}. + */ + private final transient EnumSet delegate; + + private ImmutableEnumSet(EnumSet delegate) { + this.delegate = delegate; + } + + @Override boolean isPartialView() { + return false; + } + + @Override public UnmodifiableIterator iterator() { + return Iterators.unmodifiableIterator(delegate.iterator()); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override public boolean contains(Object object) { + return delegate.contains(object); + } + + @Override public boolean containsAll(Collection collection) { + return delegate.containsAll(collection); + } + + @Override public boolean isEmpty() { + return delegate.isEmpty(); + } + + @Override public boolean equals(Object object) { + return object == this || delegate.equals(object); + } + + private transient int hashCode; + + @Override public int hashCode() { + int result = hashCode; + return (result == 0) ? hashCode = delegate.hashCode() : result; + } + + @Override public String toString() { + return delegate.toString(); + } + + // All callers of the constructor are restricted to >. + @Override Object writeReplace() { + return new EnumSerializedForm(delegate); + } + + /* + * This class is used to serialize ImmutableEnumSet instances. + */ + private static class EnumSerializedForm> + implements Serializable { + final EnumSet delegate; + EnumSerializedForm(EnumSet delegate) { + this.delegate = delegate; + } + Object readResolve() { + // EJ2 #76: Write readObject() methods defensively. + return new ImmutableEnumSet(delegate.clone()); + } + private static final long serialVersionUID = 0; + } +} diff --git a/java/src/game/collect/ImmutableList.java b/java/src/game/collect/ImmutableList.java new file mode 100644 index 0000000..17f8b27 --- /dev/null +++ b/java/src/game/collect/ImmutableList.java @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.ObjectArrays.arraysCopyOf; +import static game.collect.ObjectArrays.checkElementsNotNull; +import static game.collect.Preconditions.checkElementIndex; +import static game.collect.Preconditions.checkNotNull; +import static game.collect.Preconditions.checkPositionIndexes; + +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.ListIterator; +import java.util.RandomAccess; + +/** + * A high-performance, immutable, random-access {@code List} implementation. + * Does not permit null elements. + * + *

Unlike {@link Collections#unmodifiableList}, which is a view of a + * separate collection that can still change, an instance of {@code + * ImmutableList} contains its own private data and will never change. + * {@code ImmutableList} is convenient for {@code public static final} lists + * ("constant lists") and also lets you easily make a "defensive copy" of a list + * provided to your class by a caller. + * + *

Note: Although this class is not final, it cannot be subclassed as + * it has no public or protected constructors. Thus, instances of this type are + * guaranteed to be immutable. + * + *

See the Guava User Guide article on + * immutable collections. + * + * @see ImmutableMap + * @see ImmutableSet + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + + // we're overriding default serialization +public abstract class ImmutableList extends ImmutableCollection + implements List, RandomAccess { + + private static final ImmutableList EMPTY = + new RegularImmutableList(ObjectArrays.EMPTY_ARRAY); + + /** + * Returns the empty immutable list. This set behaves and performs comparably + * to {@link Collections#emptyList}, and is preferable mainly for consistency + * and maintainability of your code. + */ + // Casting to any type is safe because the list will never hold any elements. + + public static ImmutableList of() { + return (ImmutableList) EMPTY; + } + + /** + * Returns an immutable list containing a single element. This list behaves + * and performs comparably to {@link Collections#singleton}, but will not + * accept a null element. It is preferable mainly for consistency and + * maintainability of your code. + * + * @throws NullPointerException if {@code element} is null + */ +// public static ImmutableList of(E element) { +// return new SingletonImmutableList(element); +// } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2) { + return construct(e1, e2); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3) { + return construct(e1, e2, e3); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4) { + return construct(e1, e2, e3, e4); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5) { + return construct(e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableList of(E e1, E e2, E e3, E e4, E e5, E e6) { + return construct(e1, e2, e3, e4, e5, e6); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + 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); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + 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); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + 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); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + 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); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + */ + 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); + } + + // These go up to eleven. After that, you just get the varargs form, and + // whatever warnings might come along with it. :( + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any element is null + * @since 3.0 (source-compatible since 2.0) + */ + 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); + } + + /** + * Returns an immutable list containing the given elements, in order. If + * {@code elements} is a {@link Collection}, this method behaves exactly as + * {@link #copyOf(Collection)}; otherwise, it behaves exactly as {@code + * copyOf(elements.iterator()}. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableList copyOf(Iterable elements) { + checkNotNull(elements); // TODO(kevinb): is this here only for GWT? + return (elements instanceof Collection) + ? copyOf(Filter.cast(elements)) + : copyOf(elements.iterator()); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + *

Despite the method name, this method attempts to avoid actually copying + * the data when it is safe to do so. The exact circumstances under which a + * copy will or will not be performed are undocumented and subject to change. + * + *

Note that if {@code list} is a {@code List}, then {@code + * ImmutableList.copyOf(list)} returns an {@code ImmutableList} + * containing each of the strings in {@code list}, while + * ImmutableList.of(list)} returns an {@code ImmutableList>} + * containing one element (the given list itself). + * + *

This method is safe to use even when {@code elements} is a synchronized + * or concurrent collection that is currently being modified by another + * thread. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableList copyOf(Collection elements) { + if (elements instanceof ImmutableCollection) { + // all supported methods are covariant + ImmutableList list = ((ImmutableCollection) elements).asList(); + return list.isPartialView() + ? ImmutableList.asImmutableList(list.toArray()) + : list; + } + return construct(elements.toArray()); + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableList copyOf(Iterator elements) { + // We special-case for 0 or 1 elements, but going further is madness. + if (!elements.hasNext()) { + return of(); + } +// E first = elements.next(); +// if (!elements.hasNext()) { +// return of(first); +// } else { + return new ImmutableList.Builder() +// .add(first) + .addAll(elements) + .build(); +// } + } + + /** + * Returns an immutable list containing the given elements, in order. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 3.0 + */ + public static ImmutableList copyOf(E[] elements) { + switch (elements.length) { + case 0: + return ImmutableList.of(); +// case 1: +// return new SingletonImmutableList(elements[0]); + default: + return new RegularImmutableList(checkElementsNotNull(elements.clone())); + } + } + + /** + * Views the array as an immutable list. Checks for nulls; does not copy. + */ + private static ImmutableList construct(Object... elements) { + return asImmutableList(checkElementsNotNull(elements)); + } + + /** + * Views the array as an immutable list. Does not check for nulls; does not copy. + * + *

The array must be internally created. + */ + static ImmutableList asImmutableList(Object[] elements) { + return asImmutableList(elements, elements.length); + } + + /** + * Views the array as an immutable list. Copies if the specified range does not cover the complete + * array. Does not check for nulls. + */ + static ImmutableList asImmutableList(Object[] elements, int length) { + switch (length) { + case 0: + return of(); +// case 1: +// // collection had only Es in it +// ImmutableList list = new SingletonImmutableList((E) elements[0]); +// return list; + default: + if (length < elements.length) { + elements = arraysCopyOf(elements, length); + } + return new RegularImmutableList(elements); + } + } + + ImmutableList() {} + + // This declaration is needed to make List.iterator() and + // ImmutableCollection.iterator() consistent. + @Override public UnmodifiableIterator iterator() { + return listIterator(); + } + + @Override public UnmodifiableListIterator listIterator() { + return listIterator(0); + } + + @Override public UnmodifiableListIterator listIterator(int index) { + return new AbstractIndexedListIterator(size(), index) { + @Override + protected E get(int index) { + return ImmutableList.this.get(index); + } + }; + } + + @Override + public int indexOf(Object object) { + return (object == null) ? -1 : indexOfImpl(this, object); + } + + @Override + public int lastIndexOf(Object object) { + return (object == null) ? -1 : lastIndexOfImpl(this, object); + } + + @Override + public boolean contains(Object object) { + return indexOf(object) >= 0; + } + + // constrain the return type to ImmutableList + + /** + * Returns an immutable list of the elements between the specified {@code + * fromIndex}, inclusive, and {@code toIndex}, exclusive. (If {@code + * fromIndex} and {@code toIndex} are equal, the empty immutable list is + * returned.) + */ + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); + int length = toIndex - fromIndex; + switch (length) { + case 0: + return of(); +// case 1: +// return of(get(fromIndex)); + default: + return subListUnchecked(fromIndex, toIndex); + } + } + + /** + * Called by the default implementation of {@link #subList} when {@code + * toIndex - fromIndex > 1}, after index validation has already been + * performed. + */ + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + return new SubList(fromIndex, toIndex - fromIndex); + } + + class SubList extends ImmutableList { + transient final int offset; + transient final int length; + + SubList(int offset, int length) { + this.offset = offset; + this.length = length; + } + + @Override + public int size() { + return length; + } + + @Override + public E get(int index) { + checkElementIndex(index, length); + return ImmutableList.this.get(index + offset); + } + + @Override + public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, length); + return ImmutableList.this.subList(fromIndex + offset, toIndex + offset); + } + + @Override + boolean isPartialView() { + return true; + } + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final boolean addAll(int index, Collection newElements) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final E set(int index, E element) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void add(int index, E element) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the list unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final E remove(int index) { + throw new UnsupportedOperationException(); + } + + /** + * Returns this list instance. + * + * @since 2.0 + */ + @Override public final ImmutableList asList() { + return this; + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + // this loop is faster for RandomAccess instances, which ImmutableLists are + int size = size(); + for (int i = 0; i < size; i++) { + dst[offset + i] = get(i); + } + return offset + size; + } + + /** + * Returns a view of this immutable list in reverse order. For example, {@code + * ImmutableList.of(1, 2, 3).reverse()} is equivalent to {@code + * ImmutableList.of(3, 2, 1)}. + * + * @return a view of this immutable list in reverse order + * @since 7.0 + */ + public ImmutableList reverse() { + return new ReverseImmutableList(this); + } + + private static class ReverseImmutableList extends ImmutableList { + private final transient ImmutableList forwardList; + + ReverseImmutableList(ImmutableList backingList) { + this.forwardList = backingList; + } + + private int reverseIndex(int index) { + return (size() - 1) - index; + } + + private int reversePosition(int index) { + return size() - index; + } + + @Override public ImmutableList reverse() { + return forwardList; + } + + @Override public boolean contains(Object object) { + return forwardList.contains(object); + } + + @Override public int indexOf(Object object) { + int index = forwardList.lastIndexOf(object); + return (index >= 0) ? reverseIndex(index) : -1; + } + + @Override public int lastIndexOf(Object object) { + int index = forwardList.indexOf(object); + return (index >= 0) ? reverseIndex(index) : -1; + } + + @Override public ImmutableList subList(int fromIndex, int toIndex) { + checkPositionIndexes(fromIndex, toIndex, size()); + return forwardList.subList( + reversePosition(toIndex), reversePosition(fromIndex)).reverse(); + } + + @Override public E get(int index) { + checkElementIndex(index, size()); + return forwardList.get(reverseIndex(index)); + } + + @Override public int size() { + return forwardList.size(); + } + + @Override boolean isPartialView() { + return forwardList.isPartialView(); + } + } + + /** + * An implementation of {@link List#equals(Object)}. + */ + static boolean equalsImpl(List list, Object object) { + if (object == checkNotNull(list)) { + return true; + } + if (!(object instanceof List)) { + return false; + } + + List o = (List) object; + + return list.size() == o.size() + && Iterators.elementsEqual(list.iterator(), o.iterator()); + } + + /** + * An implementation of {@link List#indexOf(Object)}. + */ + static int indexOfImpl(List list, Object element) { + ListIterator listIterator = list.listIterator(); + while (listIterator.hasNext()) { + if (Preconditions.equal(element, listIterator.next())) { + return listIterator.previousIndex(); + } + } + return -1; + } + + /** + * An implementation of {@link List#lastIndexOf(Object)}. + */ + static int lastIndexOfImpl(List list, Object element) { + ListIterator listIterator = list.listIterator(list.size()); + while (listIterator.hasPrevious()) { + if (Preconditions.equal(element, listIterator.previous())) { + return listIterator.nextIndex(); + } + } + return -1; + } + + @Override public boolean equals(Object obj) { + return equalsImpl(this, obj); + } + + @Override public int hashCode() { + int hashCode = 1; + int n = size(); + for (int i = 0; i < n; i++) { + hashCode = 31 * hashCode + get(i).hashCode(); + + hashCode = ~~hashCode; + // needed to deal with GWT integer overflow + } + return hashCode; + } + + /* + * Serializes ImmutableLists as their logical contents. This ensures that + * implementation types do not leak into the serialized representation. + */ + static class SerializedForm implements Serializable { + final Object[] elements; + SerializedForm(Object[] elements) { + this.elements = elements; + } + Object readResolve() { + return copyOf(elements); + } + private static final long serialVersionUID = 0; + } + + private void readObject(ObjectInputStream stream) + throws InvalidObjectException { + throw new InvalidObjectException("Use SerializedForm"); + } + + @Override Object writeReplace() { + return new SerializedForm(toArray()); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder + * created by the {@link Builder} constructor. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for creating immutable list instances, especially {@code public + * static final} lists ("constant lists"). Example:

   {@code
+   *
+   *   public static final ImmutableList GOOGLE_COLORS
+   *       = new ImmutableList.Builder()
+   *           .addAll(WEBSAFE_COLORS)
+   *           .add(new Color(0, 191, 255))
+   *           .build();}
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple lists in series. Each new list contains all the + * elements of the ones created before it. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static final class Builder extends ImmutableCollection.ArrayBasedBuilder { + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableList#builder}. + */ + public Builder() { + this(DEFAULT_INITIAL_CAPACITY); + } + + // TODO(user): consider exposing this + Builder(int capacity) { + super(capacity); + } + + /** + * Adds {@code element} to the {@code ImmutableList}. + * + * @param element the element to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + @Override public Builder add(E element) { + super.add(element); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableList}. + * + * @param elements the {@code Iterable} to add to the {@code ImmutableList} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + @Override public Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableList}. + * + * @param elements the {@code Iterable} to add to the {@code ImmutableList} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + @Override public Builder add(E... elements) { + super.add(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableList}. + * + * @param elements the {@code Iterable} to add to the {@code ImmutableList} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + @Override public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + /** + * Returns a newly-created {@code ImmutableList} based on the contents of + * the {@code Builder}. + */ + @Override public ImmutableList build() { + return asImmutableList(contents, size); + } + } +} diff --git a/java/src/game/collect/ImmutableMap.java b/java/src/game/collect/ImmutableMap.java new file mode 100644 index 0000000..4afd22a --- /dev/null +++ b/java/src/game/collect/ImmutableMap.java @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkEntryNotNull; + +import java.io.Serializable; +import java.util.EnumMap; +import java.util.Map; + +import game.collect.ImmutableMapEntry.TerminalEntry; + +/** + * An immutable, hash-based {@link Map} with reliable user-specified iteration + * order. Does not permit null keys or values. + * + *

Unlike {@link Collections#unmodifiableMap}, which is a view of a + * separate map which can still change, an instance of {@code ImmutableMap} + * contains its own data and will never change. {@code ImmutableMap} is + * convenient for {@code public static final} maps ("constant maps") and also + * lets you easily make a "defensive copy" of a map provided to your class by a + * caller. + * + *

Performance notes: unlike {@link HashMap}, {@code ImmutableMap} is + * not optimized for element types that have slow {@link Object#equals} or + * {@link Object#hashCode} implementations. You can get better performance by + * having your element type cache its own hash codes, and by making use of the + * cached values to short-circuit a slow {@code equals} algorithm. + * + *

See the Guava User Guide article on + * immutable collections. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +// // we're overriding default serialization +public abstract class ImmutableMap implements Map, Serializable { + + /** + * Returns the empty map. This map behaves and performs comparably to + * {@link Collections#emptyMap}, and is preferable mainly for consistency + * and maintainability of your code. + */ + public static ImmutableMap of() { + return (ImmutableMap)EmptyImmutableMap.INSTANCE; + } + + /** + * Returns an immutable map containing a single entry. This map behaves and + * performs comparably to {@link Collections#singletonMap} but will not accept + * a null key or value. It is preferable mainly for consistency and + * maintainability of your code. + */ +// public static ImmutableMap of(K k1, V v1) { +// return ImmutableBiMap.of(k1, v1); +// } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + public static ImmutableMap of(K k1, V v1, K k2, V v2) { + return new RegularImmutableMap(entryOf(k1, v1), entryOf(k2, v2)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + public static ImmutableMap of( + K k1, V v1, K k2, V v2, K k3, V v3) { + return new RegularImmutableMap( + entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + public static ImmutableMap of( + K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return new RegularImmutableMap( + entryOf(k1, v1), entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4)); + } + + /** + * Returns an immutable map containing the given entries, in order. + * + * @throws IllegalArgumentException if duplicate keys are provided + */ + 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(entryOf(k1, v1), + entryOf(k2, v2), entryOf(k3, v3), entryOf(k4, v4), entryOf(k5, v5)); + } + + // looking for of() with > 5 entries? Use the builder instead. + + /** + * Verifies that {@code key} and {@code value} are non-null, and returns a new + * immutable entry with those values. + * + *

A call to {@link Map.Entry#setValue} on the returned entry will always + * throw {@link UnsupportedOperationException}. + */ + static TerminalEntry entryOf(K key, V value) { + checkEntryNotNull(key, value); + return new TerminalEntry(key, value); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder + * created by the {@link Builder} constructor. + */ + public static Builder builder() { + return new Builder(); + } + + static void checkNoConflict(boolean safe, String conflictDescription, + Entry entry1, Entry entry2) { + if (!safe) { + throw new IllegalArgumentException( + "Multiple entries with same " + conflictDescription + ": " + entry1 + " and " + entry2); + } + } + + /** + * A builder for creating immutable map instances, especially {@code public + * static final} maps ("constant maps"). Example:

   {@code
+   *
+   *   static final ImmutableMap WORD_TO_INT =
+   *       new ImmutableMap.Builder()
+   *           .put("one", 1)
+   *           .put("two", 2)
+   *           .put("three", 3)
+   *           .build();}
+ * + *

For small immutable maps, the {@code ImmutableMap.of()} methods are + * even more convenient. + * + *

Builder instances can be reused - it is safe to call {@link #build} + * multiple times to build multiple maps in series. Each map is a superset of + * the maps created before it. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static class Builder { + TerminalEntry[] entries; + int size; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableMap#builder}. + */ + public Builder() { + this(ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY); + } + + + Builder(int initialCapacity) { + this.entries = new TerminalEntry[initialCapacity]; + this.size = 0; + } + + private void ensureCapacity(int minCapacity) { + if (minCapacity > entries.length) { + entries = ObjectArrays.arraysCopyOf( + entries, ImmutableCollection.Builder.expandedCapacity(entries.length, minCapacity)); + } + } + + /** + * Associates {@code key} with {@code value} in the built map. Duplicate + * keys are not allowed, and will cause {@link #build} to fail. + */ + public Builder put(K key, V value) { + ensureCapacity(size + 1); + TerminalEntry entry = entryOf(key, value); + // don't inline this: we want to fail atomically if key or value is null + entries[size++] = entry; + return this; + } + + /** + * Adds the given {@code entry} to the map, making it immutable if + * necessary. Duplicate keys are not allowed, and will cause {@link #build} + * to fail. + * + * @since 11.0 + */ + public Builder put(Entry entry) { + return put(entry.getKey(), entry.getValue()); + } + + /** + * Associates all of the given map's keys and values in the built map. + * Duplicate keys are not allowed, and will cause {@link #build} to fail. + * + * @throws NullPointerException if any key or value in {@code map} is null + */ + public Builder putAll(Map map) { + ensureCapacity(size + map.size()); + for (Entry entry : map.entrySet()) { + put(entry); + } + return this; + } + + /* + * TODO(kevinb): Should build() and the ImmutableBiMap & ImmutableSortedMap + * versions throw an IllegalStateException instead? + */ + + /** + * Returns a newly-created immutable map. + * + * @throws IllegalArgumentException if duplicate keys were added + */ + public ImmutableMap build() { + switch (size) { + case 0: + return of(); +// case 1: +// return of(entries[0].getKey(), entries[0].getValue()); + default: + return new RegularImmutableMap(size, entries); + } + } + } + + /** + * Returns an immutable map containing the same entries as {@code map}. If + * {@code map} somehow contains entries with duplicate keys (for example, if + * it is a {@code SortedMap} whose comparator is not consistent with + * equals), the results of this method are undefined. + * + *

Despite the method name, this method attempts to avoid actually copying + * the data when it is safe to do so. The exact circumstances under which a + * copy will or will not be performed are undocumented and subject to change. + * + * @throws NullPointerException if any key or value in {@code map} is null + */ + public static ImmutableMap copyOf( + Map map) { + if ((map instanceof ImmutableMap)) { // && !(map instanceof ImmutableSortedMap)) { + // TODO(user): Make ImmutableMap.copyOf(immutableBiMap) call copyOf() + // on the ImmutableMap delegate(), rather than the bimap itself + + // safe since map is not writable + ImmutableMap kvMap = (ImmutableMap) map; + if (!kvMap.isPartialView()) { + return kvMap; + } + } else if (map instanceof EnumMap) { + return copyOfEnumMapUnsafe(map); + } + Entry[] entries = map.entrySet().toArray(EMPTY_ENTRY_ARRAY); + switch (entries.length) { + case 0: + return of(); +// case 1: +// // all entries will be Entry's +// Entry onlyEntry = (Entry) entries[0]; +// return of(onlyEntry.getKey(), onlyEntry.getValue()); + default: + return new RegularImmutableMap(entries); + } + } + + // If the map is an EnumMap, it must have key type K for some >. + + private static ImmutableMap copyOfEnumMapUnsafe(Map map) { + return copyOfEnumMap((EnumMap) map); + } + + private static , V> ImmutableMap copyOfEnumMap( + Map original) { + EnumMap copy = new EnumMap(original); + for (Map.Entry entry : copy.entrySet()) { + checkEntryNotNull(entry.getKey(), entry.getValue()); + } + return ImmutableEnumMap.asImmutable(copy); + } + + private static final Entry[] EMPTY_ENTRY_ARRAY = new Entry[0]; + + ImmutableMap() {} + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V put(K k, V v) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final V remove(Object o) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void putAll(Map map) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the map unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + @Override + public boolean containsValue(Object value) { + return values().contains(value); + } + + // Overriding to mark it Nullable + @Override + public abstract V get(Object key); + + private transient ImmutableSet> entrySet; + + /** + * Returns an immutable set of the mappings in this map. The entries are in + * the same order as the parameters used to build this map. + */ + @Override + public ImmutableSet> entrySet() { + ImmutableSet> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + abstract ImmutableSet> createEntrySet(); + + private transient ImmutableSet keySet; + + /** + * Returns an immutable set of the keys in this map. These keys are in + * the same order as the parameters used to build this map. + */ + @Override + public ImmutableSet keySet() { + ImmutableSet result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + ImmutableSet createKeySet() { + return new ImmutableMapKeySet(this); + } + + private transient ImmutableCollection values; + + /** + * Returns an immutable collection of the values in this map. The values are + * in the same order as the parameters used to build this map. + */ + @Override + public ImmutableCollection values() { + ImmutableCollection result = values; + return (result == null) ? values = new ImmutableMapValues(this) : result; + } + + // cached so that this.multimapView().inverse() only computes inverse once +// private transient ImmutableSetMultimap multimapView; + + /** + * Returns a multimap view of the map. + * + * @since 14.0 + */ +// @Beta +// public ImmutableSetMultimap asMultimap() { +// ImmutableSetMultimap result = multimapView; +// return (result == null) ? (multimapView = createMultimapView()) : result; +// } +// +// private ImmutableSetMultimap createMultimapView() { +// ImmutableMap> map = viewMapValuesAsSingletonSets(); +// return new ImmutableSetMultimap(map, map.size(), null); +// } + +// private ImmutableMap> viewMapValuesAsSingletonSets() { +// return new MapViewOfValuesAsSingletonSets(this); +// } +// +// private static final class MapViewOfValuesAsSingletonSets +// extends ImmutableMap> { +// private final ImmutableMap delegate; +// +// MapViewOfValuesAsSingletonSets(ImmutableMap delegate) { +// this.delegate = checkNotNull(delegate); +// } +// +// @Override public int size() { +// return delegate.size(); +// } +// +// @Override public boolean containsKey(Object key) { +// return delegate.containsKey(key); +// } +// +// @Override public ImmutableSet get(Object key) { +// V outerValue = delegate.get(key); +// return (outerValue == null) ? null : ImmutableSet.of(outerValue); +// } +// +// @Override boolean isPartialView() { +// return false; +// } +// +// @Override ImmutableSet>> createEntrySet() { +// return new ImmutableMapEntrySet>() { +// @Override ImmutableMap> map() { +// return MapViewOfValuesAsSingletonSets.this; +// } +// +// @Override +// public UnmodifiableIterator>> iterator() { +// final Iterator> backingIterator = delegate.entrySet().iterator(); +// return new UnmodifiableIterator>>() { +// @Override public boolean hasNext() { +// return backingIterator.hasNext(); +// } +// +// @Override public Entry> next() { +// final Entry backingEntry = backingIterator.next(); +// return new AbstractMapEntry>() { +// @Override public K getKey() { +// return backingEntry.getKey(); +// } +// +// @Override public ImmutableSet getValue() { +// return ImmutableSet.of(backingEntry.getValue()); +// } +// }; +// } +// }; +// } +// }; +// } +// } + + @Override public boolean equals(Object object) { + return Maps.equalsImpl(this, object); + } + + abstract boolean isPartialView(); + + @Override public int hashCode() { + // not caching hash code since it could change if map values are mutable + // in a way that modifies their hash codes + return entrySet().hashCode(); + } + +// @Override public String toString() { +// return Maps.toStringImpl(this); +// } + + /** + * Serialized type for all ImmutableMap instances. It captures the logical + * contents and they are reconstructed using public factory methods. This + * ensures that the implementation types remain as implementation details. + */ +// static class SerializedForm implements Serializable { +// private final Object[] keys; +// private final Object[] values; +// SerializedForm(ImmutableMap map) { +// keys = new Object[map.size()]; +// values = new Object[map.size()]; +// int i = 0; +// for (Entry entry : map.entrySet()) { +// keys[i] = entry.getKey(); +// values[i] = entry.getValue(); +// i++; +// } +// } +// Object readResolve() { +// Builder builder = new Builder(); +// return createMap(builder); +// } +// Object createMap(Builder builder) { +// for (int i = 0; i < keys.length; i++) { +// builder.put(keys[i], values[i]); +// } +// return builder.build(); +// } +// private static final long serialVersionUID = 0; +// } +// +// Object writeReplace() { +// return new SerializedForm(this); +// } +} diff --git a/java/src/game/collect/ImmutableMapEntry.java b/java/src/game/collect/ImmutableMapEntry.java new file mode 100644 index 0000000..4d49e44 --- /dev/null +++ b/java/src/game/collect/ImmutableMapEntry.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkEntryNotNull; + +/** + * Implementation of {@code Map.Entry} for {@link ImmutableMap} that adds extra methods to traverse + * hash buckets for the key and the value. This allows reuse in {@link RegularImmutableMap} and + * {@link RegularImmutableBiMap}, which don't have to recopy the entries created by their + * {@code Builder} implementations. + * + * @author Louis Wasserman + */ + +abstract class ImmutableMapEntry extends ImmutableEntry { + ImmutableMapEntry(K key, V value) { + super(key, value); + checkEntryNotNull(key, value); + } + + ImmutableMapEntry(ImmutableMapEntry contents) { + super(contents.getKey(), contents.getValue()); + // null check would be redundant + } + + + abstract ImmutableMapEntry getNextInKeyBucket(); + + + abstract ImmutableMapEntry getNextInValueBucket(); + + static final class TerminalEntry extends ImmutableMapEntry { + TerminalEntry(ImmutableMapEntry contents) { + super(contents); + } + + TerminalEntry(K key, V value) { + super(key, value); + } + + @Override + + ImmutableMapEntry getNextInKeyBucket() { + return null; + } + + @Override + + ImmutableMapEntry getNextInValueBucket() { + return null; + } + } +} diff --git a/java/src/game/collect/ImmutableMapEntrySet.java b/java/src/game/collect/ImmutableMapEntrySet.java new file mode 100644 index 0000000..2c5af4b --- /dev/null +++ b/java/src/game/collect/ImmutableMapEntrySet.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.io.Serializable; +import java.util.Map.Entry; + +/** + * {@code entrySet()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ + +abstract class ImmutableMapEntrySet extends ImmutableSet> { + ImmutableMapEntrySet() {} + + abstract ImmutableMap map(); + + @Override + public int size() { + return map().size(); + } + + @Override + public boolean contains(Object object) { + if (object instanceof Entry) { + Entry entry = (Entry) object; + V value = map().get(entry.getKey()); + return value != null && value.equals(entry.getValue()); + } + return false; + } + + @Override + boolean isPartialView() { + return map().isPartialView(); + } + + + @Override + Object writeReplace() { + return new EntrySetSerializedForm(map()); + } + + + private static class EntrySetSerializedForm implements Serializable { + final ImmutableMap map; + EntrySetSerializedForm(ImmutableMap map) { + this.map = map; + } + Object readResolve() { + return map.entrySet(); + } + private static final long serialVersionUID = 0; + } +} diff --git a/java/src/game/collect/ImmutableMapKeySet.java b/java/src/game/collect/ImmutableMapKeySet.java new file mode 100644 index 0000000..9e8b1bd --- /dev/null +++ b/java/src/game/collect/ImmutableMapKeySet.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.io.Serializable; +import java.util.Map.Entry; + +/** + * {@code keySet()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ + +final class ImmutableMapKeySet extends ImmutableSet { + private final ImmutableMap map; + + ImmutableMapKeySet(ImmutableMap map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public UnmodifiableIterator iterator() { + return asList().iterator(); + } + + @Override + public boolean contains(Object object) { + return map.containsKey(object); + } + + @Override + ImmutableList createAsList() { + final ImmutableList> entryList = map.entrySet().asList(); + return new ImmutableAsList() { + + @Override + public K get(int index) { + return entryList.get(index).getKey(); + } + + @Override + ImmutableCollection delegateCollection() { + return ImmutableMapKeySet.this; + } + + }; + } + + @Override + boolean isPartialView() { + return true; + } + + + @Override Object writeReplace() { + return new KeySetSerializedForm(map); + } + + + private static class KeySetSerializedForm implements Serializable { + final ImmutableMap map; + KeySetSerializedForm(ImmutableMap map) { + this.map = map; + } + Object readResolve() { + return map.keySet(); + } + private static final long serialVersionUID = 0; + } +} diff --git a/java/src/game/collect/ImmutableMapValues.java b/java/src/game/collect/ImmutableMapValues.java new file mode 100644 index 0000000..63b4fa3 --- /dev/null +++ b/java/src/game/collect/ImmutableMapValues.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.io.Serializable; +import java.util.Map.Entry; + +/** + * {@code values()} implementation for {@link ImmutableMap}. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + */ + +final class ImmutableMapValues extends ImmutableCollection { + private final ImmutableMap map; + + ImmutableMapValues(ImmutableMap map) { + this.map = map; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public UnmodifiableIterator iterator() { + return Maps.valueIterator(map.entrySet().iterator()); + } + + @Override + public boolean contains(Object object) { + return object != null && Iterators.contains(iterator(), object); + } + + @Override + boolean isPartialView() { + return true; + } + + @Override + ImmutableList createAsList() { + final ImmutableList> entryList = map.entrySet().asList(); + return new ImmutableAsList() { + @Override + public V get(int index) { + return entryList.get(index).getValue(); + } + + @Override + ImmutableCollection delegateCollection() { + return ImmutableMapValues.this; + } + }; + } + + + @Override Object writeReplace() { + return new SerializedForm(map); + } + + + private static class SerializedForm implements Serializable { + final ImmutableMap map; + SerializedForm(ImmutableMap map) { + this.map = map; + } + Object readResolve() { + return map.values(); + } + private static final long serialVersionUID = 0; + } +} diff --git a/java/src/game/collect/ImmutableSet.java b/java/src/game/collect/ImmutableSet.java new file mode 100644 index 0000000..4903f1a --- /dev/null +++ b/java/src/game/collect/ImmutableSet.java @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.ObjectArrays.checkElementNotNull; +import static game.collect.Preconditions.checkArgument; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Set; + +/** + * A high-performance, immutable {@code Set} with reliable, user-specified + * iteration order. Does not permit null elements. + * + *

Unlike {@link Collections#unmodifiableSet}, which is a view of a + * separate collection that can still change, an instance of this class contains + * its own private data and will never change. This class is convenient + * for {@code public static final} sets ("constant sets") and also lets you + * easily make a "defensive copy" of a set provided to your class by a caller. + * + *

Warning: Like most sets, an {@code ImmutableSet} will not function + * correctly if an element is modified after being placed in the set. For this + * reason, and to avoid general confusion, it is strongly recommended to place + * only immutable objects into this collection. + * + *

This class has been observed to perform significantly better than {@link + * HashSet} for objects with very fast {@link Object#hashCode} implementations + * (as a well-behaved immutable object should). While this class's factory + * methods create hash-based instances, the {@link ImmutableSortedSet} subclass + * performs binary searches instead. + * + *

Note: Although this class is not final, it cannot be subclassed + * outside its package as it has no public or protected constructors. Thus, + * instances of this type are guaranteed to be immutable. + * + *

See the Guava User Guide article on + * immutable collections. + * + * @see ImmutableList + * @see ImmutableMap + * @author Kevin Bourrillion + * @author Nick Kralevich + * @since 2.0 (imported from Google Collections Library) + */ + + // we're overriding default serialization +public abstract class ImmutableSet extends ImmutableCollection + implements Set { + /** + * Returns the empty immutable set. This set behaves and performs comparably + * to {@link Collections#emptySet}, and is preferable mainly for consistency + * and maintainability of your code. + */ + // Casting to any type is safe because the set will never hold any elements. + + public static ImmutableSet of() { + return (ImmutableSet) EmptyImmutableSet.INSTANCE; + } + + /** + * Returns an immutable set containing a single element. This set behaves and + * performs comparably to {@link Collections#singleton}, but will not accept + * a null element. It is preferable mainly for consistency and + * maintainability of your code. + */ +// public static ImmutableSet of(E element) { +// return new SingletonImmutableSet(element); +// } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableSet of(E e1, E e2) { + return construct(2, e1, e2); + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableSet of(E e1, E e2, E e3) { + return construct(3, e1, e2, e3); + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableSet of(E e1, E e2, E e3, E e4) { + return construct(4, e1, e2, e3, e4); + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. + * + * @throws NullPointerException if any element is null + */ + public static ImmutableSet of(E e1, E e2, E e3, E e4, E e5) { + return construct(5, e1, e2, e3, e4, e5); + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. + * + * @throws NullPointerException if any element is null + * @since 3.0 (source-compatible since 2.0) + */ + public static ImmutableSet of(E e1, E e2, E e3, E e4, E e5, E e6, + E... others) { + final int paramCount = 6; + Object[] elements = new Object[paramCount + 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, paramCount, others.length); + return construct(elements.length, elements); + } + + /** + * Constructs an {@code ImmutableSet} from the first {@code n} elements of the specified array. + * If {@code k} is the size of the returned {@code ImmutableSet}, then the unique elements of + * {@code elements} will be in the first {@code k} positions, and {@code elements[i] == null} for + * {@code k <= i < n}. + * + *

This may modify {@code elements}. Additionally, if {@code n == elements.length} and + * {@code elements} contains no duplicates, {@code elements} may be used without copying in the + * returned {@code ImmutableSet}, in which case it may no longer be modified. + * + *

{@code elements} may contain only values of type {@code E}. + * + * @throws NullPointerException if any of the first {@code n} elements of {@code elements} is + * null + */ + private static ImmutableSet construct(int n, Object... elements) { + switch (n) { + case 0: + return of(); +// case 1: +// // safe; elements contains only E's +// E elem = (E) elements[0]; +// return of(elem); + default: + // continue below to handle the general case + } + int tableSize = chooseTableSize(n); + Object[] table = new Object[tableSize]; + int mask = tableSize - 1; + int hashCode = 0; + int uniques = 0; + for (int i = 0; i < n; i++) { + Object element = checkElementNotNull(elements[i], i); + int hash = element.hashCode(); + for (int j = Hashing.smear(hash); ; j++) { + int index = j & mask; + Object value = table[index]; + if (value == null) { + // Came to an empty slot. Put the element here. + elements[uniques++] = element; + table[index] = element; + hashCode += hash; + break; + } else if (value.equals(element)) { + break; + } + } + } + Arrays.fill(elements, uniques, n, null); +// if (uniques == 1) { +// // There is only one element or elements are all duplicates +// // we are careful to only pass in E +// E element = (E) elements[0]; +// return new SingletonImmutableSet(element, hashCode); +// } else + if (tableSize != chooseTableSize(uniques)) { + // Resize the table when the array includes too many duplicates. + // when this happens, we have already made a copy + return construct(uniques, elements); + } else { + Object[] uniqueElements = (uniques < elements.length) + ? ObjectArrays.arraysCopyOf(elements, uniques) + : elements; + return new RegularImmutableSet(uniqueElements, hashCode, table, mask); + } + } + + // We use power-of-2 tables, and this is the highest int that's a power of 2 + static final int MAX_TABLE_SIZE = 1 << (Integer.SIZE - 2); + + // Represents how tightly we can pack things, as a maximum. + private static final double DESIRED_LOAD_FACTOR = 0.7; + + // If the set has this many elements, it will "max out" the table size + private static final int CUTOFF = + (int) (MAX_TABLE_SIZE * DESIRED_LOAD_FACTOR); + + /** + * Returns an array size suitable for the backing array of a hash table that + * uses open addressing with linear probing in its implementation. The + * returned size is the smallest power of two that can hold setSize elements + * with the desired load factor. + * + *

Do not call this method with setSize < 2. + */ +// @VisibleForTesting + static int chooseTableSize(int setSize) { + setSize = setSize < 2 ? 2 : setSize; + // Correct the size for open addressing to match desired load factor. + if (setSize < CUTOFF) { + // Round up to the next highest power of 2. + int tableSize = Integer.highestOneBit(setSize - 1) << 1; + while (tableSize * DESIRED_LOAD_FACTOR < setSize) { + tableSize <<= 1; + } + return tableSize; + } + + // The table can't be completely full or we'll get infinite reprobes + checkArgument(setSize < MAX_TABLE_SIZE, "collection too large"); + return MAX_TABLE_SIZE; + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 3.0 + */ + public static ImmutableSet copyOf(E[] elements) { + switch (elements.length) { + case 0: + return of(); +// case 1: +// return of(elements[0]); + default: + return construct(elements.length, elements.clone()); + } + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. This method iterates over {@code elements} at most once. + * + *

Note that if {@code s} is a {@code Set}, then {@code + * ImmutableSet.copyOf(s)} returns an {@code ImmutableSet} containing + * each of the strings in {@code s}, while {@code ImmutableSet.of(s)} returns + * a {@code ImmutableSet>} containing one element (the given set + * itself). + * + *

Despite the method name, this method attempts to avoid actually copying + * the data when it is safe to do so. The exact circumstances under which a + * copy will or will not be performed are undocumented and subject to change. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSet copyOf(Iterable elements) { + return (elements instanceof Collection) + ? copyOf(Filter.cast(elements)) + : copyOf(elements.iterator()); + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. + * + * @throws NullPointerException if any of {@code elements} is null + */ + public static ImmutableSet copyOf(Iterator elements) { + // We special-case for 0 or 1 elements, but anything further is madness. + if (!elements.hasNext()) { + return of(); + } +// E first = elements.next(); +// if (!elements.hasNext()) { +// return of(first); +// } else { + return new ImmutableSet.Builder() +// .add(first) + .addAll(elements) + .build(); +// } + } + + /** + * Returns an immutable set containing the given elements, in order. Repeated + * occurrences of an element (according to {@link Object#equals}) after the + * first are ignored. This method iterates over {@code elements} at most + * once. + * + *

Note that if {@code s} is a {@code Set}, then {@code + * ImmutableSet.copyOf(s)} returns an {@code ImmutableSet} containing + * each of the strings in {@code s}, while {@code ImmutableSet.of(s)} returns + * a {@code ImmutableSet>} containing one element (the given set + * itself). + * + *

Note: Despite what the method name suggests, {@code copyOf} will + * return constant-space views, rather than linear-space copies, of some + * inputs known to be immutable. For some other immutable inputs, such as key + * sets of an {@code ImmutableMap}, it still performs a copy in order to avoid + * holding references to the values of the map. The heuristics used in this + * decision are undocumented and subject to change except that: + *

    + *
  • A full copy will be done of any {@code ImmutableSortedSet}.
  • + *
  • {@code ImmutableSet.copyOf()} is idempotent with respect to pointer + * equality.
  • + *
+ * + *

This method is safe to use even when {@code elements} is a synchronized + * or concurrent collection that is currently being modified by another + * thread. + * + * @throws NullPointerException if any of {@code elements} is null + * @since 7.0 (source-compatible since 2.0) + */ + public static ImmutableSet copyOf(Collection elements) { + /* + * TODO(user): consider checking for ImmutableAsList here + * TODO(user): consider checking for Multiset here + */ + if (elements instanceof ImmutableSet) { +// && !(elements instanceof ImmutableSortedSet)) { + // all supported methods are covariant + 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() {} + + /** Returns {@code true} if the {@code hashCode()} method runs quickly. */ + boolean isHashCodeFast() { + return false; + } + + @Override public boolean equals(Object object) { + if (object == this) { + return true; + } else if (object instanceof ImmutableSet + && isHashCodeFast() + && ((ImmutableSet) object).isHashCodeFast() + && hashCode() != object.hashCode()) { + return false; + } + return Sets.equalsImpl(this, object); + } + + @Override public int hashCode() { + return Sets.hashCodeImpl(this); + } + + // This declaration is needed to make Set.iterator() and + // ImmutableCollection.iterator() consistent. + @Override public abstract UnmodifiableIterator iterator(); + + /* + * This class is used to serialize all ImmutableSet instances, except for + * ImmutableEnumSet/ImmutableSortedSet, regardless of implementation type. It + * captures their "logical contents" and they are reconstructed using public + * static factories. This is necessary to ensure that the existence of a + * particular implementation type is an implementation detail. + */ + private static class SerializedForm implements Serializable { + final Object[] elements; + SerializedForm(Object[] elements) { + this.elements = elements; + } + Object readResolve() { + return copyOf(elements); + } + private static final long serialVersionUID = 0; + } + + @Override Object writeReplace() { + return new SerializedForm(toArray()); + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder + * created by the {@link Builder} constructor. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for creating immutable set instances, especially {@code public + * static final} sets ("constant sets"). Example:

   {@code
+   *
+   *   public static final ImmutableSet GOOGLE_COLORS =
+   *       new ImmutableSet.Builder()
+   *           .addAll(WEBSAFE_COLORS)
+   *           .add(new Color(0, 191, 255))
+   *           .build();}
+ * + *

Builder instances can be reused; it is safe to call {@link #build} multiple + * times to build multiple sets in series. Each set is a superset of the set + * created before it. + * + * @since 2.0 (imported from Google Collections Library) + */ + public static class Builder extends ImmutableCollection.ArrayBasedBuilder { + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableSet#builder}. + */ + public Builder() { + this(DEFAULT_INITIAL_CAPACITY); + } + + Builder(int capacity) { + super(capacity); + } + + /** + * Adds {@code element} to the {@code ImmutableSet}. If the {@code + * ImmutableSet} already contains {@code element}, then {@code add} has no + * effect (only the previously added element is retained). + * + * @param element the element to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code element} is null + */ + @Override public Builder add(E element) { + super.add(element); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSet}, + * ignoring duplicate elements (only the first duplicate element is added). + * + * @param elements the elements to add + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + @Override public Builder add(E... elements) { + super.add(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSet}, + * ignoring duplicate elements (only the first duplicate element is added). + * + * @param elements the {@code Iterable} to add to the {@code ImmutableSet} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + @Override public Builder addAll(Iterable elements) { + super.addAll(elements); + return this; + } + + /** + * Adds each element of {@code elements} to the {@code ImmutableSet}, + * ignoring duplicate elements (only the first duplicate element is added). + * + * @param elements the elements to add to the {@code ImmutableSet} + * @return this {@code Builder} object + * @throws NullPointerException if {@code elements} is null or contains a + * null element + */ + @Override public Builder addAll(Iterator elements) { + super.addAll(elements); + return this; + } + + /** + * Returns a newly-created {@code ImmutableSet} based on the contents of + * the {@code Builder}. + */ + @Override public ImmutableSet build() { + ImmutableSet result = construct(size, contents); + // construct has the side effect of deduping contents, so we update size + // accordingly. + size = result.size(); + return result; + } + } +} diff --git a/java/src/game/collect/ImmutableTable.java b/java/src/game/collect/ImmutableTable.java new file mode 100644 index 0000000..9527f39 --- /dev/null +++ b/java/src/game/collect/ImmutableTable.java @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * An immutable {@link Table} with reliable user-specified iteration order. + * Does not permit null keys or values. + * + *

Note: Although this class is not final, it cannot be subclassed as + * it has no public or protected constructors. Thus, instances of this class are + * guaranteed to be immutable. + * + *

See the Guava User Guide article on + * immutable collections. + * + * @author Gregory Kick + * @since 11.0 + */ + +// TODO(gak): make serializable +public abstract class ImmutableTable extends AbstractTable { + private static final ImmutableTable EMPTY + = new SparseImmutableTable( + ImmutableList.>of(), + ImmutableSet.of(), ImmutableSet.of()); + + /** Returns an empty immutable table. */ + + public static ImmutableTable of() { + return (ImmutableTable) EMPTY; + } + +// /** Returns an immutable table containing a single cell. */ +// public static ImmutableTable of(R rowKey, +// C columnKey, V value) { +// return new SingletonImmutableTable(rowKey, columnKey, value); +// } + + /** + * Returns an immutable copy of the provided table. + * + *

The {@link Table#cellSet()} iteration order of the provided table + * determines the iteration ordering of all views in the returned table. Note + * that some views of the original table and the copied table may have + * different iteration orders. For more control over the ordering, create a + * {@link Builder} and call {@link Builder#orderRowsBy}, + * {@link Builder#orderColumnsBy}, and {@link Builder#putAll} + * + *

Despite the method name, this method attempts to avoid actually copying + * the data when it is safe to do so. The exact circumstances under which a + * copy will or will not be performed are undocumented and subject to change. + */ + 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: +// Cell onlyCell +// = Iterables.getOnlyElement(table.cellSet()); +// return ImmutableTable.of(onlyCell.getRowKey(), +// onlyCell.getColumnKey(), onlyCell.getValue()); + default: + ImmutableSet.Builder> cellSetBuilder + = ImmutableSet.builder(); + for (Cell cell : + table.cellSet()) { + /* + * Must cast to be able to create a Cell rather than a + * Cell + */ + cellSetBuilder.add(cellOf((R) cell.getRowKey(), + (C) cell.getColumnKey(), (V) cell.getValue())); + } + return RegularImmutableTable.forCells(cellSetBuilder.build()); + } + } + } + + /** + * Returns a new builder. The generated builder is equivalent to the builder + * created by the {@link Builder#ImmutableTable.Builder()} constructor. + */ +// public static Builder builder() { +// return new Builder(); +// } + + public static Cell immutableCell( + R rowKey, C columnKey, V value) { + return new ImmutableCell(rowKey, columnKey, value); + } + + static final class ImmutableCell + extends AbstractCell implements Serializable { + private final R rowKey; + private final C columnKey; + private final V value; + + ImmutableCell( + R rowKey, C columnKey, V value) { + this.rowKey = rowKey; + this.columnKey = columnKey; + this.value = value; + } + + @Override + public R getRowKey() { + return rowKey; + } + @Override + public C getColumnKey() { + return columnKey; + } + @Override + public V getValue() { + return value; + } + + private static final long serialVersionUID = 0; + } + + abstract static class AbstractCell implements Cell { + // needed for serialization + AbstractCell() {} + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Cell) { + Cell other = (Cell) obj; + return Preconditions.equal(getRowKey(), other.getRowKey()) + && Preconditions.equal(getColumnKey(), other.getColumnKey()) + && Preconditions.equal(getValue(), other.getValue()); + } + return false; + } + + @Override public int hashCode() { + return Arrays.hashCode(new Object[] {getRowKey(), getColumnKey(), getValue()}); + } + + @Override public String toString() { + return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); + } + } + + /** + * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are + * non-null, and returns a new entry with those values. + */ + static Cell cellOf(R rowKey, C columnKey, V value) { + return immutableCell(checkNotNull(rowKey), checkNotNull(columnKey), + checkNotNull(value)); + } + + /** + * A builder for creating immutable table instances, especially {@code public + * static final} tables ("constant tables"). Example:

   {@code
+   *
+   *   static final ImmutableTable SPREADSHEET =
+   *       new ImmutableTable.Builder()
+   *           .put(1, 'A', "foo")
+   *           .put(1, 'B', "bar")
+   *           .put(2, 'A', "baz")
+   *           .build();}
+ * + *

By default, the order in which cells are added to the builder determines + * the iteration ordering of all views in the returned table, with {@link + * #putAll} following the {@link Table#cellSet()} iteration order. However, if + * {@link #orderRowsBy} or {@link #orderColumnsBy} is called, the views are + * sorted by the supplied comparators. + * + * For empty or single-cell immutable tables, {@link #of()} and + * {@link #of(Object, Object, Object)} are even more convenient. + * + *

Builder instances can be reused - it is safe to call {@link #build} + * multiple times to build multiple tables in series. Each table is a superset + * of the tables created before it. + * + * @since 11.0 + */ + public static final class Builder { + private final List> cells = Lists.newArrayList(); + private Comparator rowComparator; + private Comparator columnComparator; + + /** + * Creates a new builder. The returned builder is equivalent to the builder + * generated by {@link ImmutableTable#builder}. + */ + public Builder() {} + + /** + * Specifies the ordering of the generated table's rows. + */ + public Builder orderRowsBy(Comparator rowComparator) { + this.rowComparator = checkNotNull(rowComparator); + return this; + } + + /** + * Specifies the ordering of the generated table's columns. + */ + public Builder orderColumnsBy( + Comparator columnComparator) { + this.columnComparator = checkNotNull(columnComparator); + return this; + } + + /** + * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code + * value} in the built table. Duplicate key pairs are not allowed and will + * cause {@link #build} to fail. + */ + public Builder put(R rowKey, C columnKey, V value) { + cells.add(cellOf(rowKey, columnKey, value)); + return this; + } + + /** + * Adds the given {@code cell} to the table, making it immutable if + * necessary. Duplicate key pairs are not allowed and will cause {@link + * #build} to fail. + */ + public Builder put( + Cell cell) { + if (cell instanceof ImmutableCell) { + checkNotNull(cell.getRowKey()); + checkNotNull(cell.getColumnKey()); + checkNotNull(cell.getValue()); + // all supported methods are covariant + Cell immutableCell = (Cell) cell; + cells.add(immutableCell); + } else { + put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); + } + return this; + } + + /** + * Associates all of the given table's keys and values in the built table. + * Duplicate row key column key pairs are not allowed, and will cause + * {@link #build} to fail. + * + * @throws NullPointerException if any key or value in {@code table} is null + */ + public Builder putAll( + Table table) { + for (Cell cell : table.cellSet()) { + put(cell); + } + return this; + } + + /** + * Returns a newly-created immutable table. + * + * @throws IllegalArgumentException if duplicate key pairs were added + */ + public ImmutableTable build() { + int size = cells.size(); + switch (size) { + case 0: + return of(); +// case 1: +// return new SingletonImmutableTable( +// Iterables.getOnlyElement(cells)); + default: + return RegularImmutableTable.forCells( + cells, rowComparator, columnComparator); + } + } + } + + ImmutableTable() {} + + @Override public ImmutableSet> cellSet() { + return (ImmutableSet>) super.cellSet(); + } + + @Override + abstract ImmutableSet> createCellSet(); + + @Override + final UnmodifiableIterator> cellIterator() { + throw new AssertionError("should never be called"); + } + + @Override + public ImmutableCollection values() { + return (ImmutableCollection) super.values(); + } + + @Override + abstract ImmutableCollection createValues(); + + @Override + final Iterator valuesIterator() { + throw new AssertionError("should never be called"); + } + + private static T firstNonNull(T first, T second) { + return first != null ? first : checkNotNull(second); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if {@code columnKey} is {@code null} + */ + @Override public ImmutableMap column(C columnKey) { + checkNotNull(columnKey); + return firstNonNull( + (ImmutableMap) columnMap().get(columnKey), + ImmutableMap.of()); + } + + @Override public ImmutableSet columnKeySet() { + return columnMap().keySet(); + } + + /** + * {@inheritDoc} + * + *

The value {@code Map} instances in the returned map are + * {@link ImmutableMap} instances as well. + */ + @Override public abstract ImmutableMap> columnMap(); + + /** + * {@inheritDoc} + * + * @throws NullPointerException if {@code rowKey} is {@code null} + */ + @Override public ImmutableMap row(R rowKey) { + checkNotNull(rowKey); + return firstNonNull( + (ImmutableMap) rowMap().get(rowKey), + ImmutableMap.of()); + } + + @Override public ImmutableSet rowKeySet() { + return rowMap().keySet(); + } + + /** + * {@inheritDoc} + * + *

The value {@code Map} instances in the returned map are + * {@link ImmutableMap} instances as well. + */ + @Override public abstract ImmutableMap> rowMap(); + + @Override + public boolean contains(Object rowKey, Object columnKey) { + return get(rowKey, columnKey) != null; + } + + @Override + public boolean containsValue(Object value) { + return values().contains(value); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated @Override public final void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated @Override public final V put(R rowKey, C columnKey, V value) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated @Override public final void putAll( + Table table) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the table unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated @Override public final V remove(Object rowKey, Object columnKey) { + throw new UnsupportedOperationException(); + } +} diff --git a/java/src/game/collect/Iterables.java b/java/src/game/collect/Iterables.java new file mode 100644 index 0000000..b424a55 --- /dev/null +++ b/java/src/game/collect/Iterables.java @@ -0,0 +1,1029 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkNotNull; + +import java.util.Iterator; +import java.util.List; +import java.util.RandomAccess; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * This class contains static utility methods that operate on or return objects + * of type {@code Iterable}. Except as noted, each method has a corresponding + * {@link Iterator}-based method in the {@link Iterators} class. + * + *

Performance notes: Unless otherwise noted, all of the iterables + * produced in this class are lazy, which means that their iterators + * only advance the backing iteration when absolutely necessary. + * + *

See the Guava User Guide article on + * {@code Iterables}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Iterables { + private Iterables() {} + + /** Returns an unmodifiable view of {@code iterable}. */ +// public static Iterable unmodifiableIterable( +// final Iterable iterable) { +// checkNotNull(iterable); +// if (iterable instanceof UnmodifiableIterable || +// iterable instanceof ImmutableCollection) { +// return iterable; +// } +// return new UnmodifiableIterable(iterable); +// } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ +// @Deprecated public static Iterable unmodifiableIterable( +// ImmutableCollection iterable) { +// return checkNotNull(iterable); +// } + +// private static final class UnmodifiableIterable extends FluentIterable { +// private final Iterable iterable; +// +// private UnmodifiableIterable(Iterable iterable) { +// this.iterable = iterable; +// } +// +// @Override +// public Iterator iterator() { +// return Iterators.unmodifiableIterator(iterable.iterator()); +// } +// +// @Override +// public String toString() { +// return iterable.toString(); +// } +// // no equals and hashCode; it would break the contract! +// } + + /** + * Returns the number of elements in {@code iterable}. + */ +// public static int size(Iterable iterable) { +// return (iterable instanceof Collection) +// ? ((Collection) iterable).size() +// : Iterators.size(iterable.iterator()); +// } + + /** + * Returns {@code true} if {@code iterable} contains any object for which {@code equals(element)} + * is true. + */ +// public static boolean contains(Iterable iterable, Object element) { +// if (iterable instanceof Collection) { +// Collection collection = (Collection) iterable; +// return Filter.safeContains(collection, element); +// } +// return Iterators.contains(iterable.iterator(), element); +// } + + /** + * Removes, from an iterable, every element that belongs to the provided + * collection. + * + *

This method calls {@link Collection#removeAll} if {@code iterable} is a + * collection, and {@link Iterators#removeAll} otherwise. + * + * @param removeFrom the iterable to (potentially) remove elements from + * @param elementsToRemove the elements to remove + * @return {@code true} if any element was removed from {@code iterable} + */ +// public static boolean removeAll( +// Iterable removeFrom, Collection elementsToRemove) { +// return (removeFrom instanceof Collection) +// ? ((Collection) removeFrom).removeAll(checkNotNull(elementsToRemove)) +// : Iterators.removeAll(removeFrom.iterator(), elementsToRemove); +// } + + /** + * Removes, from an iterable, every element that does not belong to the + * provided collection. + * + *

This method calls {@link Collection#retainAll} if {@code iterable} is a + * collection, and {@link Iterators#retainAll} otherwise. + * + * @param removeFrom the iterable to (potentially) remove elements from + * @param elementsToRetain the elements to retain + * @return {@code true} if any element was removed from {@code iterable} + */ +// public static boolean retainAll( +// Iterable removeFrom, Collection elementsToRetain) { +// return (removeFrom instanceof Collection) +// ? ((Collection) removeFrom).retainAll(checkNotNull(elementsToRetain)) +// : Iterators.retainAll(removeFrom.iterator(), elementsToRetain); +// } + + /** + * Removes, from an iterable, every element that satisfies the provided + * predicate. + * + * @param removeFrom the iterable to (potentially) remove elements from + * @param predicate a predicate that determines whether an element should + * be removed + * @return {@code true} if any elements were removed from the iterable + * + * @throws UnsupportedOperationException if the iterable does not support + * {@code remove()}. + * @since 2.0 + */ + public static boolean removeIf( + Iterable removeFrom, Predicate predicate) { + if (removeFrom instanceof RandomAccess && removeFrom instanceof List) { + return removeIfFromRandomAccessList( + (List) removeFrom, checkNotNull(predicate)); + } + return Iterators.removeIf(removeFrom.iterator(), predicate); + } + + private static boolean removeIfFromRandomAccessList( + List list, Predicate predicate) { + // Note: Not all random access lists support set() so we need to deal with + // those that don't and attempt the slower remove() based solution. + int from = 0; + int to = 0; + + for (; from < list.size(); from++) { + T element = list.get(from); + if (!predicate.test(element)) { + if (from > to) { + try { + list.set(to, element); + } catch (UnsupportedOperationException e) { + slowRemoveIfForRemainingElements(list, predicate, to, from); + return true; + } + } + to++; + } + } + + // Clear the tail of any remaining items + list.subList(to, list.size()).clear(); + return from != to; + } + + private static void slowRemoveIfForRemainingElements(List list, + Predicate predicate, int to, int from) { + // Here we know that: + // * (to < from) and that both are valid indices. + // * Everything with (index < to) should be kept. + // * Everything with (to <= index < from) should be removed. + // * The element with (index == from) should be kept. + // * Everything with (index > from) has not been checked yet. + + // Check from the end of the list backwards (minimize expected cost of + // moving elements when remove() is called). Stop before 'from' because + // we already know that should be kept. + for (int n = list.size() - 1; n > from; n--) { + if (predicate.test(list.get(n))) { + list.remove(n); + } + } + // And now remove everything in the range [to, from) (going backwards). + for (int n = from - 1; n >= to; n--) { + list.remove(n); + } + } + + /** + * Removes and returns the first matching element, or returns {@code null} if there is none. + */ +// +// static T removeFirstMatching(Iterable removeFrom, Predicate predicate) { +// checkNotNull(predicate); +// Iterator iterator = removeFrom.iterator(); +// while (iterator.hasNext()) { +// T next = iterator.next(); +// if (predicate.apply(next)) { +// iterator.remove(); +// return next; +// } +// } +// return null; +// } + + /** + * Determines whether two iterables contain equal elements in the same order. + * More specifically, this method returns {@code true} if {@code iterable1} + * and {@code iterable2} contain the same number of elements and every element + * of {@code iterable1} is equal to the corresponding element of + * {@code iterable2}. + */ +// 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()); +// } + + /** + * Returns a string representation of {@code iterable}, with the format {@code + * [e1, e2, ..., en]} (that is, identical to {@link java.util.Arrays + * Arrays}{@code .toString(Iterables.toArray(iterable))}). Note that for + * most implementations of {@link Collection}, {@code + * collection.toString()} also gives the same result, but that behavior is not + * generally guaranteed. + */ +// public static String toString(Iterable iterable) { +// return Iterators.toString(iterable.iterator()); +// } + + /** + * Returns the single element contained in {@code iterable}. + * + * @throws NoSuchElementException if the iterable is empty + * @throws IllegalArgumentException if the iterable contains multiple + * elements + */ +// public static T getOnlyElement(Iterable iterable) { +// return Iterators.getOnlyElement(iterable.iterator()); +// } + + /** + * Returns the single element contained in {@code iterable}, or {@code + * defaultValue} if the iterable is empty. + * + * @throws IllegalArgumentException if the iterator contains multiple + * elements + */ +// +// public static T getOnlyElement( +// Iterable iterable, T defaultValue) { +// return Iterators.getOnlyElement(iterable.iterator(), defaultValue); +// } + + /** + * Copies an iterable's elements into an array. + * + * @param iterable the iterable to copy + * @param type the type of the elements + * @return a newly-allocated array into which all the elements of the iterable + * have been copied + */ +// +// public static T[] toArray(Iterable iterable, Class type) { +// Collection collection = toCollection(iterable); +// T[] array = ObjectArrays.newArray(type, collection.size()); +// return collection.toArray(array); +// } + + /** + * Copies an iterable's elements into an array. + * + * @param iterable the iterable to copy + * @return a newly-allocated array into which all the elements of the iterable + * have been copied + */ +// static Object[] toArray(Iterable iterable) { +// return toCollection(iterable).toArray(); +// } + + /** + * Converts an iterable into a collection. If the iterable is already a + * collection, it is returned. Otherwise, an {@link java.util.ArrayList} is + * created with the contents of the iterable in the same iteration order. + */ +// private static Collection toCollection(Iterable iterable) { +// return (iterable instanceof Collection) +// ? (Collection) iterable +// : Lists.newArrayList(iterable.iterator()); +// } + + /** + * Adds all elements in {@code iterable} to {@code collection}. + * + * @return {@code true} if {@code collection} was modified as a result of this + * operation. + */ +// public static boolean addAll( +// Collection addTo, Iterable elementsToAdd) { +// if (elementsToAdd instanceof Collection) { +// Collection c = Filter.cast(elementsToAdd); +// return addTo.addAll(c); +// } +// return Iterators.addAll(addTo, checkNotNull(elementsToAdd).iterator()); +// } + + /** + * Returns the number of elements in the specified iterable that equal the + * specified object. This implementation avoids a full iteration when the + * iterable is a {@link Multiset} or {@link Set}. + * + * @see Collections#frequency + */ +// public static int frequency(Iterable iterable, Object element) { +// if ((iterable instanceof Multiset)) { +// return ((Multiset) iterable).count(element); +// } else if ((iterable instanceof Set)) { +// return ((Set) iterable).contains(element) ? 1 : 0; +// } +// return Iterators.frequency(iterable.iterator(), element); +// } + + /** + * Returns an iterable whose iterators cycle indefinitely over the elements of + * {@code iterable}. + * + *

That iterator supports {@code remove()} if {@code iterable.iterator()} + * does. After {@code remove()} is called, subsequent cycles omit the removed + * element, which is no longer in {@code iterable}. The iterator's + * {@code hasNext()} method returns {@code true} until {@code iterable} is + * empty. + * + *

Warning: Typical uses of the resulting iterator may produce an + * infinite loop. You should use an explicit {@code break} or be certain that + * you will eventually remove all the elements. + * + *

To cycle over the iterable {@code n} times, use the following: + * {@code Iterables.concat(Collections.nCopies(n, iterable))} + */ +// public static Iterable cycle(final Iterable iterable) { +// checkNotNull(iterable); +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// return Iterators.cycle(iterable); +// } +// @Override public String toString() { +// return iterable.toString() + " (cycled)"; +// } +// }; +// } + + /** + * Returns an iterable whose iterators cycle indefinitely over the provided + * elements. + * + *

After {@code remove} is invoked on a generated iterator, the removed + * element will no longer appear in either that iterator or any other iterator + * created from the same source iterable. That is, this method behaves exactly + * as {@code Iterables.cycle(Lists.newArrayList(elements))}. The iterator's + * {@code hasNext} method returns {@code true} until all of the original + * elements have been removed. + * + *

Warning: Typical uses of the resulting iterator may produce an + * infinite loop. You should use an explicit {@code break} or be certain that + * you will eventually remove all the elements. + * + *

To cycle over the elements {@code n} times, use the following: + * {@code Iterables.concat(Collections.nCopies(n, Arrays.asList(elements)))} + */ +// public static Iterable cycle(T... elements) { +// return cycle(Lists.newArrayList(elements)); +// } + + /** + * Combines two iterables into a single iterable. The returned iterable has an + * iterator that traverses the elements in {@code a}, followed by the elements + * in {@code b}. The source iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the + * corresponding input iterator supports it. + */ +// public static Iterable concat( +// Iterable a, Iterable b) { +// return concat(ImmutableList.of(a, b)); +// } + + /** + * Combines three iterables into a single iterable. The returned iterable has + * an iterator that traverses the elements in {@code a}, followed by the + * elements in {@code b}, followed by the elements in {@code c}. The source + * iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the + * corresponding input iterator supports it. + */ +// public static Iterable concat(Iterable a, +// Iterable b, Iterable c) { +// return concat(ImmutableList.of(a, b, c)); +// } + + /** + * Combines four iterables into a single iterable. The returned iterable has + * an iterator that traverses the elements in {@code a}, followed by the + * elements in {@code b}, followed by the elements in {@code c}, followed by + * the elements in {@code d}. The source iterators are not polled until + * necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the + * corresponding input iterator supports it. + */ +// public static Iterable concat(Iterable a, +// Iterable b, Iterable c, +// Iterable d) { +// return concat(ImmutableList.of(a, b, c, d)); +// } + + /** + * Combines multiple iterables into a single iterable. The returned iterable + * has an iterator that traverses the elements of each iterable in + * {@code inputs}. The input iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the + * corresponding input iterator supports it. + * + * @throws NullPointerException if any of the provided iterables is null + */ +// public static Iterable concat(Iterable... inputs) { +// return concat(ImmutableList.copyOf(inputs)); +// } + + /** + * Combines multiple iterables into a single iterable. The returned iterable + * has an iterator that traverses the elements of each iterable in + * {@code inputs}. The input iterators are not polled until necessary. + * + *

The returned iterable's iterator supports {@code remove()} when the + * corresponding input iterator supports it. The methods of the returned + * iterable may throw {@code NullPointerException} if any of the input + * iterators is null. + */ +// public static Iterable concat( +// final Iterable> inputs) { +// checkNotNull(inputs); +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// return Iterators.concat(iterators(inputs)); +// } +// }; +// } + + /** + * Returns an iterator over the iterators of the given iterables. + */ +// private static Iterator> iterators( +// Iterable> iterables) { +// return new TransformedIterator, Iterator>( +// iterables.iterator()) { +// @Override +// Iterator transform(Iterable from) { +// return from.iterator(); +// } +// }; +// } + + /** + * Divides an iterable into unmodifiable sublists of the given size (the final + * iterable may be smaller). For example, partitioning an iterable containing + * {@code [a, b, c, d, e]} with a partition size of 3 yields {@code + * [[a, b, c], [d, e]]} -- an outer iterable containing two inner lists of + * three and two elements, all in the original order. + * + *

Iterators returned by the returned iterable do not support the {@link + * Iterator#remove()} method. The returned lists implement {@link + * RandomAccess}, whether or not the input list does. + * + *

Note: if {@code iterable} is a {@link List}, use {@link + * Lists#partition(List, int)} instead. + * + * @param iterable the iterable to return a partitioned view of + * @param size the desired size of each partition (the last may be smaller) + * @return an iterable of unmodifiable lists containing the elements of {@code + * iterable} divided into partitions + * @throws IllegalArgumentException if {@code size} is nonpositive + */ +// public static Iterable> partition( +// final Iterable iterable, final int size) { +// checkNotNull(iterable); +// checkArgument(size > 0); +// return new FluentIterable>() { +// @Override +// public Iterator> iterator() { +// return Iterators.partition(iterable.iterator(), size); +// } +// }; +// } + + /** + * Divides an iterable into unmodifiable sublists of the given size, padding + * the final iterable with null values if necessary. For example, partitioning + * an iterable containing {@code [a, b, c, d, e]} with a partition size of 3 + * yields {@code [[a, b, c], [d, e, null]]} -- an outer iterable containing + * two inner lists of three elements each, all in the original order. + * + *

Iterators returned by the returned iterable do not support the {@link + * Iterator#remove()} method. + * + * @param iterable the iterable to return a partitioned view of + * @param size the desired size of each partition + * @return an iterable of unmodifiable lists containing the elements of {@code + * iterable} divided into partitions (the final iterable may have + * trailing null elements) + * @throws IllegalArgumentException if {@code size} is nonpositive + */ +// public static Iterable> paddedPartition( +// final Iterable iterable, final int size) { +// checkNotNull(iterable); +// checkArgument(size > 0); +// return new FluentIterable>() { +// @Override +// public Iterator> iterator() { +// return Iterators.paddedPartition(iterable.iterator(), size); +// } +// }; +// } + + /** + * Returns the elements of {@code unfiltered} that satisfy a predicate. The + * resulting iterable's iterator does not support {@code remove()}. + */ +// public static Iterable filter( +// final Iterable unfiltered, final Predicate predicate) { +// checkNotNull(unfiltered); +// checkNotNull(predicate); +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// return Iterators.filter(unfiltered.iterator(), predicate); +// } +// }; +// } + + /** + * Returns all instances of class {@code type} in {@code unfiltered}. The + * returned iterable has elements whose class is {@code type} or a subclass of + * {@code type}. The returned iterable's iterator does not support + * {@code remove()}. + * + * @param unfiltered an iterable containing objects of any type + * @param type the type of elements desired + * @return an unmodifiable iterable containing all elements of the original + * iterable that were of the requested type + */ +// +// public static Iterable filter( +// final Iterable unfiltered, final Class type) { +// checkNotNull(unfiltered); +// checkNotNull(type); +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// return Iterators.filter(unfiltered.iterator(), type); +// } +// }; +// } + + /** + * Returns {@code true} if any element in {@code iterable} satisfies the predicate. + */ + public static boolean any( + Iterable iterable, Predicate predicate) { + return Iterators.any(iterable.iterator(), predicate); + } + + /** + * Returns {@code true} if every element in {@code iterable} satisfies the + * predicate. If {@code iterable} is empty, {@code true} is returned. + */ + public static boolean all( + Iterable iterable, Predicate predicate) { + return Iterators.all(iterable.iterator(), predicate); + } + + /** + * Returns the first element in {@code iterable} that satisfies the given + * predicate; use this method only when such an element is known to exist. If + * it is possible that no element will match, use {@link #tryFind} or + * {@link #find(Iterable, Predicate, Object)} instead. + * + * @throws NoSuchElementException if no element in {@code iterable} matches + * the given predicate + */ +// public static T find(Iterable iterable, +// Predicate predicate) { +// return Iterators.find(iterable.iterator(), predicate); +// } + + /** + * Returns the first element in {@code iterable} that satisfies the given + * predicate, or {@code defaultValue} if none found. Note that this can + * usually be handled more naturally using {@code + * tryFind(iterable, predicate).or(defaultValue)}. + * + * @since 7.0 + */ +// +// public static T find(Iterable iterable, +// Predicate predicate, T defaultValue) { +// return Iterators.find(iterable.iterator(), predicate, defaultValue); +// } + + /** + * Returns an {@link Optional} containing the first element in {@code + * iterable} that satisfies the given predicate, if such an element exists. + * + *

Warning: avoid using a {@code predicate} that matches {@code + * null}. If {@code null} is matched in {@code iterable}, a + * NullPointerException will be thrown. + * + * @since 11.0 + */ +// public static Optional tryFind(Iterable iterable, +// Predicate predicate) { +// return Iterators.tryFind(iterable.iterator(), predicate); +// } + + /** + * Returns the index in {@code iterable} of the first element that satisfies + * the provided {@code predicate}, or {@code -1} if the Iterable has no such + * elements. + * + *

More formally, returns the lowest index {@code i} such that + * {@code predicate.apply(Iterables.get(iterable, i))} returns {@code true}, + * or {@code -1} if there is no such index. + * + * @since 2.0 + */ +// public static int indexOf( +// Iterable iterable, Predicate predicate) { +// return Iterators.indexOf(iterable.iterator(), predicate); +// } + + /** + * Returns an iterable that applies {@code function} to each element of {@code + * fromIterable}. + * + *

The returned iterable's iterator supports {@code remove()} if the + * provided iterator does. After a successful {@code remove()} call, + * {@code fromIterable} no longer contains the corresponding element. + * + *

If the input {@code Iterable} is known to be a {@code List} or other + * {@code Collection}, consider {@link Lists#transform} and {@link + * Filter#transform}. + */ + public static Iterable transform(final Iterable fromIterable, + final Function function) { + checkNotNull(fromIterable); + checkNotNull(function); + return new Iterable() { + @Override + public Iterator iterator() { + return Iterators.transform(fromIterable.iterator(), function); + } + }; + } + + /** + * Returns the element at the specified position in an iterable. + * + * @param position position of the element to return + * @return the element at the specified position in {@code iterable} + * @throws IndexOutOfBoundsException if {@code position} is negative or + * greater than or equal to the size of {@code iterable} + */ +// public static T get(Iterable iterable, int position) { +// checkNotNull(iterable); +// return (iterable instanceof List) +// ? ((List) iterable).get(position) +// : Iterators.get(iterable.iterator(), position); +// } + + /** + * Returns the element at the specified position in an iterable or a default + * value otherwise. + * + * @param position position of the element to return + * @param defaultValue the default value to return if {@code position} is + * greater than or equal to the size of the iterable + * @return the element at the specified position in {@code iterable} or + * {@code defaultValue} if {@code iterable} contains fewer than + * {@code position + 1} elements. + * @throws IndexOutOfBoundsException if {@code position} is negative + * @since 4.0 + */ +// +// public static T get(Iterable iterable, int position, T defaultValue) { +// 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); +// } +// } + + /** + * Returns the first element in {@code iterable} or {@code defaultValue} if + * the iterable is empty. The {@link Iterators} analog to this method is + * {@link Iterators#getNext}. + * + *

If no default value is desired (and the caller instead wants a + * {@link NoSuchElementException} to be thrown), it is recommended that + * {@code iterable.iterator().next()} is used instead. + * + * @param defaultValue the default value to return if the iterable is empty + * @return the first element of {@code iterable} or the default value + * @since 7.0 + */ +// +// public static T getFirst(Iterable iterable, T defaultValue) { +// return Iterators.getNext(iterable.iterator(), defaultValue); +// } + + /** + * Returns the last element of {@code iterable}. + * + * @return the last element of {@code iterable} + * @throws NoSuchElementException if the iterable is empty + */ +// public static T getLast(Iterable iterable) { +// // TODO(kevinb): Support a concurrently modified collection? +// if (iterable instanceof List) { +// List list = (List) iterable; +// if (list.isEmpty()) { +// throw new NoSuchElementException(); +// } +// return getLastInNonemptyList(list); +// } +// +// return Iterators.getLast(iterable.iterator()); +// } + + /** + * Returns the last element of {@code iterable} or {@code defaultValue} if + * the iterable is empty. + * + * @param defaultValue the value to return if {@code iterable} is empty + * @return the last element of {@code iterable} or the default value + * @since 3.0 + */ +// +// public static T getLast(Iterable iterable, T defaultValue) { +// if (iterable instanceof Collection) { +// Collection c = Filter.cast(iterable); +// if (c.isEmpty()) { +// return defaultValue; +// } else 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); +// } + + /** + * Returns a view of {@code iterable} that skips its first + * {@code numberToSkip} elements. If {@code iterable} contains fewer than + * {@code numberToSkip} elements, the returned iterable skips all of its + * elements. + * + *

Modifications to the underlying {@link Iterable} before a call to + * {@code iterator()} are reflected in the returned iterator. That is, the + * iterator skips the first {@code numberToSkip} elements that exist when the + * {@code Iterator} is created, not when {@code skip()} is called. + * + *

The returned iterable's iterator supports {@code remove()} if the + * iterator of the underlying iterable supports it. Note that it is + * not possible to delete the last skipped element by immediately + * calling {@code remove()} on that iterator, as the {@code Iterator} + * contract states that a call to {@code remove()} before a call to + * {@code next()} will throw an {@link IllegalStateException}. + * + * @since 3.0 + */ +// public static Iterable skip(final Iterable iterable, +// final int numberToSkip) { +// checkNotNull(iterable); +// checkArgument(numberToSkip >= 0, "number to skip cannot be negative"); +// +// if (iterable instanceof List) { +// final List list = (List) iterable; +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// // TODO(kevinb): Support a concurrently modified collection? +// int toSkip = Math.min(list.size(), numberToSkip); +// return list.subList(toSkip, list.size()).iterator(); +// } +// }; +// } +// +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// final Iterator iterator = iterable.iterator(); +// +// Iterators.advance(iterator, numberToSkip); +// +// /* +// * We can't just return the iterator because an immediate call to its +// * remove() method would remove one of the skipped elements instead of +// * throwing an IllegalStateException. +// */ +// return new Iterator() { +// boolean atStart = true; +// +// @Override +// public boolean hasNext() { +// return iterator.hasNext(); +// } +// +// @Override +// public T next() { +// T result = iterator.next(); +// atStart = false; // not called if next() fails +// return result; +// } +// +// @Override +// public void remove() { +// checkRemove(!atStart); +// iterator.remove(); +// } +// }; +// } +// }; +// } + + /** + * Creates an iterable with the first {@code limitSize} elements of the given + * iterable. If the original iterable does not contain that many elements, the + * returned iterable will have the same behavior as the original iterable. The + * returned iterable's iterator supports {@code remove()} if the original + * iterator does. + * + * @param iterable the iterable to limit + * @param limitSize the maximum number of elements in the returned iterable + * @throws IllegalArgumentException if {@code limitSize} is negative + * @since 3.0 + */ +// public static Iterable limit( +// final Iterable iterable, final int limitSize) { +// checkNotNull(iterable); +// checkArgument(limitSize >= 0, "limit is negative"); +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// return Iterators.limit(iterable.iterator(), limitSize); +// } +// }; +// } + + /** + * Returns a view of the supplied iterable that wraps each generated + * {@link Iterator} through {@link Iterators#consumingIterator(Iterator)}. + * + *

Note: If {@code iterable} is a {@link Queue}, the returned iterable will + * get entries from {@link Queue#remove()} since {@link Queue}'s iteration + * order is undefined. Calling {@link Iterator#hasNext()} on a generated + * iterator from the returned iterable may cause an item to be immediately + * dequeued for return on a subsequent call to {@link Iterator#next()}. + * + * @param iterable the iterable to wrap + * @return a view of the supplied iterable that wraps each generated iterator + * through {@link Iterators#consumingIterator(Iterator)}; for queues, + * an iterable that generates iterators that return and consume the + * queue's elements in queue order + * + * @see Iterators#consumingIterator(Iterator) + * @since 2.0 + */ +// public static Iterable consumingIterable(final Iterable iterable) { +// if (iterable instanceof Queue) { +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// return new ConsumingQueueIterator((Queue) iterable); +// } +// +// @Override +// public String toString() { +// return "Iterables.consumingIterable(...)"; +// } +// }; +// } +// +// checkNotNull(iterable); +// +// return new FluentIterable() { +// @Override +// public Iterator iterator() { +// return Iterators.consumingIterator(iterable.iterator()); +// } +// +// @Override +// public String toString() { +// return "Iterables.consumingIterable(...)"; +// } +// }; +// } +// +// private static class ConsumingQueueIterator extends AbstractIterator { +// private final Queue queue; +// +// private ConsumingQueueIterator(Queue queue) { +// this.queue = queue; +// } +// +// @Override public T computeNext() { +// try { +// return queue.remove(); +// } catch (NoSuchElementException e) { +// return endOfData(); +// } +// } +// } + + // Methods only in Iterables, not in Iterators + + /** + * Determines if the given iterable contains no elements. + * + *

There is no precise {@link Iterator} equivalent to this method, since + * one can only ask an iterator whether it has any elements remaining + * (which one does using {@link Iterator#hasNext}). + * + * @return {@code true} if the iterable contains no elements + */ +// public static boolean isEmpty(Iterable iterable) { +// if (iterable instanceof Collection) { +// return ((Collection) iterable).isEmpty(); +// } +// return !iterable.iterator().hasNext(); +// } + + /** + * Returns an iterable over the merged contents of all given + * {@code iterables}. Equivalent entries will not be de-duplicated. + * + *

Callers must ensure that the source {@code iterables} are in + * non-descending order as this method does not sort its input. + * + *

For any equivalent elements across all {@code iterables}, it is + * undefined which element is returned first. + * + * @since 11.0 + */ +// @Beta +// public static Iterable mergeSorted( +// final Iterable> iterables, +// final Comparator comparator) { +// checkNotNull(iterables, "iterables"); +// checkNotNull(comparator, "comparator"); +// Iterable iterable = new FluentIterable() { +// @Override +// public Iterator iterator() { +// return Iterators.mergeSorted( +// Iterables.transform(iterables, Iterables.toIterator()), +// comparator); +// } +// }; +// return new UnmodifiableIterable(iterable); +// } + + // TODO(user): Is this the best place for this? Move to fluent functions? + // Useful as a public method? +// private static Function, Iterator> +// toIterator() { +// return new Function, Iterator>() { +// @Override +// public Iterator apply(Iterable iterable) { +// return iterable.iterator(); +// } +// }; +// } +} diff --git a/java/src/game/collect/Iterators.java b/java/src/game/collect/Iterators.java new file mode 100644 index 0000000..d486067 --- /dev/null +++ b/java/src/game/collect/Iterators.java @@ -0,0 +1,1296 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkRemove; +import static game.collect.Preconditions.checkArgument; +import static game.collect.Preconditions.checkNotNull; +import static game.util.Predicates.equalTo; +import static game.util.Predicates.in; +import static game.util.Predicates.instanceOf; +import static game.util.Predicates.not; + +import java.util.Collection; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * This class contains static utility methods that operate on or return objects + * of type {@link Iterator}. Except as noted, each method has a corresponding + * {@link Iterable}-based method in the {@link Iterables} class. + * + *

Performance notes: Unless otherwise noted, all of the iterators + * produced in this class are lazy, which means that they only advance + * the backing iteration when absolutely necessary. + * + *

See the Guava User Guide section on + * {@code Iterators}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Iterators { + private Iterators() {} + + static final UnmodifiableListIterator EMPTY_LIST_ITERATOR + = new UnmodifiableListIterator() { + @Override + public boolean hasNext() { + return false; + } + @Override + public Object next() { + throw new NoSuchElementException(); + } + @Override + public boolean hasPrevious() { + return false; + } + @Override + public Object previous() { + throw new NoSuchElementException(); + } + @Override + public int nextIndex() { + return 0; + } + @Override + public int previousIndex() { + return -1; + } + }; + + /** + * Returns the empty iterator. + * + *

The {@link Iterable} equivalent of this method is {@link + * ImmutableSet#of()}. + */ + public static UnmodifiableIterator emptyIterator() { + return emptyListIterator(); + } + + /** + * Returns the empty iterator. + * + *

The {@link Iterable} equivalent of this method is {@link + * ImmutableSet#of()}. + */ + // Casting to any type is safe since there are no actual elements. + + static UnmodifiableListIterator emptyListIterator() { + return (UnmodifiableListIterator) EMPTY_LIST_ITERATOR; + } + + private static final Iterator EMPTY_MODIFIABLE_ITERATOR = + new Iterator() { + @Override public boolean hasNext() { + return false; + } + + @Override public Object next() { + throw new NoSuchElementException(); + } + + @Override public void remove() { + checkRemove(false); + } + }; + + /** + * Returns the empty {@code Iterator} that throws + * {@link IllegalStateException} instead of + * {@link UnsupportedOperationException} on a call to + * {@link Iterator#remove()}. + */ + // Casting to any type is safe since there are no actual elements. + + static Iterator emptyModifiableIterator() { + return (Iterator) EMPTY_MODIFIABLE_ITERATOR; + } + + /** Returns an unmodifiable view of {@code iterator}. */ + public static UnmodifiableIterator unmodifiableIterator( + final Iterator iterator) { + checkNotNull(iterator); + if (iterator instanceof UnmodifiableIterator) { + return (UnmodifiableIterator) iterator; + } + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + @Override + public T next() { + return iterator.next(); + } + }; + } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ +// @Deprecated public static UnmodifiableIterator unmodifiableIterator( +// UnmodifiableIterator iterator) { +// return checkNotNull(iterator); +// } + + /** + * Returns the number of elements remaining in {@code iterator}. The iterator + * will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + */ + public static int size(Iterator iterator) { + int count = 0; + while (iterator.hasNext()) { + iterator.next(); + count++; + } + return count; + } + + /** + * Returns {@code true} if {@code iterator} contains {@code element}. + */ + public static boolean contains(Iterator iterator, Object element) { + return any(iterator, equalTo(element)); + } + + /** + * Traverses an iterator and removes every element that belongs to the + * provided collection. The iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param elementsToRemove the elements to remove + * @return {@code true} if any element was removed from {@code iterator} + */ + public static boolean removeAll( + Iterator removeFrom, Collection elementsToRemove) { + return removeIf(removeFrom, in(elementsToRemove)); + } + + /** + * Removes every element that satisfies the provided predicate from the + * iterator. The iterator will be left exhausted: its {@code hasNext()} + * method will return {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param predicate a predicate that determines whether an element should + * be removed + * @return {@code true} if any elements were removed from the iterator + * @since 2.0 + */ + public static boolean removeIf( + Iterator removeFrom, Predicate predicate) { + checkNotNull(predicate); + boolean modified = false; + while (removeFrom.hasNext()) { + if (predicate.test(removeFrom.next())) { + removeFrom.remove(); + modified = true; + } + } + return modified; + } + + /** + * Traverses an iterator and removes every element that does not belong to the + * provided collection. The iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. + * + * @param removeFrom the iterator to (potentially) remove elements from + * @param elementsToRetain the elements to retain + * @return {@code true} if any element was removed from {@code iterator} + */ + public static boolean retainAll( + Iterator removeFrom, Collection elementsToRetain) { + return removeIf(removeFrom, not(in(elementsToRetain))); + } + + /** + * Determines whether two iterators contain equal elements in the same order. + * More specifically, this method returns {@code true} if {@code iterator1} + * and {@code iterator2} contain the same number of elements and every element + * of {@code iterator1} is equal to the corresponding element of + * {@code iterator2}. + * + *

Note that this will modify the supplied iterators, since they will have + * been advanced some number of elements forward. + */ + public static boolean elementsEqual( + Iterator iterator1, Iterator iterator2) { + while (iterator1.hasNext()) { + if (!iterator2.hasNext()) { + return false; + } + Object o1 = iterator1.next(); + Object o2 = iterator2.next(); + if (!Preconditions.equal(o1, o2)) { + return false; + } + } + return !iterator2.hasNext(); + } + + /** + * Returns a string representation of {@code iterator}, with the format + * {@code [e1, e2, ..., en]}. The iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. + */ +// public static String toString(Iterator iterator) { +// return Filter.STANDARD_JOINER +// .appendTo(new StringBuilder().append('['), iterator) +// .append(']') +// .toString(); +// } + + /** + * Returns the single element contained in {@code iterator}. + * + * @throws NoSuchElementException if the iterator is empty + * @throws IllegalArgumentException if the iterator contains multiple + * elements. The state of the iterator is unspecified. + */ +// public static T getOnlyElement(Iterator iterator) { +// T first = iterator.next(); +// if (!iterator.hasNext()) { +// return first; +// } +// +// StringBuilder sb = new StringBuilder(); +// sb.append("expected one element but was: <" + first); +// for (int i = 0; i < 4 && iterator.hasNext(); i++) { +// sb.append(", " + iterator.next()); +// } +// if (iterator.hasNext()) { +// sb.append(", ..."); +// } +// sb.append('>'); +// +// throw new IllegalArgumentException(sb.toString()); +// } + + /** + * Returns the single element contained in {@code iterator}, or {@code + * defaultValue} if the iterator is empty. + * + * @throws IllegalArgumentException if the iterator contains multiple + * elements. The state of the iterator is unspecified. + */ +// +// public static T getOnlyElement(Iterator iterator, T defaultValue) { +// return iterator.hasNext() ? getOnlyElement(iterator) : defaultValue; +// } + + /** + * Copies an iterator's elements into an array. The iterator will be left + * exhausted: its {@code hasNext()} method will return {@code false}. + * + * @param iterator the iterator to copy + * @param type the type of the elements + * @return a newly-allocated array into which all the elements of the iterator + * have been copied + */ +// +// public static T[] toArray( +// Iterator iterator, Class type) { +// List list = Lists.newArrayList(iterator); +// return Iterables.toArray(list, type); +// } + + /** + * Adds all elements in {@code iterator} to {@code collection}. The iterator + * will be left exhausted: its {@code hasNext()} method will return + * {@code false}. + * + * @return {@code true} if {@code collection} was modified as a result of this + * operation + */ + public static boolean addAll( + Collection addTo, Iterator iterator) { + checkNotNull(addTo); + checkNotNull(iterator); + boolean wasModified = false; + while (iterator.hasNext()) { + wasModified |= addTo.add(iterator.next()); + } + return wasModified; + } + + /** + * Returns the number of elements in the specified iterator that equal the + * specified object. The iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. + * + * @see Collections#frequency + */ +// public static int frequency(Iterator iterator, Object element) { +// return size(filter(iterator, equalTo(element))); +// } + + /** + * Returns an iterator that cycles indefinitely over the elements of {@code + * iterable}. + * + *

The returned iterator supports {@code remove()} if the provided iterator + * does. After {@code remove()} is called, subsequent cycles omit the removed + * element, which is no longer in {@code iterable}. The iterator's + * {@code hasNext()} method returns {@code true} until {@code iterable} is + * empty. + * + *

Warning: Typical uses of the resulting iterator may produce an + * infinite loop. You should use an explicit {@code break} or be certain that + * you will eventually remove all the elements. + */ +// public static Iterator cycle(final Iterable iterable) { +// checkNotNull(iterable); +// return new Iterator() { +// Iterator iterator = emptyIterator(); +// Iterator removeFrom; +// +// @Override +// public boolean hasNext() { +// if (!iterator.hasNext()) { +// iterator = iterable.iterator(); +// } +// return iterator.hasNext(); +// } +// @Override +// public T next() { +// if (!hasNext()) { +// throw new NoSuchElementException(); +// } +// removeFrom = iterator; +// return iterator.next(); +// } +// @Override +// public void remove() { +// checkRemove(removeFrom != null); +// removeFrom.remove(); +// removeFrom = null; +// } +// }; +// } + + /** + * Returns an iterator that cycles indefinitely over the provided elements. + * + *

The returned iterator supports {@code remove()}. After {@code remove()} + * is called, subsequent cycles omit the removed + * element, but {@code elements} does not change. The iterator's + * {@code hasNext()} method returns {@code true} until all of the original + * elements have been removed. + * + *

Warning: Typical uses of the resulting iterator may produce an + * infinite loop. You should use an explicit {@code break} or be certain that + * you will eventually remove all the elements. + */ +// public static Iterator cycle(T... elements) { +// return cycle(Lists.newArrayList(elements)); +// } + + /** + * Combines two iterators into a single iterator. The returned iterator + * iterates across the elements in {@code a}, followed by the elements in + * {@code b}. The source iterators are not polled until necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + *

Note: the current implementation is not suitable for nested + * concatenated iterators, i.e. the following should be avoided when in a loop: + * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the + * resulting iterator has a cubic complexity to the depth of the nesting. + */ +// public static Iterator concat(Iterator a, +// Iterator b) { +// return concat(ImmutableList.of(a, b).iterator()); +// } + + /** + * Combines three iterators into a single iterator. The returned iterator + * iterates across the elements in {@code a}, followed by the elements in + * {@code b}, followed by the elements in {@code c}. The source iterators + * are not polled until necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + *

Note: the current implementation is not suitable for nested + * concatenated iterators, i.e. the following should be avoided when in a loop: + * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the + * resulting iterator has a cubic complexity to the depth of the nesting. + */ +// public static Iterator concat(Iterator a, +// Iterator b, Iterator c) { +// return concat(ImmutableList.of(a, b, c).iterator()); +// } + + /** + * Combines four iterators into a single iterator. The returned iterator + * iterates across the elements in {@code a}, followed by the elements in + * {@code b}, followed by the elements in {@code c}, followed by the elements + * in {@code d}. The source iterators are not polled until necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + *

Note: the current implementation is not suitable for nested + * concatenated iterators, i.e. the following should be avoided when in a loop: + * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the + * resulting iterator has a cubic complexity to the depth of the nesting. + */ +// public static Iterator concat(Iterator a, +// Iterator b, Iterator c, +// Iterator d) { +// return concat(ImmutableList.of(a, b, c, d).iterator()); +// } + + /** + * Combines multiple iterators into a single iterator. The returned iterator + * iterates across the elements of each iterator in {@code inputs}. The input + * iterators are not polled until necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding + * input iterator supports it. + * + *

Note: the current implementation is not suitable for nested + * concatenated iterators, i.e. the following should be avoided when in a loop: + * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the + * resulting iterator has a cubic complexity to the depth of the nesting. + * + * @throws NullPointerException if any of the provided iterators is null + */ +// public static Iterator concat(Iterator... inputs) { +// return concat(ImmutableList.copyOf(inputs).iterator()); +// } + + /** + * Combines multiple iterators into a single iterator. The returned iterator + * iterates across the elements of each iterator in {@code inputs}. The input + * iterators are not polled until necessary. + * + *

The returned iterator supports {@code remove()} when the corresponding + * input iterator supports it. The methods of the returned iterator may throw + * {@code NullPointerException} if any of the input iterators is null. + * + *

Note: the current implementation is not suitable for nested + * concatenated iterators, i.e. the following should be avoided when in a loop: + * {@code iterator = Iterators.concat(iterator, suffix);}, since iteration over the + * resulting iterator has a cubic complexity to the depth of the nesting. + */ +// public static Iterator concat( +// final Iterator> inputs) { +// checkNotNull(inputs); +// return new Iterator() { +// Iterator current = emptyIterator(); +// Iterator removeFrom; +// +// @Override +// public boolean hasNext() { +// // http://code.google.com/p/google-collections/issues/detail?id=151 +// // current.hasNext() might be relatively expensive, worth minimizing. +// boolean currentHasNext; +// // checkNotNull eager for GWT +// // note: it must be here & not where 'current' is assigned, +// // because otherwise we'll have called inputs.next() before throwing +// // the first NPE, and the next time around we'll call inputs.next() +// // again, incorrectly moving beyond the error. +// while (!(currentHasNext = checkNotNull(current).hasNext()) +// && inputs.hasNext()) { +// current = inputs.next(); +// } +// return currentHasNext; +// } +// @Override +// public T next() { +// if (!hasNext()) { +// throw new NoSuchElementException(); +// } +// removeFrom = current; +// return current.next(); +// } +// @Override +// public void remove() { +// checkRemove(removeFrom != null); +// removeFrom.remove(); +// removeFrom = null; +// } +// }; +// } + + /** + * Divides an iterator into unmodifiable sublists of the given size (the final + * list may be smaller). For example, partitioning an iterator containing + * {@code [a, b, c, d, e]} with a partition size of 3 yields {@code + * [[a, b, c], [d, e]]} -- an outer iterator containing two inner lists of + * three and two elements, all in the original order. + * + *

The returned lists implement {@link java.util.RandomAccess}. + * + * @param iterator the iterator to return a partitioned view of + * @param size the desired size of each partition (the last may be smaller) + * @return an iterator of immutable lists containing the elements of {@code + * iterator} divided into partitions + * @throws IllegalArgumentException if {@code size} is nonpositive + */ +// public static UnmodifiableIterator> partition( +// Iterator iterator, int size) { +// return partitionImpl(iterator, size, false); +// } + + /** + * Divides an iterator into unmodifiable sublists of the given size, padding + * the final iterator with null values if necessary. For example, partitioning + * an iterator containing {@code [a, b, c, d, e]} with a partition size of 3 + * yields {@code [[a, b, c], [d, e, null]]} -- an outer iterator containing + * two inner lists of three elements each, all in the original order. + * + *

The returned lists implement {@link java.util.RandomAccess}. + * + * @param iterator the iterator to return a partitioned view of + * @param size the desired size of each partition + * @return an iterator of immutable lists containing the elements of {@code + * iterator} divided into partitions (the final iterable may have + * trailing null elements) + * @throws IllegalArgumentException if {@code size} is nonpositive + */ +// 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) { +// checkNotNull(iterator); +// checkArgument(size > 0); +// return new UnmodifiableIterator>() { +// @Override +// public boolean hasNext() { +// return iterator.hasNext(); +// } +// @Override +// public List next() { +// if (!hasNext()) { +// throw new NoSuchElementException(); +// } +// Object[] array = new Object[size]; +// int count = 0; +// for (; count < size && iterator.hasNext(); count++) { +// array[count] = iterator.next(); +// } +// for (int i = count; i < size; i++) { +// array[i] = null; // for GWT +// } +// +// // we only put Ts in it +// List list = Collections.unmodifiableList( +// (List) Arrays.asList(array)); +// return (pad || count == size) ? list : list.subList(0, count); +// } +// }; +// } + + /** + * Returns the elements of {@code unfiltered} that satisfy a predicate. + */ + public static UnmodifiableIterator filter( + final Iterator unfiltered, final Predicate predicate) { + checkNotNull(unfiltered); + checkNotNull(predicate); + return new AbstractIterator() { + @Override protected T computeNext() { + while (unfiltered.hasNext()) { + T element = unfiltered.next(); + if (predicate.test(element)) { + return element; + } + } + return endOfData(); + } + }; + } + + /** + * Returns all instances of class {@code type} in {@code unfiltered}. The + * returned iterator has elements whose class is {@code type} or a subclass of + * {@code type}. + * + * @param unfiltered an iterator containing objects of any type + * @param type the type of elements desired + * @return an unmodifiable iterator containing all elements of the original + * iterator that were of the requested type + */ + // can cast to because non-Ts are removed + + public static UnmodifiableIterator filter( + Iterator unfiltered, Class type) { + return (UnmodifiableIterator) filter(unfiltered, instanceOf(type)); + } + + /** + * Returns {@code true} if one or more elements returned by {@code iterator} + * satisfy the given predicate. + */ + public static boolean any( + Iterator iterator, Predicate predicate) { + return indexOf(iterator, predicate) != -1; + } + + /** + * Returns {@code true} if every element returned by {@code iterator} + * satisfies the given predicate. If {@code iterator} is empty, {@code true} + * is returned. + */ + public static boolean all( + Iterator iterator, Predicate predicate) { + checkNotNull(predicate); + while (iterator.hasNext()) { + T element = iterator.next(); + if (!predicate.test(element)) { + return false; + } + } + return true; + } + + /** + * Returns the first element in {@code iterator} that satisfies the given + * predicate; use this method only when such an element is known to exist. If + * no such element is found, the iterator will be left exhausted: its {@code + * hasNext()} method will return {@code false}. If it is possible that + * no element will match, use {@link #tryFind} or {@link + * #find(Iterator, Predicate, Object)} instead. + * + * @throws NoSuchElementException if no element in {@code iterator} matches + * the given predicate + */ +// public static T find( +// Iterator iterator, Predicate predicate) { +// return filter(iterator, predicate).next(); +// } + + /** + * Returns the first element in {@code iterator} that satisfies the given + * predicate. If no such element is found, {@code defaultValue} will be + * returned from this method and the iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. Note that this can + * usually be handled more naturally using {@code + * tryFind(iterator, predicate).or(defaultValue)}. + * + * @since 7.0 + */ +// +// public static T find(Iterator iterator, Predicate predicate, +// T defaultValue) { +// return getNext(filter(iterator, predicate), defaultValue); +// } + + /** + * Returns an {@link Optional} containing the first element in {@code + * iterator} that satisfies the given predicate, if such an element exists. If + * no such element is found, an empty {@link Optional} will be returned from + * this method and the iterator will be left exhausted: its {@code + * hasNext()} method will return {@code false}. + * + *

Warning: avoid using a {@code predicate} that matches {@code + * null}. If {@code null} is matched in {@code iterator}, a + * NullPointerException will be thrown. + * + * @since 11.0 + */ +// public static Optional tryFind( +// Iterator iterator, Predicate predicate) { +// UnmodifiableIterator filteredIterator = filter(iterator, predicate); +// return filteredIterator.hasNext() +// ? Optional.of(filteredIterator.next()) +// : Optional.absent(); +// } + + /** + * Returns the index in {@code iterator} of the first element that satisfies + * the provided {@code predicate}, or {@code -1} if the Iterator has no such + * elements. + * + *

More formally, returns the lowest index {@code i} such that + * {@code predicate.apply(Iterators.get(iterator, i))} returns {@code true}, + * or {@code -1} if there is no such index. + * + *

If -1 is returned, the iterator will be left exhausted: its + * {@code hasNext()} method will return {@code false}. Otherwise, + * the iterator will be set to the element which satisfies the + * {@code predicate}. + * + * @since 2.0 + */ + public static int indexOf( + Iterator iterator, Predicate predicate) { + checkNotNull(predicate, "predicate"); + for (int i = 0; iterator.hasNext(); i++) { + T current = iterator.next(); + if (predicate.test(current)) { + return i; + } + } + return -1; + } + + /** + * Returns an iterator that applies {@code function} to each element of {@code + * fromIterator}. + * + *

The returned iterator supports {@code remove()} if the provided iterator + * does. After a successful {@code remove()} call, {@code fromIterator} no + * longer contains the corresponding element. + */ + public static Iterator transform(final Iterator fromIterator, + final Function function) { + checkNotNull(function); + return new TransformedIterator(fromIterator) { + @Override + T transform(F from) { + return function.apply(from); + } + }; + } + + /** + * Advances {@code iterator} {@code position + 1} times, returning the + * element at the {@code position}th position. + * + * @param position position of the element to return + * @return the element at the specified position in {@code iterator} + * @throws IndexOutOfBoundsException if {@code position} is negative or + * greater than or equal to the number of elements remaining in + * {@code iterator} + */ +// public static T get(Iterator iterator, int position) { +// checkNonnegative(position); +// int skipped = advance(iterator, position); +// if (!iterator.hasNext()) { +// throw new IndexOutOfBoundsException("position (" + position +// + ") must be less than the number of elements that remained (" +// + skipped + ")"); +// } +// return iterator.next(); +// } + +// static void checkNonnegative(int position) { +// if (position < 0) { +// throw new IndexOutOfBoundsException("position (" + position +// + ") must not be negative"); +// } +// } + + /** + * Advances {@code iterator} {@code position + 1} times, returning the + * element at the {@code position}th position or {@code defaultValue} + * otherwise. + * + * @param position position of the element to return + * @param defaultValue the default value to return if the iterator is empty + * or if {@code position} is greater than the number of elements + * remaining in {@code iterator} + * @return the element at the specified position in {@code iterator} or + * {@code defaultValue} if {@code iterator} produces fewer than + * {@code position + 1} elements. + * @throws IndexOutOfBoundsException if {@code position} is negative + * @since 4.0 + */ +// +// public static T get(Iterator iterator, int position, T defaultValue) { +// checkNonnegative(position); +// advance(iterator, position); +// return getNext(iterator, defaultValue); +// } + + /** + * Returns the next element in {@code iterator} or {@code defaultValue} if + * the iterator is empty. The {@link Iterables} analog to this method is + * {@link Iterables#getFirst}. + * + * @param defaultValue the default value to return if the iterator is empty + * @return the next element of {@code iterator} or the default value + * @since 7.0 + */ +// +// public static T getNext(Iterator iterator, T defaultValue) { +// return iterator.hasNext() ? iterator.next() : defaultValue; +// } + + /** + * Advances {@code iterator} to the end, returning the last element. + * + * @return the last element of {@code iterator} + * @throws NoSuchElementException if the iterator is empty + */ +// public static T getLast(Iterator iterator) { +// while (true) { +// T current = iterator.next(); +// if (!iterator.hasNext()) { +// return current; +// } +// } +// } + + /** + * Advances {@code iterator} to the end, returning the last element or + * {@code defaultValue} if the iterator is empty. + * + * @param defaultValue the default value to return if the iterator is empty + * @return the last element of {@code iterator} + * @since 3.0 + */ +// +// public static T getLast(Iterator iterator, T defaultValue) { +// return iterator.hasNext() ? getLast(iterator) : defaultValue; +// } + + /** + * Calls {@code next()} on {@code iterator}, either {@code numberToAdvance} times + * or until {@code hasNext()} returns {@code false}, whichever comes first. + * + * @return the number of elements the iterator was advanced + * @since 13.0 (since 3.0 as {@code Iterators.skip}) + */ +// public static int advance(Iterator iterator, int numberToAdvance) { +// checkNotNull(iterator); +// checkArgument(numberToAdvance >= 0, "numberToAdvance must be nonnegative"); +// +// int i; +// for (i = 0; i < numberToAdvance && iterator.hasNext(); i++) { +// iterator.next(); +// } +// return i; +// } + + /** + * Creates an iterator returning the first {@code limitSize} elements of the + * given iterator. If the original iterator does not contain that many + * elements, the returned iterator will have the same behavior as the original + * iterator. The returned iterator supports {@code remove()} if the original + * iterator does. + * + * @param iterator the iterator to limit + * @param limitSize the maximum number of elements in the returned iterator + * @throws IllegalArgumentException if {@code limitSize} is negative + * @since 3.0 + */ +// public static Iterator limit( +// final Iterator iterator, final int limitSize) { +// checkNotNull(iterator); +// checkArgument(limitSize >= 0, "limit is negative"); +// return new Iterator() { +// private int count; +// +// @Override +// public boolean hasNext() { +// return count < limitSize && iterator.hasNext(); +// } +// +// @Override +// public T next() { +// if (!hasNext()) { +// throw new NoSuchElementException(); +// } +// count++; +// return iterator.next(); +// } +// +// @Override +// public void remove() { +// iterator.remove(); +// } +// }; +// } + + /** + * Returns a view of the supplied {@code iterator} that removes each element + * from the supplied {@code iterator} as it is returned. + * + *

The provided iterator must support {@link Iterator#remove()} or + * else the returned iterator will fail on the first call to {@code + * next}. + * + * @param iterator the iterator to remove and return elements from + * @return an iterator that removes and returns elements from the + * supplied iterator + * @since 2.0 + */ +// public static Iterator consumingIterator(final Iterator iterator) { +// checkNotNull(iterator); +// return new UnmodifiableIterator() { +// @Override +// public boolean hasNext() { +// return iterator.hasNext(); +// } +// +// @Override +// public T next() { +// T next = iterator.next(); +// iterator.remove(); +// return next; +// } +// +// @Override +// public String toString() { +// return "Iterators.consumingIterator(...)"; +// } +// }; +// } + + /** + * Deletes and returns the next value from the iterator, or returns + * {@code null} if there is no such value. + */ +// +// static T pollNext(Iterator iterator) { +// if (iterator.hasNext()) { +// T result = iterator.next(); +// iterator.remove(); +// return result; +// } else { +// return null; +// } +// } + + // Methods only in Iterators, not in Iterables + + /** + * Clears the iterator using its remove method. + */ + static void clear(Iterator iterator) { + checkNotNull(iterator); + while (iterator.hasNext()) { + iterator.next(); + iterator.remove(); + } + } + + /** + * Returns an iterator containing the elements of {@code array} in order. The + * returned iterator is a view of the array; subsequent changes to the array + * will be reflected in the iterator. + * + *

Note: It is often preferable to represent your data using a + * collection type, for example using {@link Arrays#asList(Object[])}, making + * this method unnecessary. + * + *

The {@code Iterable} equivalent of this method is either {@link + * Arrays#asList(Object[])}, {@link ImmutableList#copyOf(Object[])}}, + * or {@link ImmutableList#of}. + */ + public static UnmodifiableIterator forArray(final T... array) { + return forArray(array, 0, array.length, 0); + } + + /** + * Returns a list iterator containing the elements in the specified range of + * {@code array} in order, starting at the specified index. + * + *

The {@code Iterable} equivalent of this method is {@code + * Arrays.asList(array).subList(offset, offset + length).listIterator(index)}. + */ + static UnmodifiableListIterator forArray( + final T[] array, final int offset, int length, int index) { + checkArgument(length >= 0); + int end = offset + length; + + // Technically we should give a slightly more descriptive error on overflow + Preconditions.checkPositionIndexes(offset, end, array.length); + Preconditions.checkPositionIndex(index, length); + if (length == 0) { + return emptyListIterator(); + } + + /* + * We can't use call the two-arg constructor with arguments (offset, end) + * because the returned Iterator is a ListIterator that may be moved back + * past the beginning of the iteration. + */ + return new AbstractIndexedListIterator(length, index) { + @Override protected T get(int index) { + return array[offset + index]; + } + }; + } + + /** + * Returns an iterator containing only {@code value}. + * + *

The {@link Iterable} equivalent of this method is {@link + * Collections#singleton}. + */ +// public static UnmodifiableIterator singletonIterator( +// final T value) { +// return new UnmodifiableIterator() { +// boolean done; +// @Override +// public boolean hasNext() { +// return !done; +// } +// @Override +// public T next() { +// if (done) { +// throw new NoSuchElementException(); +// } +// done = true; +// return value; +// } +// }; +// } + + /** + * Adapts an {@code Enumeration} to the {@code Iterator} interface. + * + *

This method has no equivalent in {@link Iterables} because viewing an + * {@code Enumeration} as an {@code Iterable} is impossible. However, the + * contents can be copied into a collection using {@link + * Collections#list}. + */ +// public static UnmodifiableIterator forEnumeration( +// final Enumeration enumeration) { +// checkNotNull(enumeration); +// return new UnmodifiableIterator() { +// @Override +// public boolean hasNext() { +// return enumeration.hasMoreElements(); +// } +// @Override +// public T next() { +// return enumeration.nextElement(); +// } +// }; +// } + + /** + * Adapts an {@code Iterator} to the {@code Enumeration} interface. + * + *

The {@code Iterable} equivalent of this method is either {@link + * Collections#enumeration} (if you have a {@link Collection}), or + * {@code Iterators.asEnumeration(collection.iterator())}. + */ +// public static Enumeration asEnumeration(final Iterator iterator) { +// checkNotNull(iterator); +// return new Enumeration() { +// @Override +// public boolean hasMoreElements() { +// return iterator.hasNext(); +// } +// @Override +// public T nextElement() { +// return iterator.next(); +// } +// }; +// } + + /** + * Implementation of PeekingIterator that avoids peeking unless necessary. + */ +// private static class PeekingImpl implements PeekingIterator { +// +// private final Iterator iterator; +// private boolean hasPeeked; +// private E peekedElement; +// +// public PeekingImpl(Iterator iterator) { +// this.iterator = checkNotNull(iterator); +// } +// +// @Override +// public boolean hasNext() { +// return hasPeeked || iterator.hasNext(); +// } +// +// @Override +// public E next() { +// if (!hasPeeked) { +// return iterator.next(); +// } +// E result = peekedElement; +// hasPeeked = false; +// peekedElement = null; +// return result; +// } +// +// @Override +// public void remove() { +// checkState(!hasPeeked, "Can't remove after you've peeked at next"); +// iterator.remove(); +// } +// +// @Override +// public E peek() { +// if (!hasPeeked) { +// peekedElement = iterator.next(); +// hasPeeked = true; +// } +// return peekedElement; +// } +// } + + /** + * Returns a {@code PeekingIterator} backed by the given iterator. + * + *

Calls to the {@code peek} method with no intervening calls to {@code + * next} do not affect the iteration, and hence return the same object each + * time. A subsequent call to {@code next} is guaranteed to return the same + * object again. For example:

   {@code
+   *
+   *   PeekingIterator peekingIterator =
+   *       Iterators.peekingIterator(Iterators.forArray("a", "b"));
+   *   String a1 = peekingIterator.peek(); // returns "a"
+   *   String a2 = peekingIterator.peek(); // also returns "a"
+   *   String a3 = peekingIterator.next(); // also returns "a"}
+ * + *

Any structural changes to the underlying iteration (aside from those + * performed by the iterator's own {@link PeekingIterator#remove()} method) + * will leave the iterator in an undefined state. + * + *

The returned iterator does not support removal after peeking, as + * explained by {@link PeekingIterator#remove()}. + * + *

Note: If the given iterator is already a {@code PeekingIterator}, + * it might be returned to the caller, although this is neither + * guaranteed to occur nor required to be consistent. For example, this + * method might choose to pass through recognized implementations of + * {@code PeekingIterator} when the behavior of the implementation is + * known to meet the contract guaranteed by this method. + * + *

There is no {@link Iterable} equivalent to this method, so use this + * method to wrap each individual iterator as it is generated. + * + * @param iterator the backing iterator. The {@link PeekingIterator} assumes + * ownership of this iterator, so users should cease making direct calls + * to it after calling this method. + * @return a peeking iterator backed by that iterator. Apart from the + * additional {@link PeekingIterator#peek()} method, this iterator behaves + * exactly the same as {@code iterator}. + */ +// public static PeekingIterator peekingIterator( +// Iterator iterator) { +// if (iterator instanceof PeekingImpl) { +// // Safe to cast to because PeekingImpl only uses T +// // covariantly (and cannot be subclassed to add non-covariant uses). +// +// PeekingImpl peeking = (PeekingImpl) iterator; +// return peeking; +// } +// return new PeekingImpl(iterator); +// } + + /** + * Simply returns its argument. + * + * @deprecated no need to use this + * @since 10.0 + */ +// @Deprecated public static PeekingIterator peekingIterator( +// PeekingIterator iterator) { +// return checkNotNull(iterator); +// } + + /** + * Returns an iterator over the merged contents of all given + * {@code iterators}, traversing every element of the input iterators. + * Equivalent entries will not be de-duplicated. + * + *

Callers must ensure that the source {@code iterators} are in + * non-descending order as this method does not sort its input. + * + *

For any equivalent elements across all {@code iterators}, it is + * undefined which element is returned first. + * + * @since 11.0 + */ +// @Beta +// public static UnmodifiableIterator mergeSorted( +// Iterable> iterators, +// Comparator comparator) { +// checkNotNull(iterators, "iterators"); +// checkNotNull(comparator, "comparator"); +// +// return new MergingIterator(iterators, comparator); +// } + + /** + * An iterator that performs a lazy N-way merge, calculating the next value + * each time the iterator is polled. This amortizes the sorting cost over the + * iteration and requires less memory than sorting all elements at once. + * + *

Retrieving a single element takes approximately O(log(M)) time, where M + * is the number of iterators. (Retrieving all elements takes approximately + * O(N*log(M)) time, where N is the total number of elements.) + */ +// private static class MergingIterator extends UnmodifiableIterator { +// final Queue> queue; +// +// public MergingIterator(Iterable> iterators, +// final Comparator itemComparator) { +// // A comparator that's used by the heap, allowing the heap +// // to be sorted based on the top of each iterator. +// Comparator> heapComparator = +// new Comparator>() { +// @Override +// public int compare(PeekingIterator o1, PeekingIterator o2) { +// return itemComparator.compare(o1.peek(), o2.peek()); +// } +// }; +// +// queue = new PriorityQueue>(2, heapComparator); +// +// for (Iterator iterator : iterators) { +// if (iterator.hasNext()) { +// queue.add(Iterators.peekingIterator(iterator)); +// } +// } +// } +// +// @Override +// public boolean hasNext() { +// return !queue.isEmpty(); +// } +// +// @Override +// public T next() { +// PeekingIterator nextIter = queue.remove(); +// T next = nextIter.next(); +// if (nextIter.hasNext()) { +// queue.add(nextIter); +// } +// return next; +// } +// } + + /** + * Used to avoid http://bugs.sun.com/view_bug.do?bug_id=6558557 + */ + static ListIterator cast(Iterator iterator) { + return (ListIterator) iterator; + } +} diff --git a/java/src/game/collect/Lists.java b/java/src/game/collect/Lists.java new file mode 100644 index 0000000..ed6b25b --- /dev/null +++ b/java/src/game/collect/Lists.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +public final class Lists { + public static ArrayList newArrayList() { + return new ArrayList(); + } + + public static ArrayList newArrayList(E... elements) { + // Avoid integer overflow when a large array is passed in + int capacity = 5 + elements.length + (elements.length / 10); + ArrayList list = new ArrayList(capacity); + Collections.addAll(list, elements); + return list; + } + + public static ArrayList newArrayList(Iterable elements) { + // Let ArrayList's sizing logic work, if possible + return (elements instanceof Collection) + ? new ArrayList(Filter.cast(elements)) + : newArrayList(elements.iterator()); + } + + public static ArrayList newArrayList(Iterator elements) { + ArrayList list = newArrayList(); + Iterators.addAll(list, elements); + return list; + } +} diff --git a/java/src/game/collect/Maps.java b/java/src/game/collect/Maps.java new file mode 100644 index 0000000..36f0b4d --- /dev/null +++ b/java/src/game/collect/Maps.java @@ -0,0 +1,3953 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkNonnegative; +import static game.collect.Preconditions.checkNotNull; +import static game.util.Predicates.compose; + +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Static utility methods pertaining to {@link Map} instances (including instances of + * {@link SortedMap}, {@link BiMap}, etc.). Also see this class's counterparts + * {@link Lists}, {@link Sets} and {@link Queues}. + * + *

See the Guava User Guide article on + * {@code Maps}. + * + * @author Kevin Bourrillion + * @author Mike Bostock + * @author Isaac Shum + * @author Louis Wasserman + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Maps { + private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2); + + private Maps() {} + + private enum EntryFunction implements Function, Object> { + KEY { + @Override + + public Object apply(Entry entry) { + return entry.getKey(); + } + }, + VALUE { + @Override + + public Object apply(Entry entry) { + return entry.getValue(); + } + }; + } + + + static Function, K> keyFunction() { + return (Function) EntryFunction.KEY; + } + + + static Function, V> valueFunction() { + return (Function) EntryFunction.VALUE; + } + + static Iterator keyIterator(Iterator> entryIterator) { + return Iterators.transform(entryIterator, Maps.keyFunction()); + } + + static Iterator valueIterator(Iterator> entryIterator) { + return Iterators.transform(entryIterator, Maps.valueFunction()); + } + + static UnmodifiableIterator valueIterator( + final UnmodifiableIterator> entryIterator) { + return new UnmodifiableIterator() { + @Override + public boolean hasNext() { + return entryIterator.hasNext(); + } + + @Override + public V next() { + return entryIterator.next().getValue(); + } + }; + } + + /** + * Returns an immutable map instance containing the given entries. + * Internally, the returned map will be backed by an {@link EnumMap}. + * + *

The iteration order of the returned map follows the enum's iteration + * order, not the order in which the elements appear in the given map. + * + * @param map the map to make an immutable copy of + * @return an immutable map containing those entries + * @since 14.0 + */ +// +// @Beta +// public static , V> ImmutableMap immutableEnumMap( +// Map map) { +// if (map instanceof ImmutableEnumMap) { +// // safe covariant cast +// ImmutableEnumMap result = (ImmutableEnumMap) map; +// return result; +// } else if (map.isEmpty()) { +// return ImmutableMap.of(); +// } else { +// for (Map.Entry entry : map.entrySet()) { +// checkNotNull(entry.getKey()); +// checkNotNull(entry.getValue()); +// } +// return ImmutableEnumMap.asImmutable(new EnumMap(map)); +// } +// } + + /** + * Creates a mutable, empty {@code HashMap} instance. + * + *

Note: if mutability is not required, use {@link + * ImmutableMap#of()} instead. + * + *

Note: if {@code K} is an {@code enum} type, use {@link + * #newEnumMap} instead. + * + * @return a new, empty {@code HashMap} + */ + public static HashMap newHashMap() { + return new HashMap(); + } + + /** + * Creates a {@code HashMap} instance, with a high enough "initial capacity" + * that it should hold {@code expectedSize} elements without growth. + * This behavior cannot be broadly guaranteed, but it is observed to be true + * for OpenJDK 1.6. It also can't be guaranteed that the method isn't + * inadvertently oversizing the returned map. + * + * @param expectedSize the number of elements you expect to add to the + * returned map + * @return a new, empty {@code HashMap} with enough capacity to hold {@code + * expectedSize} elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static HashMap newHashMapWithExpectedSize( + int expectedSize) { + return new HashMap(capacity(expectedSize)); + } + + /** + * Returns a capacity that is sufficient to keep the map from being resized as + * long as it grows no larger than expectedSize and the load factor is >= its + * default (0.75). + */ + static int capacity(int expectedSize) { + if (expectedSize < 3) { + checkNonnegative(expectedSize, "expectedSize"); + return expectedSize + 1; + } + if (expectedSize < MAX_POWER_OF_TWO) { + return expectedSize + expectedSize / 3; + } + return Integer.MAX_VALUE; // any large value + } + + /** + * Creates a mutable {@code HashMap} instance with the same mappings as + * the specified map. + * + *

Note: if mutability is not required, use {@link + * ImmutableMap#copyOf(Map)} instead. + * + *

Note: if {@code K} is an {@link Enum} type, use {@link + * #newEnumMap} instead. + * + * @param map the mappings to be placed in the new map + * @return a new {@code HashMap} initialized with the mappings from {@code + * map} + */ + public static HashMap newHashMap( + Map map) { + return new HashMap(map); + } + + /** + * Creates a mutable, empty, insertion-ordered {@code LinkedHashMap} + * instance. + * + *

Note: if mutability is not required, use {@link + * ImmutableMap#of()} instead. + * + * @return a new, empty {@code LinkedHashMap} + */ + public static LinkedHashMap newLinkedHashMap() { + return new LinkedHashMap(); + } + + /** + * Creates a mutable, insertion-ordered {@code LinkedHashMap} instance + * with the same mappings as the specified map. + * + *

Note: if mutability is not required, use {@link + * ImmutableMap#copyOf(Map)} instead. + * + * @param map the mappings to be placed in the new map + * @return a new, {@code LinkedHashMap} initialized with the mappings from + * {@code map} + */ + public static LinkedHashMap newLinkedHashMap( + Map map) { + return new LinkedHashMap(map); + } + + /** + * Returns a general-purpose instance of {@code ConcurrentMap}, which supports + * all optional operations of the ConcurrentMap interface. It does not permit + * null keys or values. It is serializable. + * + *

This is currently accomplished by calling {@link MapMaker#makeMap()}. + * + *

It is preferable to use {@code MapMaker} directly (rather than through + * this method), as it presents numerous useful configuration options, + * such as the concurrency level, load factor, key/value reference types, + * and value computation. + * + * @return a new, empty {@code ConcurrentMap} + * @since 3.0 + */ +// public static ConcurrentMap newConcurrentMap() { +// return new MapMaker().makeMap(); +// } + + /** + * Creates a mutable, empty {@code TreeMap} instance using the natural + * ordering of its elements. + * + *

Note: if mutability is not required, use {@link + * ImmutableSortedMap#of()} instead. + * + * @return a new, empty {@code TreeMap} + */ + public static TreeMap newTreeMap() { + return new TreeMap(); + } + + /** + * Creates a mutable {@code TreeMap} instance with the same mappings as + * the specified map and using the same ordering as the specified map. + * + *

Note: if mutability is not required, use {@link + * ImmutableSortedMap#copyOfSorted(SortedMap)} instead. + * + * @param map the sorted map whose mappings are to be placed in the new map + * and whose comparator is to be used to sort the new map + * @return a new {@code TreeMap} initialized with the mappings from {@code + * map} and using the comparator of {@code map} + */ +// public static TreeMap newTreeMap(SortedMap map) { +// return new TreeMap(map); +// } + + /** + * Creates a mutable, empty {@code TreeMap} instance using the given + * comparator. + * + *

Note: if mutability is not required, use {@code + * ImmutableSortedMap.orderedBy(comparator).build()} instead. + * + * @param comparator the comparator to sort the keys with + * @return a new, empty {@code TreeMap} + */ +// public static TreeMap newTreeMap( +// Comparator comparator) { +// // Ideally, the extra type parameter "C" shouldn't be necessary. It is a +// // work-around of a compiler type inference quirk that prevents the +// // following code from being compiled: +// // Comparator> comparator = null; +// // Map, String> map = newTreeMap(comparator); +// return new TreeMap(comparator); +// } + + /** + * Creates an {@code EnumMap} instance. + * + * @param type the key type for this map + * @return a new, empty {@code EnumMap} + */ + public static , V> EnumMap newEnumMap(Class type) { + return new EnumMap(checkNotNull(type)); + } + + /** + * Creates an {@code EnumMap} with the same mappings as the specified map. + * + * @param map the map from which to initialize this {@code EnumMap} + * @return a new {@code EnumMap} initialized with the mappings from {@code + * map} + * @throws IllegalArgumentException if {@code m} is not an {@code EnumMap} + * instance and contains no mappings + */ +// public static , V> EnumMap newEnumMap( +// Map map) { +// return new EnumMap(map); +// } + + /** + * Creates an {@code IdentityHashMap} instance. + * + * @return a new, empty {@code IdentityHashMap} + */ + public static IdentityHashMap newIdentityHashMap() { + return new IdentityHashMap(); + } + + /** + * Computes the difference between two maps. This difference is an immutable + * snapshot of the state of the maps at the time this method is called. It + * will never change, even if the maps change at a later time. + * + *

Since this method uses {@code HashMap} instances internally, the keys of + * the supplied maps must be well-behaved with respect to + * {@link Object#equals} and {@link Object#hashCode}. + * + *

Note:If you only need to know whether two maps have the same + * mappings, call {@code left.equals(right)} instead of this method. + * + * @param left the map to treat as the "left" map for purposes of comparison + * @param right the map to treat as the "right" map for purposes of comparison + * @return the difference between the two maps + */ +// +// public static MapDifference difference( +// Map left, Map right) { +// if (left instanceof SortedMap) { +// SortedMap sortedLeft = (SortedMap) left; +// SortedMapDifference result = difference(sortedLeft, right); +// return result; +// } +// return difference(left, right, Equivalence.equals()); +// } + + /** + * Computes the difference between two maps. This difference is an immutable + * snapshot of the state of the maps at the time this method is called. It + * will never change, even if the maps change at a later time. + * + *

Values are compared using a provided equivalence, in the case of + * equality, the value on the 'left' is returned in the difference. + * + *

Since this method uses {@code HashMap} instances internally, the keys of + * the supplied maps must be well-behaved with respect to + * {@link Object#equals} and {@link Object#hashCode}. + * + * @param left the map to treat as the "left" map for purposes of comparison + * @param right the map to treat as the "right" map for purposes of comparison + * @param valueEquivalence the equivalence relationship to use to compare + * values + * @return the difference between the two maps + * @since 10.0 + */ +// @Beta +// public static MapDifference difference( +// Map left, Map right, +// Equivalence valueEquivalence) { +// Preconditions.checkNotNull(valueEquivalence); +// +// Map onlyOnLeft = newHashMap(); +// Map onlyOnRight = new HashMap(right); // will whittle it down +// Map onBoth = newHashMap(); +// Map> differences = newHashMap(); +// doDifference(left, right, valueEquivalence, onlyOnLeft, onlyOnRight, onBoth, differences); +// return new MapDifferenceImpl(onlyOnLeft, onlyOnRight, onBoth, differences); +// } +// +// private static void doDifference( +// Map left, Map right, +// Equivalence valueEquivalence, +// Map onlyOnLeft, Map onlyOnRight, Map onBoth, +// Map> differences) { +// for (Entry entry : left.entrySet()) { +// 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, ValueDifferenceImpl.create(leftValue, rightValue)); +// } +// } else { +// onlyOnLeft.put(leftKey, leftValue); +// } +// } +// } +// +// private static Map unmodifiableMap(Map map) { +// if (map instanceof SortedMap) { +// return Collections.unmodifiableSortedMap((SortedMap) map); +// } else { +// return Collections.unmodifiableMap(map); +// } +// } +// +// 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 = unmodifiableMap(onlyOnLeft); +// this.onlyOnRight = unmodifiableMap(onlyOnRight); +// this.onBoth = unmodifiableMap(onBoth); +// this.differences = unmodifiableMap(differences); +// } +// +// @Override +// public boolean areEqual() { +// return onlyOnLeft.isEmpty() && onlyOnRight.isEmpty() && differences.isEmpty(); +// } +// +// @Override +// public Map entriesOnlyOnLeft() { +// return onlyOnLeft; +// } +// +// @Override +// public Map entriesOnlyOnRight() { +// return onlyOnRight; +// } +// +// @Override +// public Map entriesInCommon() { +// return onBoth; +// } +// +// @Override +// public Map> entriesDiffering() { +// return differences; +// } +// +// @Override public boolean equals(Object object) { +// if (object == this) { +// return true; +// } +// if (object instanceof MapDifference) { +// MapDifference other = (MapDifference) object; +// return entriesOnlyOnLeft().equals(other.entriesOnlyOnLeft()) +// && entriesOnlyOnRight().equals(other.entriesOnlyOnRight()) +// && entriesInCommon().equals(other.entriesInCommon()) +// && entriesDiffering().equals(other.entriesDiffering()); +// } +// return false; +// } +// +// @Override public int hashCode() { +// return Objects.hashCode(entriesOnlyOnLeft(), entriesOnlyOnRight(), +// entriesInCommon(), entriesDiffering()); +// } +// +// @Override public String toString() { +// if (areEqual()) { +// return "equal"; +// } +// +// StringBuilder result = new StringBuilder("not equal"); +// if (!onlyOnLeft.isEmpty()) { +// result.append(": only on left=").append(onlyOnLeft); +// } +// if (!onlyOnRight.isEmpty()) { +// result.append(": only on right=").append(onlyOnRight); +// } +// if (!differences.isEmpty()) { +// result.append(": value differences=").append(differences); +// } +// return result.toString(); +// } +// } +// +// static class ValueDifferenceImpl +// implements MapDifference.ValueDifference { +// private final V left; +// private final V right; +// +// static ValueDifference create(V left, V right) { +// return new ValueDifferenceImpl(left, right); +// } +// +// private ValueDifferenceImpl(V left, V right) { +// this.left = left; +// this.right = right; +// } +// +// @Override +// public V leftValue() { +// return left; +// } +// +// @Override +// public V rightValue() { +// return right; +// } +// +// @Override public boolean equals(Object object) { +// if (object instanceof MapDifference.ValueDifference) { +// MapDifference.ValueDifference that = +// (MapDifference.ValueDifference) object; +// return Objects.equal(this.left, that.leftValue()) +// && Objects.equal(this.right, that.rightValue()); +// } +// return false; +// } +// +// @Override public int hashCode() { +// return Objects.hashCode(left, right); +// } +// +// @Override public String toString() { +// return "(" + left + ", " + right + ")"; +// } +// } +// +// /** +// * Computes the difference between two sorted maps, using the comparator of +// * the left map, or {@code Ordering.natural()} if the left map uses the +// * natural ordering of its elements. This difference is an immutable snapshot +// * of the state of the maps at the time this method is called. It will never +// * change, even if the maps change at a later time. +// * +// *

Since this method uses {@code TreeMap} instances internally, the keys of +// * the right map must all compare as distinct according to the comparator +// * of the left map. +// * +// *

Note:If you only need to know whether two sorted maps have the +// * same mappings, call {@code left.equals(right)} instead of this method. +// * +// * @param left the map to treat as the "left" map for purposes of comparison +// * @param right the map to treat as the "right" map for purposes of comparison +// * @return the difference between the two maps +// * @since 11.0 +// */ +// public static SortedMapDifference difference( +// SortedMap left, Map right) { +// checkNotNull(left); +// checkNotNull(right); +// Comparator comparator = orNaturalOrder(left.comparator()); +// SortedMap onlyOnLeft = Maps.newTreeMap(comparator); +// SortedMap onlyOnRight = Maps.newTreeMap(comparator); +// onlyOnRight.putAll(right); // will whittle it down +// SortedMap onBoth = Maps.newTreeMap(comparator); +// SortedMap> differences = +// Maps.newTreeMap(comparator); +// doDifference(left, right, Equivalence.equals(), onlyOnLeft, onlyOnRight, onBoth, differences); +// return new SortedMapDifferenceImpl(onlyOnLeft, onlyOnRight, onBoth, differences); +// } +// +// static class SortedMapDifferenceImpl extends MapDifferenceImpl +// implements SortedMapDifference { +// SortedMapDifferenceImpl(SortedMap onlyOnLeft, +// SortedMap onlyOnRight, SortedMap onBoth, +// SortedMap> differences) { +// super(onlyOnLeft, onlyOnRight, onBoth, differences); +// } +// +// @Override public SortedMap> entriesDiffering() { +// return (SortedMap>) super.entriesDiffering(); +// } +// +// @Override public SortedMap entriesInCommon() { +// return (SortedMap) super.entriesInCommon(); +// } +// +// @Override public SortedMap entriesOnlyOnLeft() { +// return (SortedMap) super.entriesOnlyOnLeft(); +// } +// +// @Override public SortedMap entriesOnlyOnRight() { +// return (SortedMap) super.entriesOnlyOnRight(); +// } +// } + + /** + * Returns the specified comparator if not null; otherwise returns {@code + * Ordering.natural()}. This method is an abomination of generics; the only + * purpose of this method is to contain the ugly type-casting in one place. + */ +// +// static Comparator orNaturalOrder( +// Comparator comparator) { +// if (comparator != null) { // can't use ? : because of javac bug 5080917 +// return comparator; +// } +// return (Comparator) Ordering.natural(); +// } + + /** + * Returns a live {@link Map} view whose keys are the contents of {@code set} + * and whose values are computed on demand using {@code function}. To get an + * immutable copy instead, use {@link #toMap(Iterable, Function)}. + * + *

Specifically, for each {@code k} in the backing set, the returned map + * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code + * keySet}, {@code values}, and {@code entrySet} views of the returned map + * iterate in the same order as the backing set. + * + *

Modifications to the backing set are read through to the returned map. + * The returned map supports removal operations if the backing set does. + * Removal operations write through to the backing set. The returned map + * does not support put operations. + * + *

Warning: If the function rejects {@code null}, caution is + * required to make sure the set does not contain {@code null}, because the + * view cannot stop {@code null} from being added to the set. + * + *

Warning: This method assumes that for any instance {@code k} of + * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also + * of type {@code K}. Using a key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling + * methods on the resulting map view. + * + * @since 14.0 + */ +// @Beta +// public static Map asMap( +// Set set, Function function) { +// if (set instanceof SortedSet) { +// return asMap((SortedSet) set, function); +// } else { +// return new AsMapView(set, function); +// } +// } + + /** + * Returns a view of the sorted set as a map, mapping keys from the set + * according to the specified function. + * + *

Specifically, for each {@code k} in the backing set, the returned map + * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code + * keySet}, {@code values}, and {@code entrySet} views of the returned map + * iterate in the same order as the backing set. + * + *

Modifications to the backing set are read through to the returned map. + * The returned map supports removal operations if the backing set does. + * Removal operations write through to the backing set. The returned map does + * not support put operations. + * + *

Warning: If the function rejects {@code null}, caution is + * required to make sure the set does not contain {@code null}, because the + * view cannot stop {@code null} from being added to the set. + * + *

Warning: This method assumes that for any instance {@code k} of + * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of + * type {@code K}. Using a key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling + * methods on the resulting map view. + * + * @since 14.0 + */ +// @Beta +// public static SortedMap asMap( +// SortedSet set, Function function) { +// return Platform.mapsAsMapSortedSet(set, function); +// } + +// static SortedMap asMapSortedIgnoreNavigable(SortedSet set, +// Function function) { +// return new SortedAsMapView(set, function); +// } + + /** + * Returns a view of the navigable set as a map, mapping keys from the set + * according to the specified function. + * + *

Specifically, for each {@code k} in the backing set, the returned map + * has an entry mapping {@code k} to {@code function.apply(k)}. The {@code + * keySet}, {@code values}, and {@code entrySet} views of the returned map + * iterate in the same order as the backing set. + * + *

Modifications to the backing set are read through to the returned map. + * The returned map supports removal operations if the backing set does. + * Removal operations write through to the backing set. The returned map + * does not support put operations. + * + *

Warning: If the function rejects {@code null}, caution is + * required to make sure the set does not contain {@code null}, because the + * view cannot stop {@code null} from being added to the set. + * + *

Warning: This method assumes that for any instance {@code k} of + * key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also + * of type {@code K}. Using a key type for which this may not hold, such as + * {@code ArrayList}, may risk a {@code ClassCastException} when calling + * methods on the resulting map view. + * + * @since 14.0 + */ +// @Beta +// +// public static NavigableMap asMap( +// NavigableSet set, Function function) { +// return new NavigableAsMapView(set, function); +// } + +// private static class AsMapView extends ImprovedAbstractMap { +// +// private final Set set; +// final Function function; +// +// Set backingSet() { +// return set; +// } +// +// AsMapView(Set set, Function function) { +// this.set = checkNotNull(set); +// this.function = checkNotNull(function); +// } +// +// @Override +// public Set createKeySet() { +// return removeOnlySet(backingSet()); +// } +// +// @Override +// Collection createValues() { +// return Filter.transform(set, function); +// } +// +// @Override +// public int size() { +// return backingSet().size(); +// } +// +// @Override +// public boolean containsKey(Object key) { +// return backingSet().contains(key); +// } +// +// @Override +// public V get(Object key) { +// if (Filter.safeContains(backingSet(), key)) { +// // unsafe, but Javadoc warns about it +// K k = (K) key; +// return function.apply(k); +// } else { +// return null; +// } +// } +// +// @Override +// public V remove(Object key) { +// if (backingSet().remove(key)) { +// // unsafe, but Javadoc warns about it +// K k = (K) key; +// return function.apply(k); +// } else { +// return null; +// } +// } +// +// @Override +// public void clear() { +// backingSet().clear(); +// } +// +// @Override +// protected Set> createEntrySet() { +// return new EntrySet() { +// @Override +// Map map() { +// return AsMapView.this; +// } +// +// @Override +// public Iterator> iterator() { +// return asMapEntryIterator(backingSet(), function); +// } +// }; +// } +// } + + static Iterator> asMapEntryIterator( + Set set, final Function function) { + return new TransformedIterator>(set.iterator()) { + @Override + Entry transform(final K key) { + return immutableEntry(key, function.apply(key)); + } + }; + } + +// private static class SortedAsMapView extends AsMapView +// implements SortedMap { +// +// SortedAsMapView(SortedSet set, Function function) { +// super(set, function); +// } +// +// @Override +// SortedSet backingSet() { +// return (SortedSet) super.backingSet(); +// } +// +// @Override +// public Comparator comparator() { +// return backingSet().comparator(); +// } +// +// @Override +// public Set keySet() { +// return removeOnlySortedSet(backingSet()); +// } +// +// @Override +// public SortedMap subMap(K fromKey, K toKey) { +// return asMap(backingSet().subSet(fromKey, toKey), function); +// } +// +// @Override +// public SortedMap headMap(K toKey) { +// return asMap(backingSet().headSet(toKey), function); +// } +// +// @Override +// public SortedMap tailMap(K fromKey) { +// return asMap(backingSet().tailSet(fromKey), function); +// } +// +// @Override +// public K firstKey() { +// return backingSet().first(); +// } +// +// @Override +// public K lastKey() { +// return backingSet().last(); +// } +// } + +// +// private static final class NavigableAsMapView +// extends AbstractNavigableMap { +// /* +// * Using AbstractNavigableMap is simpler than extending SortedAsMapView and rewriting all the +// * NavigableMap methods. +// */ +// +// private final NavigableSet set; +// private final Function function; +// +// NavigableAsMapView(NavigableSet ks, Function vFunction) { +// this.set = checkNotNull(ks); +// this.function = checkNotNull(vFunction); +// } +// +// @Override +// public NavigableMap subMap( +// K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { +// return asMap(set.subSet(fromKey, fromInclusive, toKey, toInclusive), function); +// } +// +// @Override +// public NavigableMap headMap(K toKey, boolean inclusive) { +// return asMap(set.headSet(toKey, inclusive), function); +// } +// +// @Override +// public NavigableMap tailMap(K fromKey, boolean inclusive) { +// return asMap(set.tailSet(fromKey, inclusive), function); +// } +// +// @Override +// public Comparator comparator() { +// return set.comparator(); +// } +// +// @Override +// +// public V get(Object key) { +// if (Filter.safeContains(set, key)) { +// // unsafe, but Javadoc warns about it +// K k = (K) key; +// return function.apply(k); +// } else { +// return null; +// } +// } +// +// @Override +// public void clear() { +// set.clear(); +// } +// +// @Override +// Iterator> entryIterator() { +// return asMapEntryIterator(set, function); +// } +// +// @Override +// Iterator> descendingEntryIterator() { +// return descendingMap().entrySet().iterator(); +// } +// +// @Override +// public NavigableSet navigableKeySet() { +// return removeOnlyNavigableSet(set); +// } +// +// @Override +// public int size() { +// return set.size(); +// } +// +// @Override +// public NavigableMap descendingMap() { +// return asMap(set.descendingSet(), function); +// } +// } + +// private static Set removeOnlySet(final Set set) { +// return new ForwardingSet() { +// @Override +// protected Set delegate() { +// return set; +// } +// +// @Override +// public boolean add(E element) { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public boolean addAll(Collection es) { +// throw new UnsupportedOperationException(); +// } +// }; +// } + +// private static SortedSet removeOnlySortedSet(final SortedSet set) { +// return new ForwardingSortedSet() { +// @Override +// protected SortedSet delegate() { +// return set; +// } +// +// @Override +// public boolean add(E element) { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public boolean addAll(Collection es) { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public SortedSet headSet(E toElement) { +// return removeOnlySortedSet(super.headSet(toElement)); +// } +// +// @Override +// public SortedSet subSet(E fromElement, E toElement) { +// return removeOnlySortedSet(super.subSet(fromElement, toElement)); +// } +// +// @Override +// public SortedSet tailSet(E fromElement) { +// return removeOnlySortedSet(super.tailSet(fromElement)); +// } +// }; +// } + +// +// private static NavigableSet removeOnlyNavigableSet(final NavigableSet set) { +// return new ForwardingNavigableSet() { +// @Override +// protected NavigableSet delegate() { +// return set; +// } +// +// @Override +// public boolean add(E element) { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public boolean addAll(Collection es) { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public SortedSet headSet(E toElement) { +// return removeOnlySortedSet(super.headSet(toElement)); +// } +// +// @Override +// public SortedSet subSet(E fromElement, E toElement) { +// return removeOnlySortedSet( +// super.subSet(fromElement, toElement)); +// } +// +// @Override +// public SortedSet tailSet(E fromElement) { +// return removeOnlySortedSet(super.tailSet(fromElement)); +// } +// +// @Override +// public NavigableSet headSet(E toElement, boolean inclusive) { +// return removeOnlyNavigableSet(super.headSet(toElement, inclusive)); +// } +// +// @Override +// public NavigableSet tailSet(E fromElement, boolean inclusive) { +// return removeOnlyNavigableSet(super.tailSet(fromElement, inclusive)); +// } +// +// @Override +// public NavigableSet subSet(E fromElement, boolean fromInclusive, +// E toElement, boolean toInclusive) { +// return removeOnlyNavigableSet(super.subSet( +// fromElement, fromInclusive, toElement, toInclusive)); +// } +// +// @Override +// public NavigableSet descendingSet() { +// return removeOnlyNavigableSet(super.descendingSet()); +// } +// }; +// } + + /** + * Returns an immutable map whose keys are the distinct elements of {@code + * keys} and whose value for each key was computed by {@code valueFunction}. + * The map's iteration order is the order of the first appearance of each key + * in {@code keys}. + * + *

If {@code keys} is a {@link Set}, a live view can be obtained instead of + * a copy using {@link Maps#asMap(Set, Function)}. + * + * @throws NullPointerException if any element of {@code keys} is + * {@code null}, or if {@code valueFunction} produces {@code null} + * for any key + * @since 14.0 + */ +// @Beta +// public static ImmutableMap toMap(Iterable keys, +// Function valueFunction) { +// return toMap(keys.iterator(), valueFunction); +// } + + /** + * Returns an immutable map whose keys are the distinct elements of {@code + * keys} and whose value for each key was computed by {@code valueFunction}. + * The map's iteration order is the order of the first appearance of each key + * in {@code keys}. + * + * @throws NullPointerException if any element of {@code keys} is + * {@code null}, or if {@code valueFunction} produces {@code null} + * for any key + * @since 14.0 + */ +// @Beta +// public static ImmutableMap toMap(Iterator keys, +// Function valueFunction) { +// checkNotNull(valueFunction); +// // Using LHM instead of a builder so as not to fail on duplicate keys +// Map builder = newLinkedHashMap(); +// while (keys.hasNext()) { +// K key = keys.next(); +// builder.put(key, valueFunction.apply(key)); +// } +// return ImmutableMap.copyOf(builder); +// } + + /** + * Returns an immutable map for which the {@link Map#values} are the given + * elements in the given order, and each key is the product of invoking a + * supplied function on its corresponding value. + * + * @param values the values to use when constructing the {@code Map} + * @param keyFunction the function used to produce the key for each value + * @return a map mapping the result of evaluating the function {@code + * keyFunction} on each value in the input collection to that value + * @throws IllegalArgumentException if {@code keyFunction} produces the same + * key for more than one value in the input collection + * @throws NullPointerException if any elements of {@code values} is null, or + * if {@code keyFunction} produces {@code null} for any value + */ +// public static ImmutableMap uniqueIndex( +// Iterable values, Function keyFunction) { +// return uniqueIndex(values.iterator(), keyFunction); +// } + + /** + * Returns an immutable map for which the {@link Map#values} are the given + * elements in the given order, and each key is the product of invoking a + * supplied function on its corresponding value. + * + * @param values the values to use when constructing the {@code Map} + * @param keyFunction the function used to produce the key for each value + * @return a map mapping the result of evaluating the function {@code + * keyFunction} on each value in the input collection to that value + * @throws IllegalArgumentException if {@code keyFunction} produces the same + * key for more than one value in the input collection + * @throws NullPointerException if any elements of {@code values} is null, or + * if {@code keyFunction} produces {@code null} for any value + * @since 10.0 + */ +// public static ImmutableMap uniqueIndex( +// Iterator values, Function keyFunction) { +// checkNotNull(keyFunction); +// ImmutableMap.Builder builder = ImmutableMap.builder(); +// while (values.hasNext()) { +// V value = values.next(); +// builder.put(keyFunction.apply(value), value); +// } +// return builder.build(); +// } + + /** + * Creates an {@code ImmutableMap} from a {@code Properties} + * instance. Properties normally derive from {@code Map}, but + * they typically contain strings, which is awkward. This method lets you get + * a plain-old-{@code Map} out of a {@code Properties}. + * + * @param properties a {@code Properties} object to be converted + * @return an immutable map containing all the entries in {@code properties} + * @throws ClassCastException if any key in {@code Properties} is not a {@code + * String} + * @throws NullPointerException if any key or value in {@code Properties} is + * null + */ +// +// public static ImmutableMap fromProperties( +// Properties properties) { +// ImmutableMap.Builder builder = ImmutableMap.builder(); +// +// for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) { +// String key = (String) e.nextElement(); +// builder.put(key, properties.getProperty(key)); +// } +// +// return builder.build(); +// } + + /** + * Returns an immutable map entry with the specified key and value. The {@link + * Entry#setValue} operation throws an {@link UnsupportedOperationException}. + * + *

The returned entry is serializable. + * + * @param key the key to be associated with the returned entry + * @param value the value to be associated with the returned entry + */ + + public static Entry immutableEntry( + K key, V value) { + return new ImmutableEntry(key, value); + } + + /** + * Returns an unmodifiable view of the specified set of entries. The {@link + * Entry#setValue} operation throws an {@link UnsupportedOperationException}, + * as do any operations that would modify the returned set. + * + * @param entrySet the entries for which to return an unmodifiable view + * @return an unmodifiable view of the entries + */ +// static Set> unmodifiableEntrySet( +// Set> entrySet) { +// return new UnmodifiableEntrySet( +// Collections.unmodifiableSet(entrySet)); +// } + + /** + * Returns an unmodifiable view of the specified map entry. The {@link + * Entry#setValue} operation throws an {@link UnsupportedOperationException}. + * This also has the side-effect of redefining {@code equals} to comply with + * the Entry contract, to avoid a possible nefarious implementation of equals. + * + * @param entry the entry for which to return an unmodifiable view + * @return an unmodifiable view of the entry + */ + static Entry unmodifiableEntry(final Entry entry) { + checkNotNull(entry); + return new AbstractMapEntry() { + @Override public K getKey() { + return entry.getKey(); + } + + @Override public V getValue() { + return entry.getValue(); + } + }; + } + + /** @see Multimaps#unmodifiableEntries */ + static class UnmodifiableEntries + extends ForwardingCollection> { + private final Collection> entries; + + UnmodifiableEntries(Collection> entries) { + this.entries = entries; + } + + @Override protected Collection> delegate() { + return entries; + } + + @Override public Iterator> iterator() { + final Iterator> delegate = super.iterator(); + return new UnmodifiableIterator>() { + @Override + public boolean hasNext() { + return delegate.hasNext(); + } + + @Override public Entry next() { + return unmodifiableEntry(delegate.next()); + } + }; + } + + // See java.util.Collections.UnmodifiableEntrySet for details on attacks. + + @Override public Object[] toArray() { + return standardToArray(); + } + + @Override public T[] toArray(T[] array) { + return standardToArray(array); + } + } + + /** @see Maps#unmodifiableEntrySet(Set) */ +// static class UnmodifiableEntrySet +// extends UnmodifiableEntries implements Set> { +// UnmodifiableEntrySet(Set> entries) { +// super(entries); +// } +// +// // See java.util.Collections.UnmodifiableEntrySet for details on attacks. +// +// @Override public boolean equals(Object object) { +// return Sets.equalsImpl(this, object); +// } +// +// @Override public int hashCode() { +// return Sets.hashCodeImpl(this); +// } +// } + + /** + * Returns a {@link Converter} that converts values using {@link BiMap#get bimap.get()}, + * and whose inverse view converts values using + * {@link BiMap#inverse bimap.inverse()}{@code .get()}. + * + *

To use a plain {@link Map} as a {@link Function}, see + * {@link java.util.function.Functions#forMap(Map)} or + * {@link java.util.function.Functions#forMap(Map, Object)}. + * + * @since 16.0 + */ +// @Beta +// public static Converter asConverter(final BiMap bimap) { +// return new BiMapConverter(bimap); +// } +// +// private static final class BiMapConverter extends Converter implements Serializable { +// private final BiMap bimap; +// +// BiMapConverter(BiMap bimap) { +// this.bimap = checkNotNull(bimap); +// } +// +// @Override +// protected B doForward(A a) { +// return convert(bimap, a); +// } +// +// @Override +// protected A doBackward(B b) { +// return convert(bimap.inverse(), b); +// } +// +// private static Y convert(BiMap bimap, X input) { +// Y output = bimap.get(input); +// checkArgument(output != null, "No non-null mapping present for input: %s", input); +// return output; +// } +// +// @Override +// public boolean equals(Object object) { +// if (object instanceof BiMapConverter) { +// BiMapConverter that = (BiMapConverter) object; +// return this.bimap.equals(that.bimap); +// } +// return false; +// } +// +// @Override +// public int hashCode() { +// return bimap.hashCode(); +// } +// +// // There's really no good way to implement toString() without printing the entire BiMap, right? +// @Override +// public String toString() { +// return "Maps.asConverter(" + bimap + ")"; +// } +// +// private static final long serialVersionUID = 0L; +// } + + /** + * Returns a synchronized (thread-safe) bimap backed by the specified bimap. + * In order to guarantee serial access, it is critical that all access + * to the backing bimap is accomplished through the returned bimap. + * + *

It is imperative that the user manually synchronize on the returned map + * when accessing any of its collection views:

   {@code
+   *
+   *   BiMap map = Maps.synchronizedBiMap(
+   *       HashBiMap.create());
+   *   ...
+   *   Set set = map.keySet();  // Needn't be in synchronized block
+   *   ...
+   *   synchronized (map) {  // Synchronizing on map, not set!
+   *     Iterator it = set.iterator(); // Must be in synchronized block
+   *     while (it.hasNext()) {
+   *       foo(it.next());
+   *     }
+   *   }}
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned bimap will be serializable if the specified bimap is + * serializable. + * + * @param bimap the bimap to be wrapped in a synchronized view + * @return a sychronized view of the specified bimap + */ +// public static BiMap synchronizedBiMap(BiMap bimap) { +// return Synchronized.biMap(bimap, null); +// } + + /** + * Returns an unmodifiable view of the specified bimap. This method allows + * modules to provide users with "read-only" access to internal bimaps. Query + * operations on the returned bimap "read through" to the specified bimap, and + * attempts to modify the returned map, whether direct or via its collection + * views, result in an {@code UnsupportedOperationException}. + * + *

The returned bimap will be serializable if the specified bimap is + * serializable. + * + * @param bimap the bimap for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified bimap + */ +// public static BiMap unmodifiableBiMap( +// BiMap bimap) { +// return new UnmodifiableBiMap(bimap, null); +// } +// +// /** @see Maps#unmodifiableBiMap(BiMap) */ +// private static class UnmodifiableBiMap +// extends ForwardingMap implements BiMap, Serializable { +// final Map unmodifiableMap; +// final BiMap delegate; +// BiMap inverse; +// transient Set values; +// +// UnmodifiableBiMap(BiMap delegate, +// BiMap inverse) { +// unmodifiableMap = Collections.unmodifiableMap(delegate); +// this.delegate = delegate; +// this.inverse = inverse; +// } +// +// @Override protected Map delegate() { +// return unmodifiableMap; +// } +// +// @Override +// public V forcePut(K key, V value) { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public BiMap inverse() { +// BiMap result = inverse; +// return (result == null) +// ? inverse = new UnmodifiableBiMap(delegate.inverse(), this) +// : result; +// } +// +// @Override public Set values() { +// Set result = values; +// return (result == null) +// ? values = Collections.unmodifiableSet(delegate.values()) +// : result; +// } +// +// private static final long serialVersionUID = 0; +// } + + /** + * Returns a view of a map where each value is transformed by a function. All + * other properties of the map, such as iteration order, are left intact. For + * example, the code:

   {@code
+   *
+   *   Map map = ImmutableMap.of("a", 4, "b", 9);
+   *   Function sqrt =
+   *       new Function() {
+   *         public Double apply(Integer in) {
+   *           return Math.sqrt((int) in);
+   *         }
+   *       };
+   *   Map transformed = Maps.transformValues(map, sqrt);
+   *   System.out.println(transformed);}
+ * + * ... prints {@code {a=2.0, b=3.0}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + *

It's acceptable for the underlying map to contain null keys, and even + * null values provided that the function is capable of accepting null input. + * The transformed map might contain null values, if the function sometimes + * gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + *

The function is applied lazily, invoked when needed. This is necessary + * for the returned map to be a view, but it means that the function will be + * applied many times for bulk operations like {@link Map#containsValue} and + * {@code Map.toString()}. For this to perform well, {@code function} should + * be fast. To avoid lazy evaluation when the returned map doesn't need to be + * a view, copy the returned map into a new map of your choosing. + */ +// public static Map transformValues( +// Map fromMap, Function function) { +// return transformEntries(fromMap, asEntryTransformer(function)); +// } + + /** + * Returns a view of a sorted map where each value is transformed by a + * function. All other properties of the map, such as iteration order, are + * left intact. For example, the code:

   {@code
+   *
+   *   SortedMap map = ImmutableSortedMap.of("a", 4, "b", 9);
+   *   Function sqrt =
+   *       new Function() {
+   *         public Double apply(Integer in) {
+   *           return Math.sqrt((int) in);
+   *         }
+   *       };
+   *   SortedMap transformed =
+   *        Maps.transformValues(map, sqrt);
+   *   System.out.println(transformed);}
+ * + * ... prints {@code {a=2.0, b=3.0}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + *

It's acceptable for the underlying map to contain null keys, and even + * null values provided that the function is capable of accepting null input. + * The transformed map might contain null values, if the function sometimes + * gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + *

The function is applied lazily, invoked when needed. This is necessary + * for the returned map to be a view, but it means that the function will be + * applied many times for bulk operations like {@link Map#containsValue} and + * {@code Map.toString()}. For this to perform well, {@code function} should + * be fast. To avoid lazy evaluation when the returned map doesn't need to be + * a view, copy the returned map into a new map of your choosing. + * + * @since 11.0 + */ +// public static SortedMap transformValues( +// SortedMap fromMap, Function function) { +// return transformEntries(fromMap, asEntryTransformer(function)); +// } + + /** + * Returns a view of a navigable map where each value is transformed by a + * function. All other properties of the map, such as iteration order, are + * left intact. For example, the code:

   {@code
+   *
+   *   NavigableMap map = Maps.newTreeMap();
+   *   map.put("a", 4);
+   *   map.put("b", 9);
+   *   Function sqrt =
+   *       new Function() {
+   *         public Double apply(Integer in) {
+   *           return Math.sqrt((int) in);
+   *         }
+   *       };
+   *   NavigableMap transformed =
+   *        Maps.transformNavigableValues(map, sqrt);
+   *   System.out.println(transformed);}
+ * + * ... prints {@code {a=2.0, b=3.0}}. + * + * Changes in the underlying map are reflected in this view. + * Conversely, this view supports removal operations, and these are reflected + * in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys, and even + * null values provided that the function is capable of accepting null input. + * The transformed map might contain null values, if the function sometimes + * gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + *

The function is applied lazily, invoked when needed. This is necessary + * for the returned map to be a view, but it means that the function will be + * applied many times for bulk operations like {@link Map#containsValue} and + * {@code Map.toString()}. For this to perform well, {@code function} should + * be fast. To avoid lazy evaluation when the returned map doesn't need to be + * a view, copy the returned map into a new map of your choosing. + * + * @since 13.0 + */ +// +// public static NavigableMap transformValues( +// NavigableMap fromMap, Function function) { +// return transformEntries(fromMap, asEntryTransformer(function)); +// } + + /** + * Returns a view of a map whose values are derived from the original map's + * entries. In contrast to {@link #transformValues}, this method's + * entry-transformation logic may depend on the key as well as the value. + * + *

All other properties of the transformed map, such as iteration order, + * are left intact. For example, the code:

   {@code
+   *
+   *   Map options =
+   *       ImmutableMap.of("verbose", true, "sort", false);
+   *   EntryTransformer flagPrefixer =
+   *       new EntryTransformer() {
+   *         public String transformEntry(String key, Boolean value) {
+   *           return value ? key : "no" + key;
+   *         }
+   *       };
+   *   Map transformed =
+   *       Maps.transformEntries(options, flagPrefixer);
+   *   System.out.println(transformed);}
+ * + * ... prints {@code {verbose=verbose, sort=nosort}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + *

It's acceptable for the underlying map to contain null keys and null + * values provided that the transformer is capable of accepting null inputs. + * The transformed map might contain null values if the transformer sometimes + * gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + *

The transformer is applied lazily, invoked when needed. This is + * necessary for the returned map to be a view, but it means that the + * transformer will be applied many times for bulk operations like {@link + * Map#containsValue} and {@link Object#toString}. For this to perform well, + * {@code transformer} should be fast. To avoid lazy evaluation when the + * returned map doesn't need to be a view, copy the returned map into a new + * map of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of + * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies + * that {@code k2} is also of type {@code K}. Using an {@code + * EntryTransformer} key type for which this may not hold, such as {@code + * ArrayList}, may risk a {@code ClassCastException} when calling methods on + * the transformed map. + * + * @since 7.0 + */ +// public static Map transformEntries( +// Map fromMap, +// EntryTransformer transformer) { +// if (fromMap instanceof SortedMap) { +// return transformEntries((SortedMap) fromMap, transformer); +// } +// return new TransformedEntriesMap(fromMap, transformer); +// } + + /** + * Returns a view of a sorted map whose values are derived from the original + * sorted map's entries. In contrast to {@link #transformValues}, this + * method's entry-transformation logic may depend on the key as well as the + * value. + * + *

All other properties of the transformed map, such as iteration order, + * are left intact. For example, the code:

   {@code
+   *
+   *   Map options =
+   *       ImmutableSortedMap.of("verbose", true, "sort", false);
+   *   EntryTransformer flagPrefixer =
+   *       new EntryTransformer() {
+   *         public String transformEntry(String key, Boolean value) {
+   *           return value ? key : "yes" + key;
+   *         }
+   *       };
+   *   SortedMap transformed =
+   *       Maps.transformEntries(options, flagPrefixer);
+   *   System.out.println(transformed);}
+ * + * ... prints {@code {sort=yessort, verbose=verbose}}. + * + *

Changes in the underlying map are reflected in this view. Conversely, + * this view supports removal operations, and these are reflected in the + * underlying map. + * + *

It's acceptable for the underlying map to contain null keys and null + * values provided that the transformer is capable of accepting null inputs. + * The transformed map might contain null values if the transformer sometimes + * gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + *

The transformer is applied lazily, invoked when needed. This is + * necessary for the returned map to be a view, but it means that the + * transformer will be applied many times for bulk operations like {@link + * Map#containsValue} and {@link Object#toString}. For this to perform well, + * {@code transformer} should be fast. To avoid lazy evaluation when the + * returned map doesn't need to be a view, copy the returned map into a new + * map of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of + * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies + * that {@code k2} is also of type {@code K}. Using an {@code + * EntryTransformer} key type for which this may not hold, such as {@code + * ArrayList}, may risk a {@code ClassCastException} when calling methods on + * the transformed map. + * + * @since 11.0 + */ +// public static SortedMap transformEntries( +// SortedMap fromMap, +// EntryTransformer transformer) { +// return Platform.mapsTransformEntriesSortedMap(fromMap, transformer); +// } + + /** + * Returns a view of a navigable map whose values are derived from the + * original navigable map's entries. In contrast to {@link + * #transformValues}, this method's entry-transformation logic may + * depend on the key as well as the value. + * + *

All other properties of the transformed map, such as iteration order, + * are left intact. For example, the code:

   {@code
+   *
+   *   NavigableMap options = Maps.newTreeMap();
+   *   options.put("verbose", false);
+   *   options.put("sort", true);
+   *   EntryTransformer flagPrefixer =
+   *       new EntryTransformer() {
+   *         public String transformEntry(String key, Boolean value) {
+   *           return value ? key : ("yes" + key);
+   *         }
+   *       };
+   *   NavigableMap transformed =
+   *       LabsMaps.transformNavigableEntries(options, flagPrefixer);
+   *   System.out.println(transformed);}
+ * + * ... prints {@code {sort=yessort, verbose=verbose}}. + * + *

Changes in the underlying map are reflected in this view. + * Conversely, this view supports removal operations, and these are reflected + * in the underlying map. + * + *

It's acceptable for the underlying map to contain null keys and null + * values provided that the transformer is capable of accepting null inputs. + * The transformed map might contain null values if the transformer sometimes + * gives a null result. + * + *

The returned map is not thread-safe or serializable, even if the + * underlying map is. + * + *

The transformer is applied lazily, invoked when needed. This is + * necessary for the returned map to be a view, but it means that the + * transformer will be applied many times for bulk operations like {@link + * Map#containsValue} and {@link Object#toString}. For this to perform well, + * {@code transformer} should be fast. To avoid lazy evaluation when the + * returned map doesn't need to be a view, copy the returned map into a new + * map of your choosing. + * + *

Warning: This method assumes that for any instance {@code k} of + * {@code EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies + * that {@code k2} is also of type {@code K}. Using an {@code + * EntryTransformer} key type for which this may not hold, such as {@code + * ArrayList}, may risk a {@code ClassCastException} when calling methods on + * the transformed map. + * + * @since 13.0 + */ +// +// public static NavigableMap transformEntries( +// final NavigableMap fromMap, +// EntryTransformer transformer) { +// return new TransformedEntriesNavigableMap(fromMap, transformer); +// } +// +// static SortedMap transformEntriesIgnoreNavigable( +// SortedMap fromMap, +// EntryTransformer transformer) { +// return new TransformedEntriesSortedMap(fromMap, transformer); +// } + + /** + * A transformation of the value of a key-value pair, using both key and value + * as inputs. To apply the transformation to a map, use + * {@link Maps#transformEntries(Map, EntryTransformer)}. + * + * @param the key type of the input and output entries + * @param the value type of the input entry + * @param the value type of the output entry + * @since 7.0 + */ +// public interface EntryTransformer { +// /** +// * Determines an output value based on a key-value pair. This method is +// * generally expected, but not absolutely required, to have the +// * following properties: +// * +// *

    +// *
  • Its execution does not cause any observable side effects. +// *
  • The computation is consistent with equals; that is, +// * {@link Objects#equal Objects.equal}{@code (k1, k2) &&} +// * {@link Objects#equal}{@code (v1, v2)} implies that {@code +// * Objects.equal(transformer.transform(k1, v1), +// * transformer.transform(k2, v2))}. +// *
+// * +// * @throws NullPointerException if the key or value is null and this +// * transformer does not accept null arguments +// */ +// V2 transformEntry(K key, V1 value); +// } + + /** + * Views a function as an entry transformer that ignores the entry key. + */ +// static EntryTransformer +// asEntryTransformer(final Function function) { +// checkNotNull(function); +// return new EntryTransformer() { +// @Override +// public V2 transformEntry(K key, V1 value) { +// return function.apply(value); +// } +// }; +// } + +// static Function asValueToValueFunction( +// final EntryTransformer transformer, final K key) { +// checkNotNull(transformer); +// return new Function() { +// @Override +// public V2 apply(V1 v1) { +// return transformer.transformEntry(key, v1); +// } +// }; +// } + +// /** +// * Views an entry transformer as a function from {@code Entry} to values. +// */ +// static Function, V2> asEntryToValueFunction( +// final EntryTransformer transformer) { +// checkNotNull(transformer); +// return new Function, V2>() { +// @Override +// public V2 apply(Entry entry) { +// return transformer.transformEntry(entry.getKey(), entry.getValue()); +// } +// }; +// } + +// /** +// * Returns a view of an entry transformed by the specified transformer. +// */ +// static Entry transformEntry( +// final EntryTransformer transformer, final Entry entry) { +// checkNotNull(transformer); +// checkNotNull(entry); +// return new AbstractMapEntry() { +// @Override +// public K getKey() { +// return entry.getKey(); +// } +// +// @Override +// public V2 getValue() { +// return transformer.transformEntry(entry.getKey(), entry.getValue()); +// } +// }; +// } + +// /** +// * Views an entry transformer as a function from entries to entries. +// */ +// static Function, Entry> asEntryToEntryFunction( +// final EntryTransformer transformer) { +// checkNotNull(transformer); +// return new Function, Entry>() { +// @Override +// public Entry apply(final Entry entry) { +// return transformEntry(transformer, entry); +// } +// }; +// } + +// static class TransformedEntriesMap +// extends ImprovedAbstractMap { +// final Map fromMap; +// final EntryTransformer transformer; +// +// TransformedEntriesMap( +// Map fromMap, +// EntryTransformer transformer) { +// this.fromMap = checkNotNull(fromMap); +// this.transformer = checkNotNull(transformer); +// } +// +// @Override public int size() { +// return fromMap.size(); +// } +// +// @Override public boolean containsKey(Object key) { +// return fromMap.containsKey(key); +// } +// +// // safe as long as the user followed the Warning in the javadoc +// +// @Override public V2 get(Object key) { +// V1 value = fromMap.get(key); +// return (value != null || fromMap.containsKey(key)) +// ? transformer.transformEntry((K) key, value) +// : null; +// } +// +// // safe as long as the user followed the Warning in the javadoc +// +// @Override public V2 remove(Object key) { +// return fromMap.containsKey(key) +// ? transformer.transformEntry((K) key, fromMap.remove(key)) +// : null; +// } +// +// @Override public void clear() { +// fromMap.clear(); +// } +// +// @Override public Set keySet() { +// return fromMap.keySet(); +// } +// +// @Override +// protected Set> createEntrySet() { +// return new EntrySet() { +// @Override Map map() { +// return TransformedEntriesMap.this; +// } +// +// @Override public Iterator> iterator() { +// return Iterators.transform(fromMap.entrySet().iterator(), +// Maps.asEntryToEntryFunction(transformer)); +// } +// }; +// } +// } + +// static class TransformedEntriesSortedMap +// extends TransformedEntriesMap implements SortedMap { +// +// protected SortedMap fromMap() { +// return (SortedMap) fromMap; +// } +// +// TransformedEntriesSortedMap(SortedMap fromMap, +// EntryTransformer transformer) { +// super(fromMap, transformer); +// } +// +// @Override public Comparator comparator() { +// return fromMap().comparator(); +// } +// +// @Override public K firstKey() { +// return fromMap().firstKey(); +// } +// +// @Override public SortedMap headMap(K toKey) { +// return transformEntries(fromMap().headMap(toKey), transformer); +// } +// +// @Override public K lastKey() { +// return fromMap().lastKey(); +// } +// +// @Override public SortedMap subMap(K fromKey, K toKey) { +// return transformEntries( +// fromMap().subMap(fromKey, toKey), transformer); +// } +// +// @Override public SortedMap tailMap(K fromKey) { +// return transformEntries(fromMap().tailMap(fromKey), transformer); +// } +// } +// +// +// private static class TransformedEntriesNavigableMap +// extends TransformedEntriesSortedMap +// implements NavigableMap { +// +// TransformedEntriesNavigableMap(NavigableMap fromMap, +// EntryTransformer transformer) { +// super(fromMap, transformer); +// } +// +// @Override public Entry ceilingEntry(K key) { +// return transformEntry(fromMap().ceilingEntry(key)); +// } +// +// @Override public K ceilingKey(K key) { +// return fromMap().ceilingKey(key); +// } +// +// @Override public NavigableSet descendingKeySet() { +// return fromMap().descendingKeySet(); +// } +// +// @Override public NavigableMap descendingMap() { +// return transformEntries(fromMap().descendingMap(), transformer); +// } +// +// @Override public Entry firstEntry() { +// return transformEntry(fromMap().firstEntry()); +// } +// @Override public Entry floorEntry(K key) { +// return transformEntry(fromMap().floorEntry(key)); +// } +// +// @Override public K floorKey(K key) { +// return fromMap().floorKey(key); +// } +// +// @Override public NavigableMap headMap(K toKey) { +// return headMap(toKey, false); +// } +// +// @Override public NavigableMap headMap(K toKey, boolean inclusive) { +// return transformEntries( +// fromMap().headMap(toKey, inclusive), transformer); +// } +// +// @Override public Entry higherEntry(K key) { +// return transformEntry(fromMap().higherEntry(key)); +// } +// +// @Override public K higherKey(K key) { +// return fromMap().higherKey(key); +// } +// +// @Override public Entry lastEntry() { +// return transformEntry(fromMap().lastEntry()); +// } +// +// @Override public Entry lowerEntry(K key) { +// return transformEntry(fromMap().lowerEntry(key)); +// } +// +// @Override public K lowerKey(K key) { +// return fromMap().lowerKey(key); +// } +// +// @Override public NavigableSet navigableKeySet() { +// return fromMap().navigableKeySet(); +// } +// +// @Override public Entry pollFirstEntry() { +// return transformEntry(fromMap().pollFirstEntry()); +// } +// +// @Override public Entry pollLastEntry() { +// return transformEntry(fromMap().pollLastEntry()); +// } +// +// @Override public NavigableMap subMap( +// K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { +// return transformEntries( +// fromMap().subMap(fromKey, fromInclusive, toKey, toInclusive), +// transformer); +// } +// +// @Override public NavigableMap subMap(K fromKey, K toKey) { +// return subMap(fromKey, true, toKey, false); +// } +// +// @Override public NavigableMap tailMap(K fromKey) { +// return tailMap(fromKey, true); +// } +// +// @Override public NavigableMap tailMap(K fromKey, boolean inclusive) { +// return transformEntries( +// fromMap().tailMap(fromKey, inclusive), transformer); +// } +// +// +// private Entry transformEntry(Entry entry) { +// return (entry == null) ? null : Maps.transformEntry(transformer, entry); +// } +// +// @Override protected NavigableMap fromMap() { +// return (NavigableMap) super.fromMap(); +// } +// } + + static Predicate> keyPredicateOnEntries(Predicate keyPredicate) { + return compose(keyPredicate, Maps.keyFunction()); + } + + static Predicate> valuePredicateOnEntries(Predicate valuePredicate) { + return compose(valuePredicate, Maps.valueFunction()); + } + + /** + * Returns a map containing the mappings in {@code unfiltered} whose keys + * satisfy a predicate. The returned map is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a key that + * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} + * methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose keys satisfy the + * filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code keyPredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + */ +// public static Map filterKeys( +// Map unfiltered, final Predicate keyPredicate) { +// if (unfiltered instanceof SortedMap) { +// return filterKeys((SortedMap) unfiltered, keyPredicate); +// } else if (unfiltered instanceof BiMap) { +// return filterKeys((BiMap) unfiltered, keyPredicate); +// } +// checkNotNull(keyPredicate); +// Predicate> entryPredicate = keyPredicateOnEntries(keyPredicate); +// return (unfiltered instanceof AbstractFilteredMap) +// ? filterFiltered((AbstractFilteredMap) unfiltered, entryPredicate) +// : new FilteredKeyMap( +// checkNotNull(unfiltered), keyPredicate, entryPredicate); +// } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose + * keys satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a key that + * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} + * methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose keys satisfy the + * filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code keyPredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + * + * @since 11.0 + */ +// public static SortedMap filterKeys( +// SortedMap unfiltered, final Predicate keyPredicate) { +// // TODO(user): Return a subclass of Maps.FilteredKeyMap for slightly better +// // performance. +// return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); +// } + + /** + * Returns a navigable map containing the mappings in {@code unfiltered} whose + * keys satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a key that + * doesn't satisfy the predicate, the map's {@code put()} and {@code putAll()} + * methods throw an {@link IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose keys satisfy the + * filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code keyPredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + * + * @since 14.0 + */ +// +// public static NavigableMap filterKeys( +// NavigableMap unfiltered, final Predicate keyPredicate) { +// // TODO(user): Return a subclass of Maps.FilteredKeyMap for slightly better +// // performance. +// return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); +// } + + /** + * Returns a bimap containing the mappings in {@code unfiltered} whose keys satisfy a predicate. + * The returned bimap is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the + * bimap and its views. When given a key that doesn't satisfy the predicate, the bimap's {@code + * put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link + * IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * bimap or its views, only mappings that satisfy the filter will be removed from the underlying + * bimap. + * + *

The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered bimap's methods, such as {@code size()}, iterate across every key in + * the underlying bimap and determine which satisfy the filter. When a live view is not + * needed, it may be faster to copy the filtered bimap and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals , as + * documented at {@link Predicate#apply}. + * + * @since 14.0 + */ +// public static BiMap filterKeys( +// BiMap unfiltered, final Predicate keyPredicate) { +// checkNotNull(keyPredicate); +// return filterEntries(unfiltered, Maps.keyPredicateOnEntries(keyPredicate)); +// } + + /** + * Returns a map containing the mappings in {@code unfiltered} whose values + * satisfy a predicate. The returned map is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a value + * that doesn't satisfy the predicate, the map's {@code put()}, {@code + * putAll()}, and {@link Entry#setValue} methods throw an {@link + * IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose values satisfy the + * filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code valuePredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + */ +// public static Map filterValues( +// Map unfiltered, final Predicate valuePredicate) { +// if (unfiltered instanceof SortedMap) { +// return filterValues((SortedMap) unfiltered, valuePredicate); +// } else if (unfiltered instanceof BiMap) { +// return filterValues((BiMap) unfiltered, valuePredicate); +// } +// return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); +// } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} whose + * values satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a value + * that doesn't satisfy the predicate, the map's {@code put()}, {@code + * putAll()}, and {@link Entry#setValue} methods throw an {@link + * IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose values satisfy the + * filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code valuePredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + * + * @since 11.0 + */ +// public static SortedMap filterValues( +// SortedMap unfiltered, final Predicate valuePredicate) { +// return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); +// } + + /** + * Returns a navigable map containing the mappings in {@code unfiltered} whose + * values satisfy a predicate. The returned map is a live view of {@code + * unfiltered}; changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a value + * that doesn't satisfy the predicate, the map's {@code put()}, {@code + * putAll()}, and {@link Entry#setValue} methods throw an {@link + * IllegalArgumentException}. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings whose values satisfy the + * filter will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code valuePredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. Do not provide a + * predicate such as {@code Predicates.instanceOf(ArrayList.class)}, which is + * inconsistent with equals. + * + * @since 14.0 + */ +// +// public static NavigableMap filterValues( +// NavigableMap unfiltered, final Predicate valuePredicate) { +// return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); +// } + + /** + * Returns a bimap containing the mappings in {@code unfiltered} whose values satisfy a + * predicate. The returned bimap is a live view of {@code unfiltered}; changes to one affect the + * other. + * + *

The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the + * bimap and its views. When given a value that doesn't satisfy the predicate, the bimap's + * {@code put()}, {@code forcePut()} and {@code putAll()} methods throw an {@link + * IllegalArgumentException}. Similarly, the map's entries have a {@link Entry#setValue} method + * that throws an {@link IllegalArgumentException} when the provided value doesn't satisfy the + * predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * bimap or its views, only mappings that satisfy the filter will be removed from the underlying + * bimap. + * + *

The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered bimap's methods, such as {@code size()}, iterate across every value in + * the underlying bimap and determine which satisfy the filter. When a live view is not + * needed, it may be faster to copy the filtered bimap and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals , as + * documented at {@link Predicate#apply}. + * + * @since 14.0 + */ +// public static BiMap filterValues( +// BiMap unfiltered, final Predicate valuePredicate) { +// return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); +// } + + /** + * Returns a map containing the mappings in {@code unfiltered} that satisfy a + * predicate. The returned map is a live view of {@code unfiltered}; changes + * to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a + * key/value pair that doesn't satisfy the predicate, the map's {@code put()} + * and {@code putAll()} methods throw an {@link IllegalArgumentException}. + * Similarly, the map's entries have a {@link Entry#setValue} method that + * throws an {@link IllegalArgumentException} when the existing key and the + * provided value don't satisfy the predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings that satisfy the filter + * will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. + */ +// 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); +// } +// checkNotNull(entryPredicate); +// return (unfiltered instanceof AbstractFilteredMap) +// ? filterFiltered((AbstractFilteredMap) unfiltered, entryPredicate) +// : new FilteredEntryMap(checkNotNull(unfiltered), entryPredicate); +// } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} that + * satisfy a predicate. The returned map is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a + * key/value pair that doesn't satisfy the predicate, the map's {@code put()} + * and {@code putAll()} methods throw an {@link IllegalArgumentException}. + * Similarly, the map's entries have a {@link Entry#setValue} method that + * throws an {@link IllegalArgumentException} when the existing key and the + * provided value don't satisfy the predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings that satisfy the filter + * will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. + * + * @since 11.0 + */ +// public static SortedMap filterEntries( +// SortedMap unfiltered, +// Predicate> entryPredicate) { +// return Platform.mapsFilterSortedMap(unfiltered, entryPredicate); +// } + +// static SortedMap filterSortedIgnoreNavigable( +// SortedMap unfiltered, +// Predicate> entryPredicate) { +// checkNotNull(entryPredicate); +// return (unfiltered instanceof FilteredEntrySortedMap) +// ? filterFiltered((FilteredEntrySortedMap) unfiltered, entryPredicate) +// : new FilteredEntrySortedMap(checkNotNull(unfiltered), entryPredicate); +// } + + /** + * Returns a sorted map containing the mappings in {@code unfiltered} that + * satisfy a predicate. The returned map is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

The resulting map's {@code keySet()}, {@code entrySet()}, and {@code + * values()} views have iterators that don't support {@code remove()}, but all + * other methods are supported by the map and its views. When given a + * key/value pair that doesn't satisfy the predicate, the map's {@code put()} + * and {@code putAll()} methods throw an {@link IllegalArgumentException}. + * Similarly, the map's entries have a {@link Entry#setValue} method that + * throws an {@link IllegalArgumentException} when the existing key and the + * provided value don't satisfy the predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called + * on the filtered map or its views, only mappings that satisfy the filter + * will be removed from the underlying map. + * + *

The returned map isn't threadsafe or serializable, even if {@code + * unfiltered} is. + * + *

Many of the filtered map's methods, such as {@code size()}, + * iterate across every key/value mapping in the underlying map and determine + * which satisfy the filter. When a live view is not needed, it may be + * faster to copy the filtered map and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with + * equals, as documented at {@link Predicate#apply}. + * + * @since 14.0 + */ +// +// public static NavigableMap filterEntries( +// NavigableMap unfiltered, +// Predicate> entryPredicate) { +// checkNotNull(entryPredicate); +// return (unfiltered instanceof FilteredEntryNavigableMap) +// ? filterFiltered((FilteredEntryNavigableMap) unfiltered, entryPredicate) +// : new FilteredEntryNavigableMap(checkNotNull(unfiltered), entryPredicate); +// } + + /** + * Returns a bimap containing the mappings in {@code unfiltered} that satisfy a predicate. The + * returned bimap is a live view of {@code unfiltered}; changes to one affect the other. + * + *

The resulting bimap's {@code keySet()}, {@code entrySet()}, and {@code values()} views have + * iterators that don't support {@code remove()}, but all other methods are supported by the bimap + * and its views. When given a key/value pair that doesn't satisfy the predicate, the bimap's + * {@code put()}, {@code forcePut()} and {@code putAll()} methods throw an + * {@link IllegalArgumentException}. Similarly, the map's entries have an {@link Entry#setValue} + * method that throws an {@link IllegalArgumentException} when the existing key and the provided + * value don't satisfy the predicate. + * + *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered + * bimap or its views, only mappings that satisfy the filter will be removed from the underlying + * bimap. + * + *

The returned bimap isn't threadsafe or serializable, even if {@code unfiltered} is. + * + *

Many of the filtered bimap's methods, such as {@code size()}, iterate across every + * key/value mapping in the underlying bimap and determine which satisfy the filter. When a live + * view is not needed, it may be faster to copy the filtered bimap and use the copy. + * + *

Warning: {@code entryPredicate} must be consistent with equals , as + * documented at {@link Predicate#apply}. + * + * @since 14.0 + */ +// public static BiMap filterEntries( +// BiMap unfiltered, Predicate> entryPredicate) { +// checkNotNull(unfiltered); +// checkNotNull(entryPredicate); +// return (unfiltered instanceof FilteredEntryBiMap) +// ? filterFiltered((FilteredEntryBiMap) unfiltered, entryPredicate) +// : new FilteredEntryBiMap(unfiltered, entryPredicate); +// } +// +// /** +// * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when +// * filtering a filtered map. +// */ +// private static Map filterFiltered(AbstractFilteredMap map, +// Predicate> entryPredicate) { +// return new FilteredEntryMap(map.unfiltered, +// Predicates.>and(map.predicate, entryPredicate)); +// } + +// private abstract static class AbstractFilteredMap +// extends ImprovedAbstractMap { +// final Map unfiltered; +// final Predicate> predicate; +// +// AbstractFilteredMap( +// Map unfiltered, Predicate> predicate) { +// this.unfiltered = unfiltered; +// this.predicate = predicate; +// } +// +// boolean apply(Object key, V value) { +// // This method is called only when the key is in the map, implying that +// // key is a K. +// +// K k = (K) key; +// return predicate.apply(Maps.immutableEntry(k, value)); +// } +// +// @Override public V put(K key, V value) { +// checkArgument(apply(key, value)); +// return unfiltered.put(key, value); +// } +// +// @Override public void putAll(Map map) { +// for (Entry entry : map.entrySet()) { +// checkArgument(apply(entry.getKey(), entry.getValue())); +// } +// unfiltered.putAll(map); +// } +// +// @Override public boolean containsKey(Object key) { +// return unfiltered.containsKey(key) && apply(key, unfiltered.get(key)); +// } +// +// @Override public V get(Object key) { +// V value = unfiltered.get(key); +// return ((value != null) && apply(key, value)) ? value : null; +// } +// +// @Override public boolean isEmpty() { +// return entrySet().isEmpty(); +// } +// +// @Override public V remove(Object key) { +// return containsKey(key) ? unfiltered.remove(key) : null; +// } +// +// @Override +// Collection createValues() { +// return new FilteredMapValues(this, unfiltered, predicate); +// } +// } +// +// 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; +// } +// +// @Override public boolean remove(Object o) { +// return Iterables.removeFirstMatching(unfiltered.entrySet(), +// Predicates.>and(predicate, Maps.valuePredicateOnEntries(equalTo(o)))) +// != null; +// } +// +// private boolean removeIf(Predicate valuePredicate) { +// return Iterables.removeIf(unfiltered.entrySet(), Predicates.>and( +// predicate, Maps.valuePredicateOnEntries(valuePredicate))); +// } +// +// @Override public boolean removeAll(Collection collection) { +// return removeIf(in(collection)); +// } +// +// @Override public boolean retainAll(Collection collection) { +// return removeIf(not(in(collection))); +// } +// +// @Override public Object[] toArray() { +// // creating an ArrayList so filtering happens once +// return Lists.newArrayList(iterator()).toArray(); +// } +// +// @Override public T[] toArray(T[] array) { +// return Lists.newArrayList(iterator()).toArray(array); +// } +// } +// +// private static class FilteredKeyMap extends AbstractFilteredMap { +// Predicate keyPredicate; +// +// FilteredKeyMap(Map unfiltered, Predicate keyPredicate, +// Predicate> entryPredicate) { +// super(unfiltered, entryPredicate); +// this.keyPredicate = keyPredicate; +// } +// +// @Override +// protected Set> createEntrySet() { +// return Sets.filter(unfiltered.entrySet(), predicate); +// } +// +// @Override +// Set createKeySet() { +// return Sets.filter(unfiltered.keySet(), keyPredicate); +// } +// +// // The cast is called only when the key is in the unfiltered map, implying +// // that key is a K. +// @Override +// +// public boolean containsKey(Object key) { +// return unfiltered.containsKey(key) && keyPredicate.apply((K) key); +// } +// } +// +// static class FilteredEntryMap extends AbstractFilteredMap { +// /** +// * Entries in this set satisfy the predicate, but they don't validate the +// * input to {@code Entry.setValue()}. +// */ +// final Set> filteredEntrySet; +// +// FilteredEntryMap( +// Map unfiltered, Predicate> entryPredicate) { +// super(unfiltered, entryPredicate); +// filteredEntrySet = Sets.filter(unfiltered.entrySet(), predicate); +// } +// +// @Override +// protected Set> createEntrySet() { +// return new EntrySet(); +// } +// +// private class EntrySet extends ForwardingSet> { +// @Override protected Set> delegate() { +// return filteredEntrySet; +// } +// +// @Override public Iterator> iterator() { +// return new TransformedIterator, Entry>(filteredEntrySet.iterator()) { +// @Override +// Entry transform(final Entry entry) { +// return new ForwardingMapEntry() { +// @Override +// protected Entry delegate() { +// return entry; +// } +// +// @Override +// public V setValue(V newValue) { +// checkArgument(apply(getKey(), newValue)); +// return super.setValue(newValue); +// } +// }; +// } +// }; +// } +// } +// +// @Override +// Set createKeySet() { +// return new KeySet(); +// } +// +// class KeySet extends Maps.KeySet { +// KeySet() { +// super(FilteredEntryMap.this); +// } +// +// @Override public boolean remove(Object o) { +// if (containsKey(o)) { +// unfiltered.remove(o); +// return true; +// } +// return false; +// } +// +// private boolean removeIf(Predicate keyPredicate) { +// return Iterables.removeIf(unfiltered.entrySet(), Predicates.>and( +// predicate, Maps.keyPredicateOnEntries(keyPredicate))); +// } +// +// @Override +// public boolean removeAll(Collection c) { +// return removeIf(in(c)); +// } +// +// @Override +// public boolean retainAll(Collection c) { +// return removeIf(not(in(c))); +// } +// +// @Override public Object[] toArray() { +// // creating an ArrayList so filtering happens once +// return Lists.newArrayList(iterator()).toArray(); +// } +// +// @Override public T[] toArray(T[] array) { +// return Lists.newArrayList(iterator()).toArray(array); +// } +// } +// } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when + * filtering a filtered sorted map. + */ +// private static SortedMap filterFiltered( +// FilteredEntrySortedMap map, +// Predicate> entryPredicate) { +// Predicate> predicate +// = Predicates.and(map.predicate, entryPredicate); +// return new FilteredEntrySortedMap(map.sortedMap(), predicate); +// } +// +// private static class FilteredEntrySortedMap +// extends FilteredEntryMap implements SortedMap { +// +// FilteredEntrySortedMap(SortedMap unfiltered, +// Predicate> entryPredicate) { +// super(unfiltered, entryPredicate); +// } +// +// SortedMap sortedMap() { +// return (SortedMap) unfiltered; +// } +// +// @Override public SortedSet keySet() { +// return (SortedSet) super.keySet(); +// } +// +// @Override +// SortedSet createKeySet() { +// return new SortedKeySet(); +// } +// +// class SortedKeySet extends KeySet implements SortedSet { +// @Override +// public Comparator comparator() { +// return sortedMap().comparator(); +// } +// +// @Override +// public SortedSet subSet(K fromElement, K toElement) { +// return (SortedSet) subMap(fromElement, toElement).keySet(); +// } +// +// @Override +// public SortedSet headSet(K toElement) { +// return (SortedSet) headMap(toElement).keySet(); +// } +// +// @Override +// public SortedSet tailSet(K fromElement) { +// return (SortedSet) tailMap(fromElement).keySet(); +// } +// +// @Override +// public K first() { +// return firstKey(); +// } +// +// @Override +// public K last() { +// return lastKey(); +// } +// } +// +// @Override public Comparator comparator() { +// return sortedMap().comparator(); +// } +// +// @Override public K firstKey() { +// // correctly throws NoSuchElementException when filtered map is empty. +// return keySet().iterator().next(); +// } +// +// @Override public K lastKey() { +// SortedMap headMap = sortedMap(); +// while (true) { +// // correctly throws NoSuchElementException when filtered map is empty. +// K key = headMap.lastKey(); +// if (apply(key, unfiltered.get(key))) { +// return key; +// } +// headMap = sortedMap().headMap(key); +// } +// } +// +// @Override public SortedMap headMap(K toKey) { +// return new FilteredEntrySortedMap(sortedMap().headMap(toKey), predicate); +// } +// +// @Override public SortedMap subMap(K fromKey, K toKey) { +// return new FilteredEntrySortedMap( +// sortedMap().subMap(fromKey, toKey), predicate); +// } +// +// @Override public SortedMap tailMap(K fromKey) { +// return new FilteredEntrySortedMap( +// sortedMap().tailMap(fromKey), predicate); +// } +// } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when + * filtering a filtered navigable map. + */ +// +// private static NavigableMap filterFiltered( +// FilteredEntryNavigableMap map, +// Predicate> entryPredicate) { +// Predicate> predicate +// = Predicates.and(map.entryPredicate, entryPredicate); +// return new FilteredEntryNavigableMap(map.unfiltered, predicate); +// } +// +// +// private static class FilteredEntryNavigableMap extends AbstractNavigableMap { +// /* +// * It's less code to extend AbstractNavigableMap and forward the filtering logic to +// * FilteredEntryMap than to extend FilteredEntrySortedMap and reimplement all the NavigableMap +// * methods. +// */ +// +// private final NavigableMap unfiltered; +// private final Predicate> entryPredicate; +// private final Map filteredDelegate; +// +// FilteredEntryNavigableMap( +// NavigableMap unfiltered, Predicate> entryPredicate) { +// this.unfiltered = checkNotNull(unfiltered); +// this.entryPredicate = entryPredicate; +// this.filteredDelegate = new FilteredEntryMap(unfiltered, entryPredicate); +// } +// +// @Override +// public Comparator comparator() { +// return unfiltered.comparator(); +// } +// +// @Override +// public NavigableSet navigableKeySet() { +// return new Maps.NavigableKeySet(this) { +// @Override +// public boolean removeAll(Collection c) { +// return Iterators.removeIf(unfiltered.entrySet().iterator(), +// Predicates.>and(entryPredicate, Maps.keyPredicateOnEntries(in(c)))); +// } +// +// @Override +// public boolean retainAll(Collection c) { +// return Iterators.removeIf(unfiltered.entrySet().iterator(), Predicates.>and( +// entryPredicate, Maps.keyPredicateOnEntries(not(in(c))))); +// } +// }; +// } +// +// @Override +// public Collection values() { +// return new FilteredMapValues(this, unfiltered, entryPredicate); +// } +// +// @Override +// Iterator> entryIterator() { +// return Iterators.filter(unfiltered.entrySet().iterator(), entryPredicate); +// } +// +// @Override +// Iterator> descendingEntryIterator() { +// return Iterators.filter(unfiltered.descendingMap().entrySet().iterator(), entryPredicate); +// } +// +// @Override +// public int size() { +// return filteredDelegate.size(); +// } +// +// @Override +// +// public V get(Object key) { +// return filteredDelegate.get(key); +// } +// +// @Override +// public boolean containsKey(Object key) { +// return filteredDelegate.containsKey(key); +// } +// +// @Override +// public V put(K key, V value) { +// return filteredDelegate.put(key, value); +// } +// +// @Override +// public V remove(Object key) { +// return filteredDelegate.remove(key); +// } +// +// @Override +// public void putAll(Map m) { +// filteredDelegate.putAll(m); +// } +// +// @Override +// public void clear() { +// filteredDelegate.clear(); +// } +// +// @Override +// public Set> entrySet() { +// return filteredDelegate.entrySet(); +// } +// +// @Override +// public Entry pollFirstEntry() { +// return Iterables.removeFirstMatching(unfiltered.entrySet(), entryPredicate); +// } +// +// @Override +// public Entry pollLastEntry() { +// return Iterables.removeFirstMatching(unfiltered.descendingMap().entrySet(), entryPredicate); +// } +// +// @Override +// public NavigableMap descendingMap() { +// return filterEntries(unfiltered.descendingMap(), entryPredicate); +// } +// +// @Override +// public NavigableMap subMap( +// K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { +// return filterEntries( +// unfiltered.subMap(fromKey, fromInclusive, toKey, toInclusive), entryPredicate); +// } +// +// @Override +// public NavigableMap headMap(K toKey, boolean inclusive) { +// return filterEntries(unfiltered.headMap(toKey, inclusive), entryPredicate); +// } +// +// @Override +// public NavigableMap tailMap(K fromKey, boolean inclusive) { +// return filterEntries(unfiltered.tailMap(fromKey, inclusive), entryPredicate); +// } +// } + + /** + * Support {@code clear()}, {@code removeAll()}, and {@code retainAll()} when + * filtering a filtered map. + */ +// private static BiMap filterFiltered( +// FilteredEntryBiMap map, Predicate> entryPredicate) { +// Predicate> predicate = Predicates.and(map.predicate, entryPredicate); +// return new FilteredEntryBiMap(map.unfiltered(), predicate); +// } +// +// static final class FilteredEntryBiMap extends FilteredEntryMap +// implements BiMap { +// private final BiMap inverse; +// +// private static Predicate> inversePredicate( +// final Predicate> forwardPredicate) { +// return new Predicate>() { +// @Override +// public boolean test(Entry input) { +// return forwardPredicate.apply( +// Maps.immutableEntry(input.getValue(), input.getKey())); +// } +// }; +// } +// +// FilteredEntryBiMap(BiMap delegate, +// Predicate> predicate) { +// super(delegate, predicate); +// this.inverse = new FilteredEntryBiMap( +// delegate.inverse(), inversePredicate(predicate), this); +// } +// +// private FilteredEntryBiMap( +// BiMap delegate, Predicate> predicate, +// BiMap inverse) { +// super(delegate, predicate); +// this.inverse = inverse; +// } +// +// BiMap unfiltered() { +// return (BiMap) unfiltered; +// } +// +// @Override +// public V forcePut(K key, V value) { +// checkArgument(apply(key, value)); +// return unfiltered().forcePut(key, value); +// } +// +// @Override +// public BiMap inverse() { +// return inverse; +// } +// +// @Override +// public Set values() { +// return inverse.keySet(); +// } +// } + + /** + * Returns an unmodifiable view of the specified navigable map. Query operations on the returned + * map read through to the specified map, and attempts to modify the returned map, whether direct + * or via its views, result in an {@code UnsupportedOperationException}. + * + *

The returned navigable map will be serializable if the specified navigable map is + * serializable. + * + * @param map the navigable map for which an unmodifiable view is to be returned + * @return an unmodifiable view of the specified navigable map + * @since 12.0 + */ +// +// public static NavigableMap unmodifiableNavigableMap(NavigableMap map) { +// checkNotNull(map); +// if (map instanceof UnmodifiableNavigableMap) { +// return map; +// } else { +// return new UnmodifiableNavigableMap(map); +// } +// } +// +// private static Entry unmodifiableOrNull(Entry entry) { +// return (entry == null) ? null : Maps.unmodifiableEntry(entry); +// } +// +// +// static class UnmodifiableNavigableMap +// extends ForwardingSortedMap implements NavigableMap, Serializable { +// private final NavigableMap delegate; +// +// UnmodifiableNavigableMap(NavigableMap delegate) { +// this.delegate = delegate; +// } +// +// UnmodifiableNavigableMap( +// NavigableMap delegate, UnmodifiableNavigableMap descendingMap) { +// this.delegate = delegate; +// this.descendingMap = descendingMap; +// } +// +// @Override +// protected SortedMap delegate() { +// return Collections.unmodifiableSortedMap(delegate); +// } +// +// @Override +// public Entry lowerEntry(K key) { +// return unmodifiableOrNull(delegate.lowerEntry(key)); +// } +// +// @Override +// public K lowerKey(K key) { +// return delegate.lowerKey(key); +// } +// +// @Override +// public Entry floorEntry(K key) { +// return unmodifiableOrNull(delegate.floorEntry(key)); +// } +// +// @Override +// public K floorKey(K key) { +// return delegate.floorKey(key); +// } +// +// @Override +// public Entry ceilingEntry(K key) { +// return unmodifiableOrNull(delegate.ceilingEntry(key)); +// } +// +// @Override +// public K ceilingKey(K key) { +// return delegate.ceilingKey(key); +// } +// +// @Override +// public Entry higherEntry(K key) { +// return unmodifiableOrNull(delegate.higherEntry(key)); +// } +// +// @Override +// public K higherKey(K key) { +// return delegate.higherKey(key); +// } +// +// @Override +// public Entry firstEntry() { +// return unmodifiableOrNull(delegate.firstEntry()); +// } +// +// @Override +// public Entry lastEntry() { +// return unmodifiableOrNull(delegate.lastEntry()); +// } +// +// @Override +// public final Entry pollFirstEntry() { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public final Entry pollLastEntry() { +// throw new UnsupportedOperationException(); +// } +// +// private transient UnmodifiableNavigableMap descendingMap; +// +// @Override +// public NavigableMap descendingMap() { +// UnmodifiableNavigableMap result = descendingMap; +// return (result == null) +// ? descendingMap = new UnmodifiableNavigableMap(delegate.descendingMap(), this) +// : result; +// } +// +// @Override +// public Set keySet() { +// return navigableKeySet(); +// } +// +// @Override +// public NavigableSet navigableKeySet() { +// return Sets.unmodifiableNavigableSet(delegate.navigableKeySet()); +// } +// +// @Override +// public NavigableSet descendingKeySet() { +// return Sets.unmodifiableNavigableSet(delegate.descendingKeySet()); +// } +// +// @Override +// public SortedMap subMap(K fromKey, K toKey) { +// return subMap(fromKey, true, toKey, false); +// } +// +// @Override +// public SortedMap headMap(K toKey) { +// return headMap(toKey, false); +// } +// +// @Override +// public SortedMap tailMap(K fromKey) { +// return tailMap(fromKey, true); +// } +// +// @Override +// public +// NavigableMap +// subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { +// return Maps.unmodifiableNavigableMap(delegate.subMap( +// fromKey, +// fromInclusive, +// toKey, +// toInclusive)); +// } +// +// @Override +// public NavigableMap headMap(K toKey, boolean inclusive) { +// return Maps.unmodifiableNavigableMap(delegate.headMap(toKey, inclusive)); +// } +// +// @Override +// public NavigableMap tailMap(K fromKey, boolean inclusive) { +// return Maps.unmodifiableNavigableMap(delegate.tailMap(fromKey, inclusive)); +// } +// } + + /** + * Returns a synchronized (thread-safe) navigable map backed by the specified + * navigable map. In order to guarantee serial access, it is critical that + * all access to the backing navigable map is accomplished + * through the returned navigable map (or its views). + * + *

It is imperative that the user manually synchronize on the returned + * navigable map when iterating over any of its collection views, or the + * collections views of any of its {@code descendingMap}, {@code subMap}, + * {@code headMap} or {@code tailMap} views.

   {@code
+   *
+   *   NavigableMap map = synchronizedNavigableMap(new TreeMap());
+   *
+   *   // Needn't be in synchronized block
+   *   NavigableSet set = map.navigableKeySet();
+   *
+   *   synchronized (map) { // Synchronizing on map, not set!
+   *     Iterator it = set.iterator(); // Must be in synchronized block
+   *     while (it.hasNext()) {
+   *       foo(it.next());
+   *     }
+   *   }}
+ * + *

or:

   {@code
+   *
+   *   NavigableMap map = synchronizedNavigableMap(new TreeMap());
+   *   NavigableMap map2 = map.subMap(foo, false, bar, true);
+   *
+   *   // Needn't be in synchronized block
+   *   NavigableSet set2 = map2.descendingKeySet();
+   *
+   *   synchronized (map) { // Synchronizing on map, not map2 or set2!
+   *     Iterator it = set2.iterator(); // Must be in synchronized block
+   *     while (it.hasNext()) {
+   *       foo(it.next());
+   *     }
+   *   }}
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned navigable map will be serializable if the specified + * navigable map is serializable. + * + * @param navigableMap the navigable map to be "wrapped" in a synchronized + * navigable map. + * @return a synchronized view of the specified navigable map. + * @since 13.0 + */ +// +// public static NavigableMap synchronizedNavigableMap( +// NavigableMap navigableMap) { +// return Synchronized.navigableMap(navigableMap); +// } + + /** + * {@code AbstractMap} extension that implements {@link #isEmpty()} as {@code + * entrySet().isEmpty()} instead of {@code size() == 0} to speed up + * implementations where {@code size()} is O(n), and it delegates the {@code + * isEmpty()} methods of its key set and value collection to this + * implementation. + */ + + abstract static class ImprovedAbstractMap extends AbstractMap { + /** + * Creates the entry set to be returned by {@link #entrySet()}. This method + * is invoked at most once on a given map, at the time when {@code entrySet} + * is first called. + */ + abstract Set> createEntrySet(); + + private transient Set> entrySet; + + @Override public Set> entrySet() { + Set> result = entrySet; + return (result == null) ? entrySet = createEntrySet() : result; + } + + private transient Set keySet; + + @Override public Set keySet() { + Set result = keySet; + return (result == null) ? keySet = createKeySet() : result; + } + + Set createKeySet() { + return new KeySet(this); + } + + private transient Collection values; + + @Override public Collection values() { + Collection result = values; + return (result == null) ? values = createValues() : result; + } + + Collection createValues() { + return new Values(this); + } + } + + /** + * Delegates to {@link Map#get}. Returns {@code null} on {@code + * ClassCastException} and {@code NullPointerException}. + */ + static V safeGet(Map map, Object key) { + checkNotNull(map); + try { + return map.get(key); + } catch (ClassCastException e) { + return null; + } catch (NullPointerException e) { + return null; + } + } + + /** + * Delegates to {@link Map#containsKey}. Returns {@code false} on {@code + * ClassCastException} and {@code NullPointerException}. + */ + static boolean safeContainsKey(Map map, Object key) { + checkNotNull(map); + try { + return map.containsKey(key); + } catch (ClassCastException e) { + return false; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Delegates to {@link Map#remove}. Returns {@code null} on {@code + * ClassCastException} and {@code NullPointerException}. + */ + static V safeRemove(Map map, Object key) { + checkNotNull(map); + try { + return map.remove(key); + } catch (ClassCastException e) { + return null; + } catch (NullPointerException e) { + return null; + } + } + + /** + * An admittedly inefficient implementation of {@link Map#containsKey}. + */ +// static boolean containsKeyImpl(Map map, Object key) { +// return Iterators.contains(keyIterator(map.entrySet().iterator()), key); +// } + + /** + * An implementation of {@link Map#containsValue}. + */ +// static boolean containsValueImpl(Map map, Object value) { +// return Iterators.contains(valueIterator(map.entrySet().iterator()), value); +// } + + /** + * Implements {@code Collection.contains} safely for forwarding collections of + * map entries. If {@code o} is an instance of {@code Map.Entry}, it is + * wrapped using {@link #unmodifiableEntry} to protect against a possible + * nefarious equals method. + * + *

Note that {@code c} is the backing (delegate) collection, rather than + * the forwarding collection. + * + * @param c the delegate (unwrapped) collection of map entries + * @param o the object that might be contained in {@code c} + * @return {@code true} if {@code c} contains {@code o} + */ + static boolean containsEntryImpl(Collection> c, Object o) { + if (!(o instanceof Entry)) { + return false; + } + return c.contains(unmodifiableEntry((Entry) o)); + } + + /** + * Implements {@code Collection.remove} safely for forwarding collections of + * map entries. If {@code o} is an instance of {@code Map.Entry}, it is + * wrapped using {@link #unmodifiableEntry} to protect against a possible + * nefarious equals method. + * + *

Note that {@code c} is backing (delegate) collection, rather than the + * forwarding collection. + * + * @param c the delegate (unwrapped) collection of map entries + * @param o the object to remove from {@code c} + * @return {@code true} if {@code c} was changed + */ +// static boolean removeEntryImpl(Collection> c, Object o) { +// if (!(o instanceof Entry)) { +// return false; +// } +// return c.remove(unmodifiableEntry((Entry) o)); +// } + + /** + * An implementation of {@link Map#equals}. + */ + 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()); + } + return false; + } + +// static final MapJoiner STANDARD_JOINER = +// Filter.STANDARD_JOINER.withKeyValueSeparator("="); +// +// /** +// * An implementation of {@link Map#toString}. +// */ +// static String toStringImpl(Map map) { +// StringBuilder sb +// = Filter.newStringBuilderForCollection(map.size()).append('{'); +// STANDARD_JOINER.appendTo(sb, map); +// return sb.append('}').toString(); +// } + + /** + * An implementation of {@link Map#putAll}. + */ +// static void putAllImpl( +// Map self, Map map) { +// for (Map.Entry entry : map.entrySet()) { +// self.put(entry.getKey(), entry.getValue()); +// } +// } + + static class KeySet extends Sets.ImprovedAbstractSet { + final Map map; + + KeySet(Map map) { + this.map = checkNotNull(map); + } + + Map map() { + return map; + } + + @Override public Iterator iterator() { + return keyIterator(map().entrySet().iterator()); + } + + @Override public int size() { + return map().size(); + } + + @Override public boolean isEmpty() { + return map().isEmpty(); + } + + @Override public boolean contains(Object o) { + return map().containsKey(o); + } + + @Override public boolean remove(Object o) { + if (contains(o)) { + map().remove(o); + return true; + } + return false; + } + + @Override public void clear() { + map().clear(); + } + } + +// +// static K keyOrNull(Entry entry) { +// return (entry == null) ? null : entry.getKey(); +// } +// +// +// static V valueOrNull(Entry entry) { +// return (entry == null) ? null : entry.getValue(); +// } + +// static class SortedKeySet extends KeySet implements SortedSet { +// SortedKeySet(SortedMap map) { +// super(map); +// } +// +// @Override +// SortedMap map() { +// return (SortedMap) super.map(); +// } +// +// @Override +// public Comparator comparator() { +// return map().comparator(); +// } +// +// @Override +// public SortedSet subSet(K fromElement, K toElement) { +// return new SortedKeySet(map().subMap(fromElement, toElement)); +// } +// +// @Override +// public SortedSet headSet(K toElement) { +// return new SortedKeySet(map().headMap(toElement)); +// } +// +// @Override +// public SortedSet tailSet(K fromElement) { +// return new SortedKeySet(map().tailMap(fromElement)); +// } +// +// @Override +// public K first() { +// return map().firstKey(); +// } +// +// @Override +// public K last() { +// return map().lastKey(); +// } +// } + +// +// static class NavigableKeySet extends SortedKeySet implements NavigableSet { +// NavigableKeySet(NavigableMap map) { +// super(map); +// } +// +// @Override +// NavigableMap map() { +// return (NavigableMap) map; +// } +// +// @Override +// public K lower(K e) { +// return map().lowerKey(e); +// } +// +// @Override +// public K floor(K e) { +// return map().floorKey(e); +// } +// +// @Override +// public K ceiling(K e) { +// return map().ceilingKey(e); +// } +// +// @Override +// public K higher(K e) { +// return map().higherKey(e); +// } +// +// @Override +// public K pollFirst() { +// return keyOrNull(map().pollFirstEntry()); +// } +// +// @Override +// public K pollLast() { +// return keyOrNull(map().pollLastEntry()); +// } +// +// @Override +// public NavigableSet descendingSet() { +// return map().descendingKeySet(); +// } +// +// @Override +// public Iterator descendingIterator() { +// return descendingSet().iterator(); +// } +// +// @Override +// public NavigableSet subSet( +// K fromElement, +// boolean fromInclusive, +// K toElement, +// boolean toInclusive) { +// return map().subMap(fromElement, fromInclusive, toElement, toInclusive).navigableKeySet(); +// } +// +// @Override +// public NavigableSet headSet(K toElement, boolean inclusive) { +// return map().headMap(toElement, inclusive).navigableKeySet(); +// } +// +// @Override +// public NavigableSet tailSet(K fromElement, boolean inclusive) { +// return map().tailMap(fromElement, inclusive).navigableKeySet(); +// } +// +// @Override +// public SortedSet subSet(K fromElement, K toElement) { +// return subSet(fromElement, true, toElement, false); +// } +// +// @Override +// public SortedSet headSet(K toElement) { +// return headSet(toElement, false); +// } +// +// @Override +// public SortedSet tailSet(K fromElement) { +// return tailSet(fromElement, true); +// } +// } + + static class Values extends AbstractCollection { + final Map map; + + Values(Map map) { + this.map = checkNotNull(map); + } + + final Map map() { + return map; + } + + @Override public Iterator iterator() { + return valueIterator(map().entrySet().iterator()); + } + + @Override public boolean remove(Object o) { + try { + return super.remove(o); + } catch (UnsupportedOperationException e) { + for (Entry entry : map().entrySet()) { + if (Preconditions.equal(o, entry.getValue())) { + map().remove(entry.getKey()); + return true; + } + } + return false; + } + } + + @Override public boolean removeAll(Collection c) { + try { + return super.removeAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + Set toRemove = Sets.newHashSet(); + for (Entry entry : map().entrySet()) { + if (c.contains(entry.getValue())) { + toRemove.add(entry.getKey()); + } + } + return map().keySet().removeAll(toRemove); + } + } + + @Override public boolean retainAll(Collection c) { + try { + return super.retainAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + Set toRetain = Sets.newHashSet(); + for (Entry entry : map().entrySet()) { + if (c.contains(entry.getValue())) { + toRetain.add(entry.getKey()); + } + } + return map().keySet().retainAll(toRetain); + } + } + + @Override public int size() { + return map().size(); + } + + @Override public boolean isEmpty() { + return map().isEmpty(); + } + + @Override public boolean contains(Object o) { + return map().containsValue(o); + } + + @Override public void clear() { + map().clear(); + } + } + + abstract static class EntrySet + extends Sets.ImprovedAbstractSet> { + abstract Map map(); + + @Override public int size() { + return map().size(); + } + + @Override public void clear() { + map().clear(); + } + + @Override public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + Object key = entry.getKey(); + V value = Maps.safeGet(map(), key); + return Preconditions.equal(value, entry.getValue()) + && (value != null || map().containsKey(key)); + } + return false; + } + + @Override public boolean isEmpty() { + return map().isEmpty(); + } + + @Override public boolean remove(Object o) { + if (contains(o)) { + Entry entry = (Entry) o; + return map().keySet().remove(entry.getKey()); + } + return false; + } + + @Override public boolean removeAll(Collection c) { + try { + return super.removeAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + // if the iterators don't support remove + return Sets.removeAllImpl(this, c.iterator()); + } + } + + @Override public boolean retainAll(Collection c) { + try { + return super.retainAll(checkNotNull(c)); + } catch (UnsupportedOperationException e) { + // if the iterators don't support remove + Set keys = Sets.newHashSetWithExpectedSize(c.size()); + for (Object o : c) { + if (contains(o)) { + Entry entry = (Entry) o; + keys.add(entry.getKey()); + } + } + return map().keySet().retainAll(keys); + } + } + } + +// +// abstract static class DescendingMap extends ForwardingMap +// implements NavigableMap { +// +// abstract NavigableMap forward(); +// +// @Override +// protected final Map delegate() { +// return forward(); +// } +// +// private transient Comparator comparator; +// +// +// @Override +// public Comparator comparator() { +// Comparator result = comparator; +// if (result == null) { +// Comparator forwardCmp = forward().comparator(); +// if (forwardCmp == null) { +// forwardCmp = (Comparator) Ordering.natural(); +// } +// result = comparator = reverse(forwardCmp); +// } +// return result; +// } +// +// // If we inline this, we get a javac error. +// private static Ordering reverse(Comparator forward) { +// return Ordering.from(forward).reverse(); +// } +// +// @Override +// public K firstKey() { +// return forward().lastKey(); +// } +// +// @Override +// public K lastKey() { +// return forward().firstKey(); +// } +// +// @Override +// public Entry lowerEntry(K key) { +// return forward().higherEntry(key); +// } +// +// @Override +// public K lowerKey(K key) { +// return forward().higherKey(key); +// } +// +// @Override +// public Entry floorEntry(K key) { +// return forward().ceilingEntry(key); +// } +// +// @Override +// public K floorKey(K key) { +// return forward().ceilingKey(key); +// } +// +// @Override +// public Entry ceilingEntry(K key) { +// return forward().floorEntry(key); +// } +// +// @Override +// public K ceilingKey(K key) { +// return forward().floorKey(key); +// } +// +// @Override +// public Entry higherEntry(K key) { +// return forward().lowerEntry(key); +// } +// +// @Override +// public K higherKey(K key) { +// return forward().lowerKey(key); +// } +// +// @Override +// public Entry firstEntry() { +// return forward().lastEntry(); +// } +// +// @Override +// public Entry lastEntry() { +// return forward().firstEntry(); +// } +// +// @Override +// public Entry pollFirstEntry() { +// return forward().pollLastEntry(); +// } +// +// @Override +// public Entry pollLastEntry() { +// return forward().pollFirstEntry(); +// } +// +// @Override +// public NavigableMap descendingMap() { +// return forward(); +// } +// +// private transient Set> entrySet; +// +// @Override +// public Set> entrySet() { +// Set> result = entrySet; +// return (result == null) ? entrySet = createEntrySet() : result; +// } +// +// abstract Iterator> entryIterator(); +// +// Set> createEntrySet() { +// return new EntrySet() { +// @Override +// Map map() { +// return DescendingMap.this; +// } +// +// @Override +// public Iterator> iterator() { +// return entryIterator(); +// } +// }; +// } +// +// @Override +// public Set keySet() { +// return navigableKeySet(); +// } +// +// private transient NavigableSet navigableKeySet; +// +// @Override +// public NavigableSet navigableKeySet() { +// NavigableSet result = navigableKeySet; +// return (result == null) ? navigableKeySet = new NavigableKeySet(this) : result; +// } +// +// @Override +// public NavigableSet descendingKeySet() { +// return forward().navigableKeySet(); +// } +// +// @Override +// public +// NavigableMap +// subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { +// return forward().subMap(toKey, toInclusive, fromKey, fromInclusive).descendingMap(); +// } +// +// @Override +// public NavigableMap headMap(K toKey, boolean inclusive) { +// return forward().tailMap(toKey, inclusive).descendingMap(); +// } +// +// @Override +// public NavigableMap tailMap(K fromKey, boolean inclusive) { +// return forward().headMap(fromKey, inclusive).descendingMap(); +// } +// +// @Override +// public SortedMap subMap(K fromKey, K toKey) { +// return subMap(fromKey, true, toKey, false); +// } +// +// @Override +// public SortedMap headMap(K toKey) { +// return headMap(toKey, false); +// } +// +// @Override +// public SortedMap tailMap(K fromKey) { +// return tailMap(fromKey, true); +// } +// +// @Override +// public Collection values() { +// return new Values(this); +// } +// +// @Override +// public String toString() { +// return standardToString(); +// } +// } +} diff --git a/java/src/game/collect/ObjectArrays.java b/java/src/game/collect/ObjectArrays.java new file mode 100644 index 0000000..2087cbe --- /dev/null +++ b/java/src/game/collect/ObjectArrays.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.lang.reflect.Array; +import java.util.Collection; + +/** + * Static utility methods pertaining to object arrays. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +final class ObjectArrays { + static final Object[] EMPTY_ARRAY = new Object[0]; + + private ObjectArrays() {} + + /** + * Returns a new array of the given length with the specified component type. + * + * @param type the component type + * @param length the length of the new array + */ +// +// +// public static T[] newArray(Class type, int length) { +// return (T[]) Array.newInstance(type, length); +// } + + /** + * Returns a new array of the given length with the same type as a reference + * array. + * + * @param reference any array of the desired type + * @param length the length of the new array + */ + public static T[] newArray(T[] reference, int length) { +// return Platform.newArray(reference, length); + Class type = reference.getClass().getComponentType(); + + // the cast is safe because + // result.getClass() == reference.getClass().getComponentType() + + T[] result = (T[]) Array.newInstance(type, length); + return result; + } + + /** + * Returns a new array that contains the concatenated contents of two arrays. + * + * @param first the first array of elements to concatenate + * @param second the second array of elements to concatenate + * @param type the component type of the returned array + */ +// +// 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; +// } + + /** + * Returns a new array that prepends {@code element} to {@code array}. + * + * @param element the element to prepend to the front of {@code array} + * @param array the array of elements to append + * @return an array whose size is one larger than {@code array}, with + * {@code element} occupying the first position, and the + * elements of {@code array} occupying the remaining elements. + */ +// public static T[] concat(T element, T[] array) { +// T[] result = newArray(array, array.length + 1); +// result[0] = element; +// System.arraycopy(array, 0, result, 1, array.length); +// return result; +// } + + /** + * Returns a new array that appends {@code element} to {@code array}. + * + * @param array the array of elements to prepend + * @param element the element to append to the end + * @return an array whose size is one larger than {@code array}, with + * the same contents as {@code array}, plus {@code element} occupying the + * last position. + */ +// public static T[] concat(T[] array, T element) { +// T[] result = arraysCopyOf(array, array.length + 1); +// result[array.length] = element; +// return result; +// } + + /** GWT safe version of Arrays.copyOf. */ + 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; + } + + /** + * Returns an array containing all of the elements in the specified + * collection; the runtime type of the returned array is that of the specified + * array. If the collection fits in the specified array, it is returned + * therein. Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of the specified collection. + * + *

If the collection fits in the specified array with room to spare (i.e., + * the array has more elements than the collection), the element in the array + * immediately following the end of the collection is set to {@code null}. + * This is useful in determining the length of the collection only if + * the caller knows that the collection does not contain any null elements. + * + *

This method returns the elements in the order they are returned by the + * collection's iterator. + * + *

TODO(kevinb): support concurrently modified collections? + * + * @param c the collection for which to return an array of elements + * @param array the array in which to place the collection elements + * @throws ArrayStoreException if the runtime type of the specified array is + * not a supertype of the runtime type of every element in the specified + * collection + */ + 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; + } + + /** + * Implementation of {@link Collection#toArray(Object[])} for collections backed by an object + * array. the runtime type of the returned array is that of the specified array. If the collection + * fits in the specified array, it is returned therein. Otherwise, a new array is allocated with + * the runtime type of the specified array and the size of the specified collection. + * + *

If the collection fits in the specified array with room to spare (i.e., the array has more + * elements than the collection), the element in the array immediately following the end of the + * collection is set to {@code null}. This is useful in determining the length of the collection + * only if the caller knows that the collection does not contain any null elements. + */ +// static T[] toArrayImpl(Object[] src, int offset, int len, T[] dst) { +// 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; +// } + + /** + * Returns an array containing all of the elements in the specified + * collection. This method returns the elements in the order they are returned + * by the collection's iterator. The returned array is "safe" in that no + * references to it are maintained by the collection. The caller is thus free + * to modify the returned array. + * + *

This method assumes that the collection size doesn't change while the + * method is running. + * + *

TODO(kevinb): support concurrently modified collections? + * + * @param c the collection for which to return an array of elements + */ +// static Object[] toArrayImpl(Collection c) { +// return fillArray(c, new Object[c.size()]); +// } + + /** + * Returns a copy of the specified subrange of the specified array that is literally an Object[], + * and not e.g. a {@code String[]}. + */ +// static Object[] copyAsObjectArray(Object[] elements, int offset, int length) { +// checkPositionIndexes(offset, offset + length, elements.length); +// if (length == 0) { +// return EMPTY_ARRAY; +// } +// 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; + for (Object element : elements) { + array[i++] = element; + } + return array; + } + + /** + * Swaps {@code array[i]} with {@code array[j]}. + */ +// 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; + } + + // We do this instead of Preconditions.checkNotNull to save boxing and array + // creation cost. + static Object checkElementNotNull(Object element, int index) { + if (element == null) { + throw new NullPointerException("at index " + index); + } + return element; + } +} diff --git a/java/src/game/collect/Preconditions.java b/java/src/game/collect/Preconditions.java new file mode 100644 index 0000000..18dbe7e --- /dev/null +++ b/java/src/game/collect/Preconditions.java @@ -0,0 +1,350 @@ +package game.collect; + +final class Preconditions { + private Preconditions() {} + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @throws IllegalArgumentException if {@code expression} is false + */ + public static void checkArgument(boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalArgumentException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message + * in square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws IllegalArgumentException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or + * {@code errorMessageArgs} is null (don't let this happen) + */ + public static void checkArgument(boolean expression, + String errorMessageTemplate, + Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + public static void checkState(boolean expression, Object errorMessage) { + if (!expression) { + throw new IllegalStateException(String.valueOf(errorMessage)); + } + } + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message + * in square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @throws IllegalStateException if {@code expression} is false + * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or + * {@code errorMessageArgs} is null (don't let this happen) + */ +// public static void checkState(boolean expression, +// String errorMessageTemplate, +// Object... errorMessageArgs) { +// if (!expression) { +// throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); +// } +// } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will be converted to a + * string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @param errorMessageTemplate a template for the exception message should the check fail. The + * message is formed by replacing each {@code %s} placeholder in the template with an + * argument. These are matched by position - the first {@code %s} gets {@code + * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message + * in square braces. Unmatched placeholders will be left as-is. + * @param errorMessageArgs the arguments to be substituted into the message template. Arguments + * are converted to strings using {@link String#valueOf(Object)}. + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ +// public static T checkNotNull(T reference, +// String errorMessageTemplate, +// Object... errorMessageArgs) { +// if (reference == null) { +// // If either of these parameters is null, the right thing happens anyway +// throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); +// } +// return reference; +// } + + /* + * All recent hotspots (as of 2009) *really* like to have the natural code + * + * if (guardExpression) { + * throw new BadException(messageExpression); + * } + * + * refactored so that messageExpression is moved to a separate String-returning method. + * + * if (guardExpression) { + * throw new BadException(badMsg(...)); + * } + * + * The alternative natural refactorings into void or Exception-returning methods are much slower. + * This is a big deal - we're talking factors of 2-8 in microbenchmarks, not just 10-20%. (This + * is a hotspot optimizer bug, which should be fixed, but that's a separate, big project). + * + * The coding pattern above is heavily used in java.util, e.g. in ArrayList. There is a + * RangeCheckMicroBenchmark in the JDK that was used to test this. + * + * But the methods in this class want to throw different exceptions, depending on the args, so it + * appears that this pattern is not directly applicable. But we can use the ridiculous, devious + * trick of throwing an exception in the middle of the construction of another exception. Hotspot + * is fine with that. + */ + + /** + * Ensures that {@code index} specifies a valid element in an array, list or string of size + * {@code size}. An element index may range from zero, inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ +// public static int checkElementIndex(int index, int size) { +// return checkElementIndex(index, size, "index"); +// } + + /** + * Ensures that {@code index} specifies a valid element in an array, list or string of size + * {@code size}. An element index may range from zero, inclusive, to {@code size}, exclusive. + * + * @param index a user-supplied index identifying an element of an array, list or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is not less than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkElementIndex( + int index, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(badElementIndex(index, size)); + } + return index; + } + + private static String badElementIndex(int index, int size) { + if (index < 0) { + return String.format("index (%d) must not be negative", index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index >= size + return String.format("index (%d) must be less than size (%d)", index, size); + } + } + + /** + * Ensures that {@code index} specifies a valid position in an array, list or string of + * size {@code size}. A position index may range from zero to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list or string + * @param size the size of that array, list or string + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ +// public static int checkPositionIndex(int index, int size) { +// return checkPositionIndex(index, size, "index"); +// } + + /** + * Ensures that {@code index} specifies a valid position in an array, list or string of + * size {@code size}. A position index may range from zero to {@code size}, inclusive. + * + * @param index a user-supplied index identifying a position in an array, list or string + * @param size the size of that array, list or string + * @param desc the text to use to describe this index in an error message + * @return the value of {@code index} + * @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static int checkPositionIndex(int index, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(badPositionIndex(index, size, "index")); + } + return index; + } + + private static String badPositionIndex(int index, int size, String desc) { + if (index < 0) { + return String.format("%s (%d) must not be negative", desc, index); + } else if (size < 0) { + throw new IllegalArgumentException("negative size: " + size); + } else { // index > size + return String.format("%s (%d) must not be greater than size (%s)", desc, index, size); + } + } + + /** + * Ensures that {@code start} and {@code end} specify a valid positions in an array, list + * or string of size {@code size}, and are in order. A position index may range from zero to + * {@code size}, inclusive. + * + * @param start a user-supplied index identifying a starting position in an array, list or string + * @param end a user-supplied index identifying a ending position in an array, list or string + * @param size the size of that array, list or string + * @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size}, + * or if {@code end} is less than {@code start} + * @throws IllegalArgumentException if {@code size} is negative + */ + public static void checkPositionIndexes(int start, int end, int size) { + // Carefully optimized for execution by hotspot (explanatory comment above) + 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 badPositionIndex(start, size, "start index"); + } + if (end < 0 || end > size) { + return badPositionIndex(end, size, "end index"); + } + // end < start + return String.format("end index (%d) must not be less than start index (%d)", end, start); + } + +public static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + /** + * Substitutes each {@code %s} in {@code template} with an argument. These are matched by + * position: the first {@code %s} gets {@code args[0]}, etc. If there are more arguments than + * placeholders, the unmatched arguments will be appended to the end of the formatted message in + * square braces. + * + * @param template a non-null string containing 0 or more {@code %s} placeholders. + * @param args the arguments to be substituted into the message template. Arguments are converted + * to strings using {@link String#valueOf(Object)}. Arguments can be null. + */ +// // Note that this is somewhat-improperly used from Verify.java as well. +// static String format(String template, Object... args) { +// template = String.valueOf(template); // null -> "null" +// +// // start substituting the arguments into the '%s' placeholders +// StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); +// int templateStart = 0; +// int i = 0; +// while (i < args.length) { +// int placeholderStart = template.indexOf("%s", templateStart); +// if (placeholderStart == -1) { +// break; +// } +// builder.append(template.substring(templateStart, placeholderStart)); +// builder.append(args[i++]); +// templateStart = placeholderStart + 2; +// } +// builder.append(template.substring(templateStart)); +// +// // if we run out of placeholders, append the extra args in square braces +// 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/java/src/game/collect/RegularImmutableAsList.java b/java/src/game/collect/RegularImmutableAsList.java new file mode 100644 index 0000000..a5d724d --- /dev/null +++ b/java/src/game/collect/RegularImmutableAsList.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +/** + * An {@link ImmutableAsList} implementation specialized for when the delegate collection is + * already backed by an {@code ImmutableList} or array. + * + * @author Louis Wasserman + */ + + // uses writeReplace, not default serialization +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)); + } + + @Override + ImmutableCollection delegateCollection() { + return delegate; + } + + ImmutableList delegateList() { + return delegateList; + } + + // safe covariant cast! + @Override + public UnmodifiableListIterator listIterator(int index) { + return (UnmodifiableListIterator) delegateList.listIterator(index); + } + + + @Override + int copyIntoArray(Object[] dst, int offset) { + return delegateList.copyIntoArray(dst, offset); + } + + @Override + public E get(int index) { + return delegateList.get(index); + } +} diff --git a/java/src/game/collect/RegularImmutableList.java b/java/src/game/collect/RegularImmutableList.java new file mode 100644 index 0000000..e129981 --- /dev/null +++ b/java/src/game/collect/RegularImmutableList.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +/** + * Implementation of {@link ImmutableList} with one or more elements. + * + * @author Kevin Bourrillion + */ + + // uses writeReplace(), not default serialization +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); + } + + @Override + public int size() { + return size; + } + + @Override boolean isPartialView() { + return size != array.length; + } + + @Override + int copyIntoArray(Object[] dst, int dstOff) { + System.arraycopy(array, offset, dst, dstOff, size); + return dstOff + size; + } + + // The fake cast to E is safe because the creation methods only allow E's + @Override + + public E get(int index) { + Preconditions.checkElementIndex(index, size); + return (E) array[index + offset]; + } + + @Override + public int indexOf(Object object) { + if (object == null) { + return -1; + } + for (int i = 0; i < size; i++) { + if (array[offset + i].equals(object)) { + return i; + } + } + return -1; + } + + @Override + public int lastIndexOf(Object object) { + if (object == null) { + return -1; + } + for (int i = size - 1; i >= 0; i--) { + if (array[offset + i].equals(object)) { + return i; + } + } + return -1; + } + + @Override + ImmutableList subListUnchecked(int fromIndex, int toIndex) { + return new RegularImmutableList( + array, offset + fromIndex, toIndex - fromIndex); + } + + + @Override + public UnmodifiableListIterator listIterator(int index) { + // for performance + // The fake cast to E is safe because the creation methods only allow E's + return (UnmodifiableListIterator) + Iterators.forArray(array, offset, size, index); + } + + // TODO(user): benchmark optimizations for equals() and see if they're worthwhile +} diff --git a/java/src/game/collect/RegularImmutableMap.java b/java/src/game/collect/RegularImmutableMap.java new file mode 100644 index 0000000..81023b5 --- /dev/null +++ b/java/src/game/collect/RegularImmutableMap.java @@ -0,0 +1,205 @@ +/* +* Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.CollectPreconditions.checkEntryNotNull; + +import game.collect.ImmutableMapEntry.TerminalEntry; + +/** + * Implementation of {@link ImmutableMap} with two or more entries. + * + * @author Jesse Wilson + * @author Kevin Bourrillion + * @author Gregory Kick + */ + +final class RegularImmutableMap extends ImmutableMap { + + // entries in insertion order + private final transient ImmutableMapEntry[] entries; + // array of linked lists of entries + private final transient ImmutableMapEntry[] table; + // 'and' with an int to get a table index + private final transient int mask; + + RegularImmutableMap(TerminalEntry... theEntries) { + this(theEntries.length, theEntries); + } + + /** + * Constructor for RegularImmutableMap that takes as input an array of {@code TerminalEntry} + * entries. Assumes that these entries have already been checked for null. + * + *

This allows reuse of the entry objects from the array in the actual implementation. + */ + RegularImmutableMap(int size, TerminalEntry[] theEntries) { + entries = createEntryArray(size); + int tableSize = Hashing.closedTableSize(size, MAX_LOAD_FACTOR); + table = createEntryArray(tableSize); + mask = tableSize - 1; + for (int entryIndex = 0; entryIndex < size; entryIndex++) { + + TerminalEntry entry = (TerminalEntry) theEntries[entryIndex]; + K key = entry.getKey(); + int tableIndex = Hashing.smear(key.hashCode()) & mask; + ImmutableMapEntry existing = table[tableIndex]; + // prepend, not append, so the entries can be immutable + ImmutableMapEntry newEntry = (existing == null) + ? entry + : new NonTerminalMapEntry(entry, existing); + table[tableIndex] = newEntry; + entries[entryIndex] = newEntry; + checkNoConflictInBucket(key, newEntry, existing); + } + } + + /** + * Constructor for RegularImmutableMap that makes no assumptions about the input entries. + */ + RegularImmutableMap(Entry[] theEntries) { + int size = theEntries.length; + entries = createEntryArray(size); + int tableSize = Hashing.closedTableSize(size, MAX_LOAD_FACTOR); + table = createEntryArray(tableSize); + mask = tableSize - 1; + for (int entryIndex = 0; entryIndex < size; entryIndex++) { + // all our callers carefully put in only Entrys + Entry entry = (Entry) theEntries[entryIndex]; + K key = entry.getKey(); + V value = entry.getValue(); + checkEntryNotNull(key, value); + int tableIndex = Hashing.smear(key.hashCode()) & mask; + ImmutableMapEntry existing = table[tableIndex]; + // prepend, not append, so the entries can be immutable + ImmutableMapEntry newEntry = (existing == null) + ? new TerminalEntry(key, value) + : new NonTerminalMapEntry(key, value, existing); + table[tableIndex] = newEntry; + entries[entryIndex] = newEntry; + checkNoConflictInBucket(key, newEntry, existing); + } + } + + private void checkNoConflictInBucket( + K key, ImmutableMapEntry entry, ImmutableMapEntry bucketHead) { + for (; bucketHead != null; bucketHead = bucketHead.getNextInKeyBucket()) { + checkNoConflict(!key.equals(bucketHead.getKey()), "key", entry, bucketHead); + } + } + + 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; + } + + @Override + ImmutableMapEntry getNextInKeyBucket() { + return nextInKeyBucket; + } + + @Override + + ImmutableMapEntry getNextInValueBucket() { + return null; + } + + } + + /** + * Closed addressing tends to perform well even with high load factors. + * Being conservative here ensures that the table is still likely to be + * relatively sparse (hence it misses fast) while saving space. + */ + private static final double MAX_LOAD_FACTOR = 1.2; + + /** + * Creates an {@code ImmutableMapEntry} array to hold parameterized entries. The + * result must never be upcast back to ImmutableMapEntry[] (or Object[], etc.), or + * allowed to escape the class. + */ + // Safe as long as the javadocs are followed + private ImmutableMapEntry[] createEntryArray(int size) { + return new ImmutableMapEntry[size]; + } + + @Override public V get(Object key) { + if (key == null) { + return null; + } + int index = Hashing.smear(key.hashCode()) & mask; + for (ImmutableMapEntry entry = table[index]; + entry != null; + entry = entry.getNextInKeyBucket()) { + K candidateKey = entry.getKey(); + + /* + * Assume that equals uses the == optimization when appropriate, and that + * it would check hash codes as an optimization when appropriate. If we + * did these things, it would just make things worse for the most + * performance-conscious users. + */ + if (key.equals(candidateKey)) { + return entry.getValue(); + } + } + return null; + } + + @Override + public int size() { + return entries.length; + } + + @Override boolean isPartialView() { + return false; + } + + @Override + ImmutableSet> createEntrySet() { + return new EntrySet(); + } + + // uses writeReplace(), not default serialization + private class EntrySet extends ImmutableMapEntrySet { + @Override ImmutableMap map() { + return RegularImmutableMap.this; + } + + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList> createAsList() { + return new RegularImmutableAsList>(this, entries); + } + } + + // This class is never actually serialized directly, but we have to make the + // warning go away (and suppressing would suppress for all nested classes too) + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/collect/RegularImmutableSet.java b/java/src/game/collect/RegularImmutableSet.java new file mode 100644 index 0000000..c10f204 --- /dev/null +++ b/java/src/game/collect/RegularImmutableSet.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +/** + * Implementation of {@link ImmutableSet} with two or more elements. + * + * @author Kevin Bourrillion + */ + + // uses writeReplace(), not default serialization +final class RegularImmutableSet extends ImmutableSet { + private final Object[] elements; + // the same elements in hashed positions (plus nulls) +// @VisibleForTesting + final transient Object[] table; + // 'and' with an int to get a valid table index. + 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; + } + + @Override public boolean contains(Object target) { + if (target == null) { + return false; + } + for (int i = Hashing.smear(target.hashCode()); true; i++) { + Object candidate = table[i & mask]; + if (candidate == null) { + return false; + } + if (candidate.equals(target)) { + return true; + } + } + } + + @Override + public int size() { + return elements.length; + } + + // all elements are E's + @Override + public UnmodifiableIterator iterator() { + return (UnmodifiableIterator) Iterators.forArray(elements); + } + + @Override + int copyIntoArray(Object[] dst, int offset) { + System.arraycopy(elements, 0, dst, offset, elements.length); + return offset + elements.length; + } + + @Override + ImmutableList createAsList() { + return new RegularImmutableAsList(this, elements); + } + + @Override + boolean isPartialView() { + return false; + } + + @Override public int hashCode() { + return hashCode; + } + + @Override boolean isHashCodeFast() { + return true; + } +} diff --git a/java/src/game/collect/RegularImmutableTable.java b/java/src/game/collect/RegularImmutableTable.java new file mode 100644 index 0000000..9f48e21 --- /dev/null +++ b/java/src/game/collect/RegularImmutableTable.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkNotNull; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * An implementation of {@link ImmutableTable} holding an arbitrary number of + * cells. + * + * @author Gregory Kick + */ + +abstract class RegularImmutableTable extends ImmutableTable { + RegularImmutableTable() {} + + abstract Cell getCell(int iterationIndex); + + @Override + final ImmutableSet> createCellSet() { + return isEmpty() ? ImmutableSet.>of() : new CellSet(); + } + + private final class CellSet extends ImmutableSet> { + @Override + public int size() { + return RegularImmutableTable.this.size(); + } + + @Override + public UnmodifiableIterator> iterator() { + return asList().iterator(); + } + + @Override + ImmutableList> createAsList() { + return new ImmutableAsList>() { + @Override + public Cell get(int index) { + return getCell(index); + } + + @Override + ImmutableCollection> delegateCollection() { + return CellSet.this; + } + }; + } + + @Override + public boolean contains(Object object) { + if (object instanceof Cell) { + Cell cell = (Cell) object; + Object value = get(cell.getRowKey(), cell.getColumnKey()); + return value != null && value.equals(cell.getValue()); + } + return false; + } + + @Override + boolean isPartialView() { + return false; + } + } + + abstract V getValue(int iterationIndex); + + @Override + final ImmutableCollection createValues() { + return isEmpty() ? ImmutableList.of() : new Values(); + } + + private final class Values extends ImmutableList { + @Override + public int size() { + return RegularImmutableTable.this.size(); + } + + @Override + public V get(int index) { + return getValue(index); + } + + @Override + boolean isPartialView() { + return true; + } + } + + static RegularImmutableTable forCells( + List> cells, + final Comparator rowComparator, + final Comparator columnComparator) { + checkNotNull(cells); + if (rowComparator != null || columnComparator != null) { + /* + * This sorting logic leads to a cellSet() ordering that may not be expected and that isn't + * documented in the Javadoc. If a row Comparator is provided, cellSet() iterates across the + * columns in the first row, the columns in the second row, etc. If a column Comparator is + * provided but a row Comparator isn't, cellSet() iterates across the rows in the first + * column, the rows in the second column, etc. + */ + Comparator> comparator = new Comparator>() { + @Override public int compare(Cell cell1, Cell cell2) { + int rowCompare = (rowComparator == null) ? 0 + : rowComparator.compare(cell1.getRowKey(), cell2.getRowKey()); + if (rowCompare != 0) { + return rowCompare; + } + 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, null, null); + } + + /** + * A factory that chooses the most space-efficient representation of the + * table. + */ + private static final RegularImmutableTable + forCellsInternal(Iterable> cells, + Comparator rowComparator, + Comparator columnComparator) { + ImmutableSet.Builder rowSpaceBuilder = ImmutableSet.builder(); + ImmutableSet.Builder columnSpaceBuilder = ImmutableSet.builder(); + ImmutableList> cellList = ImmutableList.copyOf(cells); + for (Cell cell : cellList) { + rowSpaceBuilder.add(cell.getRowKey()); + columnSpaceBuilder.add(cell.getColumnKey()); + } + + ImmutableSet rowSpace = rowSpaceBuilder.build(); + if (rowComparator != null) { + List rowList = Lists.newArrayList(rowSpace); + Collections.sort(rowList, rowComparator); + rowSpace = ImmutableSet.copyOf(rowList); + } + ImmutableSet columnSpace = columnSpaceBuilder.build(); + if (columnComparator != null) { + List columnList = Lists.newArrayList(columnSpace); + Collections.sort(columnList, columnComparator); + columnSpace = ImmutableSet.copyOf(columnList); + } + + // use a dense table if more than half of the cells have values + // TODO(gak): tune this condition based on empirical evidence + return (cellList.size() > (((long) rowSpace.size() * columnSpace.size()) / 2)) ? + new DenseImmutableTable(cellList, rowSpace, columnSpace) : + new SparseImmutableTable(cellList, rowSpace, columnSpace); + } +} diff --git a/java/src/game/collect/Sets.java b/java/src/game/collect/Sets.java new file mode 100644 index 0000000..3d807ba --- /dev/null +++ b/java/src/game/collect/Sets.java @@ -0,0 +1,1607 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkNotNull; + +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +/** + * Static utility methods pertaining to {@link Set} instances. Also see this + * class's counterparts {@link Lists}, {@link Maps} and {@link Queues}. + * + *

See the Guava User Guide article on + * {@code Sets}. + * + * @author Kevin Bourrillion + * @author Jared Levy + * @author Chris Povirk + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Sets { + private Sets() {} + + /** + * {@link AbstractSet} substitute without the potentially-quadratic + * {@code removeAll} implementation. + */ + abstract static class ImprovedAbstractSet extends AbstractSet { + @Override + public boolean removeAll(Collection c) { + return removeAllImpl(this, c); + } + + @Override + public boolean retainAll(Collection c) { + return super.retainAll(checkNotNull(c)); // GWT compatibility + } + } + + /** + * Returns an immutable set instance containing the given enum elements. + * Internally, the returned set will be backed by an {@link EnumSet}. + * + *

The iteration order of the returned set follows the enum's iteration + * order, not the order in which the elements are provided to the method. + * + * @param anElement one of the elements the set should contain + * @param otherElements the rest of the elements the set should contain + * @return an immutable set containing those elements, minus duplicates + */ + // http://code.google.com/p/google-web-toolkit/issues/detail?id=3028 +// +// public static > ImmutableSet immutableEnumSet( +// E anElement, E... otherElements) { +// return ImmutableEnumSet.asImmutable(EnumSet.of(anElement, otherElements)); +// } +// +// /** +// * Returns an immutable set instance containing the given enum elements. +// * Internally, the returned set will be backed by an {@link EnumSet}. +// * +// *

The iteration order of the returned set follows the enum's iteration +// * order, not the order in which the elements appear in the given collection. +// * +// * @param elements the elements, all of the same {@code enum} type, that the +// * set should contain +// * @return an immutable set containing those elements, minus duplicates +// */ +// // http://code.google.com/p/google-web-toolkit/issues/detail?id=3028 +// +// public static > ImmutableSet immutableEnumSet( +// Iterable elements) { +// if (elements instanceof ImmutableEnumSet) { +// return (ImmutableEnumSet) elements; +// } else if (elements instanceof Collection) { +// Collection collection = (Collection) elements; +// if (collection.isEmpty()) { +// return ImmutableSet.of(); +// } else { +// return ImmutableEnumSet.asImmutable(EnumSet.copyOf(collection)); +// } +// } else { +// Iterator itr = elements.iterator(); +// if (itr.hasNext()) { +// EnumSet enumSet = EnumSet.of(itr.next()); +// Iterators.addAll(enumSet, itr); +// return ImmutableEnumSet.asImmutable(enumSet); +// } else { +// return ImmutableSet.of(); +// } +// } +// } + + /** + * Returns a new {@code EnumSet} instance containing the given elements. + * Unlike {@link EnumSet#copyOf(Collection)}, this method does not produce an + * exception on an empty collection, and it may be called on any iterable, not + * just a {@code Collection}. + */ +// public static > EnumSet newEnumSet(Iterable iterable, +// Class elementType) { +// EnumSet set = EnumSet.noneOf(elementType); +// Iterables.addAll(set, iterable); +// return set; +// } + + // HashSet + + /** + * Creates a mutable, empty {@code HashSet} instance. + * + *

Note: if mutability is not required, use {@link + * ImmutableSet#of()} instead. + * + *

Note: if {@code E} is an {@link Enum} type, use {@link + * EnumSet#noneOf} instead. + * + * @return a new, empty {@code HashSet} + */ + public static HashSet newHashSet() { + return new HashSet(); + } + + /** + * Creates a mutable {@code HashSet} instance containing the given + * elements in unspecified order. + * + *

Note: if mutability is not required and the elements are + * non-null, use an overload of {@link ImmutableSet#of()} (for varargs) or + * {@link ImmutableSet#copyOf(Object[])} (for an array) instead. + * + *

Note: if {@code E} is an {@link Enum} type, use {@link + * EnumSet#of(Enum, Enum[])} instead. + * + * @param elements the elements that the set should contain + * @return a new {@code HashSet} containing those elements (minus duplicates) + */ + public static HashSet newHashSet(E... elements) { + HashSet set = newHashSetWithExpectedSize(elements.length); + Collections.addAll(set, elements); + return set; + } + + /** + * Creates a {@code HashSet} instance, with a high enough "initial capacity" + * that it should hold {@code expectedSize} elements without growth. + * This behavior cannot be broadly guaranteed, but it is observed to be true + * for OpenJDK 1.6. It also can't be guaranteed that the method isn't + * inadvertently oversizing the returned set. + * + * @param expectedSize the number of elements you expect to add to the + * returned set + * @return a new, empty {@code HashSet} with enough capacity to hold {@code + * expectedSize} elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static HashSet newHashSetWithExpectedSize(int expectedSize) { + return new HashSet(Maps.capacity(expectedSize)); + } + + /** + * Creates a mutable {@code HashSet} instance containing the given + * elements in unspecified order. + * + *

Note: if mutability is not required and the elements are + * non-null, use {@link ImmutableSet#copyOf(Iterable)} instead. + * + *

Note: if {@code E} is an {@link Enum} type, use + * {@link #newEnumSet(Iterable, Class)} instead. + * + * @param elements the elements that the set should contain + * @return a new {@code HashSet} containing those elements (minus duplicates) + */ + public static HashSet newHashSet(Iterable elements) { + return (elements instanceof Collection) + ? new HashSet(Filter.cast(elements)) + : newHashSet(elements.iterator()); + } + + /** + * Creates a mutable {@code HashSet} instance containing the given + * elements in unspecified order. + * + *

Note: if mutability is not required and the elements are + * non-null, use {@link ImmutableSet#copyOf(Iterable)} instead. + * + *

Note: if {@code E} is an {@link Enum} type, you should create an + * {@link EnumSet} instead. + * + * @param elements the elements that the set should contain + * @return a new {@code HashSet} containing those elements (minus duplicates) + */ + public static HashSet newHashSet(Iterator elements) { + HashSet set = newHashSet(); + Iterators.addAll(set, elements); + return set; + } + + + + /** + * Creates a thread-safe set backed by a hash map and containing the given + * elements. The set is backed by a {@link ConcurrentHashMap} instance, and + * thus carries the same concurrency guarantees. + * + *

Unlike {@code HashSet}, this class does NOT allow {@code null} to be + * used as an element. The set is serializable. + * + * @param elements the elements that the set should contain + * @return a new thread-safe set containing those elements (minus duplicates) + * @throws NullPointerException if {@code elements} or any of its contents is + * null + * @since 15.0 + */ +// public static Set newConcurrentHashSet( +// Iterable elements) { +// Set set = newConcurrentHashSet(); +// Iterables.addAll(set, elements); +// return set; +// } + + // LinkedHashSet + + + + /** + * Creates a {@code LinkedHashSet} instance, with a high enough "initial + * capacity" that it should hold {@code expectedSize} elements without + * growth. This behavior cannot be broadly guaranteed, but it is observed to + * be true for OpenJDK 1.6. It also can't be guaranteed that the method isn't + * inadvertently oversizing the returned set. + * + * @param expectedSize the number of elements you expect to add to the + * returned set + * @return a new, empty {@code LinkedHashSet} with enough capacity to hold + * {@code expectedSize} elements without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + * @since 11.0 + */ +// public static LinkedHashSet newLinkedHashSetWithExpectedSize( +// int expectedSize) { +// return new LinkedHashSet(Maps.capacity(expectedSize)); +// } + + /** + * Creates a mutable {@code LinkedHashSet} instance containing the + * given elements in order. + * + *

Note: if mutability is not required and the elements are + * non-null, use {@link ImmutableSet#copyOf(Iterable)} instead. + * + * @param elements the elements that the set should contain, in order + * @return a new {@code LinkedHashSet} containing those elements (minus + * duplicates) + */ +// public static LinkedHashSet newLinkedHashSet( +// Iterable elements) { +// if (elements instanceof Collection) { +// return new LinkedHashSet(Filter.cast(elements)); +// } +// LinkedHashSet set = newLinkedHashSet(); +// Iterables.addAll(set, elements); +// return set; +// } + + // TreeSet + + /** + * Creates a mutable, empty {@code TreeSet} instance sorted by the + * natural sort ordering of its elements. + * + *

Note: if mutability is not required, use {@link + * ImmutableSortedSet#of()} instead. + * + * @return a new, empty {@code TreeSet} + */ + public static TreeSet newTreeSet() { + return new TreeSet(); + } + + /** + * Creates a mutable {@code TreeSet} instance containing the given + * elements sorted by their natural ordering. + * + *

Note: if mutability is not required, use {@link + * ImmutableSortedSet#copyOf(Iterable)} instead. + * + *

Note: If {@code elements} is a {@code SortedSet} with an explicit + * comparator, this method has different behavior than + * {@link TreeSet#TreeSet(SortedSet)}, which returns a {@code TreeSet} with + * that comparator. + * + * @param elements the elements that the set should contain + * @return a new {@code TreeSet} containing those elements (minus duplicates) + */ +// public static TreeSet newTreeSet( +// Iterable elements) { +// TreeSet set = newTreeSet(); +// Iterables.addAll(set, elements); +// return set; +// } + + /** + * Creates a mutable, empty {@code TreeSet} instance with the given + * comparator. + * + *

Note: if mutability is not required, use {@code + * ImmutableSortedSet.orderedBy(comparator).build()} instead. + * + * @param comparator the comparator to use to sort the set + * @return a new, empty {@code TreeSet} + * @throws NullPointerException if {@code comparator} is null + */ +// public static TreeSet newTreeSet(Comparator comparator) { +// return new TreeSet(checkNotNull(comparator)); +// } + + + + /** + * Creates an empty {@code CopyOnWriteArraySet} instance. + * + *

Note: if you need an immutable empty {@link Set}, use + * {@link Collections#emptySet} instead. + * + * @return a new, empty {@code CopyOnWriteArraySet} + * @since 12.0 + */ +// +// public static CopyOnWriteArraySet newCopyOnWriteArraySet() { +// return new CopyOnWriteArraySet(); +// } +// +// /** +// * Creates a {@code CopyOnWriteArraySet} instance containing the given elements. +// * +// * @param elements the elements that the set should contain, in order +// * @return a new {@code CopyOnWriteArraySet} containing those elements +// * @since 12.0 +// */ +// +// public static CopyOnWriteArraySet newCopyOnWriteArraySet( +// Iterable elements) { +// // We copy elements to an ArrayList first, rather than incurring the +// // quadratic cost of adding them to the COWAS directly. +// Collection elementsCollection = (elements instanceof Collection) +// ? Filter.cast(elements) +// : Lists.newArrayList(elements); +// return new CopyOnWriteArraySet(elementsCollection); +// } + + /** + * Creates an {@code EnumSet} consisting of all enum values that are not in + * the specified collection. If the collection is an {@link EnumSet}, this + * method has the same behavior as {@link EnumSet#complementOf}. Otherwise, + * the specified collection must contain at least one element, in order to + * determine the element type. If the collection could be empty, use + * {@link #complementOf(Collection, Class)} instead of this method. + * + * @param collection the collection whose complement should be stored in the + * enum set + * @return a new, modifiable {@code EnumSet} containing all values of the enum + * that aren't present in the given collection + * @throws IllegalArgumentException if {@code collection} is not an + * {@code EnumSet} instance and contains no elements + */ +// public static > EnumSet complementOf( +// Collection collection) { +// if (collection instanceof EnumSet) { +// return EnumSet.complementOf((EnumSet) collection); +// } +// checkArgument(!collection.isEmpty(), +// "collection is empty; use the other version of this method"); +// Class type = collection.iterator().next().getDeclaringClass(); +// return makeComplementByHand(collection, type); +// } + + /** + * Creates an {@code EnumSet} consisting of all enum values that are not in + * the specified collection. This is equivalent to + * {@link EnumSet#complementOf}, but can act on any input collection, as long + * as the elements are of enum type. + * + * @param collection the collection whose complement should be stored in the + * {@code EnumSet} + * @param type the type of the elements in the set + * @return a new, modifiable {@code EnumSet} initially containing all the + * values of the enum not present in the given collection + */ +// public static > EnumSet complementOf( +// Collection collection, Class type) { +// 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; +// } + + /** + * An unmodifiable view of a set which may be backed by other sets; this view + * will change as the backing sets do. Contains methods to copy the data into + * a new set which will then remain stable. There is usually no reason to + * retain a reference of type {@code SetView}; typically, you either use it + * as a plain {@link Set}, or immediately invoke {@link #immutableCopy} or + * {@link #copyInto} and forget the {@code SetView} itself. + * + * @since 2.0 (imported from Google Collections Library) + */ +// public abstract static class SetView extends AbstractSet { +// private SetView() {} // no subclasses but our own +// +// /** +// * Returns an immutable copy of the current contents of this set view. +// * Does not support null elements. +// * +// *

Warning: this may have unexpected results if a backing set of +// * this view uses a nonstandard notion of equivalence, for example if it is +// * a {@link TreeSet} using a comparator that is inconsistent with {@link +// * Object#equals(Object)}. +// */ +// public ImmutableSet immutableCopy() { +// return ImmutableSet.copyOf(this); +// } +// +// /** +// * Copies the current contents of this set view into an existing set. This +// * method has equivalent behavior to {@code set.addAll(this)}, assuming that +// * all the sets involved are based on the same notion of equivalence. +// * +// * @return a reference to {@code set}, for convenience +// */ +// // Note: S should logically extend Set but can't due to either +// // some javac bug or some weirdness in the spec, not sure which. +// public > S copyInto(S set) { +// set.addAll(this); +// return set; +// } +// } + + /** + * Returns an unmodifiable view of the union of two sets. The returned + * set contains all elements that are contained in either backing set. + * Iterating over the returned set iterates first over all the elements of + * {@code set1}, then over each element of {@code set2}, in order, that is not + * contained in {@code set1}. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based on + * different equivalence relations (as {@link HashSet}, {@link TreeSet}, and + * the {@link Map#keySet} of an {@code IdentityHashMap} all are). + * + *

Note: The returned view performs better when {@code set1} is the + * smaller of the two sets. If you have reason to believe one of your sets + * will generally be smaller than the other, pass it first. + * + *

Further, note that the current implementation is not suitable for nested + * {@code union} views, i.e. the following should be avoided when in a loop: + * {@code union = Sets.union(union, anotherSet);}, since iterating over the resulting + * set has a cubic complexity to the depth of the nesting. + */ +// public static SetView union( +// final Set set1, final Set set2) { +// checkNotNull(set1, "set1"); +// checkNotNull(set2, "set2"); +// +// final Set set2minus1 = difference(set2, set1); +// +// return new SetView() { +// @Override public int size() { +// return set1.size() + set2minus1.size(); +// } +// @Override public boolean isEmpty() { +// return set1.isEmpty() && set2.isEmpty(); +// } +// @Override public Iterator iterator() { +// return Iterators.unmodifiableIterator( +// Iterators.concat(set1.iterator(), set2minus1.iterator())); +// } +// @Override public boolean contains(Object object) { +// return set1.contains(object) || set2.contains(object); +// } +// @Override public > S copyInto(S set) { +// set.addAll(set1); +// set.addAll(set2); +// return set; +// } +// @Override public ImmutableSet immutableCopy() { +// return new ImmutableSet.Builder() +// .addAll(set1).addAll(set2).build(); +// } +// }; +// } + + /** + * Returns an unmodifiable view of the intersection of two sets. The + * returned set contains all elements that are contained by both backing sets. + * The iteration order of the returned set matches that of {@code set1}. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based + * on different equivalence relations (as {@code HashSet}, {@code TreeSet}, + * and the keySet of an {@code IdentityHashMap} all are). + * + *

Note: The returned view performs slightly better when {@code + * set1} is the smaller of the two sets. If you have reason to believe one of + * your sets will generally be smaller than the other, pass it first. + * Unfortunately, since this method sets the generic type of the returned set + * based on the type of the first set passed, this could in rare cases force + * you to make a cast, for example:

   {@code
+   *
+   *   Set aFewBadObjects = ...
+   *   Set manyBadStrings = ...
+   *
+   *   // impossible for a non-String to be in the intersection
+   *   SuppressWarnings("unchecked")
+   *   Set badStrings = (Set) Sets.intersection(
+   *       aFewBadObjects, manyBadStrings);}
+   *
+   * 

This is unfortunate, but should come up only very rarely. + */ +// public static SetView intersection( +// final Set set1, final Set set2) { +// checkNotNull(set1, "set1"); +// checkNotNull(set2, "set2"); +// +// final Predicate inSet2 = Predicates.in(set2); +// return new SetView() { +// @Override public Iterator iterator() { +// return Iterators.filter(set1.iterator(), inSet2); +// } +// @Override public int size() { +// return Iterators.size(iterator()); +// } +// @Override public boolean isEmpty() { +// return !iterator().hasNext(); +// } +// @Override public boolean contains(Object object) { +// return set1.contains(object) && set2.contains(object); +// } +// @Override public boolean containsAll(Collection collection) { +// return set1.containsAll(collection) +// && set2.containsAll(collection); +// } +// }; +// } + + /** + * Returns an unmodifiable view of the difference of two sets. The + * returned set contains all elements that are contained by {@code set1} and + * not contained by {@code set2}. {@code set2} may also contain elements not + * present in {@code set1}; these are simply ignored. The iteration order of + * the returned set matches that of {@code set1}. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based + * on different equivalence relations (as {@code HashSet}, {@code TreeSet}, + * and the keySet of an {@code IdentityHashMap} all are). + */ +// public static SetView difference( +// final Set set1, final Set set2) { +// checkNotNull(set1, "set1"); +// checkNotNull(set2, "set2"); +// +// final Predicate notInSet2 = Predicates.not(Predicates.in(set2)); +// return new SetView() { +// @Override public Iterator iterator() { +// return Iterators.filter(set1.iterator(), notInSet2); +// } +// @Override public int size() { +// return Iterators.size(iterator()); +// } +// @Override public boolean isEmpty() { +// return set2.containsAll(set1); +// } +// @Override public boolean contains(Object element) { +// return set1.contains(element) && !set2.contains(element); +// } +// }; +// } + + /** + * Returns an unmodifiable view of the symmetric difference of two + * sets. The returned set contains all elements that are contained in either + * {@code set1} or {@code set2} but not in both. The iteration order of the + * returned set is undefined. + * + *

Results are undefined if {@code set1} and {@code set2} are sets based + * on different equivalence relations (as {@code HashSet}, {@code TreeSet}, + * and the keySet of an {@code IdentityHashMap} all are). + * + * @since 3.0 + */ +// public static SetView symmetricDifference( +// Set set1, Set set2) { +// checkNotNull(set1, "set1"); +// checkNotNull(set2, "set2"); +// +// // TODO(kevinb): Replace this with a more efficient implementation +// return difference(union(set1, set2), intersection(set1, set2)); +// } + + /** + * Returns the elements of {@code unfiltered} that satisfy a predicate. The + * returned set is a live view of {@code unfiltered}; changes to one affect + * the other. + * + *

The resulting set's iterator does not support {@code remove()}, but all + * other set methods are supported. When given an element that doesn't satisfy + * the predicate, the set's {@code add()} and {@code addAll()} methods throw + * an {@link IllegalArgumentException}. When methods such as {@code + * removeAll()} and {@code clear()} are called on the filtered set, only + * elements that satisfy the filter will be removed from the underlying set. + * + *

The returned set isn't threadsafe or serializable, even if + * {@code unfiltered} is. + * + *

Many of the filtered set's methods, such as {@code size()}, iterate + * across every element in the underlying set and determine which elements + * satisfy the filter. When a live view is not needed, it may be faster + * to copy {@code Iterables.filter(unfiltered, predicate)} and use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, + * as documented at {@link Predicate#apply}. Do not provide a predicate such + * as {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent + * with equals. (See {@link Iterables#filter(Iterable, Class)} for related + * functionality.) + */ + // TODO(kevinb): how to omit that last sentence when building GWT javadoc? +// public static Set filter( +// Set unfiltered, Predicate predicate) { +// if (unfiltered instanceof SortedSet) { +// return filter((SortedSet) unfiltered, predicate); +// } +// if (unfiltered instanceof FilteredSet) { +// // Support clear(), removeAll(), and retainAll() when filtering a filtered +// // collection. +// FilteredSet filtered = (FilteredSet) unfiltered; +// Predicate combinedPredicate +// = Predicates.and(filtered.predicate, predicate); +// return new FilteredSet( +// (Set) filtered.unfiltered, combinedPredicate); +// } +// +// return new FilteredSet( +// checkNotNull(unfiltered), checkNotNull(predicate)); +// } + +// private static class FilteredSet extends FilteredCollection +// implements Set { +// FilteredSet(Set unfiltered, Predicate predicate) { +// super(unfiltered, predicate); +// } +// +// @Override public boolean equals(Object object) { +// return equalsImpl(this, object); +// } +// +// @Override public int hashCode() { +// return hashCodeImpl(this); +// } +// } + + /** + * Returns the elements of a {@code SortedSet}, {@code unfiltered}, that + * satisfy a predicate. The returned set is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

The resulting set's iterator does not support {@code remove()}, but all + * other set methods are supported. When given an element that doesn't satisfy + * the predicate, the set's {@code add()} and {@code addAll()} methods throw + * an {@link IllegalArgumentException}. When methods such as + * {@code removeAll()} and {@code clear()} are called on the filtered set, + * only elements that satisfy the filter will be removed from the underlying + * set. + * + *

The returned set isn't threadsafe or serializable, even if + * {@code unfiltered} is. + * + *

Many of the filtered set's methods, such as {@code size()}, iterate across + * every element in the underlying set and determine which elements satisfy + * the filter. When a live view is not needed, it may be faster to copy + * {@code Iterables.filter(unfiltered, predicate)} and use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, + * as documented at {@link Predicate#apply}. Do not provide a predicate such as + * {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent with + * equals. (See {@link Iterables#filter(Iterable, Class)} for related + * functionality.) + * + * @since 11.0 + */ +// public static SortedSet filter( +// SortedSet unfiltered, Predicate predicate) { +// return Platform.setsFilterSortedSet(unfiltered, predicate); +// } + +// static SortedSet filterSortedIgnoreNavigable( +// SortedSet unfiltered, Predicate predicate) { +// if (unfiltered instanceof FilteredSet) { +// // Support clear(), removeAll(), and retainAll() when filtering a filtered +// // collection. +// FilteredSet filtered = (FilteredSet) unfiltered; +// Predicate combinedPredicate +// = Predicates.and(filtered.predicate, predicate); +// return new FilteredSortedSet( +// (SortedSet) filtered.unfiltered, combinedPredicate); +// } +// +// return new FilteredSortedSet( +// checkNotNull(unfiltered), checkNotNull(predicate)); +// } +// +// private static class FilteredSortedSet extends FilteredSet +// implements SortedSet { +// +// FilteredSortedSet(SortedSet unfiltered, Predicate predicate) { +// super(unfiltered, predicate); +// } +// +// @Override +// public Comparator comparator() { +// return ((SortedSet) unfiltered).comparator(); +// } +// +// @Override +// public SortedSet subSet(E fromElement, E toElement) { +// return new FilteredSortedSet(((SortedSet) unfiltered).subSet(fromElement, toElement), +// predicate); +// } +// +// @Override +// public SortedSet headSet(E toElement) { +// return new FilteredSortedSet(((SortedSet) unfiltered).headSet(toElement), predicate); +// } +// +// @Override +// public SortedSet tailSet(E fromElement) { +// return new FilteredSortedSet(((SortedSet) unfiltered).tailSet(fromElement), predicate); +// } +// +// @Override +// public E first() { +// return iterator().next(); +// } +// +// @Override +// public E last() { +// SortedSet sortedUnfiltered = (SortedSet) unfiltered; +// while (true) { +// E element = sortedUnfiltered.last(); +// if (predicate.apply(element)) { +// return element; +// } +// sortedUnfiltered = sortedUnfiltered.headSet(element); +// } +// } +// } + + /** + * Returns the elements of a {@code NavigableSet}, {@code unfiltered}, that + * satisfy a predicate. The returned set is a live view of {@code unfiltered}; + * changes to one affect the other. + * + *

The resulting set's iterator does not support {@code remove()}, but all + * other set methods are supported. When given an element that doesn't satisfy + * the predicate, the set's {@code add()} and {@code addAll()} methods throw + * an {@link IllegalArgumentException}. When methods such as + * {@code removeAll()} and {@code clear()} are called on the filtered set, + * only elements that satisfy the filter will be removed from the underlying + * set. + * + *

The returned set isn't threadsafe or serializable, even if + * {@code unfiltered} is. + * + *

Many of the filtered set's methods, such as {@code size()}, iterate across + * every element in the underlying set and determine which elements satisfy + * the filter. When a live view is not needed, it may be faster to copy + * {@code Iterables.filter(unfiltered, predicate)} and use the copy. + * + *

Warning: {@code predicate} must be consistent with equals, + * as documented at {@link Predicate#apply}. Do not provide a predicate such as + * {@code Predicates.instanceOf(ArrayList.class)}, which is inconsistent with + * equals. (See {@link Iterables#filter(Iterable, Class)} for related + * functionality.) + * + * @since 14.0 + */ +// +// +// public static NavigableSet filter( +// NavigableSet unfiltered, Predicate predicate) { +// if (unfiltered instanceof FilteredSet) { +// // Support clear(), removeAll(), and retainAll() when filtering a filtered +// // collection. +// FilteredSet filtered = (FilteredSet) unfiltered; +// Predicate combinedPredicate +// = Predicates.and(filtered.predicate, predicate); +// return new FilteredNavigableSet( +// (NavigableSet) filtered.unfiltered, combinedPredicate); +// } +// +// return new FilteredNavigableSet( +// checkNotNull(unfiltered), checkNotNull(predicate)); +// } +// +// +// private static class FilteredNavigableSet extends FilteredSortedSet +// implements NavigableSet { +// FilteredNavigableSet(NavigableSet unfiltered, Predicate predicate) { +// super(unfiltered, predicate); +// } +// +// NavigableSet unfiltered() { +// return (NavigableSet) unfiltered; +// } +// +// @Override +// +// public E lower(E e) { +// return Iterators.getNext(headSet(e, false).descendingIterator(), null); +// } +// +// @Override +// +// public E floor(E e) { +// return Iterators.getNext(headSet(e, true).descendingIterator(), null); +// } +// +// @Override +// public E ceiling(E e) { +// return Iterables.getFirst(tailSet(e, true), null); +// } +// +// @Override +// public E higher(E e) { +// return Iterables.getFirst(tailSet(e, false), null); +// } +// +// @Override +// public E pollFirst() { +// return Iterables.removeFirstMatching(unfiltered(), predicate); +// } +// +// @Override +// public E pollLast() { +// return Iterables.removeFirstMatching(unfiltered().descendingSet(), predicate); +// } +// +// @Override +// public NavigableSet descendingSet() { +// return Sets.filter(unfiltered().descendingSet(), predicate); +// } +// +// @Override +// public Iterator descendingIterator() { +// return Iterators.filter(unfiltered().descendingIterator(), predicate); +// } +// +// @Override +// public E last() { +// return descendingIterator().next(); +// } +// +// @Override +// public NavigableSet subSet( +// E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { +// return filter( +// unfiltered().subSet(fromElement, fromInclusive, toElement, toInclusive), predicate); +// } +// +// @Override +// public NavigableSet headSet(E toElement, boolean inclusive) { +// return filter(unfiltered().headSet(toElement, inclusive), predicate); +// } +// +// @Override +// public NavigableSet tailSet(E fromElement, boolean inclusive) { +// return filter(unfiltered().tailSet(fromElement, inclusive), predicate); +// } +// } + + /** + * Returns every possible list that can be formed by choosing one element + * from each of the given sets in order; the "n-ary + * Cartesian + * product" of the sets. For example:

   {@code
+   *
+   *   Sets.cartesianProduct(ImmutableList.of(
+   *       ImmutableSet.of(1, 2),
+   *       ImmutableSet.of("A", "B", "C")))}
+ * + *

returns a set containing six lists: + * + *

    + *
  • {@code ImmutableList.of(1, "A")} + *
  • {@code ImmutableList.of(1, "B")} + *
  • {@code ImmutableList.of(1, "C")} + *
  • {@code ImmutableList.of(2, "A")} + *
  • {@code ImmutableList.of(2, "B")} + *
  • {@code ImmutableList.of(2, "C")} + *
+ * + *

The result is guaranteed to be in the "traditional", lexicographical + * order for Cartesian products that you would get from nesting for loops: + *

   {@code
+   *
+   *   for (B b0 : sets.get(0)) {
+   *     for (B b1 : sets.get(1)) {
+   *       ...
+   *       ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+   *       // operate on tuple
+   *     }
+   *   }}
+ * + *

Note that if any input set is empty, the Cartesian product will also be + * empty. If no sets at all are provided (an empty list), the resulting + * Cartesian product has one element, an empty list (counter-intuitive, but + * mathematically consistent). + * + *

Performance notes: while the cartesian product of sets of size + * {@code m, n, p} is a set of size {@code m x n x p}, its actual memory + * consumption is much smaller. When the cartesian set is constructed, the + * input sets are merely copied. Only as the resulting set is iterated are the + * individual lists created, and these are not retained after iteration. + * + * @param sets the sets to choose elements from, in the order that + * the elements chosen from those sets should appear in the resulting + * lists + * @param any common base class shared by all axes (often just {@link + * Object}) + * @return the Cartesian product, as an immutable set containing immutable + * lists + * @throws NullPointerException if {@code sets}, any one of the {@code sets}, + * or any element of a provided set is null + * @since 2.0 + */ +// public static Set> cartesianProduct( +// List> sets) { +// return CartesianSet.create(sets); +// } + + /** + * Returns every possible list that can be formed by choosing one element + * from each of the given sets in order; the "n-ary + * Cartesian + * product" of the sets. For example:

   {@code
+   *
+   *   Sets.cartesianProduct(
+   *       ImmutableSet.of(1, 2),
+   *       ImmutableSet.of("A", "B", "C"))}
+ * + *

returns a set containing six lists: + * + *

    + *
  • {@code ImmutableList.of(1, "A")} + *
  • {@code ImmutableList.of(1, "B")} + *
  • {@code ImmutableList.of(1, "C")} + *
  • {@code ImmutableList.of(2, "A")} + *
  • {@code ImmutableList.of(2, "B")} + *
  • {@code ImmutableList.of(2, "C")} + *
+ * + *

The result is guaranteed to be in the "traditional", lexicographical + * order for Cartesian products that you would get from nesting for loops: + *

   {@code
+   *
+   *   for (B b0 : sets.get(0)) {
+   *     for (B b1 : sets.get(1)) {
+   *       ...
+   *       ImmutableList tuple = ImmutableList.of(b0, b1, ...);
+   *       // operate on tuple
+   *     }
+   *   }}
+ * + *

Note that if any input set is empty, the Cartesian product will also be + * empty. If no sets at all are provided (an empty list), the resulting + * Cartesian product has one element, an empty list (counter-intuitive, but + * mathematically consistent). + * + *

Performance notes: while the cartesian product of sets of size + * {@code m, n, p} is a set of size {@code m x n x p}, its actual memory + * consumption is much smaller. When the cartesian set is constructed, the + * input sets are merely copied. Only as the resulting set is iterated are the + * individual lists created, and these are not retained after iteration. + * + * @param sets the sets to choose elements from, in the order that + * the elements chosen from those sets should appear in the resulting + * lists + * @param any common base class shared by all axes (often just {@link + * Object}) + * @return the Cartesian product, as an immutable set containing immutable + * lists + * @throws NullPointerException if {@code sets}, any one of the {@code sets}, + * or any element of a provided set is null + * @since 2.0 + */ +// public static Set> cartesianProduct( +// Set... sets) { +// return cartesianProduct(Arrays.asList(sets)); +// } +// +// private static final class CartesianSet +// extends ForwardingCollection> implements Set> { +// private transient final ImmutableList> axes; +// private transient final CartesianList delegate; +// +// static Set> create(List> sets) { +// ImmutableList.Builder> axesBuilder = +// new ImmutableList.Builder>(sets.size()); +// for (Set set : sets) { +// ImmutableSet copy = ImmutableSet.copyOf(set); +// if (copy.isEmpty()) { +// return ImmutableSet.of(); +// } +// axesBuilder.add(copy); +// } +// final ImmutableList> axes = axesBuilder.build(); +// ImmutableList> listAxes = new ImmutableList>() { +// +// @Override +// public int size() { +// return axes.size(); +// } +// +// @Override +// public List get(int index) { +// return axes.get(index).asList(); +// } +// +// @Override +// boolean isPartialView() { +// return true; +// } +// }; +// return new CartesianSet(axes, new CartesianList(listAxes)); +// } +// +// private CartesianSet( +// ImmutableList> axes, CartesianList delegate) { +// this.axes = axes; +// this.delegate = delegate; +// } +// +// @Override +// protected Collection> delegate() { +// return delegate; +// } +// +// @Override public boolean equals(Object object) { +// // Warning: this is broken if size() == 0, so it is critical that we +// // substitute an empty ImmutableSet to the user in place of this +// if (object instanceof CartesianSet) { +// CartesianSet that = (CartesianSet) object; +// return this.axes.equals(that.axes); +// } +// return super.equals(object); +// } +// +// @Override +// public int hashCode() { +// // Warning: this is broken if size() == 0, so it is critical that we +// // substitute an empty ImmutableSet to the user in place of this +// +// // It's a weird formula, but tests prove it works. +// int adjust = size() - 1; +// for (int i = 0; i < axes.size(); i++) { +// adjust *= 31; +// adjust = ~~adjust; +// // in GWT, we have to deal with integer overflow carefully +// } +// int hash = 1; +// for (Set axis : axes) { +// hash = 31 * hash + (size() / axis.size() * axis.hashCode()); +// +// hash = ~~hash; +// } +// hash += adjust; +// return ~~hash; +// } +// } + + /** + * Returns the set of all possible subsets of {@code set}. For example, + * {@code powerSet(ImmutableSet.of(1, 2))} returns the set {@code {{}, + * {1}, {2}, {1, 2}}}. + * + *

Elements appear in these subsets in the same iteration order as they + * appeared in the input set. The order in which these subsets appear in the + * outer set is undefined. Note that the power set of the empty set is not the + * empty set, but a one-element set containing the empty set. + * + *

The returned set and its constituent sets use {@code equals} to decide + * whether two elements are identical, even if the input set uses a different + * concept of equivalence. + * + *

Performance notes: while the power set of a set with size {@code + * n} is of size {@code 2^n}, its memory usage is only {@code O(n)}. When the + * power set is constructed, the input set is merely copied. Only as the + * power set is iterated are the individual subsets created, and these subsets + * themselves occupy only a small constant amount of memory. + * + * @param set the set of elements to construct a power set from + * @return the power set, as an immutable set of immutable sets + * @throws IllegalArgumentException if {@code set} has more than 30 unique + * elements (causing the power set size to exceed the {@code int} range) + * @throws NullPointerException if {@code set} is or contains {@code null} + * @see Power set article at + * Wikipedia + * @since 4.0 + */ +// +// public static Set> powerSet(Set set) { +// return new PowerSet(set); +// } + +// 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; +// } +// +// @Override +// public Iterator iterator() { +// return new UnmodifiableIterator() { +// final ImmutableList elements = inputSet.keySet().asList(); +// int remainingSetBits = mask; +// +// @Override +// public boolean hasNext() { +// return remainingSetBits != 0; +// } +// +// @Override +// public E next() { +// int index = Integer.numberOfTrailingZeros(remainingSetBits); +// if (index == 32) { +// throw new NoSuchElementException(); +// } +// remainingSetBits &= ~(1 << index); +// return elements.get(index); +// } +// }; +// } +// +// @Override +// public int size() { +// return Integer.bitCount(mask); +// } +// +// @Override +// public boolean contains(Object o) { +// Integer index = inputSet.get(o); +// return index != null && (mask & (1 << index)) != 0; +// } +// } + +// private static final class PowerSet extends AbstractSet> { +// final ImmutableMap inputSet; +// +// PowerSet(Set input) { +// ImmutableMap.Builder builder = ImmutableMap.builder(); +// int i = 0; +// for (E e : checkNotNull(input)) { +// builder.put(e, i++); +// } +// this.inputSet = builder.build(); +// checkArgument(inputSet.size() <= 30, +// "Too many elements to create power set: %s > 30", inputSet.size()); +// } +// +// @Override public int size() { +// return 1 << inputSet.size(); +// } +// +// @Override public boolean isEmpty() { +// return false; +// } +// +// @Override public Iterator> iterator() { +// return new AbstractIndexedListIterator>(size()) { +// @Override protected Set get(final int setBits) { +// return new SubSet(inputSet, setBits); +// } +// }; +// } +// +// @Override public boolean contains(Object obj) { +// if (obj instanceof Set) { +// Set set = (Set) obj; +// return inputSet.keySet().containsAll(set); +// } +// return false; +// } +// +// @Override public boolean equals(Object obj) { +// if (obj instanceof PowerSet) { +// PowerSet that = (PowerSet) obj; +// return inputSet.equals(that.inputSet); +// } +// return super.equals(obj); +// } +// +// @Override public int hashCode() { +// /* +// * The sum of the sums of the hash codes in each subset is just the sum of +// * each input element's hash code times the number of sets that element +// * appears in. Each element appears in exactly half of the 2^n sets, so: +// */ +// return inputSet.keySet().hashCode() << (inputSet.size() - 1); +// } +// +// @Override public String toString() { +// return "powerSet(" + inputSet + ")"; +// } +// } + + /** + * An implementation for {@link Set#hashCode()}. + */ + static int hashCodeImpl(Set s) { + int hashCode = 0; + for (Object o : s) { + hashCode += o != null ? o.hashCode() : 0; + + hashCode = ~~hashCode; + // Needed to deal with unusual integer overflow in GWT. + } + return hashCode; + } + + /** + * An implementation for {@link Set#equals(Object)}. + */ + static boolean equalsImpl(Set s, Object object) { + if (s == object) { + return true; + } + if (object instanceof Set) { + Set o = (Set) object; + + try { + return s.size() == o.size() && s.containsAll(o); + } catch (NullPointerException ignored) { + return false; + } catch (ClassCastException ignored) { + return false; + } + } + return false; + } + + /** + * Returns an unmodifiable view of the specified navigable set. This method + * allows modules to provide users with "read-only" access to internal + * navigable sets. Query operations on the returned set "read through" to the + * specified set, and attempts to modify the returned set, whether direct or + * via its collection views, result in an + * {@code UnsupportedOperationException}. + * + *

The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * @param set the navigable set for which an unmodifiable view is to be + * returned + * @return an unmodifiable view of the specified navigable set + * @since 12.0 + */ +// +// public static NavigableSet unmodifiableNavigableSet( +// NavigableSet set) { +// if (set instanceof ImmutableSortedSet +// || set instanceof UnmodifiableNavigableSet) { +// return set; +// } +// return new UnmodifiableNavigableSet(set); +// } +// +// +// static final class UnmodifiableNavigableSet +// extends ForwardingSortedSet implements NavigableSet, Serializable { +// private final NavigableSet delegate; +// +// UnmodifiableNavigableSet(NavigableSet delegate) { +// this.delegate = checkNotNull(delegate); +// } +// +// @Override +// protected SortedSet delegate() { +// return Collections.unmodifiableSortedSet(delegate); +// } +// +// @Override +// public E lower(E e) { +// return delegate.lower(e); +// } +// +// @Override +// public E floor(E e) { +// return delegate.floor(e); +// } +// +// @Override +// public E ceiling(E e) { +// return delegate.ceiling(e); +// } +// +// @Override +// public E higher(E e) { +// return delegate.higher(e); +// } +// +// @Override +// public E pollFirst() { +// throw new UnsupportedOperationException(); +// } +// +// @Override +// public E pollLast() { +// throw new UnsupportedOperationException(); +// } +// +// private transient UnmodifiableNavigableSet descendingSet; +// +// @Override +// public NavigableSet descendingSet() { +// UnmodifiableNavigableSet result = descendingSet; +// if (result == null) { +// result = descendingSet = new UnmodifiableNavigableSet( +// delegate.descendingSet()); +// result.descendingSet = this; +// } +// return result; +// } +// +// @Override +// public Iterator descendingIterator() { +// return Iterators.unmodifiableIterator(delegate.descendingIterator()); +// } +// +// @Override +// public NavigableSet subSet( +// E fromElement, +// boolean fromInclusive, +// E toElement, +// boolean toInclusive) { +// return unmodifiableNavigableSet(delegate.subSet( +// fromElement, +// fromInclusive, +// toElement, +// toInclusive)); +// } +// +// @Override +// public NavigableSet headSet(E toElement, boolean inclusive) { +// return unmodifiableNavigableSet(delegate.headSet(toElement, inclusive)); +// } +// +// @Override +// public NavigableSet tailSet(E fromElement, boolean inclusive) { +// return unmodifiableNavigableSet( +// delegate.tailSet(fromElement, inclusive)); +// } +// +// private static final long serialVersionUID = 0; +// } + + /** + * Returns a synchronized (thread-safe) navigable set backed by the specified + * navigable set. In order to guarantee serial access, it is critical that + * all access to the backing navigable set is accomplished + * through the returned navigable set (or its views). + * + *

It is imperative that the user manually synchronize on the returned + * sorted set when iterating over it or any of its {@code descendingSet}, + * {@code subSet}, {@code headSet}, or {@code tailSet} views.

   {@code
+   *
+   *   NavigableSet set = synchronizedNavigableSet(new TreeSet());
+   *    ...
+   *   synchronized (set) {
+   *     // Must be in the synchronized block
+   *     Iterator it = set.iterator();
+   *     while (it.hasNext()) {
+   *       foo(it.next());
+   *     }
+   *   }}
+ * + *

or:

   {@code
+   *
+   *   NavigableSet set = synchronizedNavigableSet(new TreeSet());
+   *   NavigableSet set2 = set.descendingSet().headSet(foo);
+   *    ...
+   *   synchronized (set) { // Note: set, not set2!!!
+   *     // Must be in the synchronized block
+   *     Iterator it = set2.descendingIterator();
+   *     while (it.hasNext())
+   *       foo(it.next());
+   *     }
+   *   }}
+ * + *

Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * @param navigableSet the navigable set to be "wrapped" in a synchronized + * navigable set. + * @return a synchronized view of the specified navigable set. + * @since 13.0 + */ +// +// public static NavigableSet synchronizedNavigableSet( +// NavigableSet navigableSet) { +// return Synchronized.navigableSet(navigableSet); +// } + + /** + * Remove each element in an iterable from a set. + */ + static boolean removeAllImpl(Set set, Iterator iterator) { + boolean changed = false; + while (iterator.hasNext()) { + changed |= set.remove(iterator.next()); + } + return changed; + } + + static boolean removeAllImpl(Set set, Collection collection) { + checkNotNull(collection); // for GWT +// if (collection instanceof Multiset) { +// collection = ((Multiset) collection).elementSet(); +// } + /* + * AbstractSet.removeAll(List) has quadratic behavior if the list size + * is just less than the set's size. We augment the test by + * assuming that sets have fast contains() performance, and other + * collections don't. See + * http://code.google.com/p/guava-libraries/issues/detail?id=1013 + */ + if (collection instanceof Set && collection.size() > set.size()) { + return Iterators.removeAll(set.iterator(), collection); + } else { + return removeAllImpl(set, collection.iterator()); + } + } + +// +// static class DescendingSet extends ForwardingNavigableSet { +// private final NavigableSet forward; +// +// DescendingSet(NavigableSet forward) { +// this.forward = forward; +// } +// +// @Override +// protected NavigableSet delegate() { +// return forward; +// } +// +// @Override +// public E lower(E e) { +// return forward.higher(e); +// } +// +// @Override +// public E floor(E e) { +// return forward.ceiling(e); +// } +// +// @Override +// public E ceiling(E e) { +// return forward.floor(e); +// } +// +// @Override +// public E higher(E e) { +// return forward.lower(e); +// } +// +// @Override +// public E pollFirst() { +// return forward.pollLast(); +// } +// +// @Override +// public E pollLast() { +// return forward.pollFirst(); +// } +// +// @Override +// public NavigableSet descendingSet() { +// return forward; +// } +// +// @Override +// public Iterator descendingIterator() { +// return forward.iterator(); +// } +// +// @Override +// public NavigableSet subSet( +// E fromElement, +// boolean fromInclusive, +// E toElement, +// boolean toInclusive) { +// return forward.subSet(toElement, toInclusive, fromElement, fromInclusive).descendingSet(); +// } +// +// @Override +// public NavigableSet headSet(E toElement, boolean inclusive) { +// return forward.tailSet(toElement, inclusive).descendingSet(); +// } +// +// @Override +// public NavigableSet tailSet(E fromElement, boolean inclusive) { +// return forward.headSet(fromElement, inclusive).descendingSet(); +// } +// +// +// @Override +// public Comparator comparator() { +// Comparator forwardComparator = forward.comparator(); +// if (forwardComparator == null) { +// return (Comparator) Ordering.natural().reverse(); +// } else { +// return reverse(forwardComparator); +// } +// } +// +// // If we inline this, we get a javac error. +// private static Ordering reverse(Comparator forward) { +// return Ordering.from(forward).reverse(); +// } +// +// @Override +// public E first() { +// return forward.last(); +// } +// +// @Override +// public SortedSet headSet(E toElement) { +// return standardHeadSet(toElement); +// } +// +// @Override +// public E last() { +// return forward.first(); +// } +// +// @Override +// public SortedSet subSet(E fromElement, E toElement) { +// return standardSubSet(fromElement, toElement); +// } +// +// @Override +// public SortedSet tailSet(E fromElement) { +// return standardTailSet(fromElement); +// } +// +// @Override +// public Iterator iterator() { +// return forward.descendingIterator(); +// } +// +// @Override +// public Object[] toArray() { +// return standardToArray(); +// } +// +// @Override +// public T[] toArray(T[] array) { +// return standardToArray(array); +// } +// +// @Override +// public String toString() { +// return standardToString(); +// } +// } +} diff --git a/java/src/game/collect/SparseImmutableTable.java b/java/src/game/collect/SparseImmutableTable.java new file mode 100644 index 0000000..4c1d867 --- /dev/null +++ b/java/src/game/collect/SparseImmutableTable.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package game.collect; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A {@code RegularImmutableTable} optimized for sparse data. + */ + +//@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(); + for (R row : rowSpace) { + rowIndex.put(row, rows.size()); + rows.put(row, new LinkedHashMap()); + } + Map> columns = Maps.newLinkedHashMap(); + for (C col : columnSpace) { + 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++) { + Cell cell = cellList.get(i); + R rowKey = cell.getRowKey(); + C columnKey = cell.getColumnKey(); + V value = cell.getValue(); + + iterationOrderRow[i] = rowIndex.get(rowKey); + Map thisRow = rows.get(rowKey); + iterationOrderColumn[i] = thisRow.size(); + V oldValue = thisRow.put(columnKey, value); + if (oldValue != null) { + throw new IllegalArgumentException("Duplicate value for row=" + rowKey + ", column=" + + columnKey + ": " + value + ", " + oldValue); + } + columns.get(columnKey).put(rowKey, value); + } + this.iterationOrderRow = iterationOrderRow; + this.iterationOrderColumn = iterationOrderColumn; + ImmutableMap.Builder> rowBuilder = ImmutableMap.builder(); + for (Map.Entry> row : rows.entrySet()) { + rowBuilder.put(row.getKey(), ImmutableMap.copyOf(row.getValue())); + } + this.rowMap = rowBuilder.build(); + + ImmutableMap.Builder> columnBuilder = ImmutableMap.builder(); + for (Map.Entry> col : columns.entrySet()) { + columnBuilder.put(col.getKey(), ImmutableMap.copyOf(col.getValue())); + } + this.columnMap = columnBuilder.build(); + } + + @Override public ImmutableMap> columnMap() { + return columnMap; + } + + @Override public ImmutableMap> rowMap() { + return rowMap; + } + + @Override + public int size() { + return iterationOrderRow.length; + } + + @Override + Cell getCell(int index) { + int rowIndex = iterationOrderRow[index]; + Map.Entry> rowEntry = rowMap.entrySet().asList().get(rowIndex); + ImmutableMap row = (ImmutableMap) rowEntry.getValue(); + int columnIndex = iterationOrderColumn[index]; + Map.Entry colEntry = row.entrySet().asList().get(columnIndex); + return cellOf(rowEntry.getKey(), colEntry.getKey(), colEntry.getValue()); + } + + @Override + V getValue(int index) { + int rowIndex = iterationOrderRow[index]; + ImmutableMap row = (ImmutableMap) rowMap.values().asList().get(rowIndex); + int columnIndex = iterationOrderColumn[index]; + return row.values().asList().get(columnIndex); + } +} diff --git a/java/src/game/collect/StandardTable.java b/java/src/game/collect/StandardTable.java new file mode 100644 index 0000000..0f4d1c8 --- /dev/null +++ b/java/src/game/collect/StandardTable.java @@ -0,0 +1,891 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Maps.safeContainsKey; +import static game.collect.Maps.safeGet; +import static game.collect.Preconditions.checkNotNull; +import static game.util.Predicates.alwaysTrue; +import static game.util.Predicates.equalTo; +import static game.util.Predicates.in; +import static game.util.Predicates.not; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; + +import game.collect.Maps.ImprovedAbstractMap; +import game.collect.Sets.ImprovedAbstractSet; + +/** + * {@link Table} implementation backed by a map that associates row keys with + * column key / value secondary maps. This class provides rapid access to + * records by the row key alone or by both keys, but not by just the column key. + * + *

The views returned by {@link #column}, {@link #columnKeySet()}, and {@link + * #columnMap()} have iterators that don't support {@code remove()}. Otherwise, + * all optional operations are supported. Null row keys, columns keys, and + * values are not supported. + * + *

Lookups by row key are often faster than lookups by column key, because + * the data is stored in a {@code Map>}. A method call like {@code + * column(columnKey).get(rowKey)} still runs quickly, since the row key is + * provided. However, {@code column(columnKey).size()} takes longer, since an + * iteration across all row keys occurs. + * + *

Note that this implementation is not synchronized. If multiple threads + * access this table concurrently and one of the threads modifies the table, it + * must be synchronized externally. + * + * @author Jared Levy + */ + +public class StandardTable extends AbstractTable implements Serializable { + final Map> backingMap = new HashMap>(); +// final Supplier> factory; + +// StandardTable(Map> backingMap) { +// this.backingMap = backingMap; +//// this.factory = factory; +// } + + // Accessors + + @Override public boolean contains( + Object rowKey, Object columnKey) { + return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); + } + + @Override public boolean containsColumn(Object columnKey) { + if (columnKey == null) { + return false; + } + for (Map map : backingMap.values()) { + if (safeContainsKey(map, columnKey)) { + return true; + } + } + return false; + } + + @Override public boolean containsRow(Object rowKey) { + return rowKey != null && safeContainsKey(backingMap, rowKey); + } + + @Override public boolean containsValue(Object value) { + return value != null && super.containsValue(value); + } + + @Override public V get(Object rowKey, Object columnKey) { + return (rowKey == null || columnKey == null) + ? null + : super.get(rowKey, columnKey); + } + + @Override public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override public int size() { + int size = 0; + for (Map map : backingMap.values()) { + size += map.size(); + } + return size; + } + + // Mutators + + @Override public void clear() { + backingMap.clear(); + } + + private Map getOrCreate(R rowKey) { + Map map = backingMap.get(rowKey); + if (map == null) { + map = Maps.newHashMapWithExpectedSize(0); +// map = factory.get(); + backingMap.put(rowKey, map); + } + return map; + } + + @Override public V put(R rowKey, C columnKey, V value) { + checkNotNull(rowKey); + checkNotNull(columnKey); + checkNotNull(value); + return getOrCreate(rowKey).put(columnKey, value); + } + + @Override public V remove( + Object rowKey, Object columnKey) { + if ((rowKey == null) || (columnKey == null)) { + return null; + } + Map map = safeGet(backingMap, rowKey); + if (map == null) { + return null; + } + V value = map.remove(columnKey); + if (map.isEmpty()) { + backingMap.remove(rowKey); + } + return value; + } + + private Map removeColumn(Object column) { + Map output = new LinkedHashMap(); + Iterator>> iterator + = backingMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + V value = entry.getValue().remove(column); + if (value != null) { + output.put(entry.getKey(), value); + if (entry.getValue().isEmpty()) { + iterator.remove(); + } + } + } + return output; + } + + private boolean containsMapping( + Object rowKey, Object columnKey, Object value) { + return value != null && value.equals(get(rowKey, columnKey)); + } + + /** Remove a row key / column key / value mapping, if present. */ + private boolean removeMapping(Object rowKey, Object columnKey, Object value) { + if (containsMapping(rowKey, columnKey, value)) { + remove(rowKey, columnKey); + return true; + } + return false; + } + + // Views + + /** + * Abstract set whose {@code isEmpty()} returns whether the table is empty and + * whose {@code clear()} clears all table mappings. + */ + private abstract class TableSet extends ImprovedAbstractSet { + @Override public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override public void clear() { + backingMap.clear(); + } + } + + /** + * {@inheritDoc} + * + *

The set's iterator traverses the mappings for the first row, the + * mappings for the second row, and so on. + * + *

Each cell is an immutable snapshot of a row key / column key / value + * mapping, taken at the time the cell is returned by a method call to the + * set or its iterator. + */ + @Override public Set> cellSet() { + return super.cellSet(); + } + + @Override Iterator> cellIterator() { + return new CellIterator(); + } + + private class CellIterator implements Iterator> { + final Iterator>> rowIterator + = backingMap.entrySet().iterator(); + Entry> rowEntry; + Iterator> columnIterator + = Iterators.emptyModifiableIterator(); + + @Override public boolean hasNext() { + return rowIterator.hasNext() || columnIterator.hasNext(); + } + + @Override public Cell next() { + if (!columnIterator.hasNext()) { + rowEntry = rowIterator.next(); + columnIterator = rowEntry.getValue().entrySet().iterator(); + } + Entry columnEntry = columnIterator.next(); + return ImmutableTable.immutableCell( + rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); + } + + @Override public void remove() { + columnIterator.remove(); + if (rowEntry.getValue().isEmpty()) { + rowIterator.remove(); + } + } + } + + @Override public Map row(R rowKey) { + return new Row(rowKey); + } + + class Row extends ImprovedAbstractMap { + final R rowKey; + + Row(R rowKey) { + this.rowKey = checkNotNull(rowKey); + } + + Map backingRowMap; + + Map backingRowMap() { + return (backingRowMap == null + || (backingRowMap.isEmpty() && backingMap.containsKey(rowKey))) + ? backingRowMap = computeBackingRowMap() + : backingRowMap; + } + + Map computeBackingRowMap() { + return backingMap.get(rowKey); + } + + // Call this every time we perform a removal. + void maintainEmptyInvariant() { + if (backingRowMap() != null && backingRowMap.isEmpty()) { + backingMap.remove(rowKey); + backingRowMap = null; + } + } + + @Override + public boolean containsKey(Object key) { + Map backingRowMap = backingRowMap(); + return (key != null && backingRowMap != null) + && Maps.safeContainsKey(backingRowMap, key); + } + + @Override + public V get(Object key) { + Map backingRowMap = backingRowMap(); + return (key != null && backingRowMap != null) + ? Maps.safeGet(backingRowMap, key) + : null; + } + + @Override + public V put(C key, V value) { + checkNotNull(key); + checkNotNull(value); + if (backingRowMap != null && !backingRowMap.isEmpty()) { + return backingRowMap.put(key, value); + } + return StandardTable.this.put(rowKey, key, value); + } + + @Override + public V remove(Object key) { + Map backingRowMap = backingRowMap(); + if (backingRowMap == null) { + return null; + } + V result = Maps.safeRemove(backingRowMap, key); + maintainEmptyInvariant(); + return result; + } + + @Override + public void clear() { + Map backingRowMap = backingRowMap(); + if (backingRowMap != null) { + backingRowMap.clear(); + } + maintainEmptyInvariant(); + } + + @Override + protected Set> createEntrySet() { + return new RowEntrySet(); + } + + private final class RowEntrySet extends Maps.EntrySet { + @Override + Map map() { + return Row.this; + } + + @Override + public int size() { + Map map = backingRowMap(); + return (map == null) ? 0 : map.size(); + } + + @Override + public Iterator> iterator() { + final Map map = backingRowMap(); + if (map == null) { + return Iterators.emptyModifiableIterator(); + } + final Iterator> iterator = map.entrySet().iterator(); + return new Iterator>() { + @Override public boolean hasNext() { + return iterator.hasNext(); + } + @Override public Entry next() { + final Entry entry = iterator.next(); + return new ForwardingMapEntry() { + @Override protected Entry delegate() { + return entry; + } + @Override public V setValue(V value) { + return super.setValue(checkNotNull(value)); + } + @Override + public boolean equals(Object object) { + // TODO(user): identify why this affects GWT tests + return standardEquals(object); + } + }; + } + + @Override + public void remove() { + iterator.remove(); + maintainEmptyInvariant(); + } + }; + } + } + } + + /** + * {@inheritDoc} + * + *

The returned map's views have iterators that don't support + * {@code remove()}. + */ + @Override public Map column(C columnKey) { + return new Column(columnKey); + } + + private class Column extends ImprovedAbstractMap { + final C columnKey; + + Column(C columnKey) { + this.columnKey = checkNotNull(columnKey); + } + + @Override public V put(R key, V value) { + return StandardTable.this.put(key, columnKey, value); + } + + @Override public V get(Object key) { + return StandardTable.this.get(key, columnKey); + } + + @Override public boolean containsKey(Object key) { + return StandardTable.this.contains(key, columnKey); + } + + @Override public V remove(Object key) { + return StandardTable.this.remove(key, columnKey); + } + + /** + * Removes all {@code Column} mappings whose row key and value satisfy the + * given predicate. + */ + boolean removeFromColumnIf(Predicate> predicate) { + boolean changed = false; + Iterator>> iterator + = backingMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry> entry = iterator.next(); + Map map = entry.getValue(); + V value = map.get(columnKey); + if (value != null + && predicate.test(Maps.immutableEntry(entry.getKey(), value))) { + map.remove(columnKey); + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override Set> createEntrySet() { + return new EntrySet(); + } + + private class EntrySet extends ImprovedAbstractSet> { + @Override public Iterator> iterator() { + return new EntrySetIterator(); + } + + @Override public int size() { + int size = 0; + for (Map map : backingMap.values()) { + if (map.containsKey(columnKey)) { + size++; + } + } + return size; + } + + @Override public boolean isEmpty() { + return !containsColumn(columnKey); + } + + @Override public void clear() { + removeFromColumnIf(alwaysTrue()); + } + + @Override public boolean contains(Object o) { + if (o instanceof Entry) { + Entry entry = (Entry) o; + return containsMapping(entry.getKey(), columnKey, entry.getValue()); + } + return false; + } + + @Override public boolean remove(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return removeMapping(entry.getKey(), columnKey, entry.getValue()); + } + return false; + } + + @Override public boolean retainAll(Collection c) { + return removeFromColumnIf(not(in(c))); + } + } + + private class EntrySetIterator extends AbstractIterator> { + final Iterator>> iterator + = backingMap.entrySet().iterator(); + @Override protected Entry computeNext() { + while (iterator.hasNext()) { + final Entry> entry = iterator.next(); + if (entry.getValue().containsKey(columnKey)) { + return new AbstractMapEntry() { + @Override public R getKey() { + return entry.getKey(); + } + @Override public V getValue() { + return entry.getValue().get(columnKey); + } + @Override public V setValue(V value) { + return entry.getValue().put(columnKey, checkNotNull(value)); + } + }; + } + } + return endOfData(); + } + } + + @Override Set createKeySet() { + return new KeySet(); + } + + private class KeySet extends Maps.KeySet { + KeySet() { + super(Column.this); + } + + @Override public boolean contains(Object obj) { + return StandardTable.this.contains(obj, columnKey); + } + + @Override public boolean remove(Object obj) { + return StandardTable.this.remove(obj, columnKey) != null; + } + + @Override public boolean retainAll(final Collection c) { + return removeFromColumnIf(Maps.keyPredicateOnEntries(not(in(c)))); + } + } + + @Override + Collection createValues() { + return new Values(); + } + + private class Values extends Maps.Values { + Values() { + super(Column.this); + } + + @Override public boolean remove(Object obj) { + return obj != null && removeFromColumnIf(Maps.valuePredicateOnEntries(equalTo(obj))); + } + + @Override public boolean removeAll(final Collection c) { + return removeFromColumnIf(Maps.valuePredicateOnEntries(in(c))); + } + + @Override public boolean retainAll(final Collection c) { + return removeFromColumnIf(Maps.valuePredicateOnEntries(not(in(c)))); + } + } + } + + @Override public Set rowKeySet() { + return rowMap().keySet(); + } + + private transient Set columnKeySet; + + /** + * {@inheritDoc} + * + *

The returned set has an iterator that does not support {@code remove()}. + * + *

The set's iterator traverses the columns of the first row, the + * columns of the second row, etc., skipping any columns that have + * appeared previously. + */ + @Override + public Set columnKeySet() { + Set result = columnKeySet; + return (result == null) ? columnKeySet = new ColumnKeySet() : result; + } + + private class ColumnKeySet extends TableSet { + @Override public Iterator iterator() { + return createColumnKeyIterator(); + } + + @Override public int size() { + return Iterators.size(iterator()); + } + + @Override public boolean remove(Object obj) { + if (obj == null) { + return false; + } + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + if (map.keySet().remove(obj)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override public boolean removeAll(Collection c) { + checkNotNull(c); + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + // map.keySet().removeAll(c) can throw a NPE when map is a TreeMap with + // natural ordering and c contains a null. + if (Iterators.removeAll(map.keySet().iterator(), c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + Iterator> iterator = backingMap.values().iterator(); + while (iterator.hasNext()) { + Map map = iterator.next(); + if (map.keySet().retainAll(c)) { + changed = true; + if (map.isEmpty()) { + iterator.remove(); + } + } + } + return changed; + } + + @Override public boolean contains(Object obj) { + return containsColumn(obj); + } + } + + /** + * Creates an iterator that returns each column value with duplicates + * omitted. + */ + Iterator createColumnKeyIterator() { + return new ColumnKeyIterator(); + } + + private class ColumnKeyIterator extends AbstractIterator { + // Use the same map type to support TreeMaps with comparators that aren't + // consistent with equals(). + final Map seen = Maps.newHashMapWithExpectedSize(0); + final Iterator> mapIterator = backingMap.values().iterator(); + Iterator> entryIterator = Iterators.emptyIterator(); + + @Override protected C computeNext() { + while (true) { + if (entryIterator.hasNext()) { + Entry entry = entryIterator.next(); + if (!seen.containsKey(entry.getKey())) { + seen.put(entry.getKey(), entry.getValue()); + return entry.getKey(); + } + } else if (mapIterator.hasNext()) { + entryIterator = mapIterator.next().entrySet().iterator(); + } else { + return endOfData(); + } + } + } + } + + /** + * {@inheritDoc} + * + *

The collection's iterator traverses the values for the first row, + * the values for the second row, and so on. + */ + @Override public Collection values() { + return super.values(); + } + + private transient Map> rowMap; + + @Override public Map> rowMap() { + Map> result = rowMap; + return (result == null) ? rowMap = createRowMap() : result; + } + + Map> createRowMap() { + return new RowMap(); + } + + class RowMap extends ImprovedAbstractMap> { + @Override public boolean containsKey(Object key) { + return containsRow(key); + } + + // performing cast only when key is in backing map and has the correct type + + @Override public Map get(Object key) { + return containsRow(key) ? row((R) key) : null; + } + + @Override public Map remove(Object key) { + return (key == null) ? null : backingMap.remove(key); + } + + @Override protected Set>> createEntrySet() { + return new EntrySet(); + } + + class EntrySet extends TableSet>> { + @Override public Iterator>> iterator() { + return Maps.asMapEntryIterator(backingMap.keySet(), new Function>() { + @Override + public Map apply(R rowKey) { + return row(rowKey); + } + }); + } + + @Override public int size() { + return backingMap.size(); + } + + @Override public boolean contains(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return entry.getKey() != null + && entry.getValue() instanceof Map + && Filter.safeContains(backingMap.entrySet(), entry); + } + return false; + } + + @Override public boolean remove(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + return entry.getKey() != null + && entry.getValue() instanceof Map + && backingMap.entrySet().remove(entry); + } + return false; + } + } + } + + private transient ColumnMap columnMap; + + @Override public Map> columnMap() { + ColumnMap result = columnMap; + return (result == null) ? columnMap = new ColumnMap() : result; + } + + private class ColumnMap extends ImprovedAbstractMap> { + // The cast to C occurs only when the key is in the map, implying that it + // has the correct type. + + @Override public Map get(Object key) { + return containsColumn(key) ? column((C) key) : null; + } + + @Override public boolean containsKey(Object key) { + return containsColumn(key); + } + + @Override public Map remove(Object key) { + return containsColumn(key) ? removeColumn(key) : null; + } + + @Override public Set>> createEntrySet() { + return new ColumnMapEntrySet(); + } + + @Override public Set keySet() { + return columnKeySet(); + } + + @Override Collection> createValues() { + return new ColumnMapValues(); + } + + class ColumnMapEntrySet extends TableSet>> { + @Override public Iterator>> iterator() { + return Maps.asMapEntryIterator(columnKeySet(), new Function>() { + @Override + public Map apply(C columnKey) { + return column(columnKey); + } + }); + } + + @Override public int size() { + return columnKeySet().size(); + } + + @Override public boolean contains(Object obj) { + if (obj instanceof Entry) { + Entry entry = (Entry) obj; + if (containsColumn(entry.getKey())) { + // The cast to C occurs only when the key is in the map, implying + // that it has the correct type. + + C columnKey = (C) entry.getKey(); + return get(columnKey).equals(entry.getValue()); + } + } + return false; + } + + @Override public boolean remove(Object obj) { + if (contains(obj)) { + Entry entry = (Entry) obj; + removeColumn(entry.getKey()); + return true; + } + return false; + } + + @Override public boolean removeAll(Collection c) { + /* + * We can't inherit the normal implementation (which calls + * Sets.removeAllImpl(Set, *Collection*) because, under some + * circumstances, it attempts to call columnKeySet().iterator().remove, + * which is unsupported. + */ + checkNotNull(c); + return Sets.removeAllImpl(this, c.iterator()); + } + + @Override public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (!c.contains(Maps.immutableEntry(columnKey, column(columnKey)))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + } + + private class ColumnMapValues extends Maps.Values> { + ColumnMapValues() { + super(ColumnMap.this); + } + + @Override public boolean remove(Object obj) { + for (Entry> entry : ColumnMap.this.entrySet()) { + if (entry.getValue().equals(obj)) { + removeColumn(entry.getKey()); + return true; + } + } + return false; + } + + @Override public boolean removeAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (c.contains(column(columnKey))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + + @Override public boolean retainAll(Collection c) { + checkNotNull(c); + boolean changed = false; + for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { + if (!c.contains(column(columnKey))) { + removeColumn(columnKey); + changed = true; + } + } + return changed; + } + } + } + + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/collect/Table.java b/java/src/game/collect/Table.java new file mode 100644 index 0000000..7ed64a2 --- /dev/null +++ b/java/src/game/collect/Table.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A collection that associates an ordered pair of keys, called a row key and a + * column key, with a single value. A table may be sparse, with only a small + * fraction of row key / column key pairs possessing a corresponding value. + * + *

The mappings corresponding to a given row key may be viewed as a {@link + * Map} whose keys are the columns. The reverse is also available, associating a + * column with a row key / value map. Note that, in some implementations, data + * access by column key may have fewer supported operations or worse performance + * than data access by row key. + * + *

The methods returning collections or maps always return views of the + * underlying table. Updating the table can change the contents of those + * collections, and updating the collections will change the table. + * + *

All methods that modify the table are optional, and the views returned by + * the table may or may not be modifiable. When modification isn't supported, + * those methods will throw an {@link UnsupportedOperationException}. + * + *

See the Guava User Guide article on + * {@code Table}. + * + * @author Jared Levy + * @param the type of the table row keys + * @param the type of the table column keys + * @param the type of the mapped values + * @since 7.0 + */ + +public interface Table { + // TODO(jlevy): Consider adding methods similar to ConcurrentMap methods. + + // Accessors + + /** + * Returns {@code true} if the table contains a mapping with the specified + * row and column keys. + * + * @param rowKey key of row to search for + * @param columnKey key of column to search for + */ + boolean contains(Object rowKey, Object columnKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified + * row key. + * + * @param rowKey key of row to search for + */ + boolean containsRow(Object rowKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified + * column. + * + * @param columnKey key of column to search for + */ + boolean containsColumn(Object columnKey); + + /** + * Returns {@code true} if the table contains a mapping with the specified + * value. + * + * @param value value to search for + */ + boolean containsValue(Object value); + + /** + * Returns the value corresponding to the given row and column keys, or + * {@code null} if no such mapping exists. + * + * @param rowKey key of row to search for + * @param columnKey key of column to search for + */ + V get(Object rowKey, Object columnKey); + + /** Returns {@code true} if the table contains no mappings. */ + boolean isEmpty(); + + /** + * Returns the number of row key / column key / value mappings in the table. + */ + int size(); + + /** + * Compares the specified object with this table for equality. Two tables are + * equal when their cell views, as returned by {@link #cellSet}, are equal. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code for this table. The hash code of a table is defined + * as the hash code of its cell view, as returned by {@link #cellSet}. + */ + @Override + int hashCode(); + + // Mutators + + /** Removes all mappings from the table. */ + void clear(); + + /** + * Associates the specified value with the specified keys. If the table + * already contained a mapping for those keys, the old value is replaced with + * the specified value. + * + * @param rowKey row key that the value should be associated with + * @param columnKey column key that the value should be associated with + * @param value value to be associated with the specified keys + * @return the value previously associated with the keys, or {@code null} if + * no mapping existed for the keys + */ + V put(R rowKey, C columnKey, V value); + + /** + * Copies all mappings from the specified table to this table. The effect is + * equivalent to calling {@link #put} with each row key / column key / value + * mapping in {@code table}. + * + * @param table the table to add to this table + */ + void putAll(Table table); + + /** + * Removes the mapping, if any, associated with the given keys. + * + * @param rowKey row key of mapping to be removed + * @param columnKey column key of mapping to be removed + * @return the value previously associated with the keys, or {@code null} if + * no such value existed + */ + V remove(Object rowKey, Object columnKey); + + // Views + + /** + * Returns a view of all mappings that have the given row key. For each row + * key / column key / value mapping in the table with that row key, the + * returned map associates the column key with the value. If no mappings in + * the table have the provided row key, an empty map is returned. + * + *

Changes to the returned map will update the underlying table, and vice + * versa. + * + * @param rowKey key of row to search for in the table + * @return the corresponding map from column keys to values + */ + Map row(R rowKey); + + /** + * Returns a view of all mappings that have the given column key. For each row + * key / column key / value mapping in the table with that column key, the + * returned map associates the row key with the value. If no mappings in the + * table have the provided column key, an empty map is returned. + * + *

Changes to the returned map will update the underlying table, and vice + * versa. + * + * @param columnKey key of column to search for in the table + * @return the corresponding map from row keys to values + */ + Map column(C columnKey); + + /** + * Returns a set of all row key / column key / value triplets. Changes to the + * returned set will update the underlying table, and vice versa. The cell set + * does not support the {@code add} or {@code addAll} methods. + * + * @return set of table cells consisting of row key / column key / value + * triplets + */ + Set> cellSet(); + + /** + * Returns a set of row keys that have one or more values in the table. + * Changes to the set will update the underlying table, and vice versa. + * + * @return set of row keys + */ + Set rowKeySet(); + + /** + * Returns a set of column keys that have one or more values in the table. + * Changes to the set will update the underlying table, and vice versa. + * + * @return set of column keys + */ + Set columnKeySet(); + + /** + * Returns a collection of all values, which may contain duplicates. Changes + * to the returned collection will update the underlying table, and vice + * versa. + * + * @return collection of values + */ + Collection values(); + + /** + * Returns a view that associates each row key with the corresponding map from + * column keys to values. Changes to the returned map will update this table. + * The returned map does not support {@code put()} or {@code putAll()}, or + * {@code setValue()} on its entries. + * + *

In contrast, the maps returned by {@code rowMap().get()} have the same + * behavior as those returned by {@link #row}. Those maps may support {@code + * setValue()}, {@code put()}, and {@code putAll()}. + * + * @return a map view from each row key to a secondary map from column keys to + * values + */ + Map> rowMap(); + + /** + * Returns a view that associates each column key with the corresponding map + * from row keys to values. Changes to the returned map will update this + * table. The returned map does not support {@code put()} or {@code putAll()}, + * or {@code setValue()} on its entries. + * + *

In contrast, the maps returned by {@code columnMap().get()} have the + * same behavior as those returned by {@link #column}. Those maps may support + * {@code setValue()}, {@code put()}, and {@code putAll()}. + * + * @return a map view from each column key to a secondary map from row keys to + * values + */ + Map> columnMap(); + + /** + * Row key / column key / value triplet corresponding to a mapping in a table. + * + * @since 7.0 + */ + interface Cell { + /** + * Returns the row key of this cell. + */ + R getRowKey(); + + /** + * Returns the column key of this cell. + */ + C getColumnKey(); + + /** + * Returns the value of this cell. + */ + V getValue(); + + /** + * Compares the specified object with this cell for equality. Two cells are + * equal when they have equal row keys, column keys, and values. + */ + @Override + boolean equals(Object obj); + + /** + * Returns the hash code of this cell. + * + *

The hash code of a table cell is equal to {@link + * Objects#hashCode}{@code (e.getRowKey(), e.getColumnKey(), e.getValue())}. + */ + @Override + int hashCode(); + } +} diff --git a/java/src/game/collect/TransformedIterator.java b/java/src/game/collect/TransformedIterator.java new file mode 100644 index 0000000..1c12f07 --- /dev/null +++ b/java/src/game/collect/TransformedIterator.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2012 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import static game.collect.Preconditions.checkNotNull; + +import java.util.Iterator; + +/** + * An iterator that transforms a backing iterator; for internal use. This avoids + * the object overhead of constructing a {@link Function} for internal methods. + * + * @author Louis Wasserman + */ + +abstract class TransformedIterator implements Iterator { + final Iterator backingIterator; + + TransformedIterator(Iterator backingIterator) { + this.backingIterator = checkNotNull(backingIterator); + } + + abstract T transform(F from); + + @Override + public final boolean hasNext() { + return backingIterator.hasNext(); + } + + @Override + public final T next() { + return transform(backingIterator.next()); + } + + @Override + public final void remove() { + backingIterator.remove(); + } +} diff --git a/java/src/game/collect/UnmodifiableIterator.java b/java/src/game/collect/UnmodifiableIterator.java new file mode 100644 index 0000000..95024eb --- /dev/null +++ b/java/src/game/collect/UnmodifiableIterator.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.Iterator; + +/** + * An iterator that does not support {@link #remove}. + * + * @author Jared Levy + * @since 2.0 (imported from Google Collections Library) + */ + +public abstract class UnmodifiableIterator implements Iterator { + /** Constructor for use by subclasses. */ + protected UnmodifiableIterator() {} + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated + @Override + public final void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/java/src/game/collect/UnmodifiableListIterator.java b/java/src/game/collect/UnmodifiableListIterator.java new file mode 100644 index 0000000..1a62751 --- /dev/null +++ b/java/src/game/collect/UnmodifiableListIterator.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.collect; + +import java.util.ListIterator; + +/** + * A list iterator that does not support {@link #remove}, {@link #add}, or + * {@link #set}. + * + * @since 7.0 + * @author Louis Wasserman + */ + +abstract class UnmodifiableListIterator + extends UnmodifiableIterator implements ListIterator { + /** Constructor for use by subclasses. */ + protected UnmodifiableListIterator() {} + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated @Override public final void add(E e) { + throw new UnsupportedOperationException(); + } + + /** + * Guaranteed to throw an exception and leave the underlying data unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ + @Deprecated @Override public final void set(E e) { + throw new UnsupportedOperationException(); + } +} diff --git a/java/src/game/color/DyeColor.java b/java/src/game/color/DyeColor.java index b53aec1..8681ee5 100755 --- a/java/src/game/color/DyeColor.java +++ b/java/src/game/color/DyeColor.java @@ -2,7 +2,7 @@ package game.color; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.properties.IStringSerializable; diff --git a/java/src/game/command/ArgumentParser.java b/java/src/game/command/ArgumentParser.java index 635636f..509d9ad 100644 --- a/java/src/game/command/ArgumentParser.java +++ b/java/src/game/command/ArgumentParser.java @@ -2,7 +2,7 @@ package game.command; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; public abstract class ArgumentParser { public static String[][] splitString(String str) { diff --git a/java/src/game/command/CachedExecutable.java b/java/src/game/command/CachedExecutable.java index f6e9dcf..3d98ea1 100644 --- a/java/src/game/command/CachedExecutable.java +++ b/java/src/game/command/CachedExecutable.java @@ -4,8 +4,8 @@ import java.lang.reflect.Method; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; public class CachedExecutable { private final Executable executable; diff --git a/java/src/game/command/EnumParser.java b/java/src/game/command/EnumParser.java index 9ee0235..86e5482 100644 --- a/java/src/game/command/EnumParser.java +++ b/java/src/game/command/EnumParser.java @@ -2,7 +2,7 @@ package game.command; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; public class EnumParser extends DefaultingParser { private final Class clazz; diff --git a/java/src/game/command/ScriptArgs.java b/java/src/game/command/ScriptArgs.java index e3fca20..feaaeff 100644 --- a/java/src/game/command/ScriptArgs.java +++ b/java/src/game/command/ScriptArgs.java @@ -4,9 +4,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; public class ScriptArgs { private final String command; diff --git a/java/src/game/command/ScriptEnvironment.java b/java/src/game/command/ScriptEnvironment.java index 33ae005..75d1229 100644 --- a/java/src/game/command/ScriptEnvironment.java +++ b/java/src/game/command/ScriptEnvironment.java @@ -6,8 +6,8 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.Server; import game.color.TextColor; diff --git a/java/src/game/command/ScriptExecutable.java b/java/src/game/command/ScriptExecutable.java index 7411184..7c55f8a 100644 --- a/java/src/game/command/ScriptExecutable.java +++ b/java/src/game/command/ScriptExecutable.java @@ -5,8 +5,8 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.command.DoubleParser.DefType; import game.world.Vec3; diff --git a/java/src/game/command/WorldParser.java b/java/src/game/command/WorldParser.java index 2d6d513..7287ed5 100644 --- a/java/src/game/command/WorldParser.java +++ b/java/src/game/command/WorldParser.java @@ -2,7 +2,7 @@ package game.command; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.dimension.Dimension; import game.world.WorldServer; diff --git a/java/src/game/command/commands/CommandHelp.java b/java/src/game/command/commands/CommandHelp.java index 02da35c..1992a5a 100644 --- a/java/src/game/command/commands/CommandHelp.java +++ b/java/src/game/command/commands/CommandHelp.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Map.Entry; import java.util.function.Function; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.command.ArgumentParser; import game.command.CachedExecutable; diff --git a/java/src/game/command/commands/CommandSpawn.java b/java/src/game/command/commands/CommandSpawn.java index 4f3fd51..709df71 100644 --- a/java/src/game/command/commands/CommandSpawn.java +++ b/java/src/game/command/commands/CommandSpawn.java @@ -2,7 +2,7 @@ package game.command.commands; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.command.ScriptEnvironment; import game.command.ScriptException; diff --git a/java/src/game/dimension/DimType.java b/java/src/game/dimension/DimType.java index 8bc412f..e2fa8e4 100755 --- a/java/src/game/dimension/DimType.java +++ b/java/src/game/dimension/DimType.java @@ -2,7 +2,7 @@ package game.dimension; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; public enum DimType { STAR("star", "Stern %s", true, false, false, false, false, false, true), diff --git a/java/src/game/dimension/Dimension.java b/java/src/game/dimension/Dimension.java index 2648754..3cad8cf 100755 --- a/java/src/game/dimension/Dimension.java +++ b/java/src/game/dimension/Dimension.java @@ -4,9 +4,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.biome.Biome; import game.block.LeavesType; diff --git a/java/src/game/dimension/Domain.java b/java/src/game/dimension/Domain.java index 038a1fe..91e640c 100755 --- a/java/src/game/dimension/Domain.java +++ b/java/src/game/dimension/Domain.java @@ -2,7 +2,7 @@ package game.dimension; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; public final class Domain extends Nameable implements Comparable { public final String id; diff --git a/java/src/game/dimension/Galaxy.java b/java/src/game/dimension/Galaxy.java index 30bb6b8..fa44e2f 100755 --- a/java/src/game/dimension/Galaxy.java +++ b/java/src/game/dimension/Galaxy.java @@ -2,7 +2,7 @@ package game.dimension; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; public final class Galaxy extends Nameable implements Comparable { public final String id; diff --git a/java/src/game/dimension/Planet.java b/java/src/game/dimension/Planet.java index 9c23687..19689b7 100755 --- a/java/src/game/dimension/Planet.java +++ b/java/src/game/dimension/Planet.java @@ -2,7 +2,7 @@ package game.dimension; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; public final class Planet extends Dimension { private final Set moons = Sets.newTreeSet(); diff --git a/java/src/game/dimension/Sector.java b/java/src/game/dimension/Sector.java index 440105c..4d11d4e 100755 --- a/java/src/game/dimension/Sector.java +++ b/java/src/game/dimension/Sector.java @@ -2,7 +2,7 @@ package game.dimension; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; public final class Sector extends Nameable implements Comparable { public final String id; diff --git a/java/src/game/dimension/Star.java b/java/src/game/dimension/Star.java index 8bce018..6cfa58d 100755 --- a/java/src/game/dimension/Star.java +++ b/java/src/game/dimension/Star.java @@ -2,7 +2,7 @@ package game.dimension; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.world.State; diff --git a/java/src/game/enchantment/Enchantment.java b/java/src/game/enchantment/Enchantment.java index e981617..b212fd0 100755 --- a/java/src/game/enchantment/Enchantment.java +++ b/java/src/game/enchantment/Enchantment.java @@ -4,8 +4,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.entity.DamageSource; import game.entity.Entity; diff --git a/java/src/game/enchantment/EnchantmentHelper.java b/java/src/game/enchantment/EnchantmentHelper.java index 74ac5ce..364d437 100755 --- a/java/src/game/enchantment/EnchantmentHelper.java +++ b/java/src/game/enchantment/EnchantmentHelper.java @@ -4,8 +4,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.entity.DamageSource; import game.entity.Entity; diff --git a/java/src/game/entity/DataWatcher.java b/java/src/game/entity/DataWatcher.java index 6c13746..5b95477 100755 --- a/java/src/game/entity/DataWatcher.java +++ b/java/src/game/entity/DataWatcher.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.item.ItemStack; import game.network.PacketBuffer; diff --git a/java/src/game/entity/EntityTrackerEntry.java b/java/src/game/entity/EntityTrackerEntry.java index c34fd71..64709e0 100755 --- a/java/src/game/entity/EntityTrackerEntry.java +++ b/java/src/game/entity/EntityTrackerEntry.java @@ -4,7 +4,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.entity.attributes.AttributeInstance; import game.entity.attributes.AttributeMap; diff --git a/java/src/game/entity/animal/EntityDragon.java b/java/src/game/entity/animal/EntityDragon.java index 9774603..2934d08 100755 --- a/java/src/game/entity/animal/EntityDragon.java +++ b/java/src/game/entity/animal/EntityDragon.java @@ -2,7 +2,7 @@ package game.entity.animal; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.DamageSource; import game.entity.Entity; diff --git a/java/src/game/entity/animal/EntityHorse.java b/java/src/game/entity/animal/EntityHorse.java index 31ba79a..2ee9bed 100755 --- a/java/src/game/entity/animal/EntityHorse.java +++ b/java/src/game/entity/animal/EntityHorse.java @@ -1,6 +1,6 @@ package game.entity.animal; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.ai.EntityAIFollowParent; import game.ai.EntityAILookIdle; @@ -44,7 +44,7 @@ public class EntityHorse extends EntityAnimal implements IInvBasic { private static final Predicate horseBreedingSelector = new Predicate() { - public boolean apply(Entity p_apply_1_) + public boolean test(Entity p_apply_1_) { return p_apply_1_ instanceof EntityHorse && ((EntityHorse)p_apply_1_).isBreeding(); } diff --git a/java/src/game/entity/animal/EntityOcelot.java b/java/src/game/entity/animal/EntityOcelot.java index da1b07f..3b237d7 100755 --- a/java/src/game/entity/animal/EntityOcelot.java +++ b/java/src/game/entity/animal/EntityOcelot.java @@ -1,6 +1,6 @@ package game.entity.animal; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.ai.EntityAIAvoidEntity; import game.ai.EntityAIFollowOwner; @@ -56,7 +56,7 @@ public class EntityOcelot extends EntityTameable this.tasks.addTask(10, new EntityAIWander(this, 0.8D)); this.tasks.addTask(11, new EntityAIWatchClosest(this, null, 10.0F)); this.targets.addTask(1, new EntityAITargetNonTamed(this, EntityAnimal.class, false, new Predicate() { - public boolean apply(EntityAnimal entity) { + public boolean test(EntityAnimal entity) { return entity instanceof EntityChicken || entity instanceof EntityMouse; } })); @@ -368,7 +368,7 @@ public class EntityOcelot extends EntityTameable { this.avoidEntity = new EntityAIAvoidEntity(this, EntityNPC.class, // new Predicate() { -// public boolean apply(EntityNPC entity) { +// public boolean test(EntityNPC entity) { // return !entity.capabilities.disableTarget; // } // } , diff --git a/java/src/game/entity/animal/EntityRabbit.java b/java/src/game/entity/animal/EntityRabbit.java index ef813c3..ae781ff 100755 --- a/java/src/game/entity/animal/EntityRabbit.java +++ b/java/src/game/entity/animal/EntityRabbit.java @@ -1,6 +1,6 @@ package game.entity.animal; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.ai.EntityAIAttackOnCollide; import game.ai.EntityAIAvoidEntity; @@ -336,7 +336,7 @@ public class EntityRabbit extends EntityAnimal { this.grow(this.rand.range(250, 350)); if(Config.rabbitMateChance > 0 && this.rand.chance(Config.rabbitMateChance) && this.getGrowingAge() == 0 && !this.isInLove() && !this.worldObj.getEntitiesWithinAABB(EntityRabbit.class, this.getEntityBoundingBox().expand(15.0f, 15.0f, 15.0f), new Predicate() { - public boolean apply(EntityRabbit entity) { + public boolean test(EntityRabbit entity) { return entity != EntityRabbit.this && entity.getGrowingAge() == 0; } }).isEmpty()) diff --git a/java/src/game/entity/animal/EntitySheep.java b/java/src/game/entity/animal/EntitySheep.java index a068404..2410159 100755 --- a/java/src/game/entity/animal/EntitySheep.java +++ b/java/src/game/entity/animal/EntitySheep.java @@ -2,7 +2,7 @@ package game.entity.animal; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.ai.EntityAIEatGrass; import game.ai.EntityAIFollowParent; diff --git a/java/src/game/entity/animal/EntityWolf.java b/java/src/game/entity/animal/EntityWolf.java index 41a1ba8..756d56a 100755 --- a/java/src/game/entity/animal/EntityWolf.java +++ b/java/src/game/entity/animal/EntityWolf.java @@ -1,6 +1,6 @@ package game.entity.animal; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.ai.EntityAIAttackOnCollide; import game.ai.EntityAIBeg; @@ -74,7 +74,7 @@ public class EntityWolf extends EntityTameable this.targets.addTask(3, new EntityAIHurtByTarget(this, true)); this.targets.addTask(4, new EntityAITargetNonTamed(this, EntityAnimal.class, false, new Predicate() { - public boolean apply(Entity p_apply_1_) + public boolean test(Entity p_apply_1_) { return p_apply_1_ instanceof EntitySheep || p_apply_1_ instanceof EntityRabbit; } diff --git a/java/src/game/entity/attributes/Attribute.java b/java/src/game/entity/attributes/Attribute.java index e1987e9..25804a4 100755 --- a/java/src/game/entity/attributes/Attribute.java +++ b/java/src/game/entity/attributes/Attribute.java @@ -2,7 +2,7 @@ package game.entity.attributes; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.util.ExtMath; diff --git a/java/src/game/entity/attributes/AttributeInstance.java b/java/src/game/entity/attributes/AttributeInstance.java index 332cb3d..e2e2cb3 100755 --- a/java/src/game/entity/attributes/AttributeInstance.java +++ b/java/src/game/entity/attributes/AttributeInstance.java @@ -4,9 +4,9 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; public class AttributeInstance { diff --git a/java/src/game/entity/attributes/AttributeMap.java b/java/src/game/entity/attributes/AttributeMap.java index d77c069..af5666e 100755 --- a/java/src/game/entity/attributes/AttributeMap.java +++ b/java/src/game/entity/attributes/AttributeMap.java @@ -5,8 +5,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Maps; +import game.collect.Sets; public class AttributeMap { diff --git a/java/src/game/entity/attributes/LowerStringMap.java b/java/src/game/entity/attributes/LowerStringMap.java index 74e5f0e..11110bb 100755 --- a/java/src/game/entity/attributes/LowerStringMap.java +++ b/java/src/game/entity/attributes/LowerStringMap.java @@ -4,7 +4,7 @@ import java.util.Collection; import java.util.Map; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; public class LowerStringMap implements Map { diff --git a/java/src/game/entity/item/EntityFalling.java b/java/src/game/entity/item/EntityFalling.java index 5fc61b2..301de6f 100755 --- a/java/src/game/entity/item/EntityFalling.java +++ b/java/src/game/entity/item/EntityFalling.java @@ -2,7 +2,7 @@ package game.entity.item; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.block.Block; import game.block.BlockAnvil; diff --git a/java/src/game/entity/item/EntityHopperCart.java b/java/src/game/entity/item/EntityHopperCart.java index 0be650b..d21856d 100755 --- a/java/src/game/entity/item/EntityHopperCart.java +++ b/java/src/game/entity/item/EntityHopperCart.java @@ -2,7 +2,7 @@ package game.entity.item; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.DamageSource; import game.entity.npc.EntityNPC; @@ -175,7 +175,7 @@ public class EntityHopperCart extends EntityCartContainer implements IHopper else { List list = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.getEntityBoundingBox().expand(0.25D, 0.0D, 0.25D), new Predicate() { - public boolean apply(EntityItem entity) { + public boolean test(EntityItem entity) { return entity.isEntityAlive(); } }); diff --git a/java/src/game/entity/item/EntityXp.java b/java/src/game/entity/item/EntityXp.java index e83d289..15cc98f 100755 --- a/java/src/game/entity/item/EntityXp.java +++ b/java/src/game/entity/item/EntityXp.java @@ -122,7 +122,7 @@ public class EntityXp extends Entity implements IObjectData // { // List list = this.worldObj.getEntitiesWithinAABB(EntityXp.class, // this.getEntityBoundingBox().expand(1.5D, 1.0D, 1.5D), new Predicate() { -// public boolean apply(EntityXp entity) { +// public boolean test(EntityXp entity) { // return entity != EntityXp.this && entity.isEntityAlive() && EntityXp.this.xpValue + entity.xpValue <= 2477; // } // }); diff --git a/java/src/game/entity/npc/Alignment.java b/java/src/game/entity/npc/Alignment.java index d2e560d..f38481c 100755 --- a/java/src/game/entity/npc/Alignment.java +++ b/java/src/game/entity/npc/Alignment.java @@ -2,7 +2,7 @@ package game.entity.npc; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.color.TextColor; diff --git a/java/src/game/entity/npc/ClassInfo.java b/java/src/game/entity/npc/ClassInfo.java index 5021300..c23053f 100755 --- a/java/src/game/entity/npc/ClassInfo.java +++ b/java/src/game/entity/npc/ClassInfo.java @@ -2,7 +2,7 @@ package game.entity.npc; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.item.RngLoot; import game.rng.WeightedList; diff --git a/java/src/game/entity/npc/EntityChaosMarine.java b/java/src/game/entity/npc/EntityChaosMarine.java index 1da0aa8..f840f2c 100755 --- a/java/src/game/entity/npc/EntityChaosMarine.java +++ b/java/src/game/entity/npc/EntityChaosMarine.java @@ -2,7 +2,7 @@ package game.entity.npc; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.attributes.Attributes; import game.init.SpeciesRegistry; diff --git a/java/src/game/entity/npc/EntityMobNPC.java b/java/src/game/entity/npc/EntityMobNPC.java index ac29970..b7373ad 100755 --- a/java/src/game/entity/npc/EntityMobNPC.java +++ b/java/src/game/entity/npc/EntityMobNPC.java @@ -303,7 +303,7 @@ public abstract class EntityMobNPC extends EntityNPC // public AITargetAggressor(EntityMobNPC p_i45829_1_) // { // super(p_i45829_1_, EntityLiving.class, 10, true, false, new Predicate() { -// public boolean apply(EntityLiving entity) { +// public boolean test(EntityLiving entity) { // return entity.isPlayer(); // || entity instanceof EntityNPC; // } // }); diff --git a/java/src/game/entity/npc/EntityNPC.java b/java/src/game/entity/npc/EntityNPC.java index 883a2fc..dac0ea0 100755 --- a/java/src/game/entity/npc/EntityNPC.java +++ b/java/src/game/entity/npc/EntityNPC.java @@ -3,7 +3,7 @@ package game.entity.npc; import java.lang.reflect.InvocationTargetException; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.Game; import game.ai.AIRangedAttack; @@ -275,7 +275,7 @@ public abstract class EntityNPC extends EntityLiving // this.tasks.addTask(1, new EntityAIAttackOnCollide(this, EntityLivingBase.class, 0.6D, true, true)); this.tasks.addTask(1, new EntityAINpcMate(this)); this.tasks.addTask(2, new EntityAIAvoidEntity(this, EntityLiving.class, new Predicate() { - public boolean apply(EntityLiving entity) { + public boolean test(EntityLiving entity) { return entity != EntityNPC.this && EntityNPC.this.shouldFlee(entity); } }, 8.0F, 1.1D, 1.1D) { @@ -302,7 +302,7 @@ public abstract class EntityNPC extends EntityLiving // } }); this.tasks.addTask(3, new EntityAIAvoidEntity(this, EntityLiving.class, new Predicate() { - public boolean apply(EntityLiving entity) { + public boolean test(EntityLiving entity) { return entity != EntityNPC.this && EntityNPC.this.shouldFlee(entity); } }, 8.0F, 1.1D, 1.1D) { @@ -365,7 +365,7 @@ public abstract class EntityNPC extends EntityLiving } }); this.targets.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 10, true, false, new Predicate() { - public boolean apply(EntityLiving entity) { + public boolean test(EntityLiving entity) { if(entity != null && entity != EntityNPC.this && EntityNPC.this.shouldFlee(entity)) { EntityNPC.this.setAttackTarget(null); EntityNPC.this.fleeing = true; diff --git a/java/src/game/entity/npc/EntityPrimarch.java b/java/src/game/entity/npc/EntityPrimarch.java index 6800921..e805235 100755 --- a/java/src/game/entity/npc/EntityPrimarch.java +++ b/java/src/game/entity/npc/EntityPrimarch.java @@ -2,7 +2,7 @@ package game.entity.npc; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.attributes.Attributes; import game.properties.IStringSerializable; diff --git a/java/src/game/entity/npc/EntitySpaceMarine.java b/java/src/game/entity/npc/EntitySpaceMarine.java index 876b441..1ad84e5 100755 --- a/java/src/game/entity/npc/EntitySpaceMarine.java +++ b/java/src/game/entity/npc/EntitySpaceMarine.java @@ -2,7 +2,7 @@ package game.entity.npc; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.attributes.Attributes; import game.init.SpeciesRegistry; diff --git a/java/src/game/entity/npc/EntityZombie.java b/java/src/game/entity/npc/EntityZombie.java index 7009b27..0785dc8 100755 --- a/java/src/game/entity/npc/EntityZombie.java +++ b/java/src/game/entity/npc/EntityZombie.java @@ -2,7 +2,7 @@ package game.entity.npc; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.ai.EntityAIMoveThroughVillage; import game.entity.DamageSource; @@ -212,7 +212,7 @@ public class EntityZombie extends EntityNPC if ((double)this.worldObj.rand.floatv() < 0.05D) { List list = this.worldObj.getEntitiesWithinAABB(EntityChicken.class, this.getEntityBoundingBox().expand(5.0D, 3.0D, 5.0D), new Predicate() { - public boolean apply(EntityChicken entity) { + public boolean test(EntityChicken entity) { return entity.isEntityAlive() && entity.passenger == null && entity.vehicle == null; } }); diff --git a/java/src/game/entity/npc/SpeciesInfo.java b/java/src/game/entity/npc/SpeciesInfo.java index e386880..3e4ad5e 100755 --- a/java/src/game/entity/npc/SpeciesInfo.java +++ b/java/src/game/entity/npc/SpeciesInfo.java @@ -3,10 +3,10 @@ package game.entity.npc; import java.util.List; import java.util.Map; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.BiMap; +import game.collect.HashBiMap; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.init.SpeciesRegistry; diff --git a/java/src/game/entity/types/EntityAnimal.java b/java/src/game/entity/types/EntityAnimal.java index 9db2c92..c9a965e 100755 --- a/java/src/game/entity/types/EntityAnimal.java +++ b/java/src/game/entity/types/EntityAnimal.java @@ -2,7 +2,7 @@ package game.entity.types; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.block.Block; import game.entity.DamageSource; diff --git a/java/src/game/entity/types/EntityLiving.java b/java/src/game/entity/types/EntityLiving.java index 0e6a4b7..779ac36 100755 --- a/java/src/game/entity/types/EntityLiving.java +++ b/java/src/game/entity/types/EntityLiving.java @@ -5,9 +5,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import com.google.common.base.Predicate; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import java.util.function.Predicate; +import game.collect.Lists; +import game.collect.Maps; import game.ai.EntityAIBase; import game.ai.EntityAIMoveTowardsRestriction; @@ -2125,7 +2125,7 @@ public abstract class EntityLiving extends Entity { List list = this.worldObj.getEntitiesInAABBexcluding(this, this.getEntityBoundingBox().expand(0.20000000298023224D, 0.0D, 0.20000000298023224D), new Predicate() { - public boolean apply(Entity p_apply_1_) + public boolean test(Entity p_apply_1_) { return p_apply_1_.canBePushed(); } diff --git a/java/src/game/future/AbstractFuture.java b/java/src/game/future/AbstractFuture.java new file mode 100644 index 0000000..e63a476 --- /dev/null +++ b/java/src/game/future/AbstractFuture.java @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +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; + +/** + * An abstract implementation of the {@link ListenableFuture} interface. This + * class is preferable to {@link java.util.concurrent.FutureTask} for two + * reasons: It implements {@code ListenableFuture}, and it does not implement + * {@code Runnable}. (If you want a {@code Runnable} implementation of {@code + * ListenableFuture}, create a {@link ListenableFutureTask}, or submit your + * tasks to a {@link ListeningExecutorService}.) + * + *

This class implements all methods in {@code ListenableFuture}. + * Subclasses should provide a way to set the result of the computation through + * the protected methods {@link #set(Object)} and + * {@link #setException(Throwable)}. Subclasses may also override {@link + * #interruptTask()}, which will be invoked automatically if a call to {@link + * #cancel(boolean) cancel(true)} succeeds in canceling the future. + * + *

{@code AbstractFuture} uses an {@link AbstractQueuedSynchronizer} to deal + * with concurrency issues and guarantee thread safety. + * + *

The state changing methods all return a boolean indicating success or + * failure in changing the future's state. Valid states are running, + * completed, failed, or cancelled. + * + *

This class uses an {@link ExecutionList} to guarantee that all registered + * listeners will be executed, either when the future finishes or, for listeners + * that are added after the future completes, immediately. + * {@code Runnable}-{@code Executor} pairs are stored in the execution list but + * are not necessarily executed in the order in which they were added. (If a + * listener is added after the Future is complete, it will be executed + * immediately, even if earlier listeners have not been executed. Additionally, + * executors need not guarantee FIFO execution, or different listeners may run + * in different executors.) + * + * @author Sven Mawson + * @since 1.0 + */ +abstract class AbstractFuture implements ListenableFuture { + + /** Synchronization control for AbstractFutures. */ + private final Sync sync = new Sync(); + + // The execution list to hold our executors. + private final ExecutionList executionList = new ExecutionList(); + + /** + * Constructor for use by subclasses. + */ + protected AbstractFuture() {} + + /* + * Improve the documentation of when InterruptedException is thrown. Our + * behavior matches the JDK's, but the JDK's documentation is misleading. + */ + /** + * {@inheritDoc} + * + *

The default {@link AbstractFuture} implementation throws {@code + * InterruptedException} if the current thread is interrupted before or during + * the call, even if the value is already available. + * + * @throws InterruptedException if the current thread was interrupted before + * or during the call (optional but recommended). + * @throws CancellationException {@inheritDoc} + */ + @Override + public V get(long timeout, TimeUnit unit) throws InterruptedException, + TimeoutException, ExecutionException { + return sync.get(unit.toNanos(timeout)); + } + + /* + * Improve the documentation of when InterruptedException is thrown. Our + * behavior matches the JDK's, but the JDK's documentation is misleading. + */ + /** + * {@inheritDoc} + * + *

The default {@link AbstractFuture} implementation throws {@code + * InterruptedException} if the current thread is interrupted before or during + * the call, even if the value is already available. + * + * @throws InterruptedException if the current thread was interrupted before + * or during the call (optional but recommended). + * @throws CancellationException {@inheritDoc} + */ + @Override + public V get() throws InterruptedException, ExecutionException { + return sync.get(); + } + + @Override + public boolean isDone() { + return sync.isDone(); + } + + @Override + public boolean isCancelled() { + return sync.isCancelled(); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (!sync.cancel(mayInterruptIfRunning)) { + return false; + } + executionList.execute(); + if (mayInterruptIfRunning) { + interruptTask(); + } + return true; + } + + /** + * Subclasses can override this method to implement interruption of the + * future's computation. The method is invoked automatically by a successful + * call to {@link #cancel(boolean) cancel(true)}. + * + *

The default implementation does nothing. + * + * @since 10.0 + */ + protected void interruptTask() { + } + + /** + * Returns true if this future was cancelled with {@code + * mayInterruptIfRunning} set to {@code true}. + * + * @since 14.0 + */ + protected final boolean wasInterrupted() { + return sync.wasInterrupted(); + } + + /** + * {@inheritDoc} + * + * @since 10.0 + */ + @Override + public void addListener(Runnable listener, Executor exec) { + executionList.add(listener, exec); + } + + /** + * Subclasses should invoke this method to set the result of the computation + * to {@code value}. This will set the state of the future to + * {@link AbstractFuture.Sync#COMPLETED} and invoke the listeners if the + * state was successfully changed. + * + * @param value the value that was the result of the task. + * @return true if the state was successfully changed. + */ + protected boolean set(V value) { + boolean result = sync.set(value); + if (result) { + executionList.execute(); + } + return result; + } + + /** + * Subclasses should invoke this method to set the result of the computation + * to an error, {@code throwable}. This will set the state of the future to + * {@link AbstractFuture.Sync#COMPLETED} and invoke the listeners if the + * state was successfully changed. + * + * @param throwable the exception that the task failed with. + * @return true if the state was successfully changed. + */ + protected boolean setException(Throwable throwable) { + boolean result = sync.setException(throwable); + if (result) { + executionList.execute(); + } + return result; + } + + /** + *

Following the contract of {@link AbstractQueuedSynchronizer} we create a + * private subclass to hold the synchronizer. This synchronizer is used to + * implement the blocking and waiting calls as well as to handle state changes + * in a thread-safe manner. The current state of the future is held in the + * Sync state, and the lock is released whenever the state changes to + * {@link #COMPLETED}, {@link #CANCELLED}, or {@link #INTERRUPTED} + * + *

To avoid races between threads doing release and acquire, we transition + * to the final state in two steps. One thread will successfully CAS from + * RUNNING to COMPLETING, that thread will then set the result of the + * computation, and only then transition to COMPLETED, CANCELLED, or + * INTERRUPTED. + * + *

We don't use the integer argument passed between acquire methods so we + * pass around a -1 everywhere. + */ + static final class Sync extends AbstractQueuedSynchronizer { + + private static final long serialVersionUID = 0L; + + /* Valid states. */ + 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; + + /* + * Acquisition succeeds if the future is done, otherwise it fails. + */ + @Override + protected int tryAcquireShared(int ignored) { + if (isDone()) { + return 1; + } + return -1; + } + + /* + * We always allow a release to go through, this means the state has been + * successfully changed and the result is available. + */ + @Override + protected boolean tryReleaseShared(int finalState) { + setState(finalState); + return true; + } + + /** + * Blocks until the task is complete or the timeout expires. Throws a + * {@link TimeoutException} if the timer expires, otherwise behaves like + * {@link #get()}. + */ + V get(long nanos) throws TimeoutException, CancellationException, + ExecutionException, InterruptedException { + + // Attempt to acquire the shared lock with a timeout. + if (!tryAcquireSharedNanos(-1, nanos)) { + throw new TimeoutException("Timeout waiting for task."); + } + + return getValue(); + } + + /** + * Blocks until {@link #complete(Object, Throwable, int)} has been + * successfully called. Throws a {@link CancellationException} if the task + * was cancelled, or a {@link ExecutionException} if the task completed with + * an error. + */ + V get() throws CancellationException, ExecutionException, + InterruptedException { + + // Acquire the shared lock allowing interruption. + acquireSharedInterruptibly(-1); + return getValue(); + } + + /** + * Implementation of the actual value retrieval. Will return the value + * on success, an exception on failure, a cancellation on cancellation, or + * an illegal state if the synchronizer is in an invalid state. + */ + private V getValue() throws CancellationException, ExecutionException { + int state = getState(); + switch (state) { + case COMPLETED: + if (exception != null) { + throw new ExecutionException(exception); + } else { + return value; + } + + case CANCELLED: + case INTERRUPTED: + throw cancellationExceptionWithCause( + "Task was cancelled.", exception); + + default: + throw new IllegalStateException( + "Error, synchronizer in invalid state: " + state); + } + } + + /** + * Checks if the state is {@link #COMPLETED}, {@link #CANCELLED}, or {@link + * INTERRUPTED}. + */ + boolean isDone() { + return (getState() & (COMPLETED | CANCELLED | INTERRUPTED)) != 0; + } + + /** + * Checks if the state is {@link #CANCELLED} or {@link #INTERRUPTED}. + */ + boolean isCancelled() { + return (getState() & (CANCELLED | INTERRUPTED)) != 0; + } + + /** + * Checks if the state is {@link #INTERRUPTED}. + */ + boolean wasInterrupted() { + return getState() == INTERRUPTED; + } + + /** + * Transition to the COMPLETED state and set the value. + */ + boolean set(V v) { + return complete(v, null, COMPLETED); + } + + /** + * Transition to the COMPLETED state and set the exception. + */ + boolean setException(Throwable t) { + return complete(null, t, COMPLETED); + } + + /** + * Transition to the CANCELLED or INTERRUPTED state. + */ + boolean cancel(boolean interrupt) { + return complete(null, null, interrupt ? INTERRUPTED : CANCELLED); + } + + /** + * Implementation of completing a task. Either {@code v} or {@code t} will + * be set but not both. The {@code finalState} is the state to change to + * from {@link #RUNNING}. If the state is not in the RUNNING state we + * return {@code false} after waiting for the state to be set to a valid + * final state ({@link #COMPLETED}, {@link #CANCELLED}, or {@link + * #INTERRUPTED}). + * + * @param v the value to set as the result of the computation. + * @param t the exception to set as the result of the computation. + * @param finalState the state to transition to. + */ + private boolean complete(V v, Throwable t, + int finalState) { + boolean doCompletion = compareAndSetState(RUNNING, COMPLETING); + if (doCompletion) { + // If this thread successfully transitioned to COMPLETING, set the value + // and exception and then release to the final state. + this.value = v; + // Don't actually construct a CancellationException until necessary. + this.exception = ((finalState & (CANCELLED | INTERRUPTED)) != 0) + ? new CancellationException("Future.cancel() was called.") : t; + releaseShared(finalState); + } else if (getState() == COMPLETING) { + // If some other thread is currently completing the future, block until + // they are done so we can guarantee completion. + acquireShared(-1); + } + return doCompletion; + } + } + + static final CancellationException cancellationExceptionWithCause( + String message, Throwable cause) { + CancellationException exception = new CancellationException(message); + exception.initCause(cause); + return exception; + } +} diff --git a/java/src/game/future/ExecutionError.java b/java/src/game/future/ExecutionError.java new file mode 100644 index 0000000..cf8eae0 --- /dev/null +++ b/java/src/game/future/ExecutionError.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +/** + * {@link Error} variant of {@link java.util.concurrent.ExecutionException}. As + * with {@code ExecutionException}, the error's {@linkplain #getCause() cause} + * comes from a failed task, possibly run in another thread. That cause should + * itself be an {@code Error}; if not, use {@code ExecutionException} or {@link + * UncheckedExecutionException}. This allows the client code to continue to + * distinguish between exceptions and errors, even when they come from other + * threads. + * + * @author Chris Povirk + * @since 10.0 + */ + +class ExecutionError extends Error { + /** + * Creates a new instance with {@code null} as its detail message. + */ + protected ExecutionError() {} + + /** + * Creates a new instance with the given detail message. + */ + protected ExecutionError(String message) { + super(message); + } + + /** + * Creates a new instance with the given detail message and cause. + */ + public ExecutionError(String message, Error cause) { + super(message, cause); + } + + /** + * Creates a new instance with the given cause. + */ + public ExecutionError(Error cause) { + super(cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/future/ExecutionList.java b/java/src/game/future/ExecutionList.java new file mode 100644 index 0000000..79e86cf --- /dev/null +++ b/java/src/game/future/ExecutionList.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +import java.util.concurrent.Executor; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + *

A list of listeners, each with an associated {@code Executor}, that + * guarantees that every {@code Runnable} that is {@linkplain #add added} will + * be executed after {@link #execute()} is called. Any {@code Runnable} added + * after the call to {@code execute} is still guaranteed to execute. There is no + * guarantee, however, that listeners will be executed in the order that they + * are added. + * + *

Exceptions thrown by a listener will be propagated up to the executor. + * Any exception thrown during {@code Executor.execute} (e.g., a {@code + * RejectedExecutionException} or an exception thrown by {@linkplain + * MoreExecutors#sameThreadExecutor inline execution}) will be caught and + * logged. + * + * @author Nishant Thakkar + * @author Sven Mawson + * @since 1.0 + */ +final class ExecutionList { + // Logger to log exceptions caught when running runnables. +// @VisibleForTesting + static final Logger log = Logger.getLogger(ExecutionList.class.getName()); + + /** + * The runnable, executor pairs to execute. This acts as a stack threaded through the + * {@link RunnableExecutorPair#next} field. + */ +// @GuardedBy("this") + private RunnableExecutorPair runnables; +// @GuardedBy("this") + private boolean executed; + + /** Creates a new, empty {@link ExecutionList}. */ + public ExecutionList() {} + + /** + * Adds the {@code Runnable} and accompanying {@code Executor} to the list of + * listeners to execute. If execution has already begun, the listener is + * executed immediately. + * + *

Note: For fast, lightweight listeners that would be safe to execute in + * any thread, consider {@link MoreExecutors#sameThreadExecutor}. For heavier + * listeners, {@code sameThreadExecutor()} carries some caveats: First, the + * thread that the listener runs in depends on whether the {@code + * ExecutionList} has been executed at the time it is added. In particular, + * listeners may run in the thread that calls {@code add}. Second, the thread + * that calls {@link #execute} may be an internal implementation thread, such + * as an RPC network thread, and {@code sameThreadExecutor()} listeners may + * run in this thread. Finally, during the execution of a {@code + * sameThreadExecutor} listener, all other registered but unexecuted + * listeners are prevented from running, even if those listeners are to run + * in other executors. + */ + public void add(Runnable runnable, Executor executor) { + // Fail fast on a null. We throw NPE here because the contract of + // Executor states that it throws NPE on null listener, so we propagate + // that contract up into the add method as well. + + // Lock while we check state. We must maintain the lock while adding the + // new pair so that another thread can't run the list out from under us. + // We only add to the list if we have not yet started execution. + synchronized (this) { + if (!executed) { + runnables = new RunnableExecutorPair(runnable, executor, runnables); + return; + } + } + // Execute the runnable immediately. Because of scheduling this may end up + // getting called before some of the previously added runnables, but we're + // OK with that. If we want to change the contract to guarantee ordering + // among runnables we'd have to modify the logic here to allow it. + executeListener(runnable, executor); + } + + /** + * Runs this execution list, executing all existing pairs in the order they + * were added. However, note that listeners added after this point may be + * executed before those previously added, and note that the execution order + * of all listeners is ultimately chosen by the implementations of the + * supplied executors. + * + *

This method is idempotent. Calling it several times in parallel is + * semantically equivalent to calling it exactly once. + * + * @since 10.0 (present in 1.0 as {@code run}) + */ + public void execute() { + // Lock while we update our state so the add method above will finish adding + // any listeners before we start to run them. + RunnableExecutorPair list; + synchronized (this) { + if (executed) { + return; + } + executed = true; + list = runnables; + runnables = null; // allow GC to free listeners even if this stays around for a while. + } + // If we succeeded then list holds all the runnables we to execute. The pairs in the stack are + // in the opposite order from how they were added so we need to reverse the list to fulfill our + // contract. + // This is somewhat annoying, but turns out to be very fast in practice. Alternatively, we + // could drop the contract on the method that enforces this queue like behavior since depending + // on it is likely to be a bug anyway. + + // N.B. All writes to the list and the next pointers must have happened before the above + // synchronized block, so we can iterate the list without the lock held here. + RunnableExecutorPair reversedList = null; + while (list != null) { + RunnableExecutorPair tmp = list; + list = list.next; + tmp.next = reversedList; + reversedList = tmp; + } + while (reversedList != null) { + executeListener(reversedList.runnable, reversedList.executor); + reversedList = reversedList.next; + } + } + + /** + * Submits the given runnable to the given {@link Executor} catching and logging all + * {@linkplain RuntimeException runtime exceptions} thrown by the executor. + */ + private static void executeListener(Runnable runnable, Executor executor) { + try { + executor.execute(runnable); + } catch (RuntimeException e) { + // Log it and keep going, bad runnable and/or executor. Don't + // punish the other runnables if we're given a bad one. We only + // catch RuntimeException because we want Errors to propagate up. + log.log(Level.SEVERE, "RuntimeException while executing runnable " + + runnable + " with executor " + executor, e); + } + } + + private static final class RunnableExecutorPair { + final Runnable runnable; + final Executor executor; + RunnableExecutorPair next; + + RunnableExecutorPair(Runnable runnable, Executor executor, RunnableExecutorPair next) { + this.runnable = runnable; + this.executor = executor; + this.next = next; + } + } +} diff --git a/java/src/game/future/FutureCallback.java b/java/src/game/future/FutureCallback.java new file mode 100644 index 0000000..cdb9092 --- /dev/null +++ b/java/src/game/future/FutureCallback.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +/** + * A callback for accepting the results of a {@link java.util.concurrent.Future} + * computation asynchronously. + * + *

To attach to a {@link ListenableFuture} use {@link Futures#addCallback}. + * + * @author Anthony Zana + * @since 10.0 + */ +public interface FutureCallback { + /** + * Invoked with the result of the {@code Future} computation when it is + * successful. + */ + void onSuccess(V result); + + /** + * Invoked when a {@code Future} computation fails or is canceled. + * + *

If the future's {@link Future#get() get} method throws an {@link + * ExecutionException}, then the cause is passed to this method. Any other + * thrown object is passed unaltered. + */ + void onFailure(Throwable t); +} diff --git a/java/src/game/future/Futures.java b/java/src/game/future/Futures.java new file mode 100644 index 0000000..08fe41f --- /dev/null +++ b/java/src/game/future/Futures.java @@ -0,0 +1,1723 @@ +/* + * Copyright (C) 2006 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import game.collect.ImmutableList; +import game.collect.Sets; + +/** + * Static utility methods pertaining to the {@link Future} interface. + * + *

Many of these methods use the {@link ListenableFuture} API; consult the + * Guava User Guide article on + * {@code ListenableFuture}. + * + * @author Kevin Bourrillion + * @author Nishant Thakkar + * @author Sven Mawson + * @since 1.0 + */ +//@Beta +public final class Futures { + private Futures() {} + + /** + * Creates a {@link CheckedFuture} out of a normal {@link ListenableFuture} + * and a {@link Function} that maps from {@link Exception} instances into the + * appropriate checked type. + * + *

The given mapping function will be applied to an + * {@link InterruptedException}, a {@link CancellationException}, or an + * {@link ExecutionException}. + * See {@link Future#get()} for details on the exceptions thrown. + * + * @since 9.0 (source-compatible since 1.0) + */ +// public static CheckedFuture makeChecked( +// ListenableFuture future, Function mapper) { +// return new MappingCheckedFuture(checkNotNull(future), mapper); +// } + + private abstract static class ImmediateFuture + implements ListenableFuture { + + private static final Logger log = + Logger.getLogger(ImmediateFuture.class.getName()); + + @Override + public void addListener(Runnable listener, Executor executor) { +// checkNotNull(listener, "Runnable was null."); +// checkNotNull(executor, "Executor was null."); + try { + executor.execute(listener); + } catch (RuntimeException e) { + // ListenableFuture's contract is that it will not throw unchecked + // exceptions, so log the bad runnable and/or executor and swallow it. + log.log(Level.SEVERE, "RuntimeException while executing runnable " + + listener + " with executor " + executor, e); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public abstract V get() throws ExecutionException; + + @Override + public V get(long timeout, TimeUnit unit) throws ExecutionException { +// checkNotNull(unit); + return get(); + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + } + + private static class ImmediateSuccessfulFuture extends ImmediateFuture { + + private final V value; + + ImmediateSuccessfulFuture(V value) { + this.value = value; + } + + @Override + public V get() { + return value; + } + } + +// private static class ImmediateSuccessfulCheckedFuture +// extends ImmediateFuture implements CheckedFuture { +// +// private final V value; +// +// ImmediateSuccessfulCheckedFuture(V value) { +// this.value = value; +// } +// +// @Override +// public V get() { +// return value; +// } +// +// @Override +// public V checkedGet() { +// return value; +// } +// +// @Override +// public V checkedGet(long timeout, TimeUnit unit) { +// checkNotNull(unit); +// return value; +// } +// } + + private static class ImmediateFailedFuture extends ImmediateFuture { + + private final Throwable thrown; + + ImmediateFailedFuture(Throwable thrown) { + this.thrown = thrown; + } + + @Override + public V get() throws ExecutionException { + throw new ExecutionException(thrown); + } + } + +// private static class ImmediateCancelledFuture extends ImmediateFuture { +// +// private final CancellationException thrown; +// +// ImmediateCancelledFuture() { +// this.thrown = new CancellationException("Immediate cancelled future."); +// } +// +// @Override +// public boolean isCancelled() { +// return true; +// } +// +// @Override +// public V get() { +// throw AbstractFuture.cancellationExceptionWithCause( +// "Task was cancelled.", thrown); +// } +// } + +// private static class ImmediateFailedFuture +// extends ImmediateFuture implements ListenableFuture { +// +//// private final X thrown; +// +// ImmediateFailedCheckedFuture(X thrown) { +// this.thrown = thrown; +// } +// +// @Override +// public V get() throws ExecutionException { +// throw new ExecutionException(thrown); +// } +// +// @Override +// public V checkedGet() throws X { +// throw thrown; +// } +// +// @Override +// public V checkedGet(long timeout, TimeUnit unit) throws X { +// checkNotNull(unit); +// throw thrown; +// } +// } + + /** + * Creates a {@code ListenableFuture} which has its value set immediately upon + * construction. The getters just return the value. This {@code Future} can't + * be canceled or timed out and its {@code isDone()} method always returns + * {@code true}. + */ + public static ListenableFuture immediateFuture(V value) { + return new ImmediateSuccessfulFuture(value); + } + + /** + * Returns a {@code CheckedFuture} which has its value set immediately upon + * construction. + * + *

The returned {@code Future} can't be cancelled, and its {@code isDone()} + * method always returns {@code true}. Calling {@code get()} or {@code + * checkedGet()} will immediately return the provided value. + */ +// public static CheckedFuture +// immediateCheckedFuture(V value) { +// return new ImmediateSuccessfulCheckedFuture(value); +// } + + /** + * Returns a {@code ListenableFuture} which has an exception set immediately + * upon construction. + * + *

The returned {@code Future} can't be cancelled, and its {@code isDone()} + * method always returns {@code true}. Calling {@code get()} will immediately + * throw the provided {@code Throwable} wrapped in an {@code + * ExecutionException}. + */ + public static ListenableFuture immediateFailedFuture( + Throwable throwable) { +// checkNotNull(throwable); + return new ImmediateFailedFuture(throwable); + } + + /** + * Creates a {@code ListenableFuture} which is cancelled immediately upon + * construction, so that {@code isCancelled()} always returns {@code true}. + * + * @since 14.0 + */ +// public static ListenableFuture immediateCancelledFuture() { +// return new ImmediateCancelledFuture(); +// } + + /** + * Returns a {@code CheckedFuture} which has an exception set immediately upon + * construction. + * + *

The returned {@code Future} can't be cancelled, and its {@code isDone()} + * method always returns {@code true}. Calling {@code get()} will immediately + * throw the provided {@code Exception} wrapped in an {@code + * ExecutionException}, and calling {@code checkedGet()} will throw the + * provided exception itself. + */ +// public static ListenableFuture +// immediateFailedCheckedFuture(X exception) { +// checkNotNull(exception); +// return new ImmediateFailedCheckedFuture(exception); +// } + + /** + * Returns a {@code Future} whose result is taken from the given primary + * {@code input} or, if the primary input fails, from the {@code Future} + * provided by the {@code fallback}. {@link FutureFallback#create} is not + * invoked until the primary input has failed, so if the primary input + * succeeds, it is never invoked. If, during the invocation of {@code + * fallback}, an exception is thrown, this exception is used as the result of + * the output {@code Future}. + * + *

Below is an example of a fallback that returns a default value if an + * exception occurs: + * + *

   {@code
+   *   ListenableFuture fetchCounterFuture = ...;
+   *
+   *   // Falling back to a zero counter in case an exception happens when
+   *   // processing the RPC to fetch counters.
+   *   ListenableFuture faultTolerantFuture = Futures.withFallback(
+   *       fetchCounterFuture, new FutureFallback() {
+   *         public ListenableFuture create(Throwable t) {
+   *           // Returning "0" as the default for the counter when the
+   *           // exception happens.
+   *           return immediateFuture(0);
+   *         }
+   *       });}
+ * + *

The fallback can also choose to propagate the original exception when + * desired: + * + *

   {@code
+   *   ListenableFuture fetchCounterFuture = ...;
+   *
+   *   // Falling back to a zero counter only in case the exception was a
+   *   // TimeoutException.
+   *   ListenableFuture faultTolerantFuture = Futures.withFallback(
+   *       fetchCounterFuture, new FutureFallback() {
+   *         public ListenableFuture create(Throwable t) {
+   *           if (t instanceof TimeoutException) {
+   *             return immediateFuture(0);
+   *           }
+   *           return immediateFailedFuture(t);
+   *         }
+   *       });}
+ * + *

Note: If the derived {@code Future} is slow or heavyweight to create + * (whether the {@code Future} itself is slow or heavyweight to complete is + * irrelevant), consider {@linkplain #withFallback(ListenableFuture, + * FutureFallback, Executor) supplying an executor}. If you do not supply an + * executor, {@code withFallback} will use {@link + * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some + * caveats for heavier operations. For example, the call to {@code + * fallback.create} may run on an unpredictable or undesirable thread: + * + *

    + *
  • If the input {@code Future} is done at the time {@code withFallback} + * is called, {@code withFallback} will call {@code fallback.create} inline. + *
  • If the input {@code Future} is not yet done, {@code withFallback} will + * schedule {@code fallback.create} to be run by the thread that completes + * the input {@code Future}, which may be an internal system thread such as + * an RPC network thread. + *
+ * + *

Also note that, regardless of which thread executes the {@code + * sameThreadExecutor} {@code fallback.create}, all other registered but + * unexecuted listeners are prevented from running during its execution, even + * if those listeners are to run in other executors. + * + * @param input the primary input {@code Future} + * @param fallback the {@link FutureFallback} implementation to be called if + * {@code input} fails + * @since 14.0 + */ +// public static ListenableFuture withFallback( +// ListenableFuture input, +// FutureFallback fallback) { +// return withFallback(input, fallback, sameThreadExecutor()); +// } + + /** + * Returns a {@code Future} whose result is taken from the given primary + * {@code input} or, if the primary input fails, from the {@code Future} + * provided by the {@code fallback}. {@link FutureFallback#create} is not + * invoked until the primary input has failed, so if the primary input + * succeeds, it is never invoked. If, during the invocation of {@code + * fallback}, an exception is thrown, this exception is used as the result of + * the output {@code Future}. + * + *

Below is an example of a fallback that returns a default value if an + * exception occurs: + * + *

   {@code
+   *   ListenableFuture fetchCounterFuture = ...;
+   *
+   *   // Falling back to a zero counter in case an exception happens when
+   *   // processing the RPC to fetch counters.
+   *   ListenableFuture faultTolerantFuture = Futures.withFallback(
+   *       fetchCounterFuture, new FutureFallback() {
+   *         public ListenableFuture create(Throwable t) {
+   *           // Returning "0" as the default for the counter when the
+   *           // exception happens.
+   *           return immediateFuture(0);
+   *         }
+   *       }, sameThreadExecutor());}
+ * + *

The fallback can also choose to propagate the original exception when + * desired: + * + *

   {@code
+   *   ListenableFuture fetchCounterFuture = ...;
+   *
+   *   // Falling back to a zero counter only in case the exception was a
+   *   // TimeoutException.
+   *   ListenableFuture faultTolerantFuture = Futures.withFallback(
+   *       fetchCounterFuture, new FutureFallback() {
+   *         public ListenableFuture create(Throwable t) {
+   *           if (t instanceof TimeoutException) {
+   *             return immediateFuture(0);
+   *           }
+   *           return immediateFailedFuture(t);
+   *         }
+   *       }, sameThreadExecutor());}
+ * + *

When the execution of {@code fallback.create} is fast and lightweight + * (though the {@code Future} it returns need not meet these criteria), + * consider {@linkplain #withFallback(ListenableFuture, FutureFallback) + * omitting the executor} or explicitly specifying {@code + * sameThreadExecutor}. However, be aware of the caveats documented in the + * link above. + * + * @param input the primary input {@code Future} + * @param fallback the {@link FutureFallback} implementation to be called if + * {@code input} fails + * @param executor the executor that runs {@code fallback} if {@code input} + * fails + * @since 14.0 + */ +// public static ListenableFuture withFallback( +// ListenableFuture input, +// FutureFallback fallback, Executor executor) { +// checkNotNull(fallback); +// return new FallbackFuture(input, fallback, executor); +// } + + /** + * A future that falls back on a second, generated future, in case its + * original future fails. + */ +// private static class FallbackFuture extends AbstractFuture { +// +// private volatile ListenableFuture running; +// +// FallbackFuture(ListenableFuture input, +// final FutureFallback fallback, +// final Executor executor) { +// running = input; +// addCallback(running, new FutureCallback() { +// @Override +// public void onSuccess(V value) { +// set(value); +// } +// +// @Override +// public void onFailure(Throwable t) { +// if (isCancelled()) { +// return; +// } +// try { +// running = fallback.create(t); +// if (isCancelled()) { // in case cancel called in the meantime +// running.cancel(wasInterrupted()); +// return; +// } +// addCallback(running, new FutureCallback() { +// @Override +// public void onSuccess(V value) { +// set(value); +// } +// +// @Override +// public void onFailure(Throwable t) { +// if (running.isCancelled()) { +// cancel(false); +// } else { +// setException(t); +// } +// } +// }, sameThreadExecutor()); +// } catch (Throwable e) { +// setException(e); +// } +// } +// }, executor); +// } +// +// @Override +// public boolean cancel(boolean mayInterruptIfRunning) { +// if (super.cancel(mayInterruptIfRunning)) { +// running.cancel(mayInterruptIfRunning); +// return true; +// } +// return false; +// } +// } + + /** + * Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code AsyncFunction} to the result of the original + * {@code Future}. Example: + * + *

   {@code
+   *   ListenableFuture rowKeyFuture = indexService.lookUp(query);
+   *   AsyncFunction queryFunction =
+   *       new AsyncFunction() {
+   *         public ListenableFuture apply(RowKey rowKey) {
+   *           return dataService.read(rowKey);
+   *         }
+   *       };
+   *   ListenableFuture queryFuture =
+   *       transform(rowKeyFuture, queryFunction);}
+ * + *

Note: If the derived {@code Future} is slow or heavyweight to create + * (whether the {@code Future} itself is slow or heavyweight to complete is + * irrelevant), consider {@linkplain #transform(ListenableFuture, + * AsyncFunction, Executor) supplying an executor}. If you do not supply an + * executor, {@code transform} will use {@link + * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some + * caveats for heavier operations. For example, the call to {@code + * function.apply} may run on an unpredictable or undesirable thread: + * + *

    + *
  • If the input {@code Future} is done at the time {@code transform} is + * called, {@code transform} will call {@code function.apply} inline. + *
  • If the input {@code Future} is not yet done, {@code transform} will + * schedule {@code function.apply} to be run by the thread that completes the + * input {@code Future}, which may be an internal system thread such as an + * RPC network thread. + *
+ * + *

Also note that, regardless of which thread executes the {@code + * sameThreadExecutor} {@code function.apply}, all other registered but + * unexecuted listeners are prevented from running during its execution, even + * if those listeners are to run in other executors. + * + *

The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * function. That is, if the returned {@code Future} is cancelled, it will + * attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + * @param input The future to transform + * @param function A function to transform the result of the input future + * to the result of the output future + * @return A future that holds result of the function (if the input succeeded) + * or the original input's failure (if not) + * @since 11.0 + */ +// public static ListenableFuture transform(ListenableFuture input, +// AsyncFunction function) { +// return transform(input, function, MoreExecutors.sameThreadExecutor()); +// } + + /** + * Returns a new {@code ListenableFuture} whose result is asynchronously + * derived from the result of the given {@code Future}. More precisely, the + * returned {@code Future} takes its result from a {@code Future} produced by + * applying the given {@code AsyncFunction} to the result of the original + * {@code Future}. Example: + * + *

   {@code
+   *   ListenableFuture rowKeyFuture = indexService.lookUp(query);
+   *   AsyncFunction queryFunction =
+   *       new AsyncFunction() {
+   *         public ListenableFuture apply(RowKey rowKey) {
+   *           return dataService.read(rowKey);
+   *         }
+   *       };
+   *   ListenableFuture queryFuture =
+   *       transform(rowKeyFuture, queryFunction, executor);}
+ * + *

The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future and that of the future returned by the + * chain function. That is, if the returned {@code Future} is cancelled, it + * will attempt to cancel the other two, and if either of the other two is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + *

When the execution of {@code function.apply} is fast and lightweight + * (though the {@code Future} it returns need not meet these criteria), + * consider {@linkplain #transform(ListenableFuture, AsyncFunction) omitting + * the executor} or explicitly specifying {@code sameThreadExecutor}. + * However, be aware of the caveats documented in the link above. + * + * @param input The future to transform + * @param function A function to transform the result of the input future + * to the result of the output future + * @param executor Executor to run the function in. + * @return A future that holds result of the function (if the input succeeded) + * or the original input's failure (if not) + * @since 11.0 + */ +// public static ListenableFuture transform(ListenableFuture input, +// AsyncFunction function, +// Executor executor) { +// ChainingListenableFuture output = +// new ChainingListenableFuture(function, input); +// input.addListener(output, executor); +// return output; +// } + + /** + * Returns a new {@code ListenableFuture} whose result is the product of + * applying the given {@code Function} to the result of the given {@code + * Future}. Example: + * + *

   {@code
+   *   ListenableFuture queryFuture = ...;
+   *   Function> rowsFunction =
+   *       new Function>() {
+   *         public List apply(QueryResult queryResult) {
+   *           return queryResult.getRows();
+   *         }
+   *       };
+   *   ListenableFuture> rowsFuture =
+   *       transform(queryFuture, rowsFunction);}
+ * + *

Note: If the transformation is slow or heavyweight, consider {@linkplain + * #transform(ListenableFuture, Function, Executor) supplying an executor}. + * If you do not supply an executor, {@code transform} will use {@link + * MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries some + * caveats for heavier operations. For example, the call to {@code + * function.apply} may run on an unpredictable or undesirable thread: + * + *

    + *
  • If the input {@code Future} is done at the time {@code transform} is + * called, {@code transform} will call {@code function.apply} inline. + *
  • If the input {@code Future} is not yet done, {@code transform} will + * schedule {@code function.apply} to be run by the thread that completes the + * input {@code Future}, which may be an internal system thread such as an + * RPC network thread. + *
+ * + *

Also note that, regardless of which thread executes the {@code + * sameThreadExecutor} {@code function.apply}, all other registered but + * unexecuted listeners are prevented from running during its execution, even + * if those listeners are to run in other executors. + * + *

The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future. That is, if the returned {@code Future} + * is cancelled, it will attempt to cancel the input, and if the input is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + *

An example use of this method is to convert a serializable object + * returned from an RPC into a POJO. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future + * to the results of the returned future. This will be run in the thread + * that notifies input it is complete. + * @return A future that holds result of the transformation. + * @since 9.0 (in 1.0 as {@code compose}) + */ +// public static ListenableFuture transform(ListenableFuture input, +// final Function function) { +// return transform(input, function, MoreExecutors.sameThreadExecutor()); +// } + + /** + * Returns a new {@code ListenableFuture} whose result is the product of + * applying the given {@code Function} to the result of the given {@code + * Future}. Example: + * + *

   {@code
+   *   ListenableFuture queryFuture = ...;
+   *   Function> rowsFunction =
+   *       new Function>() {
+   *         public List apply(QueryResult queryResult) {
+   *           return queryResult.getRows();
+   *         }
+   *       };
+   *   ListenableFuture> rowsFuture =
+   *       transform(queryFuture, rowsFunction, executor);}
+ * + *

The returned {@code Future} attempts to keep its cancellation state in + * sync with that of the input future. That is, if the returned {@code Future} + * is cancelled, it will attempt to cancel the input, and if the input is + * cancelled, the returned {@code Future} will receive a callback in which it + * will attempt to cancel itself. + * + *

An example use of this method is to convert a serializable object + * returned from an RPC into a POJO. + * + *

When the transformation is fast and lightweight, consider {@linkplain + * #transform(ListenableFuture, Function) omitting the executor} or + * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * caveats documented in the link above. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future + * to the results of the returned future. + * @param executor Executor to run the function in. + * @return A future that holds result of the transformation. + * @since 9.0 (in 2.0 as {@code compose}) + */ +// public static ListenableFuture transform(ListenableFuture input, +// final Function function, Executor executor) { +// checkNotNull(function); +// AsyncFunction wrapperFunction +// = new AsyncFunction() { +// @Override public ListenableFuture apply(I input) { +// O output = function.apply(input); +// return immediateFuture(output); +// } +// }; +// return transform(input, wrapperFunction, executor); +// } + + /** + * Like {@link #transform(ListenableFuture, Function)} except that the + * transformation {@code function} is invoked on each call to + * {@link Future#get() get()} on the returned future. + * + *

The returned {@code Future} reflects the input's cancellation + * state directly, and any attempt to cancel the returned Future is likewise + * passed through to the input Future. + * + *

Note that calls to {@linkplain Future#get(long, TimeUnit) timed get} + * only apply the timeout to the execution of the underlying {@code Future}, + * not to the execution of the transformation function. + * + *

The primary audience of this method is callers of {@code transform} + * who don't have a {@code ListenableFuture} available and + * do not mind repeated, lazy function evaluation. + * + * @param input The future to transform + * @param function A Function to transform the results of the provided future + * to the results of the returned future. + * @return A future that returns the result of the transformation. + * @since 10.0 + */ +// public static Future lazyTransform(final Future input, +// final Function function) { +// checkNotNull(input); +// checkNotNull(function); +// return new Future() { +// +// @Override +// public boolean cancel(boolean mayInterruptIfRunning) { +// return input.cancel(mayInterruptIfRunning); +// } +// +// @Override +// public boolean isCancelled() { +// return input.isCancelled(); +// } +// +// @Override +// public boolean isDone() { +// return input.isDone(); +// } +// +// @Override +// public O get() throws InterruptedException, ExecutionException { +// return applyTransformation(input.get()); +// } +// +// @Override +// public O get(long timeout, TimeUnit unit) +// throws InterruptedException, ExecutionException, TimeoutException { +// return applyTransformation(input.get(timeout, unit)); +// } +// +// private O applyTransformation(I input) throws ExecutionException { +// try { +// return function.apply(input); +// } catch (Throwable t) { +// throw new ExecutionException(t); +// } +// } +// }; +// } + + /** + * An implementation of {@code ListenableFuture} that also implements + * {@code Runnable} so that it can be used to nest ListenableFutures. + * Once the passed-in {@code ListenableFuture} is complete, it calls the + * passed-in {@code Function} to generate the result. + * + *

For historical reasons, this class has a special case in its exception + * handling: If the given {@code AsyncFunction} throws an {@code + * UndeclaredThrowableException}, {@code ChainingListenableFuture} unwraps it + * and uses its cause as the output future's exception, rather than + * using the {@code UndeclaredThrowableException} itself as it would for other + * exception types. The reason for this is that {@code Futures.transform} used + * to require a {@code Function}, whose {@code apply} method is not allowed to + * throw checked exceptions. Nowadays, {@code Futures.transform} has an + * overload that accepts an {@code AsyncFunction}, whose {@code apply} method + * is allowed to throw checked exception. Users who wish to throw + * checked exceptions should use that overload instead, and we + * should remove the {@code UndeclaredThrowableException} special case. + */ +// private static class ChainingListenableFuture +// extends AbstractFuture implements Runnable { +// +// private AsyncFunction function; +// private ListenableFuture inputFuture; +// private volatile ListenableFuture outputFuture; +// private final CountDownLatch outputCreated = new CountDownLatch(1); +// +// private ChainingListenableFuture( +// AsyncFunction function, +// ListenableFuture inputFuture) { +// this.function = checkNotNull(function); +// this.inputFuture = checkNotNull(inputFuture); +// } +// +// @Override +// public boolean cancel(boolean mayInterruptIfRunning) { +// /* +// * Our additional cancellation work needs to occur even if +// * !mayInterruptIfRunning, so we can't move it into interruptTask(). +// */ +// if (super.cancel(mayInterruptIfRunning)) { +// // This should never block since only one thread is allowed to cancel +// // this Future. +// cancel(inputFuture, mayInterruptIfRunning); +// cancel(outputFuture, mayInterruptIfRunning); +// return true; +// } +// return false; +// } +// +// private void cancel(Future future, +// boolean mayInterruptIfRunning) { +// if (future != null) { +// future.cancel(mayInterruptIfRunning); +// } +// } +// +// @Override +// public void run() { +// try { +// I sourceResult; +// try { +// sourceResult = getUninterruptibly(inputFuture); +// } catch (CancellationException e) { +// // Cancel this future and return. +// // At this point, inputFuture is cancelled and outputFuture doesn't +// // exist, so the value of mayInterruptIfRunning is irrelevant. +// cancel(false); +// return; +// } catch (ExecutionException e) { +// // Set the cause of the exception as this future's exception +// setException(e.getCause()); +// return; +// } +// +// final ListenableFuture outputFuture = this.outputFuture = +// Preconditions.checkNotNull(function.apply(sourceResult), +// "AsyncFunction may not return null."); +// if (isCancelled()) { +// outputFuture.cancel(wasInterrupted()); +// this.outputFuture = null; +// return; +// } +// outputFuture.addListener(new Runnable() { +// @Override +// public void run() { +// try { +// set(getUninterruptibly(outputFuture)); +// } catch (CancellationException e) { +// // Cancel this future and return. +// // At this point, inputFuture and outputFuture are done, so the +// // value of mayInterruptIfRunning is irrelevant. +// cancel(false); +// return; +// } catch (ExecutionException e) { +// // Set the cause of the exception as this future's exception +// setException(e.getCause()); +// } finally { +// // Don't pin inputs beyond completion +// ChainingListenableFuture.this.outputFuture = null; +// } +// } +// }, MoreExecutors.sameThreadExecutor()); +// } catch (UndeclaredThrowableException e) { +// // Set the cause of the exception as this future's exception +// setException(e.getCause()); +// } catch (Throwable t) { +// // This exception is irrelevant in this thread, but useful for the +// // client +// setException(t); +// } finally { +// // Don't pin inputs beyond completion +// function = null; +// inputFuture = null; +// // Allow our get routines to examine outputFuture now. +// outputCreated.countDown(); +// } +// } +// } + + /** + * Returns a new {@code ListenableFuture} whose result is the product of + * calling {@code get()} on the {@code Future} nested within the given {@code + * Future}, effectively chaining the futures one after the other. Example: + * + *

   {@code
+   *   SettableFuture> nested = SettableFuture.create();
+   *   ListenableFuture dereferenced = dereference(nested);}
+ * + *

This call has the same cancellation and execution semantics as {@link + * #transform(ListenableFuture, AsyncFunction)}, in that the returned {@code + * Future} attempts to keep its cancellation state in sync with both the + * input {@code Future} and the nested {@code Future}. The transformation + * is very lightweight and therefore takes place in the same thread (either + * the thread that called {@code dereference}, or the thread in which the + * dereferenced future completes). + * + * @param nested The nested future to transform. + * @return A future that holds result of the inner future. + * @since 13.0 + */ +// +// public static ListenableFuture dereference( +// ListenableFuture> nested) { +// return Futures.transform((ListenableFuture) nested, (AsyncFunction) DEREFERENCER); +// } + + /** + * Helper {@code Function} for {@link #dereference}. + */ +// private static final AsyncFunction, Object> DEREFERENCER = +// new AsyncFunction, Object>() { +// @Override public ListenableFuture apply(ListenableFuture input) { +// return input; +// } +// }; + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its input futures, if all succeed. If any input fails, the + * returned future fails. + * + *

The list of results is in the same order as the input list. + * + *

Canceling this future will attempt to cancel all the component futures, + * and if any of the provided futures fails or is canceled, this one is, + * too. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ +// @Beta +// public static ListenableFuture> allAsList( +// ListenableFuture... futures) { +// return listFuture(ImmutableList.copyOf(futures), true, +// MoreExecutors.sameThreadExecutor()); +// } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its input futures, if all succeed. If any input fails, the + * returned future fails. + * + *

The list of results is in the same order as the input list. + * + *

Canceling this future will attempt to cancel all the component futures, + * and if any of the provided futures fails or is canceled, this one is, + * too. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ +// @Beta + public static ListenableFuture> allAsList( + Iterable> futures) { + return listFuture(ImmutableList.copyOf(futures), true, + MoreExecutors.sameThreadExecutor()); + } + + /** + * Creates a new {@code ListenableFuture} whose result is set from the + * supplied future when it completes. Cancelling the supplied future + * will also cancel the returned future, but cancelling the returned + * future will have no effect on the supplied future. + * + * @since 15.0 + */ +// public static ListenableFuture nonCancellationPropagating( +// ListenableFuture future) { +// return new NonCancellationPropagatingFuture(future); +// } + + /** + * A wrapped future that does not propagate cancellation to its delegate. + */ +// private static class NonCancellationPropagatingFuture +// extends AbstractFuture { +// NonCancellationPropagatingFuture(final ListenableFuture delegate) { +// checkNotNull(delegate); +// addCallback(delegate, new FutureCallback() { +// @Override +// public void onSuccess(V result) { +// set(result); +// } +// +// @Override +// public void onFailure(Throwable t) { +// if (delegate.isCancelled()) { +// cancel(false); +// } else { +// setException(t); +// } +// } +// }, sameThreadExecutor()); +// } +// } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its successful input futures. The list of results is in the + * same order as the input list, and if any of the provided futures fails or + * is canceled, its corresponding position will contain {@code null} (which is + * indistinguishable from the future having a successful value of + * {@code null}). + * + *

Canceling this future will attempt to cancel all the component futures. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ +// @Beta +// public static ListenableFuture> successfulAsList( +// ListenableFuture... futures) { +// return listFuture(ImmutableList.copyOf(futures), false, +// MoreExecutors.sameThreadExecutor()); +// } + + /** + * Creates a new {@code ListenableFuture} whose value is a list containing the + * values of all its successful input futures. The list of results is in the + * same order as the input list, and if any of the provided futures fails or + * is canceled, its corresponding position will contain {@code null} (which is + * indistinguishable from the future having a successful value of + * {@code null}). + * + *

Canceling this future will attempt to cancel all the component futures. + * + * @param futures futures to combine + * @return a future that provides a list of the results of the component + * futures + * @since 10.0 + */ +// @Beta +// public static ListenableFuture> successfulAsList( +// Iterable> futures) { +// return listFuture(ImmutableList.copyOf(futures), false, +// MoreExecutors.sameThreadExecutor()); +// } + + /** + * Returns a list of delegate futures that correspond to the futures received in the order + * that they complete. Delegate futures return the same value or throw the same exception + * as the corresponding input future returns/throws. + * + *

Cancelling a delegate future has no effect on any input future, since the delegate future + * does not correspond to a specific input future until the appropriate number of input + * futures have completed. At that point, it is too late to cancel the input future. + * The input future's result, which cannot be stored into the cancelled delegate future, + * is ignored. + * + * @since 17.0 + */ +// @Beta +// public static ImmutableList> inCompletionOrder( +// Iterable> futures) { +// // A CLQ may be overkill here. We could save some pointers/memory by synchronizing on an +// // ArrayDeque +// final ConcurrentLinkedQueue> delegates = +// Queues.newConcurrentLinkedQueue(); +// ImmutableList.Builder> listBuilder = ImmutableList.builder(); +// // Using SerializingExecutor here will ensure that each CompletionOrderListener executes +// // atomically and therefore that each returned future is guaranteed to be in completion order. +// // N.B. there are some cases where the use of this executor could have possibly surprising +// // effects when input futures finish at approximately the same time _and_ the output futures +// // have sameThreadExecutor listeners. In this situation, the listeners may end up running on a +// // different thread than if they were attached to the corresponding input future. We believe +// // this to be a negligible cost since: +// // 1. Using the sameThreadExecutor implies that your callback is safe to run on any thread. +// // 2. This would likely only be noticeable if you were doing something expensive or blocking on +// // a sameThreadExecutor listener on one of the output futures which is an antipattern anyway. +// SerializingExecutor executor = new SerializingExecutor(MoreExecutors.sameThreadExecutor()); +// for (final ListenableFuture future : futures) { +// AsyncSettableFuture delegate = AsyncSettableFuture.create(); +// // Must make sure to add the delegate to the queue first in case the future is already done +// delegates.add(delegate); +// future.addListener(new Runnable() { +// @Override public void run() { +// delegates.remove().setFuture(future); +// } +// }, executor); +// listBuilder.add(delegate); +// } +// return listBuilder.build(); +// } + + /** + * Registers separate success and failure callbacks to be run when the {@code + * Future}'s computation is {@linkplain java.util.concurrent.Future#isDone() + * complete} or, if the computation is already complete, immediately. + * + *

There is no guaranteed ordering of execution of callbacks, but any + * callback added through this method is guaranteed to be called once the + * computation is complete. + * + * Example:

 {@code
+   * ListenableFuture future = ...;
+   * addCallback(future,
+   *     new FutureCallback {
+   *       public void onSuccess(QueryResult result) {
+   *         storeInCache(result);
+   *       }
+   *       public void onFailure(Throwable t) {
+   *         reportError(t);
+   *       }
+   *     });}
+ * + *

Note: If the callback is slow or heavyweight, consider {@linkplain + * #addCallback(ListenableFuture, FutureCallback, Executor) supplying an + * executor}. If you do not supply an executor, {@code addCallback} will use + * {@link MoreExecutors#sameThreadExecutor sameThreadExecutor}, which carries + * some caveats for heavier operations. For example, the callback may run on + * an unpredictable or undesirable thread: + * + *

    + *
  • If the input {@code Future} is done at the time {@code addCallback} is + * called, {@code addCallback} will execute the callback inline. + *
  • If the input {@code Future} is not yet done, {@code addCallback} will + * schedule the callback to be run by the thread that completes the input + * {@code Future}, which may be an internal system thread such as an RPC + * network thread. + *
+ * + *

Also note that, regardless of which thread executes the {@code + * sameThreadExecutor} callback, all other registered but unexecuted listeners + * are prevented from running during its execution, even if those listeners + * are to run in other executors. + * + *

For a more general interface to attach a completion listener to a + * {@code Future}, see {@link ListenableFuture#addListener addListener}. + * + * @param future The future attach the callback to. + * @param callback The callback to invoke when {@code future} is completed. + * @since 10.0 + */ + public static void addCallback(ListenableFuture future, + FutureCallback callback) { + addCallback(future, callback, MoreExecutors.sameThreadExecutor()); + } + + /** + * Registers separate success and failure callbacks to be run when the {@code + * Future}'s computation is {@linkplain java.util.concurrent.Future#isDone() + * complete} or, if the computation is already complete, immediately. + * + *

The callback is run in {@code executor}. + * There is no guaranteed ordering of execution of callbacks, but any + * callback added through this method is guaranteed to be called once the + * computation is complete. + * + * Example:

 {@code
+   * ListenableFuture future = ...;
+   * Executor e = ...
+   * addCallback(future,
+   *     new FutureCallback {
+   *       public void onSuccess(QueryResult result) {
+   *         storeInCache(result);
+   *       }
+   *       public void onFailure(Throwable t) {
+   *         reportError(t);
+   *       }
+   *     }, e);}
+ * + *

When the callback is fast and lightweight, consider {@linkplain + * #addCallback(ListenableFuture, FutureCallback) omitting the executor} or + * explicitly specifying {@code sameThreadExecutor}. However, be aware of the + * caveats documented in the link above. + * + *

For a more general interface to attach a completion listener to a + * {@code Future}, see {@link ListenableFuture#addListener addListener}. + * + * @param future The future attach the callback to. + * @param callback The callback to invoke when {@code future} is completed. + * @param executor The executor to run {@code callback} when the future + * completes. + * @since 10.0 + */ + public static void addCallback(final ListenableFuture future, + final FutureCallback callback, Executor executor) { + Runnable callbackListener = new Runnable() { + @Override + public void run() { + final V value; + try { + // TODO(user): (Before Guava release), validate that this + // is the thing for IE. + value = getUninterruptibly(future); + } catch (ExecutionException e) { + callback.onFailure(e.getCause()); + return; + } catch (RuntimeException e) { + callback.onFailure(e); + return; + } catch (Error e) { + callback.onFailure(e); + return; + } + callback.onSuccess(value); + } + }; + future.addListener(callbackListener, executor); + } + + /** + * Returns the result of {@link Future#get()}, converting most exceptions to a + * new instance of the given checked exception type. This reduces boilerplate + * for a common use of {@code Future} in which it is unnecessary to + * programmatically distinguish between exception types or to extract other + * information from the exception instance. + * + *

Exceptions from {@code Future.get} are treated as follows: + *

    + *
  • Any {@link ExecutionException} has its cause wrapped in an + * {@code X} if the cause is a checked exception, an {@link + * UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an + * {@code Error}. + *
  • Any {@link InterruptedException} is wrapped in an {@code X} (after + * restoring the interrupt). + *
  • Any {@link CancellationException} is propagated untouched, as is any + * other {@link RuntimeException} (though {@code get} implementations are + * discouraged from throwing such exceptions). + *
+ * + *

The overall principle is to continue to treat every checked exception as a + * checked exception, every unchecked exception as an unchecked exception, and + * every error as an error. In addition, the cause of any {@code + * ExecutionException} is wrapped in order to ensure that the new stack trace + * matches that of the current thread. + * + *

Instances of {@code exceptionClass} are created by choosing an arbitrary + * public constructor that accepts zero or more arguments, all of type {@code + * String} or {@code Throwable} (preferring constructors with at least one + * {@code String}) and calling the constructor via reflection. If the + * exception did not already have a cause, one is set by calling {@link + * Throwable#initCause(Throwable)} on it. If no such constructor exists, an + * {@code IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code + * ExecutionException} whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code + * ExecutionException} with a {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} + * with an {@code Error} as its cause + * @throws CancellationException if {@code get} throws a {@code + * CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code + * RuntimeException} or does not have a suitable constructor + * @since 10.0 + */ +// public static V get( +// Future future, Class exceptionClass) throws X { +// checkNotNull(future); +// checkArgument(!RuntimeException.class.isAssignableFrom(exceptionClass), +// "Futures.get exception type (%s) must not be a RuntimeException", +// exceptionClass); +// try { +// return future.get(); +// } catch (InterruptedException e) { +// currentThread().interrupt(); +// throw newWithCause(exceptionClass, e); +// } catch (ExecutionException e) { +// wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); +// throw new AssertionError(); +// } +// } + + /** + * Returns the result of {@link Future#get(long, TimeUnit)}, converting most + * exceptions to a new instance of the given checked exception type. This + * reduces boilerplate for a common use of {@code Future} in which it is + * unnecessary to programmatically distinguish between exception types or to + * extract other information from the exception instance. + * + *

Exceptions from {@code Future.get} are treated as follows: + *

    + *
  • Any {@link ExecutionException} has its cause wrapped in an + * {@code X} if the cause is a checked exception, an {@link + * UncheckedExecutionException} if the cause is a {@code + * RuntimeException}, or an {@link ExecutionError} if the cause is an + * {@code Error}. + *
  • Any {@link InterruptedException} is wrapped in an {@code X} (after + * restoring the interrupt). + *
  • Any {@link TimeoutException} is wrapped in an {@code X}. + *
  • Any {@link CancellationException} is propagated untouched, as is any + * other {@link RuntimeException} (though {@code get} implementations are + * discouraged from throwing such exceptions). + *
+ * + *

The overall principle is to continue to treat every checked exception as a + * checked exception, every unchecked exception as an unchecked exception, and + * every error as an error. In addition, the cause of any {@code + * ExecutionException} is wrapped in order to ensure that the new stack trace + * matches that of the current thread. + * + *

Instances of {@code exceptionClass} are created by choosing an arbitrary + * public constructor that accepts zero or more arguments, all of type {@code + * String} or {@code Throwable} (preferring constructors with at least one + * {@code String}) and calling the constructor via reflection. If the + * exception did not already have a cause, one is set by calling {@link + * Throwable#initCause(Throwable)} on it. If no such constructor exists, an + * {@code IllegalArgumentException} is thrown. + * + * @throws X if {@code get} throws any checked exception except for an {@code + * ExecutionException} whose cause is not itself a checked exception + * @throws UncheckedExecutionException if {@code get} throws an {@code + * ExecutionException} with a {@code RuntimeException} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} + * with an {@code Error} as its cause + * @throws CancellationException if {@code get} throws a {@code + * CancellationException} + * @throws IllegalArgumentException if {@code exceptionClass} extends {@code + * RuntimeException} or does not have a suitable constructor + * @since 10.0 + */ +// public static V get( +// Future future, long timeout, TimeUnit unit, Class exceptionClass) +// throws X { +// checkNotNull(future); +// checkNotNull(unit); +// checkArgument(!RuntimeException.class.isAssignableFrom(exceptionClass), +// "Futures.get exception type (%s) must not be a RuntimeException", +// exceptionClass); +// try { +// return future.get(timeout, unit); +// } catch (InterruptedException e) { +// currentThread().interrupt(); +// throw newWithCause(exceptionClass, e); +// } catch (TimeoutException e) { +// throw newWithCause(exceptionClass, e); +// } catch (ExecutionException e) { +// wrapAndThrowExceptionOrError(e.getCause(), exceptionClass); +// throw new AssertionError(); +// } +// } + +// private static void wrapAndThrowExceptionOrError( +// Throwable cause, Class exceptionClass) throws X { +// if (cause instanceof Error) { +// throw new ExecutionError((Error) cause); +// } +// if (cause instanceof RuntimeException) { +// throw new UncheckedExecutionException(cause); +// } +// throw newWithCause(exceptionClass, cause); +// } + + /** + * Returns the result of calling {@link Future#get()} uninterruptibly on a + * task known not to throw a checked exception. This makes {@code Future} more + * suitable for lightweight, fast-running tasks that, barring bugs in the + * code, will not fail. This gives it exception-handling behavior similar to + * that of {@code ForkJoinTask.join}. + * + *

Exceptions from {@code Future.get} are treated as follows: + *

    + *
  • Any {@link ExecutionException} has its cause wrapped in an + * {@link UncheckedExecutionException} (if the cause is an {@code + * Exception}) or {@link ExecutionError} (if the cause is an {@code + * Error}). + *
  • Any {@link InterruptedException} causes a retry of the {@code get} + * call. The interrupt is restored before {@code getUnchecked} returns. + *
  • Any {@link CancellationException} is propagated untouched. So is any + * other {@link RuntimeException} ({@code get} implementations are + * discouraged from throwing such exceptions). + *
+ * + *

The overall principle is to eliminate all checked exceptions: to loop to + * avoid {@code InterruptedException}, to pass through {@code + * CancellationException}, and to wrap any exception from the underlying + * computation in an {@code UncheckedExecutionException} or {@code + * ExecutionError}. + * + *

For an uninterruptible {@code get} that preserves other exceptions, see + * {@link Uninterruptibles#getUninterruptibly(Future)}. + * + * @throws UncheckedExecutionException if {@code get} throws an {@code + * ExecutionException} with an {@code Exception} as its cause + * @throws ExecutionError if {@code get} throws an {@code ExecutionException} + * with an {@code Error} as its cause + * @throws CancellationException if {@code get} throws a {@code + * CancellationException} + * @since 10.0 + */ + public static V getUnchecked(Future future) { +// checkNotNull(future); + try { + return getUninterruptibly(future); + } catch (ExecutionException e) { + wrapAndThrowUnchecked(e.getCause()); + throw new AssertionError(); + } + } + + public static V getUninterruptibly(Future future) + throws ExecutionException { + boolean interrupted = false; + try { + while (true) { + try { + return future.get(); + } catch (InterruptedException e) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + private static void wrapAndThrowUnchecked(Throwable cause) { + if (cause instanceof Error) { + throw new ExecutionError((Error) cause); + } + /* + * It's a non-Error, non-Exception Throwable. From my survey of such + * classes, I believe that most users intended to extend Exception, so we'll + * treat it like an Exception. + */ + throw new UncheckedExecutionException(cause); + } + + /* + * TODO(user): FutureChecker interface for these to be static methods on? If + * so, refer to it in the (static-method) Futures.get documentation + */ + + /* + * Arguably we don't need a timed getUnchecked because any operation slow + * enough to require a timeout is heavyweight enough to throw a checked + * exception and therefore be inappropriate to use with getUnchecked. Further, + * it's not clear that converting the checked TimeoutException to a + * RuntimeException -- especially to an UncheckedExecutionException, since it + * wasn't thrown by the computation -- makes sense, and if we don't convert + * it, the user still has to write a try-catch block. + * + * If you think you would use this method, let us know. + */ + +// private static X newWithCause( +// Class exceptionClass, Throwable cause) { +// // getConstructors() guarantees this as long as we don't modify the array. +// +// List> constructors = +// (List) Arrays.asList(exceptionClass.getConstructors()); +// for (Constructor constructor : preferringStrings(constructors)) { +// X instance = newFromConstructor(constructor, cause); +// if (instance != null) { +// if (instance.getCause() == null) { +// instance.initCause(cause); +// } +// return instance; +// } +// } +// throw new IllegalArgumentException( +// "No appropriate constructor for exception of type " + exceptionClass +// + " in response to chained exception", cause); +// } + +// private static List> +// preferringStrings(List> constructors) { +// return WITH_STRING_PARAM_FIRST.sortedCopy(constructors); +// } +// +// private static final Ordering> WITH_STRING_PARAM_FIRST = +// Ordering.natural().onResultOf(new Function, Boolean>() { +// @Override public boolean test(Constructor input) { +// return asList(input.getParameterTypes()).contains(String.class); +// } +// }).reverse(); + +// 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)) { +// params[i] = cause; +// } else { +// return null; +// } +// } +// try { +// return constructor.newInstance(params); +// } catch (IllegalArgumentException e) { +// return null; +// } catch (InstantiationException e) { +// return null; +// } catch (IllegalAccessException e) { +// return null; +// } catch (InvocationTargetException e) { +// return null; +// } +// } + + private interface FutureCombiner { + C combine(List values); + } + + private static class CombinedFuture extends AbstractFuture> { + private static final Logger logger = + Logger.getLogger(CombinedFuture.class.getName()); + + ImmutableList> futures; + final boolean allMustSucceed; + final AtomicInteger remaining; +// FutureCombiner combiner; + List values; + final Object seenExceptionsLock = new Object(); + Set seenExceptions; + + CombinedFuture( + ImmutableList> futures, + boolean allMustSucceed, Executor listenerExecutor // , +// FutureCombiner combiner + ) { + this.futures = futures; + this.allMustSucceed = allMustSucceed; + this.remaining = new AtomicInteger(futures.size()); +// this.combiner = combiner; + this.values = new ArrayList(futures.size()); + init(listenerExecutor); + } + + /** + * Must be called at the end of the constructor. + */ + protected void init(final Executor listenerExecutor) { + // First, schedule cleanup to execute when the Future is done. + addListener(new Runnable() { + @Override + public void run() { + // Cancel all the component futures. + if (CombinedFuture.this.isCancelled()) { + for (ListenableFuture future : CombinedFuture.this.futures) { + future.cancel(CombinedFuture.this.wasInterrupted()); + } + } + + // Let go of the memory held by other futures + CombinedFuture.this.futures = null; + + // By now the values array has either been set as the Future's value, + // or (in case of failure) is no longer useful. + CombinedFuture.this.values = null; + + // The combiner may also hold state, so free that as well +// CombinedFuture.this.combiner = null; + } + }, MoreExecutors.sameThreadExecutor()); + + // Now begin the "real" initialization. + + // Corner case: List is empty. + if (futures.isEmpty()) { + set(ImmutableList.of()); + return; + } + + // Populate the results list with null initially. + for (int i = 0; i < futures.size(); ++i) { + values.add(null); + } + + // Register a listener on each Future in the list to update + // the state of this future. + // Note that if all the futures on the list are done prior to completing + // this loop, the last call to addListener() will callback to + // setOneValue(), transitively call our cleanup listener, and set + // this.futures to null. + // This is not actually a problem, since the foreach only needs + // this.futures to be non-null at the beginning of the loop. + int i = 0; + for (final ListenableFuture listenable : futures) { + final int index = i++; + listenable.addListener(new Runnable() { + @Override + public void run() { + setOneValue(index, listenable); + } + }, listenerExecutor); + } + } + + /** + * Fails this future with the given Throwable if {@link #allMustSucceed} is + * true. Also, logs the throwable if it is an {@link Error} or if + * {@link #allMustSucceed} is {@code true}, the throwable did not cause + * this future to fail, and it is the first time we've seen that particular Throwable. + */ + private void setExceptionAndMaybeLog(Throwable throwable) { + boolean visibleFromOutputFuture = false; + boolean firstTimeSeeingThisException = true; + if (allMustSucceed) { + // As soon as the first one fails, throw the exception up. + // The result of all other inputs is then ignored. + visibleFromOutputFuture = super.setException(throwable); + + synchronized (seenExceptionsLock) { + if (seenExceptions == null) { + seenExceptions = Sets.newHashSet(); + } + firstTimeSeeingThisException = seenExceptions.add(throwable); + } + } + + if (throwable instanceof Error + || (allMustSucceed && !visibleFromOutputFuture && firstTimeSeeingThisException)) { + logger.log(Level.SEVERE, "input future failed.", throwable); + } + } + + /** + * Sets the value at the given index to that of the given future. + */ + private void setOneValue(int index, Future future) { + List localValues = values; + // TODO(user): This check appears to be redundant since values is + // assigned null only after the future completes. However, values + // is not volatile so it may be possible for us to observe the changes + // to these two values in a different order... which I think is why + // we need to check both. Clear up this craziness either by making + // values volatile or proving that it doesn't need to be for some other + // reason. + if (isDone() || localValues == null) { + // Some other future failed or has been cancelled, causing this one to + // also be cancelled or have an exception set. This should only happen + // if allMustSucceed is true or if the output itself has been + // cancelled. + checkState(allMustSucceed || isCancelled(), + "Future was done before all dependencies completed"); + } + + try { + checkState(future.isDone(), + "Tried to set value from future which is not done"); + V returnValue = getUninterruptibly(future); + if (localValues != null) { + localValues.set(index, returnValue); + } + } catch (CancellationException e) { + if (allMustSucceed) { + // Set ourselves as cancelled. Let the input futures keep running + // as some of them may be used elsewhere. + cancel(false); + } + } catch (ExecutionException e) { + setExceptionAndMaybeLog(e.getCause()); + } catch (Throwable t) { + setExceptionAndMaybeLog(t); + } finally { + int newRemaining = remaining.decrementAndGet(); + checkState(newRemaining >= 0, "Less than 0 remaining futures"); + if (newRemaining == 0) { +// FutureCombiner localCombiner = combiner; + if (/* localCombiner != null && */ localValues != null) { + set(localValues); + } else { + checkState(isDone(), "Future is not done"); + } + } + } + } + + } + + /** Used for {@link #allAsList} and {@link #successfulAsList}. */ + private static ListenableFuture> listFuture( + ImmutableList> futures, + boolean allMustSucceed, Executor listenerExecutor) { + return new CombinedFuture( + futures, allMustSucceed, listenerExecutor); +// new FutureCombiner>() { +// @Override +// public List combine(List values) { +//// List result = Lists.newArrayList(); +//// for (V element : values) { +//// result.add(element); +//// } +// return Collections.unmodifiableList(values); +// } +// }); + } + + /** + * A checked future that uses a function to map from exceptions to the + * appropriate checked type. + */ +// private static class MappingCheckedFuture extends +// AbstractCheckedFuture { +// +// final Function mapper; +// +// MappingCheckedFuture(ListenableFuture delegate, +// Function mapper) { +// super(delegate); +// +// this.mapper = checkNotNull(mapper); +// } +// +// @Override +// protected X mapException(Exception e) { +// return mapper.apply(e); +// } +// } + private static void checkState(boolean expression, String errorMessage) { + if (!expression) { + throw new IllegalStateException(errorMessage); + } + } +} diff --git a/java/src/game/future/ListenableFuture.java b/java/src/game/future/ListenableFuture.java new file mode 100644 index 0000000..ab19dd2 --- /dev/null +++ b/java/src/game/future/ListenableFuture.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +import java.util.concurrent.Executor; +import java.util.concurrent.Future; + +/** + * A {@link Future} that accepts completion listeners. Each listener has an + * associated executor, and it is invoked using this executor once the future's + * computation is {@linkplain Future#isDone() complete}. If the computation has + * already completed when the listener is added, the listener will execute + * immediately. + * + *

See the Guava User Guide article on + * {@code ListenableFuture}. + * + *

Purpose

+ * + *

Most commonly, {@code ListenableFuture} is used as an input to another + * derived {@code Future}, as in {@link Futures#allAsList(Iterable) + * Futures.allAsList}. Many such methods are impossible to implement efficiently + * without listener support. + * + *

It is possible to call {@link #addListener addListener} directly, but this + * is uncommon because the {@code Runnable} interface does not provide direct + * access to the {@code Future} result. (Users who want such access may prefer + * {@link Futures#addCallback Futures.addCallback}.) Still, direct {@code + * addListener} calls are occasionally useful:

   {@code
+ *   final String name = ...;
+ *   inFlight.add(name);
+ *   ListenableFuture future = service.query(name);
+ *   future.addListener(new Runnable() {
+ *     public void run() {
+ *       processedCount.incrementAndGet();
+ *       inFlight.remove(name);
+ *       lastProcessed.set(name);
+ *       logger.info("Done with {0}", name);
+ *     }
+ *   }, executor);}
+ * + *

How to get an instance

+ * + *

Developers are encouraged to return {@code ListenableFuture} from their + * methods so that users can take advantages of the utilities built atop the + * class. The way that they will create {@code ListenableFuture} instances + * depends on how they currently create {@code Future} instances: + *

    + *
  • If they are returned from an {@code ExecutorService}, convert that + * service to a {@link ListeningExecutorService}, usually by calling {@link + * MoreExecutors#listeningDecorator(ExecutorService) + * MoreExecutors.listeningDecorator}. (Custom executors may find it more + * convenient to use {@link ListenableFutureTask} directly.) + *
  • If they are manually filled in by a call to {@link FutureTask#set} or a + * similar method, create a {@link SettableFuture} instead. (Users with more + * complex needs may prefer {@link AbstractFuture}.) + *
+ * + *

Occasionally, an API will return a plain {@code Future} and it will be + * impossible to change the return type. For this case, we provide a more + * expensive workaround in {@code JdkFutureAdapters}. However, when possible, it + * is more efficient and reliable to create a {@code ListenableFuture} directly. + * + * @author Sven Mawson + * @author Nishant Thakkar + * @since 1.0 + */ +public interface ListenableFuture extends Future { + /** + * Registers a listener to be {@linkplain Executor#execute(Runnable) run} on + * the given executor. The listener will run when the {@code Future}'s + * computation is {@linkplain Future#isDone() complete} or, if the computation + * is already complete, immediately. + * + *

There is no guaranteed ordering of execution of listeners, but any + * listener added through this method is guaranteed to be called once the + * computation is complete. + * + *

Exceptions thrown by a listener will be propagated up to the executor. + * Any exception thrown during {@code Executor.execute} (e.g., a {@code + * RejectedExecutionException} or an exception thrown by {@linkplain + * MoreExecutors#sameThreadExecutor inline execution}) will be caught and + * logged. + * + *

Note: For fast, lightweight listeners that would be safe to execute in + * any thread, consider {@link MoreExecutors#sameThreadExecutor}. For heavier + * listeners, {@code sameThreadExecutor()} carries some caveats. For + * example, the listener may run on an unpredictable or undesirable thread: + * + *

    + *
  • If this {@code Future} is done at the time {@code addListener} is + * called, {@code addListener} will execute the listener inline. + *
  • If this {@code Future} is not yet done, {@code addListener} will + * schedule the listener to be run by the thread that completes this {@code + * Future}, which may be an internal system thread such as an RPC network + * thread. + *
+ * + *

Also note that, regardless of which thread executes the + * {@code sameThreadExecutor()} listener, all other registered but unexecuted + * listeners are prevented from running during its execution, even if those + * listeners are to run in other executors. + * + *

This is the most general listener interface. For common operations + * performed using listeners, see {@link + * game.future.Futures}. For a simplified but general + * listener interface, see {@link + * game.future.Futures#addCallback addCallback()}. + * + * @param listener the listener to run when the computation is complete + * @param executor the executor to run the listener in + * @throws NullPointerException if the executor or listener was null + * @throws RejectedExecutionException if we tried to execute the listener + * immediately but the executor rejected it. + */ + void addListener(Runnable listener, Executor executor); +} diff --git a/java/src/game/future/ListenableFutureTask.java b/java/src/game/future/ListenableFutureTask.java new file mode 100644 index 0000000..38b0621 --- /dev/null +++ b/java/src/game/future/ListenableFutureTask.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; + +/** + * A {@link FutureTask} that also implements the {@link ListenableFuture} + * interface. Unlike {@code FutureTask}, {@code ListenableFutureTask} does not + * provide an overrideable {@link FutureTask#done() done()} method. For similar + * functionality, call {@link #addListener}. + * + *

+ * + * @author Sven Mawson + * @since 1.0 + */ +public class ListenableFutureTask extends FutureTask + implements ListenableFuture { + // TODO(cpovirk): explore ways of making ListenableFutureTask final. There are + // some valid reasons such as BoundedQueueExecutorService to allow extends but it + // would be nice to make it final to avoid unintended usage. + + // The execution list to hold our listeners. + private final ExecutionList executionList = new ExecutionList(); + + /** + * Creates a {@code ListenableFutureTask} that will upon running, execute the + * given {@code Callable}. + * + * @param callable the callable task + * @since 10.0 + */ + public static ListenableFutureTask create(Callable callable) { + return new ListenableFutureTask(callable); + } + + /** + * Creates a {@code ListenableFutureTask} that will upon running, execute the + * given {@code Runnable}, and arrange that {@code get} will return the + * given result on successful completion. + * + * @param runnable the runnable task + * @param result the result to return on successful completion. If you don't + * need a particular result, consider using constructions of the form: + * {@code ListenableFuture f = ListenableFutureTask.create(runnable, + * null)} + * @since 10.0 + */ + public static ListenableFutureTask create( + Runnable runnable, V result) { + return new ListenableFutureTask(runnable, result); + } + + ListenableFutureTask(Callable callable) { + super(callable); + } + + ListenableFutureTask(Runnable runnable, V result) { + super(runnable, result); + } + + @Override + public void addListener(Runnable listener, Executor exec) { + executionList.add(listener, exec); + } + + /** + * Internal implementation detail used to invoke the listeners. + */ + @Override + protected void done() { + executionList.execute(); + } +} diff --git a/java/src/game/future/MoreExecutors.java b/java/src/game/future/MoreExecutors.java new file mode 100644 index 0000000..5e3e49a --- /dev/null +++ b/java/src/game/future/MoreExecutors.java @@ -0,0 +1,889 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Factory and utility methods for {@link java.util.concurrent.Executor}, {@link + * ExecutorService}, and {@link ThreadFactory}. + * + * @author Eric Fellheimer + * @author Kyle Littlefield + * @author Justin Mahoney + * @since 3.0 + */ +final class MoreExecutors { + private MoreExecutors() {} + + /** + * Converts the given ThreadPoolExecutor into an ExecutorService that exits + * when the application is complete. It does so by using daemon threads and + * adding a shutdown hook to wait for their completion. + * + *

This is mainly for fixed thread pools. + * See {@link Executors#newFixedThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the + * application is finished + * @param terminationTimeout how long to wait for the executor to + * finish before terminating the JVM + * @param timeUnit unit of time for the time parameter + * @return an unmodifiable version of the input which will not hang the JVM + */ +// @Beta +// public static ExecutorService getExitingExecutorService( +// ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { +// return new Application() +// .getExitingExecutorService(executor, terminationTimeout, timeUnit); +// } + + /** + * Converts the given ScheduledThreadPoolExecutor into a + * ScheduledExecutorService that exits when the application is complete. It + * does so by using daemon threads and adding a shutdown hook to wait for + * their completion. + * + *

This is mainly for fixed thread pools. + * See {@link Executors#newScheduledThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the + * application is finished + * @param terminationTimeout how long to wait for the executor to + * finish before terminating the JVM + * @param timeUnit unit of time for the time parameter + * @return an unmodifiable version of the input which will not hang the JVM + */ +// @Beta +// public static ScheduledExecutorService getExitingScheduledExecutorService( +// ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { +// return new Application() +// .getExitingScheduledExecutorService(executor, terminationTimeout, timeUnit); +// } + + /** + * Add a shutdown hook to wait for thread completion in the given + * {@link ExecutorService service}. This is useful if the given service uses + * daemon threads, and we want to keep the JVM from exiting immediately on + * shutdown, instead giving these daemon threads a chance to terminate + * normally. + * @param service ExecutorService which uses daemon threads + * @param terminationTimeout how long to wait for the executor to finish + * before terminating the JVM + * @param timeUnit unit of time for the time parameter + */ +// @Beta +// public static void addDelayedShutdownHook( +// ExecutorService service, long terminationTimeout, TimeUnit timeUnit) { +// new Application() +// .addDelayedShutdownHook(service, terminationTimeout, timeUnit); +// } + + /** + * Converts the given ThreadPoolExecutor into an ExecutorService that exits + * when the application is complete. It does so by using daemon threads and + * adding a shutdown hook to wait for their completion. + * + *

This method waits 120 seconds before continuing with JVM termination, + * even if the executor has not finished its work. + * + *

This is mainly for fixed thread pools. + * See {@link Executors#newFixedThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the + * application is finished + * @return an unmodifiable version of the input which will not hang the JVM + */ +// @Beta +// public static ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { +// return new Application().getExitingExecutorService(executor); +// } + + /** + * Converts the given ThreadPoolExecutor into a ScheduledExecutorService that + * exits when the application is complete. It does so by using daemon threads + * and adding a shutdown hook to wait for their completion. + * + *

This method waits 120 seconds before continuing with JVM termination, + * even if the executor has not finished its work. + * + *

This is mainly for fixed thread pools. + * See {@link Executors#newScheduledThreadPool(int)}. + * + * @param executor the executor to modify to make sure it exits when the + * application is finished + * @return an unmodifiable version of the input which will not hang the JVM + */ +// @Beta +// public static ScheduledExecutorService getExitingScheduledExecutorService( +// ScheduledThreadPoolExecutor executor) { +// return new Application().getExitingScheduledExecutorService(executor); +// } + + /** Represents the current application to register shutdown hooks. */ +// @VisibleForTesting static class Application { +// +// final ExecutorService getExitingExecutorService( +// ThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { +// useDaemonThreadFactory(executor); +// ExecutorService service = Executors.unconfigurableExecutorService(executor); +// addDelayedShutdownHook(service, terminationTimeout, timeUnit); +// return service; +// } +// +// final ScheduledExecutorService getExitingScheduledExecutorService( +// ScheduledThreadPoolExecutor executor, long terminationTimeout, TimeUnit timeUnit) { +// useDaemonThreadFactory(executor); +// ScheduledExecutorService service = Executors.unconfigurableScheduledExecutorService(executor); +// addDelayedShutdownHook(service, terminationTimeout, timeUnit); +// return service; +// } +// +// final void addDelayedShutdownHook( +// final ExecutorService service, final long terminationTimeout, final TimeUnit timeUnit) { +// checkNotNull(service); +// checkNotNull(timeUnit); +// addShutdownHook(MoreExecutors.newThread("DelayedShutdownHook-for-" + service, new Runnable() { +// @Override +// public void run() { +// try { +// // We'd like to log progress and failures that may arise in the +// // following code, but unfortunately the behavior of logging +// // is undefined in shutdown hooks. +// // This is because the logging code installs a shutdown hook of its +// // own. See Cleaner class inside {@link LogManager}. +// service.shutdown(); +// service.awaitTermination(terminationTimeout, timeUnit); +// } catch (InterruptedException ignored) { +// // We're shutting down anyway, so just ignore. +// } +// } +// })); +// } +// +// final ExecutorService getExitingExecutorService(ThreadPoolExecutor executor) { +// return getExitingExecutorService(executor, 120, TimeUnit.SECONDS); +// } +// +// final ScheduledExecutorService getExitingScheduledExecutorService( +// ScheduledThreadPoolExecutor executor) { +// return getExitingScheduledExecutorService(executor, 120, TimeUnit.SECONDS); +// } +// +// @VisibleForTesting void addShutdownHook(Thread hook) { +// Runtime.getRuntime().addShutdownHook(hook); +// } +// } +// +// private static void useDaemonThreadFactory(ThreadPoolExecutor executor) { +// executor.setThreadFactory(new ThreadFactoryBuilder() +// .setDaemon(true) +// .setThreadFactory(executor.getThreadFactory()) +// .build()); +// } + + /** + * Creates an executor service that runs each task in the thread + * that invokes {@code execute/submit}, as in {@link CallerRunsPolicy} This + * applies both to individually submitted tasks and to collections of tasks + * submitted via {@code invokeAll} or {@code invokeAny}. In the latter case, + * tasks will run serially on the calling thread. Tasks are run to + * completion before a {@code Future} is returned to the caller (unless the + * executor has been shutdown). + * + *

Although all tasks are immediately executed in the thread that + * submitted the task, this {@code ExecutorService} imposes a small + * locking overhead on each task submission in order to implement shutdown + * and termination behavior. + * + *

The implementation deviates from the {@code ExecutorService} + * specification with regards to the {@code shutdownNow} method. First, + * "best-effort" with regards to canceling running tasks is implemented + * as "no-effort". No interrupts or other attempts are made to stop + * threads executing tasks. Second, the returned list will always be empty, + * as any submitted task is considered to have started execution. + * This applies also to tasks given to {@code invokeAll} or {@code invokeAny} + * which are pending serial execution, even the subset of the tasks that + * have not yet started execution. It is unclear from the + * {@code ExecutorService} specification if these should be included, and + * it's much easier to implement the interpretation that they not be. + * Finally, a call to {@code shutdown} or {@code shutdownNow} may result + * in concurrent calls to {@code invokeAll/invokeAny} throwing + * RejectedExecutionException, although a subset of the tasks may already + * have been executed. + * + * @since 10.0 (mostly source-compatible since 3.0) + */ + public static SameThreadExecutorService sameThreadExecutor() { + return new SameThreadExecutorService(); + } + + // See sameThreadExecutor javadoc for behavioral notes. + private static class SameThreadExecutorService + extends AbstractExecutorService implements ExecutorService { + /** + * Lock used whenever accessing the state variables + * (runningTasks, shutdown, terminationCondition) of the executor + */ + private final Lock lock = new ReentrantLock(); + + /** Signaled after the executor is shutdown and running tasks are done */ + private final Condition termination = lock.newCondition(); + + /* + * Conceptually, these two variables describe the executor being in + * one of three states: + * - Active: shutdown == false + * - Shutdown: runningTasks > 0 and shutdown == true + * - Terminated: runningTasks == 0 and shutdown == true + */ + private int runningTasks = 0; + private boolean shutdown = false; + + @Override + public void execute(Runnable command) { + startTask(); + try { + command.run(); + } finally { + endTask(); + } + } + + @Override + public boolean isShutdown() { + lock.lock(); + try { + return shutdown; + } finally { + lock.unlock(); + } + } + + @Override + public void shutdown() { + lock.lock(); + try { + shutdown = true; + } finally { + lock.unlock(); + } + } + + // See sameThreadExecutor javadoc for unusual behavior of this method. + @Override + public List shutdownNow() { + shutdown(); + return Collections.emptyList(); + } + + @Override + public boolean isTerminated() { + lock.lock(); + try { + return shutdown && runningTasks == 0; + } finally { + lock.unlock(); + } + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + lock.lock(); + try { + for (;;) { + if (isTerminated()) { + return true; + } else if (nanos <= 0) { + return false; + } else { + nanos = termination.awaitNanos(nanos); + } + } + } finally { + lock.unlock(); + } + } + + /** + * Checks if the executor has been shut down and increments the running + * task count. + * + * @throws RejectedExecutionException if the executor has been previously + * shutdown + */ + private void startTask() { + lock.lock(); + try { + if (isShutdown()) { + throw new RejectedExecutionException("Executor already shutdown"); + } + runningTasks++; + } finally { + lock.unlock(); + } + } + + /** + * Decrements the running task count. + */ + private void endTask() { + lock.lock(); + try { + runningTasks--; + if (isTerminated()) { + termination.signalAll(); + } + } finally { + lock.unlock(); + } + } + + @Override protected final ListenableFutureTask newTaskFor(Runnable runnable, T value) { + return ListenableFutureTask.create(runnable, value); + } + + @Override protected final ListenableFutureTask newTaskFor(Callable callable) { + return ListenableFutureTask.create(callable); + } + + @Override public ListenableFuture submit(Runnable task) { + return (ListenableFuture) super.submit(task); + } + + @Override public ListenableFuture submit(Runnable task, T result) { + return (ListenableFuture) super.submit(task, result); + } + + @Override public ListenableFuture submit(Callable task) { + return (ListenableFuture) super.submit(task); + } + } + + /** + * Creates an {@link ExecutorService} whose {@code submit} and {@code + * invokeAll} methods submit {@link ListenableFutureTask} instances to the + * given delegate executor. Those methods, as well as {@code execute} and + * {@code invokeAny}, are implemented in terms of calls to {@code + * delegate.execute}. All other methods are forwarded unchanged to the + * delegate. This implies that the returned {@code ListeningExecutorService} + * never calls the delegate's {@code submit}, {@code invokeAll}, and {@code + * invokeAny} methods, so any special handling of tasks must be implemented in + * the delegate's {@code execute} method or by wrapping the returned {@code + * ListeningExecutorService}. + * + *

If the delegate executor was already an instance of {@code + * ListeningExecutorService}, it is returned untouched, and the rest of this + * documentation does not apply. + * + * @since 10.0 + */ +// public static ListeningExecutorService listeningDecorator( +// ExecutorService delegate) { +// return (delegate instanceof ListeningExecutorService) +// ? (ListeningExecutorService) delegate +// : (delegate instanceof ScheduledExecutorService) +// ? new ScheduledListeningDecorator((ScheduledExecutorService) delegate) +// : new ListeningDecorator(delegate); +// } + + /** + * Creates a {@link ScheduledExecutorService} whose {@code submit} and {@code + * invokeAll} methods submit {@link ListenableFutureTask} instances to the + * given delegate executor. Those methods, as well as {@code execute} and + * {@code invokeAny}, are implemented in terms of calls to {@code + * delegate.execute}. All other methods are forwarded unchanged to the + * delegate. This implies that the returned {@code + * ListeningScheduledExecutorService} never calls the delegate's {@code + * submit}, {@code invokeAll}, and {@code invokeAny} methods, so any special + * handling of tasks must be implemented in the delegate's {@code execute} + * method or by wrapping the returned {@code + * ListeningScheduledExecutorService}. + * + *

If the delegate executor was already an instance of {@code + * ListeningScheduledExecutorService}, it is returned untouched, and the rest + * of this documentation does not apply. + * + * @since 10.0 + */ +// public static ListeningScheduledExecutorService listeningDecorator( +// ScheduledExecutorService delegate) { +// return (delegate instanceof ListeningScheduledExecutorService) +// ? (ListeningScheduledExecutorService) delegate +// : new ScheduledListeningDecorator(delegate); +// } + +// private static class ListeningDecorator +// extends AbstractListeningExecutorService { +// private final ExecutorService delegate; +// +// ListeningDecorator(ExecutorService delegate) { +// this.delegate = checkNotNull(delegate); +// } +// +// @Override +// public boolean awaitTermination(long timeout, TimeUnit unit) +// throws InterruptedException { +// return delegate.awaitTermination(timeout, unit); +// } +// +// @Override +// public boolean isShutdown() { +// return delegate.isShutdown(); +// } +// +// @Override +// public boolean isTerminated() { +// return delegate.isTerminated(); +// } +// +// @Override +// public void shutdown() { +// delegate.shutdown(); +// } +// +// @Override +// public List shutdownNow() { +// return delegate.shutdownNow(); +// } +// +// @Override +// public void execute(Runnable command) { +// delegate.execute(command); +// } +// } +// +// private static class ScheduledListeningDecorator +// extends ListeningDecorator implements ListeningScheduledExecutorService { +// +// final ScheduledExecutorService delegate; +// +// ScheduledListeningDecorator(ScheduledExecutorService delegate) { +// super(delegate); +// this.delegate = checkNotNull(delegate); +// } +// +// @Override +// public ListenableScheduledFuture schedule( +// Runnable command, long delay, TimeUnit unit) { +// ListenableFutureTask task = +// ListenableFutureTask.create(command, null); +// ScheduledFuture scheduled = delegate.schedule(task, delay, unit); +// return new ListenableScheduledTask(task, scheduled); +// } +// +// @Override +// public ListenableScheduledFuture schedule( +// Callable callable, long delay, TimeUnit unit) { +// ListenableFutureTask task = ListenableFutureTask.create(callable); +// ScheduledFuture scheduled = delegate.schedule(task, delay, unit); +// return new ListenableScheduledTask(task, scheduled); +// } +// +// @Override +// public ListenableScheduledFuture scheduleAtFixedRate( +// Runnable command, long initialDelay, long period, TimeUnit unit) { +// NeverSuccessfulListenableFutureTask task = +// new NeverSuccessfulListenableFutureTask(command); +// ScheduledFuture scheduled = +// delegate.scheduleAtFixedRate(task, initialDelay, period, unit); +// return new ListenableScheduledTask(task, scheduled); +// } +// +// @Override +// public ListenableScheduledFuture scheduleWithFixedDelay( +// Runnable command, long initialDelay, long delay, TimeUnit unit) { +// NeverSuccessfulListenableFutureTask task = +// new NeverSuccessfulListenableFutureTask(command); +// ScheduledFuture scheduled = +// delegate.scheduleWithFixedDelay(task, initialDelay, delay, unit); +// return new ListenableScheduledTask(task, scheduled); +// } +// +// private static final class ListenableScheduledTask +// extends SimpleForwardingListenableFuture +// implements ListenableScheduledFuture { +// +// private final ScheduledFuture scheduledDelegate; +// +// public ListenableScheduledTask( +// ListenableFuture listenableDelegate, +// ScheduledFuture scheduledDelegate) { +// super(listenableDelegate); +// this.scheduledDelegate = scheduledDelegate; +// } +// +// @Override +// public boolean cancel(boolean mayInterruptIfRunning) { +// boolean cancelled = super.cancel(mayInterruptIfRunning); +// if (cancelled) { +// // Unless it is cancelled, the delegate may continue being scheduled +// scheduledDelegate.cancel(mayInterruptIfRunning); +// +// // TODO(user): Cancel "this" if "scheduledDelegate" is cancelled. +// } +// return cancelled; +// } +// +// @Override +// public long getDelay(TimeUnit unit) { +// return scheduledDelegate.getDelay(unit); +// } +// +// @Override +// public int compareTo(Delayed other) { +// return scheduledDelegate.compareTo(other); +// } +// } +// +// private static final class NeverSuccessfulListenableFutureTask +// extends AbstractFuture +// implements Runnable { +// private final Runnable delegate; +// +// public NeverSuccessfulListenableFutureTask(Runnable delegate) { +// this.delegate = checkNotNull(delegate); +// } +// +// @Override public void run() { +// try { +// delegate.run(); +// } catch (Throwable t) { +// setException(t); +// throw Throwables.propagate(t); +// } +// } +// } +// } + + /* + * This following method is a modified version of one found in + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck/AbstractExecutorServiceTest.java?revision=1.30 + * which contained the following notice: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. + */ + + /** + * An implementation of {@link ExecutorService#invokeAny} for {@link ListeningExecutorService} + * implementations. + */ // static T invokeAnyImpl(ListeningExecutorService executorService, +// Collection> tasks, boolean timed, long nanos) +// throws InterruptedException, ExecutionException, TimeoutException { +// checkNotNull(executorService); +// int ntasks = tasks.size(); +// checkArgument(ntasks > 0); +// List> futures = Lists.newArrayListWithCapacity(ntasks); +// BlockingQueue> futureQueue = Queues.newLinkedBlockingQueue(); +// +// // For efficiency, especially in executors with limited +// // parallelism, check to see if previously submitted tasks are +// // done before submitting more of them. This interleaving +// // plus the exception mechanics account for messiness of main +// // loop. +// +// try { +// // Record exceptions so that if we fail to obtain any +// // result, we can throw the last exception we got. +// ExecutionException ee = null; +// long lastTime = timed ? System.nanoTime() : 0; +// Iterator> it = tasks.iterator(); +// +// futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue)); +// --ntasks; +// int active = 1; +// +// for (;;) { +// Future f = futureQueue.poll(); +// if (f == null) { +// if (ntasks > 0) { +// --ntasks; +// futures.add(submitAndAddQueueListener(executorService, it.next(), futureQueue)); +// ++active; +// } else if (active == 0) { +// break; +// } else if (timed) { +// f = futureQueue.poll(nanos, TimeUnit.NANOSECONDS); +// if (f == null) { +// throw new TimeoutException(); +// } +// long now = System.nanoTime(); +// nanos -= now - lastTime; +// lastTime = now; +// } else { +// f = futureQueue.take(); +// } +// } +// if (f != null) { +// --active; +// try { +// return f.get(); +// } catch (ExecutionException eex) { +// ee = eex; +// } catch (RuntimeException rex) { +// ee = new ExecutionException(rex); +// } +// } +// } +// +// if (ee == null) { +// ee = new ExecutionException(null); +// } +// throw ee; +// } finally { +// for (Future f : futures) { +// f.cancel(true); +// } +// } +// } + + /** + * Submits the task and adds a listener that adds the future to {@code queue} when it completes. + */ +// private static ListenableFuture submitAndAddQueueListener( +// ListeningExecutorService executorService, Callable task, +// final BlockingQueue> queue) { +// final ListenableFuture future = executorService.submit(task); +// future.addListener(new Runnable() { +// @Override public void run() { +// queue.add(future); +// } +// }, MoreExecutors.sameThreadExecutor()); +// return future; +// } + + /** + * Returns a default thread factory used to create new threads. + * + *

On AppEngine, returns {@code ThreadManager.currentRequestThreadFactory()}. + * Otherwise, returns {@link Executors#defaultThreadFactory()}. + * + * @since 14.0 + */ +// @Beta +// public static ThreadFactory platformThreadFactory() { +// if (!isAppEngine()) { +// return Executors.defaultThreadFactory(); +// } +// try { +// return (ThreadFactory) Class.forName("com.google.appengine.api.ThreadManager") +// .getMethod("currentRequestThreadFactory") +// .invoke(null); +// } catch (IllegalAccessException e) { +// throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); +// } catch (ClassNotFoundException e) { +// throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); +// } catch (NoSuchMethodException e) { +// throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); +// } catch (InvocationTargetException e) { +// throw Throwables.propagate(e.getCause()); +// } +// } + +// private static boolean isAppEngine() { +// if (System.getProperty("com.google.appengine.runtime.environment") == null) { +// return false; +// } +// try { +// // If the current environment is null, we're not inside AppEngine. +// return Class.forName("com.google.apphosting.api.ApiProxy") +// .getMethod("getCurrentEnvironment") +// .invoke(null) != null; +// } catch (ClassNotFoundException e) { +// // If ApiProxy doesn't exist, we're not on AppEngine at all. +// return false; +// } catch (InvocationTargetException e) { +// // If ApiProxy throws an exception, we're not in a proper AppEngine environment. +// return false; +// } catch (IllegalAccessException e) { +// // If the method isn't accessible, we're not on a supported version of AppEngine; +// return false; +// } catch (NoSuchMethodException e) { +// // If the method doesn't exist, we're not on a supported version of AppEngine; +// return false; +// } +// } + + /** + * Creates a thread using {@link #platformThreadFactory}, and sets its name to {@code name} + * unless changing the name is forbidden by the security manager. + */ +// static Thread newThread(String name, Runnable runnable) { +// checkNotNull(name); +// checkNotNull(runnable); +// Thread result = platformThreadFactory().newThread(runnable); +// try { +// result.setName(name); +// } catch (SecurityException e) { +// // OK if we can't set the name in this environment. +// } +// return result; +// } + + // TODO(user): provide overloads for ListeningExecutorService? ListeningScheduledExecutorService? + // TODO(user): provide overloads that take constant strings? Functions to + // calculate names? + + /** + * Creates an {@link Executor} that renames the {@link Thread threads} that its tasks run in. + * + *

The names are retrieved from the {@code nameSupplier} on the thread that is being renamed + * right before each task is run. The renaming is best effort, if a {@link SecurityManager} + * prevents the renaming then it will be skipped but the tasks will still execute. + * + * + * @param executor The executor to decorate + * @param nameSupplier The source of names for each task + */ +// static Executor renamingDecorator(final Executor executor, final Supplier nameSupplier) { +// checkNotNull(executor); +// checkNotNull(nameSupplier); +// if (isAppEngine()) { +// // AppEngine doesn't support thread renaming, so don't even try +// return executor; +// } +// return new Executor() { +// @Override public void execute(Runnable command) { +// executor.execute(Callables.threadRenaming(command, nameSupplier)); +// } +// }; +// } + + /** + * Creates an {@link ExecutorService} that renames the {@link Thread threads} that its tasks run + * in. + * + *

The names are retrieved from the {@code nameSupplier} on the thread that is being renamed + * right before each task is run. The renaming is best effort, if a {@link SecurityManager} + * prevents the renaming then it will be skipped but the tasks will still execute. + * + * + * @param service The executor to decorate + * @param nameSupplier The source of names for each task + */ +// static ExecutorService renamingDecorator(final ExecutorService service, +// final Supplier nameSupplier) { +// checkNotNull(service); +// checkNotNull(nameSupplier); +// if (isAppEngine()) { +// // AppEngine doesn't support thread renaming, so don't even try. +// return service; +// } +// return new WrappingExecutorService(service) { +// @Override protected Callable wrapTask(Callable callable) { +// return Callables.threadRenaming(callable, nameSupplier); +// } +// @Override protected Runnable wrapTask(Runnable command) { +// return Callables.threadRenaming(command, nameSupplier); +// } +// }; +// } + + /** + * Creates a {@link ScheduledExecutorService} that renames the {@link Thread threads} that its + * tasks run in. + * + *

The names are retrieved from the {@code nameSupplier} on the thread that is being renamed + * right before each task is run. The renaming is best effort, if a {@link SecurityManager} + * prevents the renaming then it will be skipped but the tasks will still execute. + * + * + * @param service The executor to decorate + * @param nameSupplier The source of names for each task + */ +// static ScheduledExecutorService renamingDecorator(final ScheduledExecutorService service, +// final Supplier nameSupplier) { +// checkNotNull(service); +// checkNotNull(nameSupplier); +// if (isAppEngine()) { +// // AppEngine doesn't support thread renaming, so don't even try. +// return service; +// } +// return new WrappingScheduledExecutorService(service) { +// @Override protected Callable wrapTask(Callable callable) { +// return Callables.threadRenaming(callable, nameSupplier); +// } +// @Override protected Runnable wrapTask(Runnable command) { +// return Callables.threadRenaming(command, nameSupplier); +// } +// }; +// } + + /** + * Shuts down the given executor gradually, first disabling new submissions and later cancelling + * existing tasks. + * + *

The method takes the following steps: + *

    + *
  1. calls {@link ExecutorService#shutdown()}, disabling acceptance of new submitted tasks. + *
  2. waits for half of the specified timeout. + *
  3. if the timeout expires, it calls {@link ExecutorService#shutdownNow()}, cancelling + * pending tasks and interrupting running tasks. + *
  4. waits for the other half of the specified timeout. + *
+ * + *

If, at any step of the process, the given executor is terminated or the calling thread is + * interrupted, the method may return without executing any remaining steps. + * + * @param service the {@code ExecutorService} to shut down + * @param timeout the maximum time to wait for the {@code ExecutorService} to terminate + * @param unit the time unit of the timeout argument + * @return {@code true) if the pool was terminated successfully, {@code false} if the + * {@code ExecutorService} could not terminate or the thread running this method + * is interrupted while waiting for the {@code ExecutorService} to terminate + * @since 17.0 + */ +// @Beta +// public static boolean shutdownAndAwaitTermination( +// ExecutorService service, long timeout, TimeUnit unit) { +// checkNotNull(unit); +// // Disable new tasks from being submitted +// service.shutdown(); +// try { +// long halfTimeoutNanos = TimeUnit.NANOSECONDS.convert(timeout, unit) / 2; +// // Wait for half the duration of the timeout for existing tasks to terminate +// if (!service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS)) { +// // Cancel currently executing tasks +// service.shutdownNow(); +// // Wait the other half of the timeout for tasks to respond to being cancelled +// service.awaitTermination(halfTimeoutNanos, TimeUnit.NANOSECONDS); +// } +// } catch (InterruptedException ie) { +// // Preserve interrupt status +// Thread.currentThread().interrupt(); +// // (Re-)Cancel if current thread also interrupted +// service.shutdownNow(); +// } +// return service.isTerminated(); +// } +} diff --git a/java/src/game/future/ThreadFactoryBuilder.java b/java/src/game/future/ThreadFactoryBuilder.java new file mode 100644 index 0000000..611bdf0 --- /dev/null +++ b/java/src/game/future/ThreadFactoryBuilder.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A ThreadFactory builder, providing any combination of these features: + *

    + *
  • whether threads should be marked as {@linkplain Thread#setDaemon daemon} + * threads + *
  • a {@linkplain ThreadFactoryBuilder#setNameFormat naming format} + *
  • a {@linkplain Thread#setPriority thread priority} + *
  • an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception + * handler} + *
  • a {@linkplain ThreadFactory#newThread backing thread factory} + *
+ *

If no backing thread factory is provided, a default backing thread factory is + * used as if by calling {@code setThreadFactory(}{@link + * Executors#defaultThreadFactory()}{@code )}. + * + * @author Kurt Alfred Kluever + * @since 4.0 + */ +public final class ThreadFactoryBuilder { + private String nameFormat = null; + private Boolean daemon = null; + private Integer priority = null; + private UncaughtExceptionHandler uncaughtExceptionHandler = null; + private ThreadFactory backingThreadFactory = null; + + /** + * Creates a new {@link ThreadFactory} builder. + */ + public ThreadFactoryBuilder() {} + + /** + * Sets the naming format to use when naming threads ({@link Thread#setName}) + * which are created with this ThreadFactory. + * + * @param nameFormat a {@link String#format(String, Object...)}-compatible + * format String, to which a unique integer (0, 1, etc.) will be supplied + * as the single parameter. This integer will be unique to the built + * instance of the ThreadFactory and will be assigned sequentially. For + * example, {@code "rpc-pool-%d"} will generate thread names like + * {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. + * @return this for the builder pattern + */ + + public ThreadFactoryBuilder setNameFormat(String nameFormat) { + String.format(nameFormat, 0); // fail fast if the format is bad or null + this.nameFormat = nameFormat; + return this; + } + + /** + * Sets daemon or not for new threads created with this ThreadFactory. + * + * @param daemon whether or not new Threads created with this ThreadFactory + * will be daemon threads + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setDaemon(boolean daemon) { + this.daemon = daemon; + return this; + } + + /** + * Sets the priority for new threads created with this ThreadFactory. + * + * @param priority the priority for new Threads created with this + * ThreadFactory + * @return this for the builder pattern + */ +// public ThreadFactoryBuilder setPriority(int priority) { + // Thread#setPriority() already checks for validity. These error messages + // are nicer though and will fail-fast. +// checkArgument(priority >= Thread.MIN_PRIORITY, +// "Thread priority (%s) must be >= %s", priority, Thread.MIN_PRIORITY); +// checkArgument(priority <= Thread.MAX_PRIORITY, +// "Thread priority (%s) must be <= %s", priority, Thread.MAX_PRIORITY); +// this.priority = priority; +// return this; +// } + + /** + * Sets the {@link UncaughtExceptionHandler} for new threads created with this + * ThreadFactory. + * + * @param uncaughtExceptionHandler the uncaught exception handler for new + * Threads created with this ThreadFactory + * @return this for the builder pattern + */ +// public ThreadFactoryBuilder setUncaughtExceptionHandler( +// UncaughtExceptionHandler uncaughtExceptionHandler) { +// this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler); +// return this; +// } + + /** + * Sets the backing {@link ThreadFactory} for new threads created with this + * ThreadFactory. Threads will be created by invoking #newThread(Runnable) on + * this backing {@link ThreadFactory}. + * + * @param backingThreadFactory the backing {@link ThreadFactory} which will + * be delegated to during thread creation. + * @return this for the builder pattern + * + * @see MoreExecutors + */ +// public ThreadFactoryBuilder setThreadFactory( +// ThreadFactory backingThreadFactory) { +// this.backingThreadFactory = checkNotNull(backingThreadFactory); +// return this; +// } + + /** + * Returns a new thread factory using the options supplied during the building + * process. After building, it is still possible to change the options used to + * build the ThreadFactory and/or build again. State is not shared amongst + * built instances. + * + * @return the fully constructed {@link ThreadFactory} + */ + 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(0) : null; + return new ThreadFactory() { + @Override 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/java/src/game/future/UncheckedExecutionException.java b/java/src/game/future/UncheckedExecutionException.java new file mode 100644 index 0000000..4f25bf8 --- /dev/null +++ b/java/src/game/future/UncheckedExecutionException.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.future; + +/** + * Unchecked variant of {@link java.util.concurrent.ExecutionException}. As with + * {@code ExecutionException}, the exception's {@linkplain #getCause() cause} + * comes from a failed task, possibly run in another thread. + * + *

{@code UncheckedExecutionException} is intended as an alternative to + * {@code ExecutionException} when the exception thrown by a task is an + * unchecked exception. However, it may also wrap a checked exception in some + * cases. + * + *

When wrapping an {@code Error} from another thread, prefer {@link + * ExecutionError}. When wrapping a checked exception, prefer {@code + * ExecutionException}. + * + * @author Charles Fry + * @since 10.0 + */ + +class UncheckedExecutionException extends RuntimeException { + /** + * Creates a new instance with {@code null} as its detail message. + */ + protected UncheckedExecutionException() {} + + /** + * Creates a new instance with the given detail message. + */ + protected UncheckedExecutionException(String message) { + super(message); + } + + /** + * Creates a new instance with the given detail message and cause. + */ + public UncheckedExecutionException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Creates a new instance with the given cause. + */ + public UncheckedExecutionException(Throwable cause) { + super(cause); + } + + private static final long serialVersionUID = 0; +} diff --git a/java/src/game/gui/Gui.java b/java/src/game/gui/Gui.java index 4c702e5..6201c8d 100644 --- a/java/src/game/gui/Gui.java +++ b/java/src/game/gui/Gui.java @@ -4,7 +4,7 @@ import java.util.List; import org.lwjgl.opengl.GL13; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.Game; import game.gui.element.Dropdown; diff --git a/java/src/game/gui/GuiConsole.java b/java/src/game/gui/GuiConsole.java index b9d2cae..c4b1ef7 100644 --- a/java/src/game/gui/GuiConsole.java +++ b/java/src/game/gui/GuiConsole.java @@ -2,7 +2,7 @@ package game.gui; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.Game; import game.color.TextColor; diff --git a/java/src/game/gui/GuiInfo.java b/java/src/game/gui/GuiInfo.java index 4e9ea7b..adacd58 100644 --- a/java/src/game/gui/GuiInfo.java +++ b/java/src/game/gui/GuiInfo.java @@ -18,13 +18,13 @@ public class GuiInfo extends Gui { private static final String[] LIBRARIES = { "LWJGL 3.3.6+1 (GLFW + OpenGL + Opus)", - "Netty 4.1.119-Final", - "Guava 33.4.0" + "Netty 4.1.119-Final" }; private static final String[] CODE = { "Albert Pham - WorldEdit (Snippets)", "Joonas Vali - NameGenerator", "LWJGL 2.9.4-nightly-20150209 - Project, Vector*, Matrix*", + "Guava 17.0 - collect + future + Predicates", "MC 1.8.9" }; diff --git a/java/src/game/gui/container/GuiContainer.java b/java/src/game/gui/container/GuiContainer.java index ce2a976..6d33c22 100755 --- a/java/src/game/gui/container/GuiContainer.java +++ b/java/src/game/gui/container/GuiContainer.java @@ -6,8 +6,8 @@ import java.util.Set; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Sets; import game.color.TextColor; import game.gui.Gui; diff --git a/java/src/game/gui/element/GuiList.java b/java/src/game/gui/element/GuiList.java index 0c3ceb3..ec8abd5 100755 --- a/java/src/game/gui/element/GuiList.java +++ b/java/src/game/gui/element/GuiList.java @@ -4,7 +4,7 @@ import java.util.List; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.gui.Gui; import game.renderer.DefaultVertexFormats; diff --git a/java/src/game/gui/world/GuiEdit.java b/java/src/game/gui/world/GuiEdit.java index e04511f..0e1a0b8 100755 --- a/java/src/game/gui/world/GuiEdit.java +++ b/java/src/game/gui/world/GuiEdit.java @@ -80,14 +80,14 @@ public class GuiEdit extends Gui implements ActButton.Callback, Textbox.Callback // if(this.player) { // this.nameField.setMaxStringLength(16); // this.nameField.setValidator(new Predicate() { -// public boolean apply(String name) { +// public boolean test(String name) { // return NetHandlerPlayServer.isValidUser(name); // } // }); // } // else { // this.nameField.setValidator(new Predicate() { -// public boolean apply(String name) { +// public boolean test(String name) { // for(int z = 0; z < name.length(); z++) { // if(GuiCreate.DISALLOWED_CHARS.contains(name.charAt(z))) // return false; diff --git a/java/src/game/gui/world/GuiWorlds.java b/java/src/game/gui/world/GuiWorlds.java index cef6363..164f98c 100755 --- a/java/src/game/gui/world/GuiWorlds.java +++ b/java/src/game/gui/world/GuiWorlds.java @@ -10,7 +10,7 @@ import java.util.Collections; import java.util.Date; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.Game; import game.color.TextColor; @@ -301,7 +301,7 @@ public class GuiWorlds extends GuiList implements ActButton. //// this.userField.setText(this.gm.localUser); //// this.userField.setValidator(new Predicate() //// { -//// public boolean apply(String name) +//// public boolean test(String name) //// { //// return StringValidator.isValidUser(name); //// } diff --git a/java/src/game/init/CraftingRegistry.java b/java/src/game/init/CraftingRegistry.java index d9d9ea7..930d644 100755 --- a/java/src/game/init/CraftingRegistry.java +++ b/java/src/game/init/CraftingRegistry.java @@ -6,8 +6,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.block.BlockBed; diff --git a/java/src/game/init/EntityRegistry.java b/java/src/game/init/EntityRegistry.java index fbbc49d..225d203 100755 --- a/java/src/game/init/EntityRegistry.java +++ b/java/src/game/init/EntityRegistry.java @@ -3,7 +3,7 @@ package game.init; import java.util.Map; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.entity.Entity; import game.entity.animal.EntityBat; diff --git a/java/src/game/init/FluidRegistry.java b/java/src/game/init/FluidRegistry.java index d5433d5..429adcf 100755 --- a/java/src/game/init/FluidRegistry.java +++ b/java/src/game/init/FluidRegistry.java @@ -3,8 +3,8 @@ package game.init; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.block.BlockDynamicLiquid; diff --git a/java/src/game/init/ItemRegistry.java b/java/src/game/init/ItemRegistry.java index b7a189b..1e7ee5d 100755 --- a/java/src/game/init/ItemRegistry.java +++ b/java/src/game/init/ItemRegistry.java @@ -4,8 +4,8 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Maps; +import game.collect.Sets; import game.block.Block; import game.block.BlockBed; diff --git a/java/src/game/init/ObjectIntIdentityMap.java b/java/src/game/init/ObjectIntIdentityMap.java index 91a88d7..ea81b20 100755 --- a/java/src/game/init/ObjectIntIdentityMap.java +++ b/java/src/game/init/ObjectIntIdentityMap.java @@ -4,9 +4,9 @@ import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; -import com.google.common.base.Predicates; -import com.google.common.collect.Iterators; -import com.google.common.collect.Lists; +import game.util.Predicates; +import game.collect.Iterators; +import game.collect.Lists; public class ObjectIntIdentityMap implements IObjectIntIterable { diff --git a/java/src/game/init/RegistryNamespaced.java b/java/src/game/init/RegistryNamespaced.java index 7a4a633..0b48909 100755 --- a/java/src/game/init/RegistryNamespaced.java +++ b/java/src/game/init/RegistryNamespaced.java @@ -3,8 +3,8 @@ package game.init; import java.util.Iterator; import java.util.Map; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import game.collect.BiMap; +import game.collect.HashBiMap; public class RegistryNamespaced extends RegistrySimple implements IObjectIntIterable { diff --git a/java/src/game/init/RegistrySimple.java b/java/src/game/init/RegistrySimple.java index 467902c..5b41625 100755 --- a/java/src/game/init/RegistrySimple.java +++ b/java/src/game/init/RegistrySimple.java @@ -5,7 +5,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; public class RegistrySimple implements IRegistry { diff --git a/java/src/game/init/RotationRegistry.java b/java/src/game/init/RotationRegistry.java index 6fb4772..52c8bc2 100755 --- a/java/src/game/init/RotationRegistry.java +++ b/java/src/game/init/RotationRegistry.java @@ -4,9 +4,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import com.google.common.base.Predicate; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import java.util.function.Predicate; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.block.BlockDoor; @@ -38,7 +38,7 @@ public abstract class RotationRegistry { if(prop == BlockDoor.FACING) { predicate = new Predicate() { @Override - public boolean apply(Integer meta) { + public boolean test(Integer meta) { return (meta & 8) == 0; } }; diff --git a/java/src/game/init/SmeltingRegistry.java b/java/src/game/init/SmeltingRegistry.java index aea4035..b8e3e6f 100755 --- a/java/src/game/init/SmeltingRegistry.java +++ b/java/src/game/init/SmeltingRegistry.java @@ -4,7 +4,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.block.Block; import game.block.BlockStoneBrick; diff --git a/java/src/game/init/SpeciesRegistry.java b/java/src/game/init/SpeciesRegistry.java index b0ba646..a5f3d63 100755 --- a/java/src/game/init/SpeciesRegistry.java +++ b/java/src/game/init/SpeciesRegistry.java @@ -4,9 +4,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.entity.npc.EntityArachnoid; import game.entity.npc.EntityBloodElf; diff --git a/java/src/game/init/TileRegistry.java b/java/src/game/init/TileRegistry.java index 1ce3a0e..d8d4147 100755 --- a/java/src/game/init/TileRegistry.java +++ b/java/src/game/init/TileRegistry.java @@ -2,7 +2,7 @@ package game.init; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.tileentity.TileEntity; import game.tileentity.TileEntityBanner; diff --git a/java/src/game/init/ToolMaterial.java b/java/src/game/init/ToolMaterial.java index 6872aa5..bff7669 100755 --- a/java/src/game/init/ToolMaterial.java +++ b/java/src/game/init/ToolMaterial.java @@ -2,7 +2,7 @@ package game.init; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.item.Item; diff --git a/java/src/game/init/UniverseRegistry.java b/java/src/game/init/UniverseRegistry.java index ad0dfcb..6b80708 100755 --- a/java/src/game/init/UniverseRegistry.java +++ b/java/src/game/init/UniverseRegistry.java @@ -5,9 +5,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.biome.Biome; import game.block.BlockColored; diff --git a/java/src/game/inventory/Container.java b/java/src/game/inventory/Container.java index b1f3195..34fa6fb 100755 --- a/java/src/game/inventory/Container.java +++ b/java/src/game/inventory/Container.java @@ -3,8 +3,8 @@ package game.inventory; import java.util.List; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Sets; import game.entity.npc.EntityNPC; import game.item.Item; diff --git a/java/src/game/inventory/ContainerLocalMenu.java b/java/src/game/inventory/ContainerLocalMenu.java index 058a42a..9b2fa72 100755 --- a/java/src/game/inventory/ContainerLocalMenu.java +++ b/java/src/game/inventory/ContainerLocalMenu.java @@ -2,7 +2,7 @@ package game.inventory; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.entity.npc.EntityNPC; import game.tileentity.ILockableContainer; diff --git a/java/src/game/inventory/ContainerPlayer.java b/java/src/game/inventory/ContainerPlayer.java index db973bd..24f9857 100755 --- a/java/src/game/inventory/ContainerPlayer.java +++ b/java/src/game/inventory/ContainerPlayer.java @@ -2,7 +2,7 @@ package game.inventory; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.attributes.AttributeMap; import game.entity.npc.EntityNPC; diff --git a/java/src/game/inventory/InventoryBasic.java b/java/src/game/inventory/InventoryBasic.java index a080a4f..38db51e 100755 --- a/java/src/game/inventory/InventoryBasic.java +++ b/java/src/game/inventory/InventoryBasic.java @@ -2,7 +2,7 @@ package game.inventory; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.npc.EntityNPC; import game.item.ItemStack; diff --git a/java/src/game/item/Item.java b/java/src/game/item/Item.java index ab26914..6faaf75 100755 --- a/java/src/game/item/Item.java +++ b/java/src/game/item/Item.java @@ -4,8 +4,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Maps; +import game.collect.Sets; import game.block.Block; import game.color.TextColor; diff --git a/java/src/game/item/ItemArmor.java b/java/src/game/item/ItemArmor.java index 698f5c4..74cedb3 100755 --- a/java/src/game/item/ItemArmor.java +++ b/java/src/game/item/ItemArmor.java @@ -4,8 +4,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.base.Predicate; -import com.google.common.collect.Sets; +import java.util.function.Predicate; +import game.collect.Sets; import game.block.BlockDispenser; import game.dispenser.BehaviorDefaultDispenseItem; @@ -38,7 +38,7 @@ public class ItemArmor extends Item int k = blockpos.getZ(); BoundingBox axisalignedbb = new BoundingBox((double)i, (double)j, (double)k, (double)(i + 1), (double)(j + 1), (double)(k + 1)); List list = source.getWorld().getEntitiesWithinAABB(EntityLiving.class, axisalignedbb, new Predicate() { - public boolean apply(EntityLiving entity) { + public boolean test(EntityLiving entity) { return entity.isEntityAlive() && entity instanceof EntityNPC && entity.getItem(ItemArmor.getArmorPosition(stack)) != null; // || entitylivingbase.isPlayer()); } diff --git a/java/src/game/item/ItemBucket.java b/java/src/game/item/ItemBucket.java index ea99b5b..65ef496 100755 --- a/java/src/game/item/ItemBucket.java +++ b/java/src/game/item/ItemBucket.java @@ -7,7 +7,7 @@ import java.util.List; import java.util.Queue; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.block.Block; import game.block.BlockDynamicLiquid; diff --git a/java/src/game/item/ItemBucketMilk.java b/java/src/game/item/ItemBucketMilk.java index 1829a1e..5ffdc20 100755 --- a/java/src/game/item/ItemBucketMilk.java +++ b/java/src/game/item/ItemBucketMilk.java @@ -3,7 +3,7 @@ package game.item; import java.util.Map; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.entity.attributes.Attribute; import game.entity.attributes.AttributeModifier; diff --git a/java/src/game/item/ItemFirework.java b/java/src/game/item/ItemFirework.java index 8dcd57c..3e867cc 100755 --- a/java/src/game/item/ItemFirework.java +++ b/java/src/game/item/ItemFirework.java @@ -2,7 +2,7 @@ package game.item; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.item.EntityFireworks; import game.entity.npc.EntityNPC; diff --git a/java/src/game/item/ItemFishFood.java b/java/src/game/item/ItemFishFood.java index 65bfd37..6564476 100755 --- a/java/src/game/item/ItemFishFood.java +++ b/java/src/game/item/ItemFishFood.java @@ -3,7 +3,7 @@ package game.item; import java.util.List; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.entity.npc.EntityNPC; import game.potion.Potion; diff --git a/java/src/game/item/ItemMagnet.java b/java/src/game/item/ItemMagnet.java index c5ad218..efa05c9 100755 --- a/java/src/game/item/ItemMagnet.java +++ b/java/src/game/item/ItemMagnet.java @@ -2,7 +2,7 @@ package game.item; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.entity.Entity; import game.entity.animal.EntityChicken; @@ -37,7 +37,7 @@ public class ItemMagnet extends Item { new BoundingBox(playerIn.posX - 40.0f, playerIn.posY + playerIn.getEyeHeight() - 40.0f, playerIn.posZ - 40.0f, playerIn.posX + 40.0f, playerIn.posY + playerIn.getEyeHeight() + 40.0f, playerIn.posZ + 40.0f), new Predicate() { - public boolean apply(Entity entity) { + public boolean test(Entity entity) { return (ItemMagnet.this.chicken || /* ? !((EntityLiving)entity).isAIDisabled() : */ entity.isMagnetic()) && entity.getDistanceToEntity(playerIn) <= 38.0f; } diff --git a/java/src/game/item/ItemMetal.java b/java/src/game/item/ItemMetal.java index 70c484b..26a8f8a 100755 --- a/java/src/game/item/ItemMetal.java +++ b/java/src/game/item/ItemMetal.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.color.TextColor; import game.entity.attributes.Attribute; diff --git a/java/src/game/item/ItemMetalBlock.java b/java/src/game/item/ItemMetalBlock.java index d4939ca..b1ab01c 100755 --- a/java/src/game/item/ItemMetalBlock.java +++ b/java/src/game/item/ItemMetalBlock.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.block.Block; import game.color.TextColor; diff --git a/java/src/game/item/ItemPotion.java b/java/src/game/item/ItemPotion.java index d670b82..9697b6b 100755 --- a/java/src/game/item/ItemPotion.java +++ b/java/src/game/item/ItemPotion.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Maps; +import game.collect.Sets; import game.color.TextColor; import game.entity.attributes.Attribute; diff --git a/java/src/game/item/ItemStack.java b/java/src/game/item/ItemStack.java index e7cb583..c6fcc06 100755 --- a/java/src/game/item/ItemStack.java +++ b/java/src/game/item/ItemStack.java @@ -6,8 +6,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.color.TextColor; diff --git a/java/src/game/item/ItemSword.java b/java/src/game/item/ItemSword.java index 271c7c1..04b77ff 100755 --- a/java/src/game/item/ItemSword.java +++ b/java/src/game/item/ItemSword.java @@ -3,7 +3,7 @@ package game.item; import java.util.Map; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.block.Block; import game.entity.attributes.Attribute; diff --git a/java/src/game/item/ItemTool.java b/java/src/game/item/ItemTool.java index eb3c1f4..dd07981 100755 --- a/java/src/game/item/ItemTool.java +++ b/java/src/game/item/ItemTool.java @@ -3,7 +3,7 @@ package game.item; import java.util.Map; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.block.Block; import game.entity.attributes.Attribute; diff --git a/java/src/game/log/Log.java b/java/src/game/log/Log.java index fd88ca8..415e6a0 100644 --- a/java/src/game/log/Log.java +++ b/java/src/game/log/Log.java @@ -4,7 +4,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.Game; import game.color.TextColor; diff --git a/java/src/game/model/BakedModel.java b/java/src/game/model/BakedModel.java index 20a00f2..8227641 100755 --- a/java/src/game/model/BakedModel.java +++ b/java/src/game/model/BakedModel.java @@ -3,7 +3,7 @@ package game.model; import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.renderer.blockmodel.BakedQuad; import game.renderer.blockmodel.BreakingFour; diff --git a/java/src/game/model/ModelBakery.java b/java/src/game/model/ModelBakery.java index 57a0f45..4c80d7a 100755 --- a/java/src/game/model/ModelBakery.java +++ b/java/src/game/model/ModelBakery.java @@ -7,9 +7,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.init.BlockRegistry; import game.init.FluidRegistry; diff --git a/java/src/game/model/ModelManager.java b/java/src/game/model/ModelManager.java index 1668942..41e86b5 100755 --- a/java/src/game/model/ModelManager.java +++ b/java/src/game/model/ModelManager.java @@ -5,7 +5,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.block.Block; import game.block.BlockLiquid; diff --git a/java/src/game/model/ModelRotation.java b/java/src/game/model/ModelRotation.java index caa78b9..5eae937 100755 --- a/java/src/game/model/ModelRotation.java +++ b/java/src/game/model/ModelRotation.java @@ -2,7 +2,7 @@ package game.model; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.renderer.Matrix4f; import game.renderer.Vector3f; diff --git a/java/src/game/nbt/NBTParser.java b/java/src/game/nbt/NBTParser.java index e367ae8..e4a2e97 100755 --- a/java/src/game/nbt/NBTParser.java +++ b/java/src/game/nbt/NBTParser.java @@ -3,7 +3,7 @@ package game.nbt; import java.util.Stack; import java.util.regex.Pattern; -import com.google.common.collect.Lists; +import game.collect.Lists; public class NBTParser { diff --git a/java/src/game/nbt/NBTTagCompound.java b/java/src/game/nbt/NBTTagCompound.java index 776f99a..4606414 100755 --- a/java/src/game/nbt/NBTTagCompound.java +++ b/java/src/game/nbt/NBTTagCompound.java @@ -7,7 +7,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; public class NBTTagCompound extends NBTBase { diff --git a/java/src/game/nbt/NBTTagList.java b/java/src/game/nbt/NBTTagList.java index 612dd56..51c1e17 100755 --- a/java/src/game/nbt/NBTTagList.java +++ b/java/src/game/nbt/NBTTagList.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.log.Log; diff --git a/java/src/game/network/IThreadListener.java b/java/src/game/network/IThreadListener.java index 8ce2389..75d05eb 100755 --- a/java/src/game/network/IThreadListener.java +++ b/java/src/game/network/IThreadListener.java @@ -1,6 +1,6 @@ package game.network; -import com.google.common.util.concurrent.ListenableFuture; +import game.future.ListenableFuture; public interface IThreadListener { diff --git a/java/src/game/network/NetConnection.java b/java/src/game/network/NetConnection.java index 05eb221..fb8e219 100755 --- a/java/src/game/network/NetConnection.java +++ b/java/src/game/network/NetConnection.java @@ -7,7 +7,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import game.future.ThreadFactoryBuilder; import game.log.Log; import game.network.NetHandler.ThreadQuickExitException; diff --git a/java/src/game/network/NetHandlerPlayClient.java b/java/src/game/network/NetHandlerPlayClient.java index 82764c3..dfa740d 100755 --- a/java/src/game/network/NetHandlerPlayClient.java +++ b/java/src/game/network/NetHandlerPlayClient.java @@ -5,7 +5,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.Game; import game.dimension.Dimension; diff --git a/java/src/game/network/NetHandlerPlayServer.java b/java/src/game/network/NetHandlerPlayServer.java index c679b51..93ddedf 100755 --- a/java/src/game/network/NetHandlerPlayServer.java +++ b/java/src/game/network/NetHandlerPlayServer.java @@ -7,9 +7,9 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; -import com.google.common.base.Predicate; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Futures; +import java.util.function.Predicate; +import game.collect.Lists; +import game.future.Futures; import game.Server; import game.block.Block; @@ -2810,7 +2810,7 @@ public class NetHandlerPlayServer extends NetHandler implements ICrafting, Scrip case MAGNET: if(this.isAdmin()) { List list = this.entity.worldObj.getEntitiesWithinAABB(Entity.class, new BoundingBox(this.entity.getPosition().subtract(new BlockPos(128, 128, 128)), this.entity.getPosition().add(new BlockPos(128, 128, 128))), new Predicate() { - public boolean apply(Entity entity) { + public boolean test(Entity entity) { return entity.isEntityAlive() && (entity instanceof EntityItem || entity instanceof EntityXp); } }); diff --git a/java/src/game/network/PacketRegistry.java b/java/src/game/network/PacketRegistry.java index 471f85c..904472c 100755 --- a/java/src/game/network/PacketRegistry.java +++ b/java/src/game/network/PacketRegistry.java @@ -2,9 +2,9 @@ package game.network; import java.util.Map; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Maps; +import game.collect.BiMap; +import game.collect.HashBiMap; +import game.collect.Maps; import game.packet.CPacketAction; import game.packet.CPacketBook; diff --git a/java/src/game/packet/S20PacketEntityProperties.java b/java/src/game/packet/S20PacketEntityProperties.java index f35f9ad..60c86b3 100755 --- a/java/src/game/packet/S20PacketEntityProperties.java +++ b/java/src/game/packet/S20PacketEntityProperties.java @@ -4,7 +4,7 @@ import java.io.IOException; import java.util.Collection; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.attributes.AttributeInstance; import game.entity.attributes.AttributeModifier; diff --git a/java/src/game/packet/S27PacketExplosion.java b/java/src/game/packet/S27PacketExplosion.java index a9a1f45..6711a79 100755 --- a/java/src/game/packet/S27PacketExplosion.java +++ b/java/src/game/packet/S27PacketExplosion.java @@ -4,7 +4,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.network.NetHandlerPlayClient; import game.network.Packet; diff --git a/java/src/game/packet/S38PacketPlayerListItem.java b/java/src/game/packet/S38PacketPlayerListItem.java index 41108f6..0007ec3 100755 --- a/java/src/game/packet/S38PacketPlayerListItem.java +++ b/java/src/game/packet/S38PacketPlayerListItem.java @@ -5,7 +5,7 @@ import java.util.Collection; import java.util.Map; import java.util.Map.Entry; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.network.NetHandlerPlayClient; import game.network.NetHandlerPlayServer; diff --git a/java/src/game/packet/SPacketChunkData.java b/java/src/game/packet/SPacketChunkData.java index ea5255a..4b10e77 100755 --- a/java/src/game/packet/SPacketChunkData.java +++ b/java/src/game/packet/SPacketChunkData.java @@ -3,7 +3,7 @@ package game.packet; import java.io.IOException; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.network.NetHandlerPlayClient; import game.network.Packet; diff --git a/java/src/game/pattern/BlockStateHelper.java b/java/src/game/pattern/BlockStateHelper.java index 4ca2a6f..2bbb7ae 100755 --- a/java/src/game/pattern/BlockStateHelper.java +++ b/java/src/game/pattern/BlockStateHelper.java @@ -3,8 +3,8 @@ package game.pattern; import java.util.Map; import java.util.Map.Entry; -import com.google.common.base.Predicate; -import com.google.common.collect.Maps; +import java.util.function.Predicate; +import game.collect.Maps; import game.block.Block; import game.properties.IProperty; @@ -25,7 +25,7 @@ public class BlockStateHelper implements Predicate return new BlockStateHelper(blockIn); } - public boolean apply(State p_apply_1_) + public boolean test(State p_apply_1_) { if (p_apply_1_ != null && p_apply_1_.getBlock().equals(this.block)) { @@ -33,7 +33,7 @@ public class BlockStateHelper implements Predicate { Object object = p_apply_1_.getValue((IProperty)entry.getKey()); - if (!((Predicate)entry.getValue()).apply(object)) + if (!((Predicate)entry.getValue()).test(object)) { return false; } diff --git a/java/src/game/potion/Potion.java b/java/src/game/potion/Potion.java index c703156..f087edf 100755 --- a/java/src/game/potion/Potion.java +++ b/java/src/game/potion/Potion.java @@ -4,7 +4,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.entity.DamageSource; import game.entity.attributes.Attribute; diff --git a/java/src/game/potion/PotionHelper.java b/java/src/game/potion/PotionHelper.java index db280a2..55014d7 100755 --- a/java/src/game/potion/PotionHelper.java +++ b/java/src/game/potion/PotionHelper.java @@ -4,8 +4,8 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; public class PotionHelper { diff --git a/java/src/game/properties/PropertyBool.java b/java/src/game/properties/PropertyBool.java index ba755a7..4d3a8a5 100755 --- a/java/src/game/properties/PropertyBool.java +++ b/java/src/game/properties/PropertyBool.java @@ -2,7 +2,7 @@ package game.properties; import java.util.Collection; -import com.google.common.collect.ImmutableSet; +import game.collect.ImmutableSet; public class PropertyBool extends PropertyHelper { diff --git a/java/src/game/properties/PropertyDirection.java b/java/src/game/properties/PropertyDirection.java index 9ed11d0..52b3ff5 100755 --- a/java/src/game/properties/PropertyDirection.java +++ b/java/src/game/properties/PropertyDirection.java @@ -2,10 +2,10 @@ package game.properties; import java.util.Collection; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; +import java.util.function.Predicate; +import game.util.Predicates; +import game.collect.Filter; +import game.collect.Lists; import game.world.Facing; @@ -29,7 +29,7 @@ public class PropertyDirection extends PropertyEnum */ public static PropertyDirection create(String name, Predicate filter) { - return create(name, Collections2.filter(Lists.newArrayList(Facing.values()), filter)); + return create(name, Filter.filter(Lists.newArrayList(Facing.values()), filter)); } /** diff --git a/java/src/game/properties/PropertyEnum.java b/java/src/game/properties/PropertyEnum.java index 4b61357..9fe4fec 100755 --- a/java/src/game/properties/PropertyEnum.java +++ b/java/src/game/properties/PropertyEnum.java @@ -3,12 +3,12 @@ package game.properties; import java.util.Collection; import java.util.Map; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import java.util.function.Predicate; +import game.util.Predicates; +import game.collect.Filter; +import game.collect.ImmutableSet; +import game.collect.Lists; +import game.collect.Maps; public class PropertyEnum & IStringSerializable> extends PropertyHelper { @@ -53,7 +53,7 @@ public class PropertyEnum & IStringSerializable> extends Prope public static & IStringSerializable> PropertyEnum create(String name, Class clazz, Predicate filter) { - return create(name, clazz, Collections2.filter(Lists.newArrayList(clazz.getEnumConstants()), filter)); + return create(name, clazz, Filter.filter(Lists.newArrayList(clazz.getEnumConstants()), filter)); } public static & IStringSerializable> PropertyEnum create(String name, Class clazz, T... values) diff --git a/java/src/game/properties/PropertyInteger.java b/java/src/game/properties/PropertyInteger.java index 8d2fc49..d3ff44f 100755 --- a/java/src/game/properties/PropertyInteger.java +++ b/java/src/game/properties/PropertyInteger.java @@ -3,8 +3,8 @@ package game.properties; import java.util.Collection; import java.util.Set; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import game.collect.ImmutableSet; +import game.collect.Sets; public class PropertyInteger extends PropertyHelper { diff --git a/java/src/game/renderer/BlockRenderer.java b/java/src/game/renderer/BlockRenderer.java index c5fd639..0747812 100755 --- a/java/src/game/renderer/BlockRenderer.java +++ b/java/src/game/renderer/BlockRenderer.java @@ -6,7 +6,7 @@ import java.util.Map; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.Game; import game.block.Block; diff --git a/java/src/game/renderer/EntityRenderer.java b/java/src/game/renderer/EntityRenderer.java index 0ce00c2..512c487 100755 --- a/java/src/game/renderer/EntityRenderer.java +++ b/java/src/game/renderer/EntityRenderer.java @@ -8,7 +8,7 @@ import java.util.List; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.Game; import game.biome.Biome; @@ -221,7 +221,7 @@ public class EntityRenderer { float exp = 1.0F; List list = this.gm.theWorld.getEntitiesInAABBexcluding(entity, entity.getEntityBoundingBox().addCoord(look.xCoord * max, look.yCoord * max, look.zCoord * max).expand((double)exp, (double)exp, (double)exp), new Predicate() { - public boolean apply(Entity p_apply_1_) + public boolean test(Entity p_apply_1_) { return p_apply_1_.canBeCollidedWith(); } diff --git a/java/src/game/renderer/ItemModelMesher.java b/java/src/game/renderer/ItemModelMesher.java index 07d3e12..80958f6 100755 --- a/java/src/game/renderer/ItemModelMesher.java +++ b/java/src/game/renderer/ItemModelMesher.java @@ -3,8 +3,8 @@ package game.renderer; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.init.ItemRegistry; import game.item.Item; diff --git a/java/src/game/renderer/RenderGlobal.java b/java/src/game/renderer/RenderGlobal.java index 0bb0aef..c91daa9 100755 --- a/java/src/game/renderer/RenderGlobal.java +++ b/java/src/game/renderer/RenderGlobal.java @@ -15,9 +15,9 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL15; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.Game; import game.audio.Sound; diff --git a/java/src/game/renderer/VertexFormat.java b/java/src/game/renderer/VertexFormat.java index b4f2253..42be6f3 100755 --- a/java/src/game/renderer/VertexFormat.java +++ b/java/src/game/renderer/VertexFormat.java @@ -2,7 +2,7 @@ package game.renderer; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.log.Log; diff --git a/java/src/game/renderer/blockmodel/ModelBlock.java b/java/src/game/renderer/blockmodel/ModelBlock.java index 57e7407..3fc27ac 100755 --- a/java/src/game/renderer/blockmodel/ModelBlock.java +++ b/java/src/game/renderer/blockmodel/ModelBlock.java @@ -2,8 +2,8 @@ package game.renderer.blockmodel; import java.util.List; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.model.ModelBakery; import game.model.ModelRotation; diff --git a/java/src/game/renderer/blockmodel/ModelGenerator.java b/java/src/game/renderer/blockmodel/ModelGenerator.java index 7334f14..818db2a 100755 --- a/java/src/game/renderer/blockmodel/ModelGenerator.java +++ b/java/src/game/renderer/blockmodel/ModelGenerator.java @@ -4,8 +4,8 @@ import java.io.FileNotFoundException; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.log.Log; import game.renderer.Vector3f; diff --git a/java/src/game/renderer/blockmodel/MultiStateMap.java b/java/src/game/renderer/blockmodel/MultiStateMap.java index b38d13f..17996e3 100755 --- a/java/src/game/renderer/blockmodel/MultiStateMap.java +++ b/java/src/game/renderer/blockmodel/MultiStateMap.java @@ -4,8 +4,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.init.BlockRegistry; import game.properties.IProperty; diff --git a/java/src/game/renderer/blockmodel/StateMap.java b/java/src/game/renderer/blockmodel/StateMap.java index 8e6aaf4..caf1a7a 100755 --- a/java/src/game/renderer/blockmodel/StateMap.java +++ b/java/src/game/renderer/blockmodel/StateMap.java @@ -3,7 +3,7 @@ package game.renderer.blockmodel; import java.util.Map; import java.util.Map.Entry; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.block.Block; import game.properties.IProperty; diff --git a/java/src/game/renderer/chunk/ChunkCompileTaskGenerator.java b/java/src/game/renderer/chunk/ChunkCompileTaskGenerator.java index e4844ad..f1014f3 100755 --- a/java/src/game/renderer/chunk/ChunkCompileTaskGenerator.java +++ b/java/src/game/renderer/chunk/ChunkCompileTaskGenerator.java @@ -3,7 +3,7 @@ package game.renderer.chunk; import java.util.List; import java.util.concurrent.locks.ReentrantLock; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.renderer.RegionRenderCacheBuilder; diff --git a/java/src/game/renderer/chunk/ChunkRenderDispatcher.java b/java/src/game/renderer/chunk/ChunkRenderDispatcher.java index 1d34493..ff897c0 100755 --- a/java/src/game/renderer/chunk/ChunkRenderDispatcher.java +++ b/java/src/game/renderer/chunk/ChunkRenderDispatcher.java @@ -7,11 +7,11 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadFactory; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFutureTask; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import game.collect.Lists; +import game.future.Futures; +import game.future.ListenableFuture; +import game.future.ListenableFutureTask; +import game.future.ThreadFactoryBuilder; import game.Game; import game.renderer.BlockLayer; diff --git a/java/src/game/renderer/chunk/ChunkRenderWorker.java b/java/src/game/renderer/chunk/ChunkRenderWorker.java index 64a644e..fecc36a 100755 --- a/java/src/game/renderer/chunk/ChunkRenderWorker.java +++ b/java/src/game/renderer/chunk/ChunkRenderWorker.java @@ -4,11 +4,10 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CancellationException; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; +import game.collect.Lists; +import game.future.FutureCallback; +import game.future.Futures; +import game.future.ListenableFuture; import game.Game; import game.entity.Entity; @@ -187,7 +186,7 @@ public class ChunkRenderWorker implements Runnable Log.JNI.error(p_onFailure_1_, "Fehler beim Rendern des Chunks"); } } - }, MoreExecutors.directExecutor()); + }); } } diff --git a/java/src/game/renderer/chunk/CompiledChunk.java b/java/src/game/renderer/chunk/CompiledChunk.java index c271c9c..947eec7 100755 --- a/java/src/game/renderer/chunk/CompiledChunk.java +++ b/java/src/game/renderer/chunk/CompiledChunk.java @@ -2,7 +2,7 @@ package game.renderer.chunk; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.renderer.BlockLayer; import game.renderer.RenderBuffer; diff --git a/java/src/game/renderer/chunk/RenderChunk.java b/java/src/game/renderer/chunk/RenderChunk.java index c33a878..be4673f 100755 --- a/java/src/game/renderer/chunk/RenderChunk.java +++ b/java/src/game/renderer/chunk/RenderChunk.java @@ -10,8 +10,8 @@ import java.util.concurrent.locks.ReentrantLock; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Maps; +import game.collect.Sets; import game.Game; import game.block.Block; diff --git a/java/src/game/renderer/entity/RenderHorse.java b/java/src/game/renderer/entity/RenderHorse.java index 6a73f7c..8763953 100755 --- a/java/src/game/renderer/entity/RenderHorse.java +++ b/java/src/game/renderer/entity/RenderHorse.java @@ -4,7 +4,7 @@ import java.util.Set; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.Game; import game.entity.animal.EntityHorse; diff --git a/java/src/game/renderer/entity/RenderManager.java b/java/src/game/renderer/entity/RenderManager.java index f0b17ef..26cf651 100755 --- a/java/src/game/renderer/entity/RenderManager.java +++ b/java/src/game/renderer/entity/RenderManager.java @@ -5,7 +5,7 @@ import java.util.Map; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.Game; import game.entity.Entity; diff --git a/java/src/game/renderer/entity/RendererLivingEntity.java b/java/src/game/renderer/entity/RendererLivingEntity.java index 482be20..3034ba3 100755 --- a/java/src/game/renderer/entity/RendererLivingEntity.java +++ b/java/src/game/renderer/entity/RendererLivingEntity.java @@ -8,7 +8,7 @@ import java.util.List; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.color.TextColor; import game.entity.Entity; diff --git a/java/src/game/renderer/layers/LayerExtra.java b/java/src/game/renderer/layers/LayerExtra.java index ef9a9ed..ff91870 100755 --- a/java/src/game/renderer/layers/LayerExtra.java +++ b/java/src/game/renderer/layers/LayerExtra.java @@ -4,7 +4,7 @@ import java.util.List; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.Game; import game.entity.npc.EntityNPC; diff --git a/java/src/game/renderer/model/ModelBase.java b/java/src/game/renderer/model/ModelBase.java index 69dbe4d..0793e10 100755 --- a/java/src/game/renderer/model/ModelBase.java +++ b/java/src/game/renderer/model/ModelBase.java @@ -3,8 +3,8 @@ package game.renderer.model; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.entity.Entity; import game.entity.types.EntityLiving; diff --git a/java/src/game/renderer/model/ModelRenderer.java b/java/src/game/renderer/model/ModelRenderer.java index 24a2fea..0c0123b 100755 --- a/java/src/game/renderer/model/ModelRenderer.java +++ b/java/src/game/renderer/model/ModelRenderer.java @@ -4,7 +4,7 @@ import java.util.List; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.renderer.RenderBuffer; import game.renderer.Tessellator; diff --git a/java/src/game/renderer/particle/EffectRenderer.java b/java/src/game/renderer/particle/EffectRenderer.java index 3a9e107..0431ab0 100755 --- a/java/src/game/renderer/particle/EffectRenderer.java +++ b/java/src/game/renderer/particle/EffectRenderer.java @@ -5,8 +5,8 @@ import java.util.Map; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.entity.Entity; diff --git a/java/src/game/renderer/particle/ParticleType.java b/java/src/game/renderer/particle/ParticleType.java index f1c69a6..4e3911e 100755 --- a/java/src/game/renderer/particle/ParticleType.java +++ b/java/src/game/renderer/particle/ParticleType.java @@ -3,8 +3,8 @@ package game.renderer.particle; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; public enum ParticleType { diff --git a/java/src/game/renderer/texture/EntityTexManager.java b/java/src/game/renderer/texture/EntityTexManager.java index 252df61..b8cce10 100755 --- a/java/src/game/renderer/texture/EntityTexManager.java +++ b/java/src/game/renderer/texture/EntityTexManager.java @@ -10,9 +10,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.Game; import game.entity.npc.EntityNPC; diff --git a/java/src/game/renderer/texture/LayeredTexture.java b/java/src/game/renderer/texture/LayeredTexture.java index 9fcf643..cda67c9 100755 --- a/java/src/game/renderer/texture/LayeredTexture.java +++ b/java/src/game/renderer/texture/LayeredTexture.java @@ -6,7 +6,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.log.Log; import game.util.FileUtils; diff --git a/java/src/game/renderer/texture/Stitcher.java b/java/src/game/renderer/texture/Stitcher.java index bf65b25..e2b827d 100755 --- a/java/src/game/renderer/texture/Stitcher.java +++ b/java/src/game/renderer/texture/Stitcher.java @@ -5,8 +5,8 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Sets; public class Stitcher { diff --git a/java/src/game/renderer/texture/TextureAtlasSprite.java b/java/src/game/renderer/texture/TextureAtlasSprite.java index 5016b1d..d7ac2d9 100755 --- a/java/src/game/renderer/texture/TextureAtlasSprite.java +++ b/java/src/game/renderer/texture/TextureAtlasSprite.java @@ -4,7 +4,7 @@ import java.awt.image.BufferedImage; import java.io.IOException; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; public class TextureAtlasSprite { diff --git a/java/src/game/renderer/texture/TextureManager.java b/java/src/game/renderer/texture/TextureManager.java index 88a167e..313f5cc 100755 --- a/java/src/game/renderer/texture/TextureManager.java +++ b/java/src/game/renderer/texture/TextureManager.java @@ -5,7 +5,7 @@ import java.io.IOException; import java.util.Map; import java.util.Map.Entry; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.log.Log; import game.renderer.GlState; diff --git a/java/src/game/renderer/texture/TextureMap.java b/java/src/game/renderer/texture/TextureMap.java index 01e3d1a..d0a89b6 100755 --- a/java/src/game/renderer/texture/TextureMap.java +++ b/java/src/game/renderer/texture/TextureMap.java @@ -7,8 +7,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.init.BlockRegistry; diff --git a/java/src/game/renderer/tileentity/TileEntityBannerRenderer.java b/java/src/game/renderer/tileentity/TileEntityBannerRenderer.java index aa44c5d..939bd81 100755 --- a/java/src/game/renderer/tileentity/TileEntityBannerRenderer.java +++ b/java/src/game/renderer/tileentity/TileEntityBannerRenderer.java @@ -6,8 +6,8 @@ import java.util.Map; import org.lwjgl.opengl.GL11; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.Game; import game.color.DyeColor; diff --git a/java/src/game/renderer/tileentity/TileEntityRendererDispatcher.java b/java/src/game/renderer/tileentity/TileEntityRendererDispatcher.java index f7178c3..a28a185 100755 --- a/java/src/game/renderer/tileentity/TileEntityRendererDispatcher.java +++ b/java/src/game/renderer/tileentity/TileEntityRendererDispatcher.java @@ -4,7 +4,7 @@ import java.util.Map; import org.lwjgl.opengl.GL13; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.entity.Entity; import game.renderer.GlState; diff --git a/java/src/game/rng/WeightedList.java b/java/src/game/rng/WeightedList.java index 1f58e19..f08fd42 100755 --- a/java/src/game/rng/WeightedList.java +++ b/java/src/game/rng/WeightedList.java @@ -5,7 +5,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.function.UnaryOperator; -import com.google.common.base.Predicate; +import java.util.function.Predicate; public class WeightedList extends ArrayList { protected boolean modified; diff --git a/java/src/game/tileentity/TileEntityBanner.java b/java/src/game/tileentity/TileEntityBanner.java index f3ebbfc..03a20a5 100755 --- a/java/src/game/tileentity/TileEntityBanner.java +++ b/java/src/game/tileentity/TileEntityBanner.java @@ -2,7 +2,7 @@ package game.tileentity; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.block.BlockFlower; import game.color.DyeColor; diff --git a/java/src/game/tileentity/TileEntityHopper.java b/java/src/game/tileentity/TileEntityHopper.java index d08aff9..96a4189 100755 --- a/java/src/game/tileentity/TileEntityHopper.java +++ b/java/src/game/tileentity/TileEntityHopper.java @@ -2,7 +2,7 @@ package game.tileentity; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.block.Block; import game.block.BlockChest; @@ -634,7 +634,7 @@ public class TileEntityHopper extends TileEntityLockable implements IHopper, ITi public static List func_181556_a(World p_181556_0_, double p_181556_1_, double p_181556_3_, double p_181556_5_) { return p_181556_0_.getEntitiesWithinAABB(EntityItem.class, new BoundingBox(p_181556_1_ - 0.5D, p_181556_3_ - 0.5D, p_181556_5_ - 0.5D, p_181556_1_ + 0.5D, p_181556_3_ + 0.5D, p_181556_5_ + 0.5D), new Predicate() { - public boolean apply(EntityItem entity) { + public boolean test(EntityItem entity) { return entity.isEntityAlive(); } }); @@ -670,7 +670,7 @@ public class TileEntityHopper extends TileEntityLockable implements IHopper, ITi if (iinventory == null) { List list = worldIn.getEntitiesInAABBexcluding((Entity)null, new BoundingBox(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), new Predicate() { - public boolean apply(Entity entity) { + public boolean test(Entity entity) { return entity instanceof IInventory && entity.isEntityAlive(); } }); diff --git a/java/src/game/tileentity/TileEntityPiston.java b/java/src/game/tileentity/TileEntityPiston.java index 0aa6890..1baff97 100755 --- a/java/src/game/tileentity/TileEntityPiston.java +++ b/java/src/game/tileentity/TileEntityPiston.java @@ -2,7 +2,7 @@ package game.tileentity; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.entity.Entity; import game.init.BlockRegistry; diff --git a/java/src/game/util/Predicates.java b/java/src/game/util/Predicates.java new file mode 100644 index 0000000..6bdfe63 --- /dev/null +++ b/java/src/game/util/Predicates.java @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package game.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Static utility methods pertaining to {@code Predicate} instances. + * + *

All methods returns serializable predicates as long as they're given + * serializable parameters. + * + *

See the Guava User Guide article on the + * use of {@code Predicate}. + * + * @author Kevin Bourrillion + * @since 2.0 (imported from Google Collections Library) + */ + +public final class Predicates { + private Predicates() {} + + // TODO(kevinb): considering having these implement a VisitablePredicate + // interface which specifies an accept(PredicateVisitor) method. + + /** + * Returns a predicate that always evaluates to {@code true}. + */ + + public static Predicate alwaysTrue() { + return ObjectPredicate.ALWAYS_TRUE.withNarrowedType(); + } + + /** + * Returns a predicate that always evaluates to {@code false}. + */ +// +// public static Predicate alwaysFalse() { +// return ObjectPredicate.ALWAYS_FALSE.withNarrowedType(); +// } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference + * being tested is null. + */ + + public static Predicate isNull() { + return ObjectPredicate.IS_NULL.withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference + * being tested is not null. + */ + + public static Predicate notNull() { + return ObjectPredicate.NOT_NULL.withNarrowedType(); + } + + /** + * Returns a predicate that evaluates to {@code true} if the given predicate + * evaluates to {@code false}. + */ + public static Predicate not(Predicate predicate) { + return new NotPredicate(predicate); + } + + /** + * Returns a predicate that evaluates to {@code true} if each of its + * components evaluates to {@code true}. The components are evaluated in + * order, and evaluation will be "short-circuited" as soon as a false + * predicate is found. It defensively copies the iterable passed in, so future + * changes to it won't alter the behavior of this predicate. If {@code + * components} is empty, the returned predicate will always evaluate to {@code + * true}. + */ +// public static Predicate and( +// Iterable> components) { +// return new AndPredicate(defensiveCopy(components)); +// } + + /** + * Returns a predicate that evaluates to {@code true} if each of its + * components evaluates to {@code true}. The components are evaluated in + * order, and evaluation will be "short-circuited" as soon as a false + * predicate is found. It defensively copies the array passed in, so future + * changes to it won't alter the behavior of this predicate. If {@code + * components} is empty, the returned predicate will always evaluate to {@code + * true}. + */ + public static Predicate and(Predicate... components) { + return new AndPredicate(defensiveCopy(components)); + } + + /** + * Returns a predicate that evaluates to {@code true} if both of its + * components evaluate to {@code true}. The components are evaluated in + * order, and evaluation will be "short-circuited" as soon as a false + * predicate is found. + */ + public static Predicate and(Predicate first, + Predicate second) { + return new AndPredicate(Predicates.asList( + first, second)); + } + + /** + * Returns a predicate that evaluates to {@code true} if any one of its + * components evaluates to {@code true}. The components are evaluated in + * order, and evaluation will be "short-circuited" as soon as a + * true predicate is found. It defensively copies the iterable passed in, so + * future changes to it won't alter the behavior of this predicate. If {@code + * components} is empty, the returned predicate will always evaluate to {@code + * false}. + */ +// public static Predicate or( +// Iterable> components) { +// return new OrPredicate(defensiveCopy(components)); +// } + + /** + * Returns a predicate that evaluates to {@code true} if any one of its + * components evaluates to {@code true}. The components are evaluated in + * order, and evaluation will be "short-circuited" as soon as a + * true predicate is found. It defensively copies the array passed in, so + * future changes to it won't alter the behavior of this predicate. If {@code + * components} is empty, the returned predicate will always evaluate to {@code + * false}. + */ +// public static Predicate or(Predicate... components) { +// return new OrPredicate(defensiveCopy(components)); +// } + + /** + * Returns a predicate that evaluates to {@code true} if either of its + * components evaluates to {@code true}. The components are evaluated in + * order, and evaluation will be "short-circuited" as soon as a + * true predicate is found. + */ +// public static Predicate or( +// Predicate first, Predicate second) { +// return new OrPredicate(Predicates.asList( +// checkNotNull(first), checkNotNull(second))); +// } + + /** + * Returns a predicate that evaluates to {@code true} if the object being + * tested {@code equals()} the given target or both are null. + */ + public static Predicate equalTo(T target) { + return (target == null) + ? Predicates.isNull() + : new IsEqualToPredicate(target); + } + + /** + * Returns a predicate that evaluates to {@code true} if the object being + * tested is an instance of the given class. If the object being tested + * is {@code null} this predicate evaluates to {@code false}. + * + *

If you want to filter an {@code Iterable} to narrow its type, consider + * using {@link game.collect.Iterables#filter(Iterable, Class)} + * in preference. + * + *

Warning: contrary to the typical assumptions about predicates (as + * documented at {@link Predicate#apply}), the returned predicate may not be + * consistent with equals. For example, {@code + * instanceOf(ArrayList.class)} will yield different results for the two equal + * instances {@code Lists.newArrayList(1)} and {@code Arrays.asList(1)}. + */ + + public static Predicate instanceOf(Class clazz) { + return new InstanceOfPredicate(clazz); + } + + /** + * Returns a predicate that evaluates to {@code true} if the class being + * tested is assignable from the given class. The returned predicate + * does not allow null inputs. + * + * @since 10.0 + */ +// +// @Beta +// public static Predicate> assignableFrom(Class clazz) { +// return new AssignableFromPredicate(clazz); +// } + + /** + * Returns a predicate that evaluates to {@code true} if the object reference + * being tested is a member of the given collection. It does not defensively + * copy the collection passed in, so future changes to it will alter the + * behavior of the predicate. + * + *

This method can technically accept any {@code Collection}, but using + * a typed collection helps prevent bugs. This approach doesn't block any + * potential users since it is always possible to use {@code + * Predicates.in()}. + * + * @param target the collection that may contain the function input + */ + public static Predicate in(Collection target) { + return new InPredicate(target); + } + + /** + * Returns the composition of a function and a predicate. For every {@code x}, + * the generated predicate returns {@code predicate(function(x))}. + * + * @return the composition of the provided function and predicate + */ + public static Predicate compose( + Predicate predicate, Function function) { + return new CompositionPredicate(predicate, function); + } + + /** + * Returns a predicate that evaluates to {@code true} if the + * {@code CharSequence} being tested contains any match for the given + * regular expression pattern. The test used is equivalent to + * {@code Pattern.compile(pattern).matcher(arg).find()} + * + * @throws java.util.regex.PatternSyntaxException if the pattern is invalid + * @since 3.0 + */ +// +// public static Predicate containsPattern(String pattern) { +// return new ContainsPatternFromStringPredicate(pattern); +// } + + /** + * Returns a predicate that evaluates to {@code true} if the + * {@code CharSequence} being tested contains any match for the given + * regular expression pattern. The test used is equivalent to + * {@code pattern.matcher(arg).find()} + * + * @since 3.0 + */ + +// public static Predicate contains(Pattern pattern) { +// return new ContainsPatternPredicate(pattern); +// } + + // End public API, begin private implementation classes. + + // Package private for GWT serialization. + enum ObjectPredicate implements Predicate { + /** @see Predicates#alwaysTrue() */ + ALWAYS_TRUE { + @Override public boolean test(Object o) { + return true; + } +// @Override public String toString() { +// return "Predicates.alwaysTrue()"; +// } + }, + /** @see Predicates#alwaysFalse() */ +// ALWAYS_FALSE { +// @Override public boolean test(Object o) { +// return false; +// } +// @Override public String toString() { +// return "Predicates.alwaysFalse()"; +// } +// }, + /** @see Predicates#isNull() */ + IS_NULL { + @Override public boolean test(Object o) { + return o == null; + } +// @Override public String toString() { +// return "Predicates.isNull()"; +// } + }, + /** @see Predicates#notNull() */ + NOT_NULL { + @Override public boolean test(Object o) { + return o != null; + } +// @Override public String toString() { +// return "Predicates.notNull()"; +// } + }; + + // safe contravariant cast + Predicate withNarrowedType() { + return (Predicate) this; + } + } + + /** @see Predicates#not(Predicate) */ + private static class NotPredicate implements Predicate, Serializable { + final Predicate predicate; + + NotPredicate(Predicate predicate) { + this.predicate = predicate; + } + @Override + public boolean test(T t) { + return !predicate.test(t); + } +// @Override public int hashCode() { +// return ~predicate.hashCode(); +// } +// @Override public boolean equals(Object obj) { +// if (obj instanceof NotPredicate) { +// NotPredicate that = (NotPredicate) obj; +// return predicate.equals(that.predicate); +// } +// return false; +// } +// @Override public String toString() { +// return "Predicates.not(" + predicate.toString() + ")"; +// } +// private static final long serialVersionUID = 0; + } + +// private static final Joiner COMMA_JOINER = Joiner.on(','); + + /** @see Predicates#and(Iterable) */ + private static class AndPredicate implements Predicate, Serializable { + private final List> components; + + private AndPredicate(List> components) { + this.components = components; + } + @Override + public boolean test(T t) { + // Avoid using the Iterator to avoid generating garbage (issue 820). + for (int i = 0; i < components.size(); i++) { + if (!components.get(i).test(t)) { + return false; + } + } + return true; + } +// @Override public int hashCode() { +// // add a random number to avoid collisions with OrPredicate +// return components.hashCode() + 0x12472c2c; +// } +// @Override public boolean equals(Object obj) { +// if (obj instanceof AndPredicate) { +// AndPredicate that = (AndPredicate) obj; +// return components.equals(that.components); +// } +// return false; +// } +// @Override public String toString() { +// return "Predicates.and(" + COMMA_JOINER.join(components) + ")"; +// } +// private static final long serialVersionUID = 0; + } + + /** @see Predicates#or(Iterable) */ +// private static class OrPredicate implements Predicate, Serializable { +// private final List> components; +// +// private OrPredicate(List> components) { +// this.components = components; +// } +// @Override +// public boolean test(T t) { +// // Avoid using the Iterator to avoid generating garbage (issue 820). +// for (int i = 0; i < components.size(); i++) { +// if (components.get(i).test(t)) { +// return true; +// } +// } +// return false; +// } +// @Override public int hashCode() { +// // add a random number to avoid collisions with AndPredicate +// return components.hashCode() + 0x053c91cf; +// } +// @Override public boolean equals(Object obj) { +// if (obj instanceof OrPredicate) { +// OrPredicate that = (OrPredicate) obj; +// return components.equals(that.components); +// } +// return false; +// } +// @Override public String toString() { +// return "Predicates.or(" + COMMA_JOINER.join(components) + ")"; +// } +// private static final long serialVersionUID = 0; +// } + + /** @see Predicates#equalTo(Object) */ + private static class IsEqualToPredicate + implements Predicate, Serializable { + private final T target; + + private IsEqualToPredicate(T target) { + this.target = target; + } + @Override + public boolean test(T t) { + return target.equals(t); + } +// @Override public int hashCode() { +// return target.hashCode(); +// } +// @Override public boolean equals(Object obj) { +// if (obj instanceof IsEqualToPredicate) { +// IsEqualToPredicate that = (IsEqualToPredicate) obj; +// return target.equals(that.target); +// } +// return false; +// } +// @Override public String toString() { +// return "Predicates.equalTo(" + target + ")"; +// } +// private static final long serialVersionUID = 0; + } + + /** @see Predicates#instanceOf(Class) */ + + private static class InstanceOfPredicate + implements Predicate, Serializable { + private final Class clazz; + + private InstanceOfPredicate(Class clazz) { + this.clazz = clazz; + } + @Override + public boolean test(Object o) { + return clazz.isInstance(o); + } +// @Override public int hashCode() { +// return clazz.hashCode(); +// } +// @Override public boolean equals(Object obj) { +// if (obj instanceof InstanceOfPredicate) { +// InstanceOfPredicate that = (InstanceOfPredicate) obj; +// return clazz == that.clazz; +// } +// return false; +// } +// @Override public String toString() { +// return "Predicates.instanceOf(" + clazz.getName() + ")"; +// } +// private static final long serialVersionUID = 0; + } + + /** @see Predicates#assignableFrom(Class) */ + +// private static class AssignableFromPredicate +// implements Predicate>, Serializable { +// private final Class clazz; +// +// private AssignableFromPredicate(Class clazz) { +// this.clazz = checkNotNull(clazz); +// } +// @Override +// public boolean test(Class input) { +// return clazz.isAssignableFrom(input); +// } +// @Override public int hashCode() { +// return clazz.hashCode(); +// } +// @Override public boolean equals(Object obj) { +// if (obj instanceof AssignableFromPredicate) { +// AssignableFromPredicate that = (AssignableFromPredicate) obj; +// return clazz == that.clazz; +// } +// return false; +// } +// @Override public String toString() { +// return "Predicates.assignableFrom(" + clazz.getName() + ")"; +// } +// private static final long serialVersionUID = 0; +// } + + /** @see Predicates#in(Collection) */ + private static class InPredicate implements Predicate, Serializable { + private final Collection target; + + private InPredicate(Collection target) { + this.target = target; + } + + @Override + public boolean test(T t) { + try { + return target.contains(t); + } catch (NullPointerException e) { + return false; + } catch (ClassCastException e) { + return false; + } + } + +// @Override public boolean equals(Object obj) { +// if (obj instanceof InPredicate) { +// InPredicate that = (InPredicate) obj; +// return target.equals(that.target); +// } +// return false; +// } +// +// @Override public int hashCode() { +// return target.hashCode(); +// } + +// @Override public String toString() { +// return "Predicates.in(" + target + ")"; +// } +// private static final long serialVersionUID = 0; + } + + /** @see Predicates#compose(Predicate, Function) */ + private static class CompositionPredicate + implements Predicate, Serializable { + final Predicate p; + final Function f; + + private CompositionPredicate(Predicate p, Function f) { + this.p = p; + this.f = f; + } + + @Override + public boolean test(A a) { + return p.test(f.apply(a)); + } + +// @Override public boolean equals(Object obj) { +// if (obj instanceof CompositionPredicate) { +// CompositionPredicate that = (CompositionPredicate) obj; +// return f.equals(that.f) && p.equals(that.p); +// } +// return false; +// } +// +// @Override public int hashCode() { +// return f.hashCode() ^ p.hashCode(); +// } + +// @Override public String toString() { +// return p.toString() + "(" + f.toString() + ")"; +// } +// +// private static final long serialVersionUID = 0; + } + + /** @see Predicates#contains(Pattern) */ + +// private static class ContainsPatternPredicate +// implements Predicate, Serializable { +// final Pattern pattern; +// +// ContainsPatternPredicate(Pattern pattern) { +// this.pattern = checkNotNull(pattern); +// } +// +// @Override +// public boolean test(CharSequence t) { +// return pattern.matcher(t).find(); +// } +// +// @Override public int hashCode() { +// // Pattern uses Object.hashCode, so we have to reach +// // inside to build a hashCode consistent with equals. +// +// return Arrays.hashCode(new Object[] {pattern.pattern(), pattern.flags()}); +// } +// +// @Override public boolean equals(Object obj) { +// if (obj instanceof ContainsPatternPredicate) { +// ContainsPatternPredicate that = (ContainsPatternPredicate) obj; +// +// // Pattern uses Object (identity) equality, so we have to reach +// // inside to compare individual fields. +// return Objects.equal(pattern.pattern(), that.pattern.pattern()) +// && Objects.equal(pattern.flags(), that.pattern.flags()); +// } +// return false; +// } +// +//// @Override public String toString() { +//// String patternString = Objects.toStringHelper(pattern) +//// .add("pattern", pattern.pattern()) +//// .add("pattern.flags", pattern.flags()) +//// .toString(); +//// return "Predicates.contains(" + patternString + ")"; +//// } +// +// private static final long serialVersionUID = 0; +// } +// +// /** @see Predicates#containsPattern(String) */ +// +// private static class ContainsPatternFromStringPredicate +// extends ContainsPatternPredicate { +// +// ContainsPatternFromStringPredicate(String string) { +// super(Pattern.compile(string)); +// } +// +//// @Override public String toString() { +//// return "Predicates.containsPattern(" + pattern.pattern() + ")"; +//// } +// +// private static final long serialVersionUID = 0; +// } + + private static List> asList( + Predicate first, Predicate second) { + // TODO(kevinb): understand why we still get a warning despite @SafeVarargs! + return Arrays.>asList(first, second); + } + + private static List defensiveCopy(T... array) { + return defensiveCopy(Arrays.asList(array)); + } + + static List defensiveCopy(Iterable iterable) { + ArrayList list = new ArrayList(); + for (T element : iterable) { + list.add(element); + } + return list; + } +} diff --git a/java/src/game/village/Village.java b/java/src/game/village/Village.java index eac4fd1..7cde6fe 100755 --- a/java/src/game/village/Village.java +++ b/java/src/game/village/Village.java @@ -3,7 +3,7 @@ package game.village; import java.util.Iterator; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.block.Block; import game.block.BlockDoor; diff --git a/java/src/game/village/VillageCollection.java b/java/src/game/village/VillageCollection.java index e93cb48..e00e7b0 100755 --- a/java/src/game/village/VillageCollection.java +++ b/java/src/game/village/VillageCollection.java @@ -3,7 +3,7 @@ package game.village; import java.util.Iterator; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.block.Block; import game.block.BlockDoor; diff --git a/java/src/game/world/BlockPos.java b/java/src/game/world/BlockPos.java index b9d67a0..9638c4c 100755 --- a/java/src/game/world/BlockPos.java +++ b/java/src/game/world/BlockPos.java @@ -2,7 +2,7 @@ package game.world; import java.util.Iterator; -import com.google.common.collect.AbstractIterator; +import game.collect.AbstractIterator; import game.entity.Entity; diff --git a/java/src/game/world/Chunk.java b/java/src/game/world/Chunk.java index a345559..51791fb 100755 --- a/java/src/game/world/Chunk.java +++ b/java/src/game/world/Chunk.java @@ -5,8 +5,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentLinkedQueue; -import com.google.common.base.Predicate; -import com.google.common.collect.Maps; +import java.util.function.Predicate; +import game.collect.Maps; import game.biome.Biome; import game.block.Block; @@ -742,7 +742,7 @@ public class Chunk { if(!this.entities[y].isEmpty()) { for(Entity entity : this.entities[y]) { if(entity.getEntityBoundingBox().intersectsWith(bb) && entity != exclude) { - if(pred == null || pred.apply(entity)) { + if(pred == null || pred.test(entity)) { list.add(entity); } @@ -753,7 +753,7 @@ public class Chunk { entity = parts[l]; if(entity != exclude && entity.getEntityBoundingBox().intersectsWith(bb) - && (pred == null || pred.apply(entity))) { + && (pred == null || pred.test(entity))) { list.add(entity); } } @@ -772,7 +772,7 @@ public class Chunk { for(int y = sy; y <= ey; ++y) { for(T entity : this.entities[y].getByClass(clazz)) { - if(entity.getEntityBoundingBox().intersectsWith(bb) && (pred == null || pred.apply(entity))) { + if(entity.getEntityBoundingBox().intersectsWith(bb) && (pred == null || pred.test(entity))) { list.add(entity); } } diff --git a/java/src/game/world/ClassInheritanceMultiMap.java b/java/src/game/world/ClassInheritanceMultiMap.java index 93470be..5137011 100755 --- a/java/src/game/world/ClassInheritanceMultiMap.java +++ b/java/src/game/world/ClassInheritanceMultiMap.java @@ -8,9 +8,9 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import com.google.common.collect.Iterators; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Iterators; +import game.collect.Lists; +import game.collect.Maps; public class ClassInheritanceMultiMap extends AbstractSet { diff --git a/java/src/game/world/Converter.java b/java/src/game/world/Converter.java index a55d658..1677d69 100755 --- a/java/src/game/world/Converter.java +++ b/java/src/game/world/Converter.java @@ -13,7 +13,7 @@ import java.util.Map.Entry; import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.Game; import game.biome.Biome; diff --git a/java/src/game/world/EmptyChunk.java b/java/src/game/world/EmptyChunk.java index 39a3d22..14e327f 100755 --- a/java/src/game/world/EmptyChunk.java +++ b/java/src/game/world/EmptyChunk.java @@ -2,7 +2,7 @@ package game.world; import java.util.List; -import com.google.common.base.Predicate; +import java.util.function.Predicate; import game.block.Block; import game.entity.Entity; diff --git a/java/src/game/world/Explosion.java b/java/src/game/world/Explosion.java index a497eac..9a111cd 100755 --- a/java/src/game/world/Explosion.java +++ b/java/src/game/world/Explosion.java @@ -4,9 +4,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.block.Block; import game.enchantment.EnchantmentProtection; diff --git a/java/src/game/world/Facing.java b/java/src/game/world/Facing.java index 61dd7e0..67ab478 100755 --- a/java/src/game/world/Facing.java +++ b/java/src/game/world/Facing.java @@ -3,9 +3,9 @@ package game.world; import java.util.Iterator; import java.util.Map; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterators; -import com.google.common.collect.Maps; +import java.util.function.Predicate; +import game.collect.Iterators; +import game.collect.Maps; import game.properties.IStringSerializable; import game.rng.Random; @@ -411,7 +411,7 @@ public enum Facing implements IStringSerializable return this.name; } - public boolean apply(Facing p_apply_1_) + public boolean test(Facing p_apply_1_) { return p_apply_1_ != null && p_apply_1_.getAxis() == this; } @@ -481,7 +481,7 @@ public enum Facing implements IStringSerializable return aenumfacing[rand.zrange(aenumfacing.length)]; } - public boolean apply(Facing p_apply_1_) + public boolean test(Facing p_apply_1_) { return p_apply_1_ != null && p_apply_1_.getAxis().getPlane() == this; } diff --git a/java/src/game/world/Region.java b/java/src/game/world/Region.java index 3ce8897..6688fd4 100755 --- a/java/src/game/world/Region.java +++ b/java/src/game/world/Region.java @@ -17,8 +17,8 @@ import java.util.Map; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.Block; import game.entity.Entity; diff --git a/java/src/game/world/Spawner.java b/java/src/game/world/Spawner.java index 01d6934..e7dee6e 100755 --- a/java/src/game/world/Spawner.java +++ b/java/src/game/world/Spawner.java @@ -2,7 +2,7 @@ package game.world; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.biome.Biome; import game.block.Block; diff --git a/java/src/game/world/State.java b/java/src/game/world/State.java index 13b8121..53cbd2a 100755 --- a/java/src/game/world/State.java +++ b/java/src/game/world/State.java @@ -6,13 +6,13 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import com.google.common.base.Function; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableTable; -import com.google.common.collect.Iterables; -import com.google.common.collect.Maps; -import com.google.common.collect.Table; +import java.util.function.Function; +import game.collect.ImmutableMap; +import game.collect.ImmutableTable; +import game.collect.Iterables; +import game.collect.Maps; +import game.collect.StandardTable; +import game.collect.Table; import game.block.Block; import game.init.BlockRegistry; @@ -123,7 +123,7 @@ public class State { throw new IllegalStateException(); } else { - Table table = HashBasedTable.create(); + Table table = new StandardTable(); for(IProperty prop : this.properties.keySet()) { for(Comparable value : prop.getAllowedValues()) { if(value != this.properties.get(prop)) { diff --git a/java/src/game/world/Weather.java b/java/src/game/world/Weather.java index 2a58c47..6d6b764 100755 --- a/java/src/game/world/Weather.java +++ b/java/src/game/world/Weather.java @@ -2,7 +2,7 @@ package game.world; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.rng.Random; import game.rng.RngItem; diff --git a/java/src/game/world/World.java b/java/src/game/world/World.java index 50980c3..56a2141 100755 --- a/java/src/game/world/World.java +++ b/java/src/game/world/World.java @@ -5,9 +5,9 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import com.google.common.base.Predicate; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import java.util.function.Predicate; +import game.collect.Lists; +import game.collect.Sets; import game.biome.Biome; import game.block.Block; @@ -557,7 +557,7 @@ public abstract class World implements IWorldAccess { break; List ents = this.getEntitiesInAABBexcluding(entity, new BoundingBox(target, target.add(1, 1, 1)), new Predicate() { - public boolean apply(Entity entity) { + public boolean test(Entity entity) { return entity.canBeCollidedWith(); } }); diff --git a/java/src/game/world/WorldClient.java b/java/src/game/world/WorldClient.java index 1e783e7..3cbf887 100755 --- a/java/src/game/world/WorldClient.java +++ b/java/src/game/world/WorldClient.java @@ -3,8 +3,8 @@ package game.world; import java.util.List; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import game.collect.Lists; +import game.collect.Sets; import game.Game; import game.audio.MovingSoundMinecart; diff --git a/java/src/game/world/WorldServer.java b/java/src/game/world/WorldServer.java index d424199..67dc997 100755 --- a/java/src/game/world/WorldServer.java +++ b/java/src/game/world/WorldServer.java @@ -12,10 +12,10 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; -import com.google.common.base.Predicate; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import java.util.function.Predicate; +import game.collect.Lists; +import game.collect.Maps; +import game.collect.Sets; import game.Server; import game.biome.Biome; @@ -614,7 +614,7 @@ public final class WorldServer extends World { BoundingBox axisalignedbb = (new BoundingBox(blockpos, new BlockPos(blockpos.getX(), World.HEIGHT, blockpos.getZ()))).expand(3.0D, 3.0D, 3.0D); List list = this.getEntitiesWithinAABB(EntityLiving.class, axisalignedbb, new Predicate() { - public boolean apply(EntityLiving p_apply_1_) { + public boolean test(EntityLiving p_apply_1_) { return p_apply_1_ != null && p_apply_1_.isEntityAlive() && WorldServer.this.canSeeSky(p_apply_1_.getPosition()); } }); diff --git a/java/src/game/worldgen/BiomeGenLayered.java b/java/src/game/worldgen/BiomeGenLayered.java index 3887249..5980b62 100755 --- a/java/src/game/worldgen/BiomeGenLayered.java +++ b/java/src/game/worldgen/BiomeGenLayered.java @@ -3,7 +3,7 @@ package game.worldgen; import java.util.List; import java.util.Set; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.biome.Biome; import game.world.BlockPos; diff --git a/java/src/game/worldgen/GeneratorDebug.java b/java/src/game/worldgen/GeneratorDebug.java index f88c9ce..4168d4f 100755 --- a/java/src/game/worldgen/GeneratorDebug.java +++ b/java/src/game/worldgen/GeneratorDebug.java @@ -2,7 +2,7 @@ package game.worldgen; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.block.Block; import game.init.BlockRegistry; diff --git a/java/src/game/worldgen/feature/WorldGenDesertWells.java b/java/src/game/worldgen/feature/WorldGenDesertWells.java index efd3a36..7dd67f9 100755 --- a/java/src/game/worldgen/feature/WorldGenDesertWells.java +++ b/java/src/game/worldgen/feature/WorldGenDesertWells.java @@ -1,6 +1,6 @@ package game.worldgen.feature; -import com.google.common.base.Predicates; +import game.util.Predicates; import game.block.BlockSand; import game.block.BlockSlab; @@ -27,7 +27,7 @@ public class WorldGenDesertWells extends FeatureGenerator position = position.down(); } - if (!field_175913_a.apply(worldIn.getState(position))) + if (!field_175913_a.test(worldIn.getState(position))) { return false; } diff --git a/java/src/game/worldgen/layer/IntCache.java b/java/src/game/worldgen/layer/IntCache.java index c78af63..38a4766 100755 --- a/java/src/game/worldgen/layer/IntCache.java +++ b/java/src/game/worldgen/layer/IntCache.java @@ -2,7 +2,7 @@ package game.worldgen.layer; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; public class IntCache { diff --git a/java/src/game/worldgen/structure/MapGenStructure.java b/java/src/game/worldgen/structure/MapGenStructure.java index 05a3bf5..7712648 100755 --- a/java/src/game/worldgen/structure/MapGenStructure.java +++ b/java/src/game/worldgen/structure/MapGenStructure.java @@ -3,7 +3,7 @@ package game.worldgen.structure; import java.util.Iterator; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.nbt.NBTBase; import game.nbt.NBTTagCompound; diff --git a/java/src/game/worldgen/structure/MapGenStructureIO.java b/java/src/game/worldgen/structure/MapGenStructureIO.java index cfb3b66..06a3f4e 100755 --- a/java/src/game/worldgen/structure/MapGenStructureIO.java +++ b/java/src/game/worldgen/structure/MapGenStructureIO.java @@ -2,7 +2,7 @@ package game.worldgen.structure; import java.util.Map; -import com.google.common.collect.Maps; +import game.collect.Maps; import game.log.Log; import game.nbt.NBTTagCompound; diff --git a/java/src/game/worldgen/structure/MapGenVillage.java b/java/src/game/worldgen/structure/MapGenVillage.java index 80fb684..fef7738 100755 --- a/java/src/game/worldgen/structure/MapGenVillage.java +++ b/java/src/game/worldgen/structure/MapGenVillage.java @@ -3,7 +3,7 @@ package game.worldgen.structure; import java.util.List; import java.util.Set; -import com.google.common.collect.Sets; +import game.collect.Sets; import game.biome.Biome; import game.nbt.NBTTagCompound; diff --git a/java/src/game/worldgen/structure/StructureBridge.java b/java/src/game/worldgen/structure/StructureBridge.java index bc9a3e0..1dcba01 100755 --- a/java/src/game/worldgen/structure/StructureBridge.java +++ b/java/src/game/worldgen/structure/StructureBridge.java @@ -2,7 +2,7 @@ package game.worldgen.structure; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.init.Blocks; import game.nbt.NBTTagCompound; diff --git a/java/src/game/worldgen/structure/StructureStronghold.java b/java/src/game/worldgen/structure/StructureStronghold.java index 46d9fb9..e76b7ac 100755 --- a/java/src/game/worldgen/structure/StructureStronghold.java +++ b/java/src/game/worldgen/structure/StructureStronghold.java @@ -3,8 +3,8 @@ package game.worldgen.structure; import java.util.List; import java.util.Map; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import game.collect.Lists; +import game.collect.Maps; import game.block.BlockSlab; import game.block.BlockStoneBrick; diff --git a/java/src/game/worldgen/structure/StructureVillage.java b/java/src/game/worldgen/structure/StructureVillage.java index 9749513..1188590 100755 --- a/java/src/game/worldgen/structure/StructureVillage.java +++ b/java/src/game/worldgen/structure/StructureVillage.java @@ -3,7 +3,7 @@ package game.worldgen.structure; import java.util.Iterator; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.biome.Biome; import game.block.Block; diff --git a/java/src/game/worldgen/tree/WorldGenBigTree.java b/java/src/game/worldgen/tree/WorldGenBigTree.java index 4cc1f73..fba563a 100755 --- a/java/src/game/worldgen/tree/WorldGenBigTree.java +++ b/java/src/game/worldgen/tree/WorldGenBigTree.java @@ -2,7 +2,7 @@ package game.worldgen.tree; import java.util.List; -import com.google.common.collect.Lists; +import game.collect.Lists; import game.block.Block; import game.block.BlockLeaves;